1 /*++
2 /* NAME
3 /*	file 3
4 /* SUMMARY
5 /*	mail delivery to arbitrary file
6 /* SYNOPSIS
7 /*	#include "local.h"
8 /*
9 /*	int	deliver_file(state, usr_attr, path)
10 /*	LOCAL_STATE state;
11 /*	USER_ATTR usr_attr;
12 /*	char	*path;
13 /* DESCRIPTION
14 /*	deliver_file() appends a message to a file, UNIX mailbox format,
15 /*	or qmail maildir format,
16 /*	with duplicate suppression. It will deliver only to non-executable
17 /*	regular files.
18 /*
19 /*	Arguments:
20 /* .IP state
21 /*	The attributes that specify the message, recipient and more.
22 /*	Attributes describing alias, include or forward expansion.
23 /*	A table with the results from expanding aliases or lists.
24 /* .IP usr_attr
25 /*	Attributes describing user rights and environment information.
26 /* .IP path
27 /*	The file to deliver to. If the name ends in '/', delivery is done
28 /*	in qmail maildir format, otherwise delivery is done in UNIX mailbox
29 /*	format.
30 /* DIAGNOSTICS
31 /*	deliver_file() returns non-zero when delivery should be tried again.
32 /* SEE ALSO
33 /*	defer(3)
34 /*	bounce(3)
35 /* LICENSE
36 /* .ad
37 /* .fi
38 /*	The Secure Mailer license must be distributed with this software.
39 /* AUTHOR(S)
40 /*	Wietse Venema
41 /*	IBM T.J. Watson Research
42 /*	P.O. Box 704
43 /*	Yorktown Heights, NY 10598, USA
44 /*--*/
45 
46 /* System library. */
47 
48 #include <sys_defs.h>
49 #include <sys/stat.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <errno.h>
53 #include <string.h>
54 
55 /* Utility library. */
56 
57 #include <msg.h>
58 #include <htable.h>
59 #include <vstring.h>
60 #include <vstream.h>
61 #include <deliver_flock.h>
62 #include <set_eugid.h>
63 
64 /* Global library. */
65 
66 #include <mail_copy.h>
67 #include <bounce.h>
68 #include <defer.h>
69 #include <sent.h>
70 #include <been_here.h>
71 #include <mail_params.h>
72 #include <mbox_conf.h>
73 #include <mbox_open.h>
74 #include <dsn_util.h>
75 
76 /* Application-specific. */
77 
78 #include "local.h"
79 
80 /* deliver_file - deliver to file */
81 
deliver_file(LOCAL_STATE state,USER_ATTR usr_attr,char * path)82 int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
83 {
84     const char *myname = "deliver_file";
85     struct stat st;
86     MBOX   *mp;
87     DSN_BUF *why = state.msg_attr.why;
88     int     mail_copy_status = MAIL_COPY_STAT_WRITE;
89     int     deliver_status;
90     int     copy_flags;
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      * DUPLICATE ELIMINATION
101      *
102      * Skip this file if it was already delivered to as this user.
103      */
104     if (been_here(state.dup_filter, "file %ld %s", (long) usr_attr.uid, path))
105 	return (0);
106 
107     /*
108      * DELIVERY POLICY
109      *
110      * Do we allow delivery to files?
111      */
112     if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0) {
113 	dsb_simple(why, "5.7.1", "mail to file is restricted");
114 	/* Account for possible owner- sender address override. */
115 	return (bounce_workaround(state));
116     }
117 
118     /*
119      * Don't deliver trace-only requests.
120      */
121     if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
122 	dsb_simple(why, "2.0.0", "delivers to file: %s", path);
123 	return (sent(BOUNCE_FLAGS(state.request),
124 		     SENT_ATTR(state.msg_attr)));
125     }
126 
127     /*
128      * DELIVERY RIGHTS
129      *
130      * Use a default uid/gid when none are given.
131      */
132     if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0)
133 	msg_panic("privileged default user id");
134     if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0)
135 	msg_panic("privileged default group id");
136 
137     /*
138      * If the name ends in /, use maildir-style delivery instead.
139      */
140     if (path[strlen(path) - 1] == '/')
141 	return (deliver_maildir(state, usr_attr, path));
142 
143     /*
144      * Deliver. From here on, no early returns or we have a memory leak.
145      */
146     if (msg_verbose)
147 	msg_info("deliver_file (%ld,%ld): %s",
148 		 (long) usr_attr.uid, (long) usr_attr.gid, path);
149     if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
150 	msg_fatal("seek queue file %s: %m", state.msg_attr.queue_id);
151 
152     /*
153      * As the specified user, open or create the file, lock it, and append
154      * the message.
155      */
156     copy_flags = MAIL_COPY_MBOX;
157     if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0)
158 	copy_flags &= ~MAIL_COPY_DELIVERED;
159 
160     set_eugid(usr_attr.uid, usr_attr.gid);
161     mp = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY,
162 		   S_IRUSR | S_IWUSR, &st, -1, -1,
163 		   local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL,
164 		   "5.2.0", why);
165     if (mp != 0) {
166 	if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
167 	    vstream_fclose(mp->fp);
168 	    dsb_simple(why, "5.7.1", "file is executable");
169 	} else {
170 	    mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
171 					 S_ISREG(st.st_mode) ? copy_flags :
172 					 (copy_flags & ~MAIL_COPY_TOFILE),
173 					 "\n", why);
174 	}
175 	mbox_release(mp);
176     }
177     set_eugid(var_owner_uid, var_owner_gid);
178 
179     /*
180      * As the mail system, bounce, defer delivery, or report success.
181      */
182     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
183 	deliver_status = DEL_STAT_DEFER;
184     } else if (mail_copy_status != 0) {
185 	vstring_sprintf_prepend(why->reason,
186 				"cannot append message to file %s: ", path);
187 	/* Account for possible owner- sender address override. */
188 	deliver_status = bounce_workaround(state);
189     } else {
190 	dsb_simple(why, "2.0.0", "delivered to file: %s", path);
191 	deliver_status = sent(BOUNCE_FLAGS(state.request),
192 			      SENT_ATTR(state.msg_attr));
193     }
194     return (deliver_status);
195 }
196