Home | History | Annotate | Line # | Download | only in virtual
      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 
     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 
    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