xref: /netbsd-src/external/ibm-public/postfix/dist/src/local/include.c (revision a30b880ed60a24c405edba78187a04247f4d9d33)
1 /*	$NetBSD: include.c,v 1.1.1.2 2013/01/02 18:59:00 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	deliver_include 3
6 /* SUMMARY
7 /*	deliver to addresses listed in include file
8 /* SYNOPSIS
9 /*	#include "local.h"
10 /*
11 /*	int	deliver_include(state, usr_attr, path)
12 /*	LOCAL_STATE state;
13 /*	USER_ATTR usr_attr;
14 /*	char	*path;
15 /* DESCRIPTION
16 /*	deliver_include() processes the contents of the named include
17 /*	file and delivers to each address listed. Some sanity checks
18 /*	are done on the include file permissions and type.
19 /*
20 /*	Arguments:
21 /* .IP state
22 /*	The attributes that specify the message, recipient and more.
23 /*	Attributes describing alias, include, or forward expansion.
24 /*	A table with the results from expanding aliases or lists.
25 /*	A table with delivered-to: addresses taken from the message.
26 /* .IP usr_attr
27 /*	Attributes describing user rights and environment.
28 /* .IP path
29 /*	Pathname of the include file.
30 /* DIAGNOSTICS
31 /*	Fatal errors: out of memory. Warnings: bad include file type
32 /*	or permissions. The result is non-zero when delivery should be
33 /*	tried again.
34 /* SEE ALSO
35 /*	token(3) tokenize list
36 /* LICENSE
37 /* .ad
38 /* .fi
39 /*	The Secure Mailer license must be distributed with this software.
40 /* AUTHOR(S)
41 /*	Wietse Venema
42 /*	IBM T.J. Watson Research
43 /*	P.O. Box 704
44 /*	Yorktown Heights, NY 10598, USA
45 /*--*/
46 
47 /* System library. */
48 
49 #include <sys_defs.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <string.h>
53 #include <fcntl.h>
54 #include <errno.h>
55 
56 /* Utility library. */
57 
58 #include <msg.h>
59 #include <htable.h>
60 #include <mymalloc.h>
61 #include <vstream.h>
62 #include <open_as.h>
63 #include <stat_as.h>
64 #include <iostuff.h>
65 #include <mypwd.h>
66 
67 /* Global library. */
68 
69 #include <bounce.h>
70 #include <defer.h>
71 #include <been_here.h>
72 #include <mail_params.h>
73 #include <ext_prop.h>
74 #include <sent.h>
75 
76 /* Application-specific. */
77 
78 #include "local.h"
79 
80 /* deliver_include - open include file and deliver */
81 
deliver_include(LOCAL_STATE state,USER_ATTR usr_attr,char * path)82 int     deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
83 {
84     const char *myname = "deliver_include";
85     struct stat st;
86     struct mypasswd *file_pwd = 0;
87     int     status;
88     VSTREAM *fp;
89     int     fd;
90 
91     /*
92      * Make verbose logging easier to understand.
93      */
94     state.level++;
95     if (msg_verbose)
96 	MSG_LOG_STATE(myname, state);
97 
98     /*
99      * DUPLICATE ELIMINATION
100      *
101      * Don't process this include file more than once as this particular user.
102      */
103     if (been_here(state.dup_filter, "include %ld %s", (long) usr_attr.uid, path))
104 	return (0);
105     state.msg_attr.exp_from = state.msg_attr.local;
106 
107     /*
108      * Can of worms. Allow this include file to be symlinked, but disallow
109      * inclusion of special files or of files with world write permission
110      * enabled.
111      */
112     if (*path != '/') {
113 	msg_warn(":include:%s uses a relative path", path);
114 	dsb_simple(state.msg_attr.why, "5.3.5",
115 		   "mail system configuration error");
116 	return (bounce_append(BOUNCE_FLAGS(state.request),
117 			      BOUNCE_ATTR(state.msg_attr)));
118     }
119     if (stat_as(path, &st, usr_attr.uid, usr_attr.gid) < 0) {
120 	msg_warn("unable to lookup :include: file %s: %m", path);
121 	dsb_simple(state.msg_attr.why, "5.3.5",
122 		   "mail system configuration error");
123 	return (bounce_append(BOUNCE_FLAGS(state.request),
124 			      BOUNCE_ATTR(state.msg_attr)));
125     }
126     if (S_ISREG(st.st_mode) == 0) {
127 	msg_warn(":include: file %s is not a regular file", path);
128 	dsb_simple(state.msg_attr.why, "5.3.5",
129 		   "mail system configuration error");
130 	return (bounce_append(BOUNCE_FLAGS(state.request),
131 			      BOUNCE_ATTR(state.msg_attr)));
132     }
133     if (st.st_mode & S_IWOTH) {
134 	msg_warn(":include: file %s is world writable", path);
135 	dsb_simple(state.msg_attr.why, "5.3.5",
136 		   "mail system configuration error");
137 	return (bounce_append(BOUNCE_FLAGS(state.request),
138 			      BOUNCE_ATTR(state.msg_attr)));
139     }
140 
141     /*
142      * DELIVERY POLICY
143      *
144      * Set the expansion type attribute so that we can decide if destinations
145      * such as /file/name and |command are allowed at all.
146      */
147     state.msg_attr.exp_type = EXPAND_TYPE_INCL;
148 
149     /*
150      * DELIVERY RIGHTS
151      *
152      * When a non-root include file is listed in a root-owned alias, use the
153      * rights of the include file owner.  We do not want to give the include
154      * file owner control of the default account.
155      *
156      * When an include file is listed in a user-owned alias or .forward file,
157      * leave the delivery rights alone. Users should not be able to make
158      * things happen with someone else's rights just by including some file
159      * that is owned by their victim.
160      */
161     if (usr_attr.uid == 0) {
162 	if ((errno = mypwuid_err(st.st_uid, &file_pwd)) != 0 || file_pwd == 0) {
163 	    msg_warn(errno ? "cannot find username for uid %ld: %m" :
164 		     "cannot find username for uid %ld", (long) st.st_uid);
165 	    msg_warn("%s: cannot find :include: file owner username", path);
166 	    dsb_simple(state.msg_attr.why, "4.3.5",
167 		       "mail system configuration error");
168 	    return (defer_append(BOUNCE_FLAGS(state.request),
169 				 BOUNCE_ATTR(state.msg_attr)));
170 	}
171 	if (file_pwd->pw_uid != 0)
172 	    SET_USER_ATTR(usr_attr, file_pwd, state.level);
173     }
174 
175     /*
176      * MESSAGE FORWARDING
177      *
178      * When no owner attribute is set (either via an owner- alias, or as part of
179      * .forward file processing), set the owner attribute, to disable direct
180      * delivery of local recipients. By now it is clear that the owner
181      * attribute should have been called forwarder instead.
182      */
183     if (state.msg_attr.owner == 0)
184 	state.msg_attr.owner = state.msg_attr.rcpt.address;
185 
186     /*
187      * From here on no early returns or we have a memory leak.
188      *
189      * FILE OPEN RIGHTS
190      *
191      * Use the delivery rights to open the include file. When no delivery rights
192      * were established sofar, the file containing the :include: is owned by
193      * root, so it should be OK to open any file that is accessible to root.
194      * The command and file delivery routines are responsible for setting the
195      * proper delivery rights. These are the rights of the default user, in
196      * case the :include: is in a root-owned alias.
197      *
198      * Don't propagate unmatched extensions unless permitted to do so.
199      */
200 #define FOPEN_AS(p,u,g) ((fd = open_as(p,O_RDONLY,0,u,g)) >= 0 ? \
201 				vstream_fdopen(fd,O_RDONLY) : 0)
202 
203     if ((fp = FOPEN_AS(path, usr_attr.uid, usr_attr.gid)) == 0) {
204 	msg_warn("cannot open include file %s: %m", path);
205 	dsb_simple(state.msg_attr.why, "5.3.5",
206 		   "mail system configuration error");
207 	status = bounce_append(BOUNCE_FLAGS(state.request),
208 			       BOUNCE_ATTR(state.msg_attr));
209     } else {
210 	if ((local_ext_prop_mask & EXT_PROP_INCLUDE) == 0)
211 	    state.msg_attr.unmatched = 0;
212 	close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC);
213 	status = deliver_token_stream(state, usr_attr, fp, (int *) 0);
214 	if (vstream_fclose(fp))
215 	    msg_warn("close %s: %m", path);
216     }
217 
218     /*
219      * Cleanup.
220      */
221     if (file_pwd)
222 	mypwfree(file_pwd);
223 
224     return (status);
225 }
226