1 /* $NetBSD: alias.c,v 1.1.1.2 2011/03/02 19:32:19 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 69 #ifdef STRCASECMP_IN_STRINGS_H 70 #include <strings.h> 71 #endif 72 73 /* Utility library. */ 74 75 #include <msg.h> 76 #include <htable.h> 77 #include <dict.h> 78 #include <argv.h> 79 #include <stringops.h> 80 #include <mymalloc.h> 81 #include <vstring.h> 82 #include <vstream.h> 83 84 /* Global library. */ 85 86 #include <mail_params.h> 87 #include <defer.h> 88 #include <maps.h> 89 #include <bounce.h> 90 #include <mypwd.h> 91 #include <canon_addr.h> 92 #include <sent.h> 93 #include <trace.h> 94 #include <dsn_mask.h> 95 96 /* Application-specific. */ 97 98 #include "local.h" 99 100 /* Application-specific. */ 101 102 #define NO 0 103 #define YES 1 104 105 /* dict_owner - find out alias database owner */ 106 107 static uid_t dict_owner(char *table) 108 { 109 const char *myname = "dict_owner"; 110 DICT *dict; 111 struct stat st; 112 113 /* 114 * This code sits here for now, but we may want to move it to the library 115 * some time. 116 */ 117 if ((dict = dict_handle(table)) == 0) 118 msg_panic("%s: can't find dictionary: %s", myname, table); 119 if (dict->stat_fd < 0) 120 return (0); 121 if (fstat(dict->stat_fd, &st) < 0) 122 msg_fatal("%s: fstat dictionary %s: %m", myname, table); 123 return (st.st_uid); 124 } 125 126 /* deliver_alias - expand alias file entry */ 127 128 int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, 129 char *name, int *statusp) 130 { 131 const char *myname = "deliver_alias"; 132 const char *alias_result; 133 char *saved_alias_result; 134 char *owner; 135 char **cpp; 136 uid_t alias_uid; 137 struct mypasswd *alias_pwd; 138 VSTRING *canon_owner; 139 DICT *dict; 140 const char *owner_rhs; /* owner alias, RHS */ 141 int alias_count; 142 int dsn_notify; 143 char *dsn_envid; 144 int dsn_ret; 145 const char *dsn_orcpt; 146 147 /* 148 * Make verbose logging easier to understand. 149 */ 150 state.level++; 151 if (msg_verbose) 152 MSG_LOG_STATE(myname, state); 153 154 /* 155 * DUPLICATE/LOOP ELIMINATION 156 * 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. 163 * 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. 168 */ 169 if (state.msg_attr.exp_from != 0 170 && strcasecmp(state.msg_attr.exp_from, name) == 0) 171 return (NO); 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)); 178 return (YES); 179 } 180 state.msg_attr.exp_from = name; 181 182 /* 183 * There are a bunch of roles that we're trying to keep track of. 184 * 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. 192 * 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). 197 */ 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) { 202 if (msg_verbose) 203 msg_info("%s: %s: %s = %s", myname, *cpp, name, alias_result); 204 205 /* 206 * Don't expand a verify-only request. 207 */ 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)); 213 return (YES); 214 } 215 216 /* 217 * DELIVERY POLICY 218 * 219 * Update the expansion type attribute, so we can decide if 220 * deliveries to |command and /file/name are allowed at all. 221 */ 222 state.msg_attr.exp_type = EXPAND_TYPE_ALIAS; 223 224 /* 225 * DELIVERY RIGHTS 226 * 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. 231 */ 232 if ((alias_uid = dict_owner(*cpp)) == 0) { 233 alias_pwd = 0; 234 RESET_USER_ATTR(usr_attr, state.level); 235 } else { 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)); 242 return (YES); 243 } 244 SET_USER_ATTR(usr_attr, alias_pwd, state.level); 245 } 246 247 /* 248 * WHERE TO REPORT DELIVERY PROBLEMS. 249 * 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. 253 * 254 * Don't match aliases that are based on regexps. 255 */ 256 #define OWNER_ASSIGN(own) \ 257 (own = (var_ownreq_special == 0 ? 0 : \ 258 concatenate("owner-", name, (char *) 0))) 259 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); 267 } else { 268 canon_owner = 0; 269 /* Note: this does not reset the envelope sender. */ 270 if (var_reset_owner_attr) 271 RESET_OWNER_ATTR(state.msg_attr, state.level); 272 } 273 274 /* 275 * EXTERNAL LOOP CONTROL 276 * 277 * Set the delivered message attribute to the recipient, so that 278 * this message will list the correct forwarding address. 279 */ 280 if (var_frozen_delivered == 0) 281 state.msg_attr.delivered = state.msg_attr.rcpt.address; 282 283 /* 284 * Deliver. 285 */ 286 alias_count = 0; 287 if (dict_errno != 0) { 288 dsb_simple(state.msg_attr.why, "4.3.0", 289 "alias database unavailable"); 290 *statusp = defer_append(BOUNCE_FLAGS(state.request), 291 BOUNCE_ATTR(state.msg_attr)); 292 } else { 293 294 /* 295 * XXX DSN 296 * 297 * When delivering to a mailing list (i.e. the envelope sender 298 * is replaced) the ENVID, NOTIFY, RET, and ORCPT parameters 299 * which accompany the redistributed message MUST NOT be 300 * derived from those of the original message. 301 * 302 * When delivering to an alias (i.e. the envelope sender is not 303 * replaced) any ENVID, RET, or ORCPT parameters are 304 * propagated to all forwarding addresses associated with 305 * that alias. The NOTIFY parameter is propagated to the 306 * forwarding addresses, except that any SUCCESS keyword is 307 * removed. 308 */ 309 #define DSN_SAVE_UPDATE(saved, old, new) do { \ 310 saved = old; \ 311 old = new; \ 312 } while (0) 313 314 DSN_SAVE_UPDATE(dsn_notify, state.msg_attr.rcpt.dsn_notify, 315 dsn_notify == DSN_NOTIFY_SUCCESS ? 316 DSN_NOTIFY_NEVER : 317 dsn_notify & ~DSN_NOTIFY_SUCCESS); 318 if (canon_owner != 0) { 319 DSN_SAVE_UPDATE(dsn_envid, state.msg_attr.dsn_envid, ""); 320 DSN_SAVE_UPDATE(dsn_ret, state.msg_attr.dsn_ret, 0); 321 DSN_SAVE_UPDATE(dsn_orcpt, state.msg_attr.rcpt.dsn_orcpt, ""); 322 state.msg_attr.rcpt.orig_addr = ""; 323 } 324 *statusp = 325 deliver_token_string(state, usr_attr, saved_alias_result, 326 &alias_count); 327 #if 0 328 if (var_ownreq_special 329 && strncmp("owner-", state.msg_attr.sender, 6) != 0 330 && alias_count > 10) 331 msg_warn("mailing list \"%s\" needs an \"owner-%s\" alias", 332 name, name); 333 #endif 334 if (alias_count < 1) { 335 msg_warn("no recipient in alias lookup result for %s", name); 336 dsb_simple(state.msg_attr.why, "4.3.0", 337 "alias database unavailable"); 338 *statusp = defer_append(BOUNCE_FLAGS(state.request), 339 BOUNCE_ATTR(state.msg_attr)); 340 } else { 341 342 /* 343 * XXX DSN 344 * 345 * When delivering to a mailing list (i.e. the envelope 346 * sender address is replaced) and NOTIFY=SUCCESS was 347 * specified, report a DSN of "delivered". 348 * 349 * When delivering to an alias (i.e. the envelope sender 350 * address is not replaced) and NOTIFY=SUCCESS was 351 * specified, report a DSN of "expanded". 352 */ 353 if (dsn_notify & DSN_NOTIFY_SUCCESS) { 354 state.msg_attr.rcpt.dsn_notify = dsn_notify; 355 if (canon_owner != 0) { 356 state.msg_attr.dsn_envid = dsn_envid; 357 state.msg_attr.dsn_ret = dsn_ret; 358 state.msg_attr.rcpt.dsn_orcpt = dsn_orcpt; 359 } 360 dsb_update(state.msg_attr.why, "2.0.0", canon_owner ? 361 "delivered" : "expanded", 362 DSB_SKIP_RMTA, DSB_SKIP_REPLY, 363 "alias expanded"); 364 (void) trace_append(BOUNCE_FLAG_NONE, 365 SENT_ATTR(state.msg_attr)); 366 } 367 } 368 } 369 myfree(saved_alias_result); 370 if (owner) 371 myfree(owner); 372 if (canon_owner) 373 vstring_free(canon_owner); 374 if (alias_pwd) 375 mypwfree(alias_pwd); 376 return (YES); 377 } 378 379 /* 380 * If the alias database was inaccessible for some reason, defer 381 * further delivery for the current top-level recipient. 382 */ 383 if (dict_errno != 0) { 384 dsb_simple(state.msg_attr.why, "4.3.0", 385 "alias database unavailable"); 386 *statusp = defer_append(BOUNCE_FLAGS(state.request), 387 BOUNCE_ATTR(state.msg_attr)); 388 return (YES); 389 } else { 390 if (msg_verbose) 391 msg_info("%s: %s: %s not found", myname, *cpp, name); 392 } 393 } 394 395 /* 396 * Try delivery to a local user instead. 397 */ 398 return (NO); 399 } 400