7 /* deliver to addresses listed in include file
11 /* int deliver_include(state, usr_attr, path)
13 /* USER_ATTR usr_attr;
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.
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.
27 /* Attributes describing user rights and environment.
29 /* Pathname of the include file.
31 /* Fatal errors: out of memory. Warnings: bad include file type
32 /* or permissions. The result is non-zero when delivery should be
35 /* token(3) tokenize list
39 /* The Secure Mailer license must be distributed with this software.
42 /* IBM T.J. Watson Research
44 /* Yorktown Heights, NY 10598, USA
55 /* Utility library. */
70 #include <been_here.h>
71 #include <mail_params.h>
75 /* Application-specific. */
79 /* deliver_include - open include file and deliver */
81 int deliver_include(LOCAL_STATE state
, USER_ATTR usr_attr
, char *path
)
83 const char *myname
= "deliver_include";
85 struct mypasswd
*file_pwd
= 0;
91 * Make verbose logging easier to understand.
95 MSG_LOG_STATE(myname
, state
);
98 * DUPLICATE ELIMINATION
100 * Don't process this include file more than once as this particular user.
102 if (been_here(state
.dup_filter
, "include %ld %s", (long) usr_attr
.uid
, path
))
104 state
.msg_attr
.exp_from
= state
.msg_attr
.local
;
107 * Can of worms. Allow this include file to be symlinked, but disallow
108 * inclusion of special files or of files with world write permission
112 msg_warn(":include:%s uses a relative path", path
);
113 dsb_simple(state
.msg_attr
.why
, "5.3.5",
114 "mail system configuration error");
115 return (bounce_append(BOUNCE_FLAGS(state
.request
),
116 BOUNCE_ATTR(state
.msg_attr
)));
118 if (stat_as(path
, &st
, usr_attr
.uid
, usr_attr
.gid
) < 0) {
119 msg_warn("unable to lookup :include: file %s: %m", path
);
120 dsb_simple(state
.msg_attr
.why
, "5.3.5",
121 "mail system configuration error");
122 return (bounce_append(BOUNCE_FLAGS(state
.request
),
123 BOUNCE_ATTR(state
.msg_attr
)));
125 if (S_ISREG(st
.st_mode
) == 0) {
126 msg_warn(":include: file %s is not a regular file", path
);
127 dsb_simple(state
.msg_attr
.why
, "5.3.5",
128 "mail system configuration error");
129 return (bounce_append(BOUNCE_FLAGS(state
.request
),
130 BOUNCE_ATTR(state
.msg_attr
)));
132 if (st
.st_mode
& S_IWOTH
) {
133 msg_warn(":include: file %s is world writable", path
);
134 dsb_simple(state
.msg_attr
.why
, "5.3.5",
135 "mail system configuration error");
136 return (bounce_append(BOUNCE_FLAGS(state
.request
),
137 BOUNCE_ATTR(state
.msg_attr
)));
143 * Set the expansion type attribute so that we can decide if destinations
144 * such as /file/name and |command are allowed at all.
146 state
.msg_attr
.exp_type
= EXPAND_TYPE_INCL
;
151 * When a non-root include file is listed in a root-owned alias, use the
152 * rights of the include file owner. We do not want to give the include
153 * file owner control of the default account.
155 * When an include file is listed in a user-owned alias or .forward file,
156 * leave the delivery rights alone. Users should not be able to make
157 * things happen with someone else's rights just by including some file
158 * that is owned by their victim.
160 if (usr_attr
.uid
== 0) {
161 if ((file_pwd
= mypwuid(st
.st_uid
)) == 0) {
162 msg_warn("cannot find username for uid %ld", (long) st
.st_uid
);
163 msg_warn("%s: cannot find :include: file owner username", path
);
164 dsb_simple(state
.msg_attr
.why
, "4.3.5",
165 "mail system configuration error");
166 return (defer_append(BOUNCE_FLAGS(state
.request
),
167 BOUNCE_ATTR(state
.msg_attr
)));
169 if (file_pwd
->pw_uid
!= 0)
170 SET_USER_ATTR(usr_attr
, file_pwd
, state
.level
);
176 * When no owner attribute is set (either via an owner- alias, or as part of
177 * .forward file processing), set the owner attribute, to disable direct
178 * delivery of local recipients. By now it is clear that the owner
179 * attribute should have been called forwarder instead.
181 if (state
.msg_attr
.owner
== 0)
182 state
.msg_attr
.owner
= state
.msg_attr
.rcpt
.address
;
185 * From here on no early returns or we have a memory leak.
189 * Use the delivery rights to open the include file. When no delivery rights
190 * were established sofar, the file containing the :include: is owned by
191 * root, so it should be OK to open any file that is accessible to root.
192 * The command and file delivery routines are responsible for setting the
193 * proper delivery rights. These are the rights of the default user, in
194 * case the :include: is in a root-owned alias.
196 * Don't propagate unmatched extensions unless permitted to do so.
198 #define FOPEN_AS(p,u,g) ((fd = open_as(p,O_RDONLY,0,u,g)) >= 0 ? \
199 vstream_fdopen(fd,O_RDONLY) : 0)
201 if ((fp
= FOPEN_AS(path
, usr_attr
.uid
, usr_attr
.gid
)) == 0) {
202 msg_warn("cannot open include file %s: %m", path
);
203 dsb_simple(state
.msg_attr
.why
, "5.3.5",
204 "mail system configuration error");
205 status
= bounce_append(BOUNCE_FLAGS(state
.request
),
206 BOUNCE_ATTR(state
.msg_attr
));
208 if ((local_ext_prop_mask
& EXT_PROP_INCLUDE
) == 0)
209 state
.msg_attr
.unmatched
= 0;
210 close_on_exec(vstream_fileno(fp
), CLOSE_ON_EXEC
);
211 status
= deliver_token_stream(state
, usr_attr
, fp
, (int *) 0);
212 if (vstream_fclose(fp
))
213 msg_warn("close %s: %m", path
);