1/* $NetBSD: mailbox.c,v 1.3 2020/03/18 19:05:22 christos Exp $ */ 2 3/*++ 4/* NAME 5/* mailbox 3 6/* SUMMARY 7/* mailbox delivery 8/* SYNOPSIS 9/* #include "virtual.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 UNIX-style mailbox or to maildir. 17/* 18/* A zero result means that the named user was not found. 19/* 20/* Arguments: 21/* .IP state 22/* The attributes that specify the message, recipient and more. 23/* .IP usr_attr 24/* Attributes describing user rights and mailbox location. 25/* .IP statusp 26/* Delivery status: see below. 27/* DIAGNOSTICS 28/* The message delivery status is non-zero when delivery should be tried 29/* again. 30/* LICENSE 31/* .ad 32/* .fi 33/* The Secure Mailer license must be distributed with this software. 34/* AUTHOR(S) 35/* Wietse Venema 36/* IBM T.J. Watson Research 37/* P.O. Box 704 38/* Yorktown Heights, NY 10598, USA 39/* 40/* Wietse Venema 41/* Google, Inc. 42/* 111 8th Avenue 43/* New York, NY 10011, USA 44/*--*/ 45 46/* System library. */ 47 48#include <sys_defs.h> 49#include <sys/stat.h> 50#include <stdlib.h> 51#include <errno.h> 52#include <string.h> 53 54/* Utility library. */ 55 56#include <msg.h> 57#include <vstring.h> 58#include <vstream.h> 59#include <mymalloc.h> 60#include <stringops.h> 61#include <set_eugid.h> 62 63/* Global library. */ 64 65#include <mail_copy.h> 66#include <mbox_open.h> 67#include <defer.h> 68#include <sent.h> 69#include <mail_params.h> 70#include <mail_addr_find.h> 71#include <dsn_util.h> 72 73/* Application-specific. */ 74 75#include "virtual.h" 76 77#define YES 1 78#define NO 0 79 80/* deliver_mailbox_file - deliver to recipient mailbox */ 81 82static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) 83{ 84 const char *myname = "deliver_mailbox_file"; 85 DSN_BUF *why = state.msg_attr.why; 86 MBOX *mp; 87 int mail_copy_status; 88 int deliver_status; 89 int copy_flags; 90 struct stat st; 91 92 /* 93 * Make verbose logging easier to understand. 94 */ 95 state.level++; 96 if (msg_verbose) 97 MSG_LOG_STATE(myname, state); 98 99 /* 100 * Don't deliver trace-only requests. 101 */ 102 if (DEL_REQ_TRACE_ONLY(state.request->flags)) { 103 dsb_simple(why, "2.0.0", "delivers to mailbox"); 104 return (sent(BOUNCE_FLAGS(state.request), 105 SENT_ATTR(state.msg_attr))); 106 } 107 108 /* 109 * Initialize. Assume the operation will fail. Set the delivered 110 * attribute to reflect the final recipient. 111 */ 112 if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) 113 msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp)); 114 state.msg_attr.delivered = state.msg_attr.rcpt.address; 115 mail_copy_status = MAIL_COPY_STAT_WRITE; 116 117 /* 118 * Lock the mailbox and open/create the mailbox file. 119 * 120 * Write the file as the recipient, so that file quota work. 121 */ 122 copy_flags = MAIL_COPY_MBOX; 123 124 set_eugid(usr_attr.uid, usr_attr.gid); 125 mp = mbox_open(usr_attr.mailbox, O_APPEND | O_WRONLY | O_CREAT, 126 S_IRUSR | S_IWUSR, &st, -1, -1, 127 virtual_mbox_lock_mask, "4.2.0", why); 128 if (mp != 0) { 129 if (S_ISREG(st.st_mode) == 0) { 130 vstream_fclose(mp->fp); 131 msg_warn("recipient %s: destination %s is not a regular file", 132 state.msg_attr.rcpt.address, usr_attr.mailbox); 133 dsb_simple(why, "5.3.5", "mail system configuration error"); 134 } else if (var_strict_mbox_owner && st.st_uid != usr_attr.uid) { 135 vstream_fclose(mp->fp); 136 dsb_simple(why, "4.2.0", 137 "destination %s is not owned by recipient", usr_attr.mailbox); 138 msg_warn("specify \"%s = no\" to ignore mailbox ownership mismatch", 139 VAR_STRICT_MBOX_OWNER); 140 } else { 141 if (vstream_fseek(mp->fp, (off_t) 0, SEEK_END) < 0) 142 msg_fatal("%s: seek mailbox file %s: %m", 143 myname, VSTREAM_PATH(mp->fp)); 144 mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp, 145 copy_flags, "\n", why); 146 } 147 mbox_release(mp); 148 } 149 set_eugid(var_owner_uid, var_owner_gid); 150 151 /* 152 * As the mail system, bounce, defer delivery, or report success. 153 */ 154 if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) { 155 deliver_status = DEL_STAT_DEFER; 156 } else if (mail_copy_status != 0) { 157 vstring_sprintf_prepend(why->reason, "delivery failed to mailbox %s: ", 158 usr_attr.mailbox); 159 deliver_status = 160 (STR(why->status)[0] == '4' ? 161 defer_append : bounce_append) 162 (BOUNCE_FLAGS(state.request), 163 BOUNCE_ATTR(state.msg_attr)); 164 } else { 165 dsb_simple(why, "2.0.0", "delivered to mailbox"); 166 deliver_status = sent(BOUNCE_FLAGS(state.request), 167 SENT_ATTR(state.msg_attr)); 168 } 169 return (deliver_status); 170} 171 172/* deliver_mailbox - deliver to recipient mailbox */ 173 174int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) 175{ 176 const char *myname = "deliver_mailbox"; 177 const char *mailbox_res; 178 const char *uid_res; 179 const char *gid_res; 180 DSN_BUF *why = state.msg_attr.why; 181 long n; 182 183 /* 184 * Make verbose logging easier to understand. 185 */ 186 state.level++; 187 if (msg_verbose) 188 MSG_LOG_STATE(myname, state); 189 190 /* 191 * Sanity check. 192 */ 193 if (*var_virt_mailbox_base != '/') 194 msg_fatal("do not specify relative pathname: %s = %s", 195 VAR_VIRT_MAILBOX_BASE, var_virt_mailbox_base); 196 197 /* 198 * Look up the mailbox location. Bounce if not found, defer in case of 199 * trouble. 200 */ 201#define IGNORE_EXTENSION ((char **) 0) 202 203 mailbox_res = mail_addr_find(virtual_mailbox_maps, state.msg_attr.user, 204 IGNORE_EXTENSION); 205 if (mailbox_res == 0) { 206 if (virtual_mailbox_maps->error == 0) 207 return (NO); 208 msg_warn("table %s: lookup %s: %m", virtual_mailbox_maps->title, 209 state.msg_attr.user); 210 dsb_simple(why, "4.3.5", "mail system configuration error"); 211 *statusp = defer_append(BOUNCE_FLAGS(state.request), 212 BOUNCE_ATTR(state.msg_attr)); 213 return (YES); 214 } 215 usr_attr.mailbox = concatenate(var_virt_mailbox_base, "/", 216 mailbox_res, (char *) 0); 217 218#define RETURN(res) { myfree(usr_attr.mailbox); return (res); } 219 220 /* 221 * Look up the mailbox owner rights. Defer in case of trouble. 222 */ 223 uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user, 224 IGNORE_EXTENSION); 225 if (uid_res == 0) { 226 msg_warn("recipient %s: not found in %s", 227 state.msg_attr.user, virtual_uid_maps->title); 228 dsb_simple(why, "4.3.5", "mail system configuration error"); 229 *statusp = defer_append(BOUNCE_FLAGS(state.request), 230 BOUNCE_ATTR(state.msg_attr)); 231 RETURN(YES); 232 } 233 if ((n = atol(uid_res)) < var_virt_minimum_uid) { 234 msg_warn("recipient %s: bad uid %s in %s", 235 state.msg_attr.user, uid_res, virtual_uid_maps->title); 236 dsb_simple(why, "4.3.5", "mail system configuration error"); 237 *statusp = defer_append(BOUNCE_FLAGS(state.request), 238 BOUNCE_ATTR(state.msg_attr)); 239 RETURN(YES); 240 } 241 usr_attr.uid = (uid_t) n; 242 243 /* 244 * Look up the mailbox group rights. Defer in case of trouble. 245 */ 246 gid_res = mail_addr_find(virtual_gid_maps, state.msg_attr.user, 247 IGNORE_EXTENSION); 248 if (gid_res == 0) { 249 msg_warn("recipient %s: not found in %s", 250 state.msg_attr.user, virtual_gid_maps->title); 251 dsb_simple(why, "4.3.5", "mail system configuration error"); 252 *statusp = defer_append(BOUNCE_FLAGS(state.request), 253 BOUNCE_ATTR(state.msg_attr)); 254 RETURN(YES); 255 } 256 if ((n = atol(gid_res)) <= 0) { 257 msg_warn("recipient %s: bad gid %s in %s", 258 state.msg_attr.user, gid_res, virtual_gid_maps->title); 259 dsb_simple(why, "4.3.5", "mail system configuration error"); 260 *statusp = defer_append(BOUNCE_FLAGS(state.request), 261 BOUNCE_ATTR(state.msg_attr)); 262 RETURN(YES); 263 } 264 usr_attr.gid = (gid_t) n; 265 266 if (msg_verbose) 267 msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u", 268 myname, state.level, usr_attr.mailbox, 269 (unsigned) usr_attr.uid, (unsigned) usr_attr.gid); 270 271 /* 272 * Deliver to mailbox or to maildir. 273 */ 274#define LAST_CHAR(s) (s[strlen(s) - 1]) 275 276 if (LAST_CHAR(usr_attr.mailbox) == '/') 277 *statusp = deliver_maildir(state, usr_attr); 278 else 279 *statusp = deliver_mailbox_file(state, usr_attr); 280 281 /* 282 * Cleanup. 283 */ 284 RETURN(YES); 285} 286