11 /* int deliver_mailbox(state, usr_attr, statusp)
13 /* USER_ATTR usr_attr;
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.
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.
31 /* Attributes describing user rights and environment.
33 /* Delivery status: see below.
35 /* The message delivery status is non-zero when delivery should be tried
40 /* The Secure Mailer license must be distributed with this software.
43 /* IBM T.J. Watson Research
45 /* Yorktown Heights, NY 10598, USA
57 /* Utility library. */
64 #include <stringops.h>
65 #include <set_eugid.h>
69 #include <mail_copy.h>
73 #include <been_here.h>
74 #include <mail_params.h>
75 #include <deliver_pass.h>
76 #include <mbox_open.h>
80 /* Application-specific. */
83 #include "biff_notify.h"
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";
95 DSN_BUF
*why
= state
.msg_attr
.why
;
109 * Make verbose logging easier to understand.
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
) {
134 mailbox
= concatenate(usr_attr
.home
, "/", var_home_mailbox
, (char *) 0);
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
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.
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
;
166 if (spool_uid
== usr_attr
.uid
) {
170 chown_uid
= usr_attr
.uid
;
171 chown_gid
= usr_attr
.gid
;
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
);
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
);
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
);
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
);
226 (STR(why
->status
)[0] == '4' ?
227 defer_append
: bounce_append
)
228 (BOUNCE_FLAGS(state
.request
), BOUNCE_ATTR(state
.msg_attr
));
230 dsb_simple(why
, "2.0.0", "delivered to mailbox");
231 deliver_status
= sent(BOUNCE_FLAGS(state
.request
),
232 SENT_ATTR(state
.msg_attr
));
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);
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";
254 struct mypasswd
*mbox_pwd
;
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.
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
))
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
);
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
);
299 * Skip delivery when this recipient does not exist.
301 if ((mbox_pwd
= mypwnam(state
.msg_attr
.user
)) == 0)
305 * No early returns or we have a memory leak.
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
);
334 } else if (*var_mail_spool_dir
&& LAST_CHAR(var_mail_spool_dir
) == '/') {
335 path
= concatenate(var_mail_spool_dir
, state
.msg_attr
.user
,
337 status
= deliver_maildir(state
, usr_attr
, path
);
340 status
= deliver_mailbox_file(state
, usr_attr
);