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