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