1 /*++
2 /* NAME
3 /* alias 3
4 /* SUMMARY
5 /* alias data base lookups
6 /* SYNOPSIS
7 /* #include "local.h"
8 /*
9 /* int deliver_alias(state, usr_attr, name, statusp)
10 /* LOCAL_STATE state;
11 /* USER_ATTR usr_attr;
12 /* char *name;
13 /* int *statusp;
14 /* DESCRIPTION
15 /* deliver_alias() looks up the expansion of the recipient in
16 /* the global alias database and delivers the message to the
17 /* listed destinations. The result is zero when no alias was found
18 /* or when the message should be delivered to the user instead.
19 /*
20 /* deliver_alias() has wired-in knowledge about a few reserved
21 /* recipient names.
22 /* .IP \(bu
23 /* When no alias is found for the local \fIpostmaster\fR or
24 /* \fImailer-daemon\fR a warning is issued and the message
25 /* is discarded.
26 /* .IP \(bu
27 /* When an alias exists for recipient \fIname\fR, and an alias
28 /* exists for \fIowner-name\fR, the sender address is changed
29 /* to \fIowner-name\fR, and the owner delivery attribute is
30 /* set accordingly. This feature is disabled with
31 /* "owner_request_special = no".
32 /* .PP
33 /* Arguments:
34 /* .IP state
35 /* Attributes that specify the message, recipient and more.
36 /* Expansion type (alias, include, .forward).
37 /* A table with the results from expanding aliases or lists.
38 /* A table with delivered-to: addresses taken from the message.
39 /* .IP usr_attr
40 /* User attributes (rights, environment).
41 /* .IP name
42 /* The alias to be looked up.
43 /* .IP statusp
44 /* Delivery status. See below.
45 /* DIAGNOSTICS
46 /* Fatal errors: out of memory. The delivery status is non-zero
47 /* when delivery should be tried again.
48 /* LICENSE
49 /* .ad
50 /* .fi
51 /* The Secure Mailer license must be distributed with this software.
52 /* AUTHOR(S)
53 /* Wietse Venema
54 /* IBM T.J. Watson Research
55 /* P.O. Box 704
56 /* Yorktown Heights, NY 10598, USA
57 /*--*/
58
59 /* System library. */
60
61 #include <sys_defs.h>
62 #include <sys/stat.h>
63 #include <unistd.h>
64 #include <string.h>
65 #include <fcntl.h>
66 #include <errno.h>
67
68 /* Utility library. */
69
70 #include <msg.h>
71 #include <htable.h>
72 #include <dict.h>
73 #include <argv.h>
74 #include <stringops.h>
75 #include <mymalloc.h>
76 #include <vstring.h>
77 #include <vstream.h>
78
79 /* Global library. */
80
81 #include <mail_params.h>
82 #include <defer.h>
83 #include <maps.h>
84 #include <bounce.h>
85 #include <mypwd.h>
86 #include <canon_addr.h>
87 #include <sent.h>
88 #include <trace.h>
89 #include <dsn_mask.h>
90
91 /* Application-specific. */
92
93 #include "local.h"
94
95 /* Application-specific. */
96
97 #define NO 0
98 #define YES 1
99
100 /* deliver_alias - expand alias file entry */
101
deliver_alias(LOCAL_STATE state,USER_ATTR usr_attr,char * name,int * statusp)102 int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
103 char *name, int *statusp)
104 {
105 const char *myname = "deliver_alias";
106 const char *alias_result;
107 char *saved_alias_result;
108 char *owner;
109 char **cpp;
110 struct mypasswd *alias_pwd;
111 VSTRING *canon_owner;
112 DICT *dict;
113 const char *owner_rhs; /* owner alias, RHS */
114 int alias_count;
115 int dsn_notify;
116 char *dsn_envid;
117 int dsn_ret;
118 const char *dsn_orcpt;
119
120 /*
121 * Make verbose logging easier to understand.
122 */
123 state.level++;
124 if (msg_verbose)
125 MSG_LOG_STATE(myname, state);
126
127 /*
128 * DUPLICATE/LOOP ELIMINATION
129 *
130 * We cannot do duplicate elimination here. Sendmail compatibility requires
131 * that we allow multiple deliveries to the same alias, even recursively!
132 * For example, we must deliver to mailbox any messages that are addressed
133 * to the alias of a user that lists that same alias in her own .forward
134 * file. Yuck! This is just an example of some really perverse semantics
135 * that people will expect Postfix to implement just like sendmail.
136 *
137 * We can recognize one special case: when an alias includes its own name,
138 * deliver to the user instead, just like sendmail. Otherwise, we just
139 * bail out when nesting reaches some unreasonable depth, and blame it on
140 * a possible alias loop.
141 */
142 if (state.msg_attr.exp_from != 0
143 && strcasecmp_utf8(state.msg_attr.exp_from, name) == 0)
144 return (NO);
145 if (state.level > 100) {
146 msg_warn("alias database loop for %s", name);
147 dsb_simple(state.msg_attr.why, "5.4.6",
148 "alias database loop for %s", name);
149 *statusp = bounce_append(BOUNCE_FLAGS(state.request),
150 BOUNCE_ATTR(state.msg_attr));
151 return (YES);
152 }
153 state.msg_attr.exp_from = name;
154
155 /*
156 * There are a bunch of roles that we're trying to keep track of.
157 *
158 * First, there's the issue of whose rights should be used when delivering
159 * to "|command" or to /file/name. With alias databases, the rights are
160 * those of who owns the alias, i.e. the database owner. With aliases
161 * owned by root, a default user is used instead. When an alias with
162 * default rights references an include file owned by an ordinary user,
163 * we must use the rights of the include file owner, otherwise the
164 * include file owner could take control of the default account.
165 *
166 * Secondly, there's the question of who to notify of delivery problems.
167 * With aliases that have an owner- alias, the latter is used to set the
168 * sender and owner attributes. Otherwise, the owner attribute is reset
169 * (the alias is globally visible and could be sent to by anyone).
170 */
171 for (cpp = alias_maps->argv->argv; *cpp; cpp++) {
172 if ((dict = dict_handle(*cpp)) == 0)
173 msg_panic("%s: dictionary not found: %s", myname, *cpp);
174 if ((alias_result = dict_get(dict, name)) != 0) {
175 if (msg_verbose)
176 msg_info("%s: %s: %s = %s", myname, *cpp, name, alias_result);
177
178 /*
179 * Don't expand a verify-only request.
180 */
181 if (state.request->flags & DEL_REQ_FLAG_MTA_VRFY) {
182 dsb_simple(state.msg_attr.why, "2.0.0",
183 "aliased to %s", alias_result);
184 *statusp = sent(BOUNCE_FLAGS(state.request),
185 SENT_ATTR(state.msg_attr));
186 return (YES);
187 }
188
189 /*
190 * DELIVERY POLICY
191 *
192 * Update the expansion type attribute, so we can decide if
193 * deliveries to |command and /file/name are allowed at all.
194 */
195 state.msg_attr.exp_type = EXPAND_TYPE_ALIAS;
196
197 /*
198 * DELIVERY RIGHTS
199 *
200 * What rights to use for |command and /file/name deliveries? The
201 * command and file code will use default rights when the alias
202 * database is owned by root, otherwise it will use the rights of
203 * the alias database owner.
204 */
205 if (dict->owner.status == DICT_OWNER_TRUSTED) {
206 alias_pwd = 0;
207 RESET_USER_ATTR(usr_attr, state.level);
208 } else {
209 if (dict->owner.status == DICT_OWNER_UNKNOWN) {
210 msg_warn("%s: no owner UID for alias database %s",
211 myname, *cpp);
212 dsb_simple(state.msg_attr.why, "4.3.0",
213 "mail system configuration error");
214 *statusp = defer_append(BOUNCE_FLAGS(state.request),
215 BOUNCE_ATTR(state.msg_attr));
216 return (YES);
217 }
218 if ((errno = mypwuid_err(dict->owner.uid, &alias_pwd)) != 0
219 || alias_pwd == 0) {
220 msg_warn(errno ?
221 "cannot find alias database owner for %s: %m" :
222 "cannot find alias database owner for %s", *cpp);
223 dsb_simple(state.msg_attr.why, "4.3.0",
224 "cannot find alias database owner");
225 *statusp = defer_append(BOUNCE_FLAGS(state.request),
226 BOUNCE_ATTR(state.msg_attr));
227 return (YES);
228 }
229 SET_USER_ATTR(usr_attr, alias_pwd, state.level);
230 }
231
232 /*
233 * WHERE TO REPORT DELIVERY PROBLEMS.
234 *
235 * Use the owner- alias if one is specified, otherwise reset the
236 * owner attribute and use the include file ownership if we can.
237 * Save the dict_lookup() result before something clobbers it.
238 *
239 * Don't match aliases that are based on regexps.
240 */
241 #define OWNER_ASSIGN(own) \
242 (own = (var_ownreq_special == 0 ? 0 : \
243 concatenate("owner-", name, (char *) 0)))
244
245 saved_alias_result = mystrdup(alias_result);
246 if (OWNER_ASSIGN(owner) != 0
247 && (owner_rhs = maps_find(alias_maps, owner, DICT_FLAG_NONE)) != 0) {
248 canon_owner = canon_addr_internal(vstring_alloc(10),
249 var_exp_own_alias ? owner_rhs : owner);
250 /* Set envelope sender and owner attribute. */
251 SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
252 } else {
253 canon_owner = 0;
254 /* Note: this does not reset the envelope sender. */
255 if (var_reset_owner_attr)
256 RESET_OWNER_ATTR(state.msg_attr, state.level);
257 }
258
259 /*
260 * EXTERNAL LOOP CONTROL
261 *
262 * Set the delivered message attribute to the recipient, so that
263 * this message will list the correct forwarding address.
264 */
265 if (var_frozen_delivered == 0)
266 state.msg_attr.delivered = state.msg_attr.rcpt.address;
267
268 /*
269 * Deliver.
270 */
271 alias_count = 0;
272 if (owner != 0 && alias_maps->error != 0) {
273 dsb_simple(state.msg_attr.why, "4.3.0",
274 "alias database unavailable");
275 *statusp = defer_append(BOUNCE_FLAGS(state.request),
276 BOUNCE_ATTR(state.msg_attr));
277 } else {
278
279 /*
280 * XXX DSN
281 *
282 * When delivering to a mailing list (i.e. the envelope sender
283 * is replaced) the ENVID, NOTIFY, RET, and ORCPT parameters
284 * which accompany the redistributed message MUST NOT be
285 * derived from those of the original message.
286 *
287 * When delivering to an alias (i.e. the envelope sender is not
288 * replaced) any ENVID, RET, or ORCPT parameters are
289 * propagated to all forwarding addresses associated with
290 * that alias. The NOTIFY parameter is propagated to the
291 * forwarding addresses, except that any SUCCESS keyword is
292 * removed.
293 */
294 #define DSN_SAVE_UPDATE(saved, old, new) do { \
295 saved = old; \
296 old = new; \
297 } while (0)
298
299 DSN_SAVE_UPDATE(dsn_notify, state.msg_attr.rcpt.dsn_notify,
300 dsn_notify == DSN_NOTIFY_SUCCESS ?
301 DSN_NOTIFY_NEVER :
302 dsn_notify & ~DSN_NOTIFY_SUCCESS);
303 if (canon_owner != 0) {
304 DSN_SAVE_UPDATE(dsn_envid, state.msg_attr.dsn_envid, "");
305 DSN_SAVE_UPDATE(dsn_ret, state.msg_attr.dsn_ret, 0);
306 DSN_SAVE_UPDATE(dsn_orcpt, state.msg_attr.rcpt.dsn_orcpt, "");
307 state.msg_attr.rcpt.orig_addr = "";
308 }
309 *statusp =
310 deliver_token_string(state, usr_attr, saved_alias_result,
311 &alias_count);
312 #if 0
313 if (var_ownreq_special
314 && strncmp("owner-", state.msg_attr.sender, 6) != 0
315 && alias_count > 10)
316 msg_warn("mailing list \"%s\" needs an \"owner-%s\" alias",
317 name, name);
318 #endif
319 if (alias_count < 1) {
320 msg_warn("no recipient in alias lookup result for %s", name);
321 dsb_simple(state.msg_attr.why, "4.3.0",
322 "alias database unavailable");
323 *statusp = defer_append(BOUNCE_FLAGS(state.request),
324 BOUNCE_ATTR(state.msg_attr));
325 } else {
326
327 /*
328 * XXX DSN
329 *
330 * When delivering to a mailing list (i.e. the envelope
331 * sender address is replaced) and NOTIFY=SUCCESS was
332 * specified, report a DSN of "delivered".
333 *
334 * When delivering to an alias (i.e. the envelope sender
335 * address is not replaced) and NOTIFY=SUCCESS was
336 * specified, report a DSN of "expanded".
337 */
338 if (dsn_notify & DSN_NOTIFY_SUCCESS) {
339 state.msg_attr.rcpt.dsn_notify = dsn_notify;
340 if (canon_owner != 0) {
341 state.msg_attr.dsn_envid = dsn_envid;
342 state.msg_attr.dsn_ret = dsn_ret;
343 state.msg_attr.rcpt.dsn_orcpt = dsn_orcpt;
344 }
345 dsb_update(state.msg_attr.why, "2.0.0", canon_owner ?
346 "delivered" : "expanded",
347 DSB_SKIP_RMTA, DSB_SKIP_REPLY,
348 "alias expanded");
349 (void) trace_append(BOUNCE_FLAG_NONE,
350 SENT_ATTR(state.msg_attr));
351 }
352 }
353 }
354 myfree(saved_alias_result);
355 if (owner)
356 myfree(owner);
357 if (canon_owner)
358 vstring_free(canon_owner);
359 if (alias_pwd)
360 mypwfree(alias_pwd);
361 return (YES);
362 }
363
364 /*
365 * If the alias database was inaccessible for some reason, defer
366 * further delivery for the current top-level recipient.
367 */
368 if (alias_result == 0 && dict->error != 0) {
369 msg_warn("%s:%s: lookup of '%s' failed",
370 dict->type, dict->name, name);
371 dsb_simple(state.msg_attr.why, "4.3.0",
372 "alias database unavailable");
373 *statusp = defer_append(BOUNCE_FLAGS(state.request),
374 BOUNCE_ATTR(state.msg_attr));
375 return (YES);
376 } else {
377 if (msg_verbose)
378 msg_info("%s: %s: %s not found", myname, *cpp, name);
379 }
380 }
381
382 /*
383 * Try delivery to a local user instead.
384 */
385 return (NO);
386 }
387