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