1 /* $NetBSD: command.c,v 1.2 2017/02/14 01:16:45 christos 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 #include <mail_parm_split.h> 80 81 /* Application-specific. */ 82 83 #include "local.h" 84 85 /* deliver_command - deliver to shell command */ 86 87 int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *command) 88 { 89 const char *myname = "deliver_command"; 90 DSN_BUF *why = state.msg_attr.why; 91 int cmd_status; 92 int deliver_status; 93 ARGV *env; 94 int copy_flags; 95 char **cpp; 96 char *cp; 97 ARGV *export_env; 98 VSTRING *exec_dir; 99 int expand_status; 100 101 /* 102 * Make verbose logging easier to understand. 103 */ 104 state.level++; 105 if (msg_verbose) 106 MSG_LOG_STATE(myname, state); 107 108 /* 109 * DUPLICATE ELIMINATION 110 * 111 * Skip this command if it was already delivered to as this user. 112 */ 113 if (been_here(state.dup_filter, "command %s:%ld %s", 114 state.msg_attr.user, (long) usr_attr.uid, command)) 115 return (0); 116 117 /* 118 * Don't deliver a trace-only request. 119 */ 120 if (DEL_REQ_TRACE_ONLY(state.request->flags)) { 121 dsb_simple(why, "2.0.0", "delivers to command: %s", command); 122 return (sent(BOUNCE_FLAGS(state.request), 123 SENT_ATTR(state.msg_attr))); 124 } 125 126 /* 127 * DELIVERY RIGHTS 128 * 129 * Choose a default uid and gid when none have been selected (i.e. values 130 * are still zero). 131 */ 132 if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0) 133 msg_panic("privileged default user id"); 134 if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0) 135 msg_panic("privileged default group id"); 136 137 /* 138 * Deliver. 139 */ 140 copy_flags = MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH 141 | MAIL_COPY_ORIG_RCPT; 142 if (local_deliver_hdr_mask & DELIVER_HDR_CMD) 143 copy_flags |= MAIL_COPY_DELIVERED; 144 145 if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) 146 msg_fatal("%s: seek queue file %s: %m", 147 myname, VSTREAM_PATH(state.msg_attr.fp)); 148 149 /* 150 * Pass additional environment information. XXX This should be 151 * configurable. However, passing untrusted information via environment 152 * parameters opens up a whole can of worms. Lesson from web servers: 153 * don't let any network data even near a shell. It causes trouble. 154 */ 155 env = argv_alloc(1); 156 if (usr_attr.home) 157 argv_add(env, "HOME", usr_attr.home, ARGV_END); 158 argv_add(env, 159 "LOGNAME", state.msg_attr.user, 160 "USER", state.msg_attr.user, 161 "SENDER", state.msg_attr.sender, 162 "RECIPIENT", state.msg_attr.rcpt.address, 163 "LOCAL", state.msg_attr.local, 164 ARGV_END); 165 if (usr_attr.shell) 166 argv_add(env, "SHELL", usr_attr.shell, ARGV_END); 167 if (state.msg_attr.domain) 168 argv_add(env, "DOMAIN", state.msg_attr.domain, ARGV_END); 169 if (state.msg_attr.extension) 170 argv_add(env, "EXTENSION", state.msg_attr.extension, ARGV_END); 171 if (state.msg_attr.rcpt.orig_addr && state.msg_attr.rcpt.orig_addr[0]) 172 argv_add(env, "ORIGINAL_RECIPIENT", state.msg_attr.rcpt.orig_addr, 173 ARGV_END); 174 175 #define EXPORT_REQUEST(name, value) \ 176 if ((value)[0]) argv_add(env, (name), (value), ARGV_END); 177 178 EXPORT_REQUEST("CLIENT_HOSTNAME", state.msg_attr.request->client_name); 179 EXPORT_REQUEST("CLIENT_ADDRESS", state.msg_attr.request->client_addr); 180 EXPORT_REQUEST("CLIENT_HELO", state.msg_attr.request->client_helo); 181 EXPORT_REQUEST("CLIENT_PROTOCOL", state.msg_attr.request->client_proto); 182 EXPORT_REQUEST("SASL_METHOD", state.msg_attr.request->sasl_method); 183 EXPORT_REQUEST("SASL_SENDER", state.msg_attr.request->sasl_sender); 184 EXPORT_REQUEST("SASL_USERNAME", state.msg_attr.request->sasl_username); 185 186 argv_terminate(env); 187 188 /* 189 * Censor out undesirable characters from exported data. 190 */ 191 for (cpp = env->argv; *cpp; cpp += 2) 192 for (cp = cpp[1]; *(cp += strspn(cp, var_cmd_exp_filter)) != 0;) 193 *cp++ = '_'; 194 195 /* 196 * Evaluate the command execution directory. Defer delivery if expansion 197 * fails. 198 */ 199 export_env = mail_parm_split(VAR_EXPORT_ENVIRON, var_export_environ); 200 exec_dir = vstring_alloc(10); 201 expand_status = local_expand(exec_dir, var_exec_directory, 202 &state, &usr_attr, var_exec_exp_filter); 203 204 if (expand_status & MAC_PARSE_ERROR) { 205 cmd_status = PIPE_STAT_DEFER; 206 dsb_simple(why, "4.3.5", "mail system configuration error"); 207 msg_warn("bad parameter value syntax for %s: %s", 208 VAR_EXEC_DIRECTORY, var_exec_directory); 209 } else { 210 cmd_status = pipe_command(state.msg_attr.fp, why, 211 CA_PIPE_CMD_UID(usr_attr.uid), 212 CA_PIPE_CMD_GID(usr_attr.gid), 213 CA_PIPE_CMD_COMMAND(command), 214 CA_PIPE_CMD_COPY_FLAGS(copy_flags), 215 CA_PIPE_CMD_SENDER(state.msg_attr.sender), 216 CA_PIPE_CMD_ORIG_RCPT(state.msg_attr.rcpt.orig_addr), 217 CA_PIPE_CMD_DELIVERED(state.msg_attr.delivered), 218 CA_PIPE_CMD_TIME_LIMIT(var_command_maxtime), 219 CA_PIPE_CMD_ENV(env->argv), 220 CA_PIPE_CMD_EXPORT(export_env->argv), 221 CA_PIPE_CMD_SHELL(var_local_cmd_shell), 222 CA_PIPE_CMD_CWD(*STR(exec_dir) ? 223 STR(exec_dir) : (char *) 0), 224 CA_PIPE_CMD_END); 225 } 226 vstring_free(exec_dir); 227 argv_free(export_env); 228 argv_free(env); 229 230 /* 231 * Depending on the result, bounce or defer the message. 232 */ 233 switch (cmd_status) { 234 case PIPE_STAT_OK: 235 dsb_simple(why, "2.0.0", "delivered to command: %s", command); 236 deliver_status = sent(BOUNCE_FLAGS(state.request), 237 SENT_ATTR(state.msg_attr)); 238 break; 239 case PIPE_STAT_BOUNCE: 240 case PIPE_STAT_DEFER: 241 /* Account for possible owner- sender address override. */ 242 deliver_status = bounce_workaround(state); 243 break; 244 case PIPE_STAT_CORRUPT: 245 deliver_status = DEL_STAT_DEFER; 246 break; 247 default: 248 msg_panic("%s: bad status %d", myname, cmd_status); 249 /* NOTREACHED */ 250 } 251 252 return (deliver_status); 253 } 254