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
deliver_mailbox_file(LOCAL_STATE state,USER_ATTR usr_attr)82 static 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
deliver_mailbox(LOCAL_STATE state,USER_ATTR usr_attr,int * statusp)174 int 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