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