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