7 /* alias data base lookups
11 /* int deliver_alias(state, usr_attr, name, statusp)
13 /* USER_ATTR usr_attr;
17 /* deliver_alias() looks up the expansion of the recipient in
18 /* the global alias database and delivers the message to the
19 /* listed destinations. The result is zero when no alias was found
20 /* or when the message should be delivered to the user instead.
22 /* deliver_alias() has wired-in knowledge about a few reserved
25 /* When no alias is found for the local \fIpostmaster\fR or
26 /* \fImailer-daemon\fR a warning is issued and the message
29 /* When an alias exists for recipient \fIname\fR, and an alias
30 /* exists for \fIowner-name\fR, the sender address is changed
31 /* to \fIowner-name\fR, and the owner delivery attribute is
32 /* set accordingly. This feature is disabled with
33 /* "owner_request_special = no".
37 /* Attributes that specify the message, recipient and more.
38 /* Expansion type (alias, include, .forward).
39 /* A table with the results from expanding aliases or lists.
40 /* A table with delivered-to: addresses taken from the message.
42 /* User attributes (rights, environment).
44 /* The alias to be looked up.
46 /* Delivery status. See below.
48 /* Fatal errors: out of memory. The delivery status is non-zero
49 /* when delivery should be tried again.
53 /* The Secure Mailer license must be distributed with this software.
56 /* IBM T.J. Watson Research
58 /* Yorktown Heights, NY 10598, USA
69 #ifdef STRCASECMP_IN_STRINGS_H
73 /* Utility library. */
79 #include <stringops.h>
86 #include <mail_params.h>
91 #include <canon_addr.h>
96 /* Application-specific. */
100 /* Application-specific. */
105 /* dict_owner - find out alias database owner */
107 static uid_t
dict_owner(char *table
)
109 const char *myname
= "dict_owner";
114 * This code sits here for now, but we may want to move it to the library
117 if ((dict
= dict_handle(table
)) == 0)
118 msg_panic("%s: can't find dictionary: %s", myname
, table
);
119 if (dict
->stat_fd
< 0)
121 if (fstat(dict
->stat_fd
, &st
) < 0)
122 msg_fatal("%s: fstat dictionary %s: %m", myname
, table
);
126 /* deliver_alias - expand alias file entry */
128 int deliver_alias(LOCAL_STATE state
, USER_ATTR usr_attr
,
129 char *name
, int *statusp
)
131 const char *myname
= "deliver_alias";
132 const char *alias_result
;
133 char *saved_alias_result
;
137 struct mypasswd
*alias_pwd
;
138 VSTRING
*canon_owner
;
140 const char *owner_rhs
; /* owner alias, RHS */
145 const char *dsn_orcpt
;
148 * Make verbose logging easier to understand.
152 MSG_LOG_STATE(myname
, state
);
155 * DUPLICATE/LOOP ELIMINATION
157 * We cannot do duplicate elimination here. Sendmail compatibility requires
158 * that we allow multiple deliveries to the same alias, even recursively!
159 * For example, we must deliver to mailbox any messags that are addressed
160 * to the alias of a user that lists that same alias in her own .forward
161 * file. Yuck! This is just an example of some really perverse semantics
162 * that people will expect Postfix to implement just like sendmail.
164 * We can recognize one special case: when an alias includes its own name,
165 * deliver to the user instead, just like sendmail. Otherwise, we just
166 * bail out when nesting reaches some unreasonable depth, and blame it on
167 * a possible alias loop.
169 if (state
.msg_attr
.exp_from
!= 0
170 && strcasecmp(state
.msg_attr
.exp_from
, name
) == 0)
172 if (state
.level
> 100) {
173 msg_warn("alias database loop for %s", name
);
174 dsb_simple(state
.msg_attr
.why
, "5.4.6",
175 "alias database loop for %s", name
);
176 *statusp
= bounce_append(BOUNCE_FLAGS(state
.request
),
177 BOUNCE_ATTR(state
.msg_attr
));
180 state
.msg_attr
.exp_from
= name
;
183 * There are a bunch of roles that we're trying to keep track of.
185 * First, there's the issue of whose rights should be used when delivering
186 * to "|command" or to /file/name. With alias databases, the rights are
187 * those of who owns the alias, i.e. the database owner. With aliases
188 * owned by root, a default user is used instead. When an alias with
189 * default rights references an include file owned by an ordinary user,
190 * we must use the rights of the include file owner, otherwise the
191 * include file owner could take control of the default account.
193 * Secondly, there's the question of who to notify of delivery problems.
194 * With aliases that have an owner- alias, the latter is used to set the
195 * sender and owner attributes. Otherwise, the owner attribute is reset
196 * (the alias is globally visible and could be sent to by anyone).
198 for (cpp
= alias_maps
->argv
->argv
; *cpp
; cpp
++) {
199 if ((dict
= dict_handle(*cpp
)) == 0)
200 msg_panic("%s: dictionary not found: %s", myname
, *cpp
);
201 if ((alias_result
= dict_get(dict
, name
)) != 0) {
203 msg_info("%s: %s: %s = %s", myname
, *cpp
, name
, alias_result
);
206 * Don't expand a verify-only request.
208 if (state
.request
->flags
& DEL_REQ_FLAG_MTA_VRFY
) {
209 dsb_simple(state
.msg_attr
.why
, "2.0.0",
210 "aliased to %s", alias_result
);
211 *statusp
= sent(BOUNCE_FLAGS(state
.request
),
212 SENT_ATTR(state
.msg_attr
));
219 * Update the expansion type attribute, so we can decide if
220 * deliveries to |command and /file/name are allowed at all.
222 state
.msg_attr
.exp_type
= EXPAND_TYPE_ALIAS
;
227 * What rights to use for |command and /file/name deliveries? The
228 * command and file code will use default rights when the alias
229 * database is owned by root, otherwise it will use the rights of
230 * the alias database owner.
232 if ((alias_uid
= dict_owner(*cpp
)) == 0) {
234 RESET_USER_ATTR(usr_attr
, state
.level
);
236 if ((alias_pwd
= mypwuid(alias_uid
)) == 0) {
237 msg_warn("cannot find alias database owner for %s", *cpp
);
238 dsb_simple(state
.msg_attr
.why
, "4.3.0",
239 "cannot find alias database owner");
240 *statusp
= defer_append(BOUNCE_FLAGS(state
.request
),
241 BOUNCE_ATTR(state
.msg_attr
));
244 SET_USER_ATTR(usr_attr
, alias_pwd
, state
.level
);
248 * WHERE TO REPORT DELIVERY PROBLEMS.
250 * Use the owner- alias if one is specified, otherwise reset the
251 * owner attribute and use the include file ownership if we can.
252 * Save the dict_lookup() result before something clobbers it.
254 * Don't match aliases that are based on regexps.
256 #define OWNER_ASSIGN(own) \
257 (own = (var_ownreq_special == 0 ? 0 : \
258 concatenate("owner-", name, (char *) 0)))
260 saved_alias_result
= mystrdup(alias_result
);
261 if (OWNER_ASSIGN(owner
) != 0
262 && (owner_rhs
= maps_find(alias_maps
, owner
, DICT_FLAG_NONE
)) != 0) {
263 canon_owner
= canon_addr_internal(vstring_alloc(10),
264 var_exp_own_alias
? owner_rhs
: owner
);
265 /* Set envelope sender and owner attribute. */
266 SET_OWNER_ATTR(state
.msg_attr
, STR(canon_owner
), state
.level
);
269 /* Note: this does not reset the envelope sender. */
270 RESET_OWNER_ATTR(state
.msg_attr
, state
.level
);
274 * EXTERNAL LOOP CONTROL
276 * Set the delivered message attribute to the recipient, so that
277 * this message will list the correct forwarding address.
279 if (var_frozen_delivered
== 0)
280 state
.msg_attr
.delivered
= state
.msg_attr
.rcpt
.address
;
286 if (dict_errno
!= 0) {
287 dsb_simple(state
.msg_attr
.why
, "4.3.0",
288 "alias database unavailable");
289 *statusp
= defer_append(BOUNCE_FLAGS(state
.request
),
290 BOUNCE_ATTR(state
.msg_attr
));
296 * When delivering to a mailing list (i.e. the envelope sender
297 * is replaced) the ENVID, NOTIFY, RET, and ORCPT parameters
298 * which accompany the redistributed message MUST NOT be
299 * derived from those of the original message.
301 * When delivering to an alias (i.e. the envelope sender is not
302 * replaced) any ENVID, RET, or ORCPT parameters are
303 * propagated to all forwarding addresses associated with
304 * that alias. The NOTIFY parameter is propagated to the
305 * forwarding addresses, except that any SUCCESS keyword is
308 #define DSN_SAVE_UPDATE(saved, old, new) do { \
313 DSN_SAVE_UPDATE(dsn_notify
, state
.msg_attr
.rcpt
.dsn_notify
,
314 dsn_notify
== DSN_NOTIFY_SUCCESS
?
316 dsn_notify
& ~DSN_NOTIFY_SUCCESS
);
317 if (canon_owner
!= 0) {
318 DSN_SAVE_UPDATE(dsn_envid
, state
.msg_attr
.dsn_envid
, "");
319 DSN_SAVE_UPDATE(dsn_ret
, state
.msg_attr
.dsn_ret
, 0);
320 DSN_SAVE_UPDATE(dsn_orcpt
, state
.msg_attr
.rcpt
.dsn_orcpt
, "");
321 state
.msg_attr
.rcpt
.orig_addr
= "";
324 deliver_token_string(state
, usr_attr
, saved_alias_result
,
327 if (var_ownreq_special
328 && strncmp("owner-", state
.msg_attr
.sender
, 6) != 0
330 msg_warn("mailing list \"%s\" needs an \"owner-%s\" alias",
333 if (alias_count
< 1) {
334 msg_warn("no recipient in alias lookup result for %s", name
);
335 dsb_simple(state
.msg_attr
.why
, "4.3.0",
336 "alias database unavailable");
337 *statusp
= defer_append(BOUNCE_FLAGS(state
.request
),
338 BOUNCE_ATTR(state
.msg_attr
));
344 * When delivering to a mailing list (i.e. the envelope
345 * sender address is replaced) and NOTIFY=SUCCESS was
346 * specified, report a DSN of "delivered".
348 * When delivering to an alias (i.e. the envelope sender
349 * address is not replaced) and NOTIFY=SUCCESS was
350 * specified, report a DSN of "expanded".
352 if (dsn_notify
& DSN_NOTIFY_SUCCESS
) {
353 state
.msg_attr
.rcpt
.dsn_notify
= dsn_notify
;
354 if (canon_owner
!= 0) {
355 state
.msg_attr
.dsn_envid
= dsn_envid
;
356 state
.msg_attr
.dsn_ret
= dsn_ret
;
357 state
.msg_attr
.rcpt
.dsn_orcpt
= dsn_orcpt
;
359 dsb_update(state
.msg_attr
.why
, "2.0.0", canon_owner
?
360 "delivered" : "expanded",
361 DSB_SKIP_RMTA
, DSB_SKIP_REPLY
,
363 (void) trace_append(BOUNCE_FLAG_NONE
,
364 SENT_ATTR(state
.msg_attr
));
368 myfree(saved_alias_result
);
372 vstring_free(canon_owner
);
379 * If the alias database was inaccessible for some reason, defer
380 * further delivery for the current top-level recipient.
382 if (dict_errno
!= 0) {
383 dsb_simple(state
.msg_attr
.why
, "4.3.0",
384 "alias database unavailable");
385 *statusp
= defer_append(BOUNCE_FLAGS(state
.request
),
386 BOUNCE_ATTR(state
.msg_attr
));
390 msg_info("%s: %s: %s not found", myname
, *cpp
, name
);
395 * Try delivery to a local user instead.