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