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