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 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