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