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