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
82static 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
174int     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