1/*	$NetBSD$	*/
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
86int     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