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