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
deliver_command(LOCAL_STATE state,USER_ATTR usr_attr,const char * command)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