7 /* message delivery to shell command
11 /* int deliver_command(state, usr_attr, command)
13 /* USER_ATTR exp_attr;
14 /* const char *command;
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.
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.
31 /* Attributes describing user rights and environment.
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.
37 /* deliver_command() returns non-zero when delivery should be
40 /* mailbox(3) deliver to mailbox
44 /* The Secure Mailer license must be distributed with this software.
47 /* IBM T.J. Watson Research
49 /* Yorktown Heights, NY 10598, USA
60 /* Utility library. */
67 #include <mac_parse.h>
74 #include <been_here.h>
75 #include <mail_params.h>
76 #include <pipe_command.h>
77 #include <mail_copy.h>
80 /* Application-specific. */
84 /* deliver_command - deliver to shell command */
86 int deliver_command(LOCAL_STATE state
, USER_ATTR usr_attr
, const char *command
)
88 const char *myname
= "deliver_command";
89 DSN_BUF
*why
= state
.msg_attr
.why
;
101 * Make verbose logging easier to understand.
105 MSG_LOG_STATE(myname
, state
);
108 * DUPLICATE ELIMINATION
110 * Skip this command if it was already delivered to as this user.
112 if (been_here(state
.dup_filter
, "command %s:%ld %s",
113 state
.msg_attr
.user
, (long) usr_attr
.uid
, command
))
117 * Don't deliver a trace-only request.
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
)));
128 * Choose a default uid and gid when none have been selected (i.e. values
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");
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
;
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
));
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.
156 argv_add(env
, "HOME", usr_attr
.home
, ARGV_END
);
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
,
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
,
174 #define EXPORT_REQUEST(name, value) \
175 if ((value)[0]) argv_add(env, (name), (value), ARGV_END);
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
);
188 * Censor out undesirable characters from exported data.
190 for (cpp
= env
->argv
; *cpp
; cpp
+= 2)
191 for (cp
= cpp
[1]; *(cp
+= strspn(cp
, var_cmd_exp_filter
)) != 0;)
195 * Evaluate the command execution directory. Defer delivery if expansion
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
);
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
);
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,
225 vstring_free(exec_dir
);
226 argv_free(export_env
);
230 * Depending on the result, bounce or defer the message.
232 switch (cmd_status
) {
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
));
238 case PIPE_STAT_BOUNCE
:
239 case PIPE_STAT_DEFER
:
241 (STR(why
->status
)[0] == '4' ?
242 defer_append
: bounce_append
)
243 (BOUNCE_FLAGS(state
.request
),
244 BOUNCE_ATTR(state
.msg_attr
));
246 case PIPE_STAT_CORRUPT
:
247 deliver_status
= DEL_STAT_DEFER
;
250 msg_panic("%s: bad status %d", myname
, cmd_status
);
254 return (deliver_status
);