1 /* $NetBSD: mailbox.c,v 1.2 2017/02/14 01:16:45 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* mailbox 3 6 /* SUMMARY 7 /* mailbox delivery 8 /* SYNOPSIS 9 /* #include "local.h" 10 /* 11 /* int deliver_mailbox(state, usr_attr, statusp) 12 /* LOCAL_STATE state; 13 /* USER_ATTR usr_attr; 14 /* int *statusp; 15 /* DESCRIPTION 16 /* deliver_mailbox() delivers to mailbox, with duplicate 17 /* suppression. The default is direct mailbox delivery to 18 /* /var/[spool/]mail/\fIuser\fR; when a \fIhome_mailbox\fR 19 /* has been configured, mail is delivered to ~/$\fIhome_mailbox\fR; 20 /* and when a \fImailbox_command\fR has been configured, the message 21 /* is piped into the command instead. 22 /* 23 /* A zero result means that the named user was not found. 24 /* 25 /* Arguments: 26 /* .IP state 27 /* The attributes that specify the message, recipient and more. 28 /* Attributes describing alias, include or forward expansion. 29 /* A table with the results from expanding aliases or lists. 30 /* .IP usr_attr 31 /* Attributes describing user rights and environment. 32 /* .IP statusp 33 /* Delivery status: see below. 34 /* DIAGNOSTICS 35 /* The message delivery status is non-zero when delivery should be tried 36 /* again. 37 /* LICENSE 38 /* .ad 39 /* .fi 40 /* The Secure Mailer license must be distributed with this software. 41 /* AUTHOR(S) 42 /* Wietse Venema 43 /* IBM T.J. Watson Research 44 /* P.O. Box 704 45 /* Yorktown Heights, NY 10598, USA 46 /*--*/ 47 48 /* System library. */ 49 50 #include <sys_defs.h> 51 #include <sys/stat.h> 52 #include <fcntl.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <errno.h> 56 57 /* Utility library. */ 58 59 #include <msg.h> 60 #include <htable.h> 61 #include <vstring.h> 62 #include <vstream.h> 63 #include <mymalloc.h> 64 #include <stringops.h> 65 #include <set_eugid.h> 66 #include <warn_stat.h> 67 68 /* Global library. */ 69 70 #include <mail_copy.h> 71 #include <defer.h> 72 #include <sent.h> 73 #include <mypwd.h> 74 #include <been_here.h> 75 #include <mail_params.h> 76 #include <deliver_pass.h> 77 #include <mbox_open.h> 78 #include <maps.h> 79 #include <dsn_util.h> 80 81 /* Application-specific. */ 82 83 #include "local.h" 84 #include "biff_notify.h" 85 86 #define YES 1 87 #define NO 0 88 89 /* deliver_mailbox_file - deliver to recipient mailbox */ 90 91 static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) 92 { 93 const char *myname = "deliver_mailbox_file"; 94 char *spool_dir; 95 char *mailbox; 96 DSN_BUF *why = state.msg_attr.why; 97 MBOX *mp; 98 int mail_copy_status; 99 int deliver_status; 100 int copy_flags; 101 VSTRING *biff; 102 long end; 103 struct stat st; 104 uid_t spool_uid; 105 gid_t spool_gid; 106 uid_t chown_uid; 107 gid_t chown_gid; 108 109 /* 110 * Make verbose logging easier to understand. 111 */ 112 state.level++; 113 if (msg_verbose) 114 MSG_LOG_STATE(myname, state); 115 116 /* 117 * Don't deliver trace-only requests. 118 */ 119 if (DEL_REQ_TRACE_ONLY(state.request->flags)) { 120 dsb_simple(why, "2.0.0", "delivers to mailbox"); 121 return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr))); 122 } 123 124 /* 125 * Initialize. Assume the operation will fail. Set the delivered 126 * attribute to reflect the final recipient. 127 */ 128 if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) 129 msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp)); 130 if (var_frozen_delivered == 0) 131 state.msg_attr.delivered = state.msg_attr.rcpt.address; 132 mail_copy_status = MAIL_COPY_STAT_WRITE; 133 if (*var_home_mailbox) { 134 spool_dir = 0; 135 mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); 136 } else { 137 spool_dir = var_mail_spool_dir; 138 mailbox = concatenate(spool_dir, "/", state.msg_attr.user, (char *) 0); 139 } 140 141 /* 142 * Mailbox delivery with least privilege. As long as we do not use root 143 * privileges this code may also work over NFS. 144 * 145 * If delivering to the recipient's home directory, perform all operations 146 * (including file locking) as that user (Mike Muuss, Army Research 147 * Laboratory, USA). 148 * 149 * If delivering to the mail spool directory, and the spool directory is 150 * world-writable, deliver as the recipient; if the spool directory is 151 * group-writable, use the recipient user id and the mail spool group id. 152 * 153 * Otherwise, use root privileges and chown the mailbox. 154 */ 155 if (spool_dir == 0 156 || stat(spool_dir, &st) < 0 157 || (st.st_mode & S_IWOTH) != 0) { 158 spool_uid = usr_attr.uid; 159 spool_gid = usr_attr.gid; 160 } else if ((st.st_mode & S_IWGRP) != 0) { 161 spool_uid = usr_attr.uid; 162 spool_gid = st.st_gid; 163 } else { 164 spool_uid = 0; 165 spool_gid = 0; 166 } 167 if (spool_uid == usr_attr.uid) { 168 chown_uid = -1; 169 chown_gid = -1; 170 } else { 171 chown_uid = usr_attr.uid; 172 chown_gid = usr_attr.gid; 173 } 174 if (msg_verbose) 175 msg_info("spool_uid/gid %ld/%ld chown_uid/gid %ld/%ld", 176 (long) spool_uid, (long) spool_gid, 177 (long) chown_uid, (long) chown_gid); 178 179 /* 180 * Lock the mailbox and open/create the mailbox file. Depending on the 181 * type of locking used, we lock first or we open first. 182 * 183 * Write the file as the recipient, so that file quota work. 184 */ 185 copy_flags = MAIL_COPY_MBOX; 186 if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0) 187 copy_flags &= ~MAIL_COPY_DELIVERED; 188 189 set_eugid(spool_uid, spool_gid); 190 mp = mbox_open(mailbox, O_APPEND | O_WRONLY | O_CREAT, 191 S_IRUSR | S_IWUSR, &st, chown_uid, chown_gid, 192 local_mbox_lock_mask, "5.2.0", why); 193 if (mp != 0) { 194 if (spool_uid != usr_attr.uid || spool_gid != usr_attr.gid) 195 set_eugid(usr_attr.uid, usr_attr.gid); 196 if (S_ISREG(st.st_mode) == 0) { 197 vstream_fclose(mp->fp); 198 dsb_simple(why, "5.2.0", 199 "destination %s is not a regular file", mailbox); 200 } else if (var_strict_mbox_owner && st.st_uid != usr_attr.uid) { 201 vstream_fclose(mp->fp); 202 dsb_simple(why, "4.2.0", 203 "destination %s is not owned by recipient", mailbox); 204 msg_warn("specify \"%s = no\" to ignore mailbox ownership mismatch", 205 VAR_STRICT_MBOX_OWNER); 206 } else { 207 end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END); 208 mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp, 209 copy_flags, "\n", why); 210 } 211 if (spool_uid != usr_attr.uid || spool_gid != usr_attr.gid) 212 set_eugid(spool_uid, spool_gid); 213 mbox_release(mp); 214 } 215 set_eugid(var_owner_uid, var_owner_gid); 216 217 /* 218 * As the mail system, bounce, defer delivery, or report success. 219 */ 220 if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) { 221 deliver_status = DEL_STAT_DEFER; 222 } else if (mail_copy_status != 0) { 223 vstring_sprintf_prepend(why->reason, 224 "cannot update mailbox %s for user %s. ", 225 mailbox, state.msg_attr.user); 226 deliver_status = 227 (STR(why->status)[0] == '4' ? 228 defer_append : bounce_append) 229 (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); 230 } else { 231 dsb_simple(why, "2.0.0", "delivered to mailbox"); 232 deliver_status = sent(BOUNCE_FLAGS(state.request), 233 SENT_ATTR(state.msg_attr)); 234 if (var_biff) { 235 biff = vstring_alloc(100); 236 vstring_sprintf(biff, "%s@%ld", usr_attr.logname, (long) end); 237 biff_notify(STR(biff), VSTRING_LEN(biff) + 1); 238 vstring_free(biff); 239 } 240 } 241 242 /* 243 * Clean up. 244 */ 245 myfree(mailbox); 246 return (deliver_status); 247 } 248 249 /* deliver_mailbox - deliver to recipient mailbox */ 250 251 int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) 252 { 253 const char *myname = "deliver_mailbox"; 254 int status; 255 struct mypasswd *mbox_pwd; 256 char *path; 257 static MAPS *transp_maps; 258 const char *map_transport; 259 static MAPS *cmd_maps; 260 const char *map_command; 261 262 /* 263 * Make verbose logging easier to understand. 264 */ 265 state.level++; 266 if (msg_verbose) 267 MSG_LOG_STATE(myname, state); 268 269 /* 270 * DUPLICATE ELIMINATION 271 * 272 * Don't come here more than once, whether or not the recipient exists. 273 */ 274 if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.local)) 275 return (YES); 276 277 /* 278 * Delegate mailbox delivery to another message transport. 279 */ 280 if (*var_mbox_transp_maps && transp_maps == 0) 281 transp_maps = maps_create(VAR_MBOX_TRANSP_MAPS, var_mbox_transp_maps, 282 DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB 283 | DICT_FLAG_UTF8_REQUEST); 284 /* The -1 is a hint for the down-stream deliver_completed() function. */ 285 if (transp_maps 286 && (map_transport = maps_find(transp_maps, state.msg_attr.user, 287 DICT_FLAG_NONE)) != 0) { 288 state.msg_attr.rcpt.offset = -1L; 289 *statusp = deliver_pass(MAIL_CLASS_PRIVATE, map_transport, 290 state.request, &state.msg_attr.rcpt); 291 return (YES); 292 } else if (transp_maps && transp_maps->error != 0) { 293 /* Details in the logfile. */ 294 dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure"); 295 *statusp = defer_append(BOUNCE_FLAGS(state.request), 296 BOUNCE_ATTR(state.msg_attr)); 297 return (YES); 298 } 299 if (*var_mailbox_transport) { 300 state.msg_attr.rcpt.offset = -1L; 301 *statusp = deliver_pass(MAIL_CLASS_PRIVATE, var_mailbox_transport, 302 state.request, &state.msg_attr.rcpt); 303 return (YES); 304 } 305 306 /* 307 * Skip delivery when this recipient does not exist. 308 */ 309 if ((errno = mypwnam_err(state.msg_attr.user, &mbox_pwd)) != 0) { 310 msg_warn("error looking up passwd info for %s: %m", 311 state.msg_attr.user); 312 dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error"); 313 *statusp = defer_append(BOUNCE_FLAGS(state.request), 314 BOUNCE_ATTR(state.msg_attr)); 315 return (YES); 316 } 317 if (mbox_pwd == 0) 318 return (NO); 319 320 /* 321 * No early returns or we have a memory leak. 322 */ 323 324 /* 325 * DELIVERY RIGHTS 326 * 327 * Use the rights of the recipient user. 328 */ 329 SET_USER_ATTR(usr_attr, mbox_pwd, state.level); 330 331 /* 332 * Deliver to mailbox, maildir or to external command. 333 */ 334 #define LAST_CHAR(s) (s[strlen(s) - 1]) 335 336 if (*var_mailbox_cmd_maps && cmd_maps == 0) 337 cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps, 338 DICT_FLAG_LOCK | DICT_FLAG_PARANOID 339 | DICT_FLAG_UTF8_REQUEST); 340 341 if (cmd_maps && (map_command = maps_find(cmd_maps, state.msg_attr.user, 342 DICT_FLAG_NONE)) != 0) { 343 status = deliver_command(state, usr_attr, map_command); 344 } else if (cmd_maps && cmd_maps->error != 0) { 345 /* Details in the logfile. */ 346 dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure"); 347 status = defer_append(BOUNCE_FLAGS(state.request), 348 BOUNCE_ATTR(state.msg_attr)); 349 } else if (*var_mailbox_command) { 350 status = deliver_command(state, usr_attr, var_mailbox_command); 351 } else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') { 352 path = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); 353 status = deliver_maildir(state, usr_attr, path); 354 myfree(path); 355 } else if (*var_mail_spool_dir && LAST_CHAR(var_mail_spool_dir) == '/') { 356 path = concatenate(var_mail_spool_dir, state.msg_attr.user, 357 "/", (char *) 0); 358 status = deliver_maildir(state, usr_attr, path); 359 myfree(path); 360 } else 361 status = deliver_mailbox_file(state, usr_attr); 362 363 /* 364 * Cleanup. 365 */ 366 mypwfree(mbox_pwd); 367 *statusp = status; 368 return (YES); 369 } 370