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