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