int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) { const char *myname = "deliver_mailbox"; int status; struct mypasswd *mbox_pwd; char *path; static MAPS *transp_maps; const char *map_transport; static MAPS *cmd_maps; const char *map_command; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * DUPLICATE ELIMINATION * * Don't come here more than once, whether or not the recipient exists. */ if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.local)) return (YES); /* * Delegate mailbox delivery to another message transport. */ if (*var_mbox_transp_maps && transp_maps == 0) transp_maps = maps_create(VAR_MBOX_TRANSP_MAPS, var_mbox_transp_maps, DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB); /* The -1 is a hint for the down-stream deliver_completed() function. */ if (transp_maps && (map_transport = maps_find(transp_maps, state.msg_attr.user, DICT_FLAG_NONE)) != 0) { state.msg_attr.rcpt.offset = -1L; *statusp = deliver_pass(MAIL_CLASS_PRIVATE, map_transport, state.request, &state.msg_attr.rcpt); return (YES); } else if (transp_maps && transp_maps->error != 0) { /* Details in the logfile. */ dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure"); *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); return (YES); } if (*var_mailbox_transport) { state.msg_attr.rcpt.offset = -1L; *statusp = deliver_pass(MAIL_CLASS_PRIVATE, var_mailbox_transport, state.request, &state.msg_attr.rcpt); return (YES); } /* * Skip delivery when this recipient does not exist. */ if ((errno = mypwnam_err(state.msg_attr.user, &mbox_pwd)) != 0) { msg_warn("error looking up passwd info for %s: %m", state.msg_attr.user); dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error"); *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); return (YES); } if (mbox_pwd == 0) return (NO); /* * No early returns or we have a memory leak. */ /* * DELIVERY RIGHTS * * Use the rights of the recipient user. */ SET_USER_ATTR(usr_attr, mbox_pwd, state.level); /* * Deliver to mailbox, maildir or to external command. */ #define LAST_CHAR(s) (s[strlen(s) - 1]) if (*var_mailbox_cmd_maps && cmd_maps == 0) cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps, DICT_FLAG_LOCK | DICT_FLAG_PARANOID); if (cmd_maps && (map_command = maps_find(cmd_maps, state.msg_attr.user, DICT_FLAG_NONE)) != 0) { status = deliver_command(state, usr_attr, map_command); } else if (cmd_maps && cmd_maps->error != 0) { /* Details in the logfile. */ dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure"); status = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); } else if (*var_mailbox_command) { status = deliver_command(state, usr_attr, var_mailbox_command); } else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') { path = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); status = deliver_maildir(state, usr_attr, path); myfree(path); } else if (*var_mail_spool_dir && LAST_CHAR(var_mail_spool_dir) == '/') { path = concatenate(var_mail_spool_dir, state.msg_attr.user, "/", (char *) 0); status = deliver_maildir(state, usr_attr, path); myfree(path); } else status = deliver_mailbox_file(state, usr_attr); /* * Cleanup. */ mypwfree(mbox_pwd); *statusp = status; return (YES); }
int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) { const char *myname = "deliver_mailbox"; const char *mailbox_res; const char *uid_res; const char *gid_res; DSN_BUF *why = state.msg_attr.why; long n; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * Sanity check. */ if (*var_virt_mailbox_base != '/') msg_fatal("do not specify relative pathname: %s = %s", VAR_VIRT_MAILBOX_BASE, var_virt_mailbox_base); /* * Look up the mailbox location. Bounce if not found, defer in case of * trouble. */ #define IGNORE_EXTENSION ((char **) 0) mailbox_res = mail_addr_find(virtual_mailbox_maps, state.msg_attr.user, IGNORE_EXTENSION); if (mailbox_res == 0) { if (virtual_mailbox_maps->error == 0) return (NO); msg_warn("table %s: lookup %s: %m", virtual_mailbox_maps->title, state.msg_attr.user); dsb_simple(why, "4.3.5", "mail system configuration error"); *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); return (YES); } usr_attr.mailbox = concatenate(var_virt_mailbox_base, "/", mailbox_res, (char *) 0); #define RETURN(res) { myfree(usr_attr.mailbox); return (res); } /* * Look up the mailbox owner rights. Defer in case of trouble. */ uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user, IGNORE_EXTENSION); if (uid_res == 0) { msg_warn("recipient %s: not found in %s", state.msg_attr.user, virtual_uid_maps->title); dsb_simple(why, "4.3.5", "mail system configuration error"); *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); RETURN(YES); } if ((n = atol(uid_res)) < var_virt_minimum_uid) { msg_warn("recipient %s: bad uid %s in %s", state.msg_attr.user, uid_res, virtual_uid_maps->title); dsb_simple(why, "4.3.5", "mail system configuration error"); *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); RETURN(YES); } usr_attr.uid = (uid_t) n; /* * Look up the mailbox group rights. Defer in case of trouble. */ gid_res = mail_addr_find(virtual_gid_maps, state.msg_attr.user, IGNORE_EXTENSION); if (gid_res == 0) { msg_warn("recipient %s: not found in %s", state.msg_attr.user, virtual_gid_maps->title); dsb_simple(why, "4.3.5", "mail system configuration error"); *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); RETURN(YES); } if ((n = atol(gid_res)) <= 0) { msg_warn("recipient %s: bad gid %s in %s", state.msg_attr.user, gid_res, virtual_gid_maps->title); dsb_simple(why, "4.3.5", "mail system configuration error"); *statusp = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); RETURN(YES); } usr_attr.gid = (gid_t) n; if (msg_verbose) msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u", myname, state.level, usr_attr.mailbox, (unsigned) usr_attr.uid, (unsigned) usr_attr.gid); /* * Deliver to mailbox or to maildir. */ #define LAST_CHAR(s) (s[strlen(s) - 1]) if (LAST_CHAR(usr_attr.mailbox) == '/') *statusp = deliver_maildir(state, usr_attr); else *statusp = deliver_mailbox_file(state, usr_attr); /* * Cleanup. */ RETURN(YES); }
int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) { const char *myname = "deliver_file"; struct stat st; MBOX *mp; DSN_BUF *why = state.msg_attr.why; int mail_copy_status = MAIL_COPY_STAT_WRITE; int deliver_status; int copy_flags; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * DUPLICATE ELIMINATION * * Skip this file if it was already delivered to as this user. */ if (been_here(state.dup_filter, "file %ld %s", (long) usr_attr.uid, path)) return (0); /* * DELIVERY POLICY * * Do we allow delivery to files? */ if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0) { dsb_simple(why, "5.7.1", "mail to file is restricted"); /* Account for possible owner- sender address override. */ return (bounce_workaround(state)); } /* * Don't deliver trace-only requests. */ if (DEL_REQ_TRACE_ONLY(state.request->flags)) { dsb_simple(why, "2.0.0", "delivers to file: %s", path); return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr))); } /* * DELIVERY RIGHTS * * Use a default uid/gid when none are given. */ if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0) msg_panic("privileged default user id"); if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0) msg_panic("privileged default group id"); /* * If the name ends in /, use maildir-style delivery instead. */ if (path[strlen(path) - 1] == '/') return (deliver_maildir(state, usr_attr, path)); /* * Deliver. From here on, no early returns or we have a memory leak. */ if (msg_verbose) msg_info("deliver_file (%ld,%ld): %s", (long) usr_attr.uid, (long) usr_attr.gid, path); if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) msg_fatal("seek queue file %s: %m", state.msg_attr.queue_id); /* * As the specified user, open or create the file, lock it, and append * the message. */ copy_flags = MAIL_COPY_MBOX; if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0) copy_flags &= ~MAIL_COPY_DELIVERED; set_eugid(usr_attr.uid, usr_attr.gid); mp = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR, &st, -1, -1, local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL, "5.2.0", why); if (mp != 0) { if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { vstream_fclose(mp->fp); dsb_simple(why, "5.7.1", "file is executable"); } else { mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp, S_ISREG(st.st_mode) ? copy_flags : (copy_flags & ~MAIL_COPY_TOFILE), "\n", why); } mbox_release(mp); } set_eugid(var_owner_uid, var_owner_gid); /* * As the mail system, bounce, defer delivery, or report success. */ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) { deliver_status = DEL_STAT_DEFER; } else if (mail_copy_status != 0) { vstring_sprintf_prepend(why->reason, "cannot append message to file %s: ", path); if (STR(why->status)[0] == '4') deliver_status = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); else /* Account for possible owner- sender address override. */ deliver_status = bounce_workaround(state); } else { dsb_simple(why, "2.0.0", "delivered to file: %s", path); deliver_status = sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)); } return (deliver_status); }