1 /* $NetBSD: command.c,v 1.1.1.2 2010/06/17 18:06:53 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* command 3 6 /* SUMMARY 7 /* message delivery to shell command 8 /* SYNOPSIS 9 /* #include "local.h" 10 /* 11 /* int deliver_command(state, usr_attr, command) 12 /* LOCAL_STATE state; 13 /* USER_ATTR exp_attr; 14 /* const char *command; 15 /* DESCRIPTION 16 /* deliver_command() runs a command with a message as standard 17 /* input. A limited amount of standard output and standard error 18 /* output is captured for diagnostics purposes. 19 /* Duplicate commands for the same recipient are suppressed. 20 /* A limited amount of information is exported via the environment: 21 /* HOME, SHELL, LOGNAME, USER, EXTENSION, DOMAIN, RECIPIENT (entire 22 /* address) LOCAL (just the local part) and SENDER. The exported 23 /* information is censored with var_cmd_filter. 24 /* 25 /* Arguments: 26 /* .IP state 27 /* The attributes that specify the message, recipient and more. 28 /* Attributes describing the alias, include or forward expansion. 29 /* A table with the results from expanding aliases or lists. 30 /* .IP usr_attr 31 /* Attributes describing user rights and environment. 32 /* .IP command 33 /* The shell command to be executed. If possible, the command is 34 /* executed without actually invoking a shell. if the command is 35 /* the mailbox_command, it is subjected to $name expansion. 36 /* DIAGNOSTICS 37 /* deliver_command() returns non-zero when delivery should be 38 /* tried again, 39 /* SEE ALSO 40 /* mailbox(3) deliver to mailbox 41 /* LICENSE 42 /* .ad 43 /* .fi 44 /* The Secure Mailer license must be distributed with this software. 45 /* AUTHOR(S) 46 /* Wietse Venema 47 /* IBM T.J. Watson Research 48 /* P.O. Box 704 49 /* Yorktown Heights, NY 10598, USA 50 /*--*/ 51 52 /* System library. */ 53 54 #include <sys_defs.h> 55 #include <unistd.h> 56 #include <stdlib.h> 57 #include <stdarg.h> 58 #include <string.h> 59 60 /* Utility library. */ 61 62 #include <msg.h> 63 #include <htable.h> 64 #include <vstring.h> 65 #include <vstream.h> 66 #include <argv.h> 67 #include <mac_parse.h> 68 69 /* Global library. */ 70 71 #include <defer.h> 72 #include <bounce.h> 73 #include <sent.h> 74 #include <been_here.h> 75 #include <mail_params.h> 76 #include <pipe_command.h> 77 #include <mail_copy.h> 78 #include <dsn_util.h> 79 80 /* Application-specific. */ 81 82 #include "local.h" 83 84 /* deliver_command - deliver to shell command */ 85 86 int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *command) 87 { 88 const char *myname = "deliver_command"; 89 DSN_BUF *why = state.msg_attr.why; 90 int cmd_status; 91 int deliver_status; 92 ARGV *env; 93 int copy_flags; 94 char **cpp; 95 char *cp; 96 ARGV *export_env; 97 VSTRING *exec_dir; 98 int expand_status; 99 100 /* 101 * Make verbose logging easier to understand. 102 */ 103 state.level++; 104 if (msg_verbose) 105 MSG_LOG_STATE(myname, state); 106 107 /* 108 * DUPLICATE ELIMINATION 109 * 110 * Skip this command if it was already delivered to as this user. 111 */ 112 if (been_here(state.dup_filter, "command %s:%ld %s", 113 state.msg_attr.user, (long) usr_attr.uid, command)) 114 return (0); 115 116 /* 117 * Don't deliver a trace-only request. 118 */ 119 if (DEL_REQ_TRACE_ONLY(state.request->flags)) { 120 dsb_simple(why, "2.0.0", "delivers to command: %s", command); 121 return (sent(BOUNCE_FLAGS(state.request), 122 SENT_ATTR(state.msg_attr))); 123 } 124 125 /* 126 * DELIVERY RIGHTS 127 * 128 * Choose a default uid and gid when none have been selected (i.e. values 129 * are still zero). 130 */ 131 if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0) 132 msg_panic("privileged default user id"); 133 if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0) 134 msg_panic("privileged default group id"); 135 136 /* 137 * Deliver. 138 */ 139 copy_flags = MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH 140 | MAIL_COPY_ORIG_RCPT; 141 if (local_deliver_hdr_mask & DELIVER_HDR_CMD) 142 copy_flags |= MAIL_COPY_DELIVERED; 143 144 if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) 145 msg_fatal("%s: seek queue file %s: %m", 146 myname, VSTREAM_PATH(state.msg_attr.fp)); 147 148 /* 149 * Pass additional environment information. XXX This should be 150 * configurable. However, passing untrusted information via environment 151 * parameters opens up a whole can of worms. Lesson from web servers: 152 * don't let any network data even near a shell. It causes trouble. 153 */ 154 env = argv_alloc(1); 155 if (usr_attr.home) 156 argv_add(env, "HOME", usr_attr.home, ARGV_END); 157 argv_add(env, 158 "LOGNAME", state.msg_attr.user, 159 "USER", state.msg_attr.user, 160 "SENDER", state.msg_attr.sender, 161 "RECIPIENT", state.msg_attr.rcpt.address, 162 "LOCAL", state.msg_attr.local, 163 ARGV_END); 164 if (usr_attr.shell) 165 argv_add(env, "SHELL", usr_attr.shell, ARGV_END); 166 if (state.msg_attr.domain) 167 argv_add(env, "DOMAIN", state.msg_attr.domain, ARGV_END); 168 if (state.msg_attr.extension) 169 argv_add(env, "EXTENSION", state.msg_attr.extension, ARGV_END); 170 if (state.msg_attr.rcpt.orig_addr && state.msg_attr.rcpt.orig_addr[0]) 171 argv_add(env, "ORIGINAL_RECIPIENT", state.msg_attr.rcpt.orig_addr, 172 ARGV_END); 173 174 #define EXPORT_REQUEST(name, value) \ 175 if ((value)[0]) argv_add(env, (name), (value), ARGV_END); 176 177 EXPORT_REQUEST("CLIENT_HOSTNAME", state.msg_attr.request->client_name); 178 EXPORT_REQUEST("CLIENT_ADDRESS", state.msg_attr.request->client_addr); 179 EXPORT_REQUEST("CLIENT_HELO", state.msg_attr.request->client_helo); 180 EXPORT_REQUEST("CLIENT_PROTOCOL", state.msg_attr.request->client_proto); 181 EXPORT_REQUEST("SASL_METHOD", state.msg_attr.request->sasl_method); 182 EXPORT_REQUEST("SASL_SENDER", state.msg_attr.request->sasl_sender); 183 EXPORT_REQUEST("SASL_USERNAME", state.msg_attr.request->sasl_username); 184 185 argv_terminate(env); 186 187 /* 188 * Censor out undesirable characters from exported data. 189 */ 190 for (cpp = env->argv; *cpp; cpp += 2) 191 for (cp = cpp[1]; *(cp += strspn(cp, var_cmd_exp_filter)) != 0;) 192 *cp++ = '_'; 193 194 /* 195 * Evaluate the command execution directory. Defer delivery if expansion 196 * fails. 197 */ 198 export_env = argv_split(var_export_environ, ", \t\r\n"); 199 exec_dir = vstring_alloc(10); 200 expand_status = local_expand(exec_dir, var_exec_directory, 201 &state, &usr_attr, var_exec_exp_filter); 202 203 if (expand_status & MAC_PARSE_ERROR) { 204 cmd_status = PIPE_STAT_DEFER; 205 dsb_simple(why, "4.3.5", "mail system configuration error"); 206 msg_warn("bad parameter value syntax for %s: %s", 207 VAR_EXEC_DIRECTORY, var_exec_directory); 208 } else { 209 cmd_status = pipe_command(state.msg_attr.fp, why, 210 PIPE_CMD_UID, usr_attr.uid, 211 PIPE_CMD_GID, usr_attr.gid, 212 PIPE_CMD_COMMAND, command, 213 PIPE_CMD_COPY_FLAGS, copy_flags, 214 PIPE_CMD_SENDER, state.msg_attr.sender, 215 PIPE_CMD_ORIG_RCPT, state.msg_attr.rcpt.orig_addr, 216 PIPE_CMD_DELIVERED, state.msg_attr.delivered, 217 PIPE_CMD_TIME_LIMIT, var_command_maxtime, 218 PIPE_CMD_ENV, env->argv, 219 PIPE_CMD_EXPORT, export_env->argv, 220 PIPE_CMD_SHELL, var_local_cmd_shell, 221 PIPE_CMD_CWD, *STR(exec_dir) ? 222 STR(exec_dir) : (char *) 0, 223 PIPE_CMD_END); 224 } 225 vstring_free(exec_dir); 226 argv_free(export_env); 227 argv_free(env); 228 229 /* 230 * Depending on the result, bounce or defer the message. 231 */ 232 switch (cmd_status) { 233 case PIPE_STAT_OK: 234 dsb_simple(why, "2.0.0", "delivered to command: %s", command); 235 deliver_status = sent(BOUNCE_FLAGS(state.request), 236 SENT_ATTR(state.msg_attr)); 237 break; 238 case PIPE_STAT_BOUNCE: 239 case PIPE_STAT_DEFER: 240 if (STR(why->status)[0] == '4') 241 deliver_status = 242 defer_append(BOUNCE_FLAGS(state.request), 243 BOUNCE_ATTR(state.msg_attr)); 244 else 245 /* Account for possible owner- sender address override. */ 246 deliver_status = bounce_workaround(state); 247 break; 248 case PIPE_STAT_CORRUPT: 249 deliver_status = DEL_STAT_DEFER; 250 break; 251 default: 252 msg_panic("%s: bad status %d", myname, cmd_status); 253 /* NOTREACHED */ 254 } 255 256 return (deliver_status); 257 } 258