xref: /netbsd-src/external/ibm-public/postfix/dist/src/pipe/pipe.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: pipe.c,v 1.1.1.2 2010/06/17 18:06:57 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	pipe 8
6 /* SUMMARY
7 /*	Postfix delivery to external command
8 /* SYNOPSIS
9 /*	\fBpipe\fR [generic Postfix daemon options] command_attributes...
10 /* DESCRIPTION
11 /*	The \fBpipe\fR(8) daemon processes requests from the Postfix queue
12 /*	manager to deliver messages to external commands.
13 /*	This program expects to be run from the \fBmaster\fR(8) process
14 /*	manager.
15 /*
16 /*	Message attributes such as sender address, recipient address and
17 /*	next-hop host name can be specified as command-line macros that are
18 /*	expanded before the external command is executed.
19 /*
20 /*	The \fBpipe\fR(8) daemon updates queue files and marks recipients
21 /*	as finished, or it informs the queue manager that delivery should
22 /*	be tried again at a later time. Delivery status reports are sent
23 /*	to the \fBbounce\fR(8), \fBdefer\fR(8) or \fBtrace\fR(8) daemon as
24 /*	appropriate.
25 /* SINGLE-RECIPIENT DELIVERY
26 /* .ad
27 /* .fi
28 /*	Some destinations cannot handle more than one recipient per
29 /*	delivery request. Examples are pagers or fax machines.
30 /*	In addition, multi-recipient delivery is undesirable when
31 /*	prepending a \fBDelivered-to:\fR or \fBX-Original-To:\fR
32 /*	message header.
33 /*
34 /*	To prevent Postfix from sending multiple recipients per delivery
35 /*	request, specify
36 /* .sp
37 /* .nf
38 /*	    \fItransport\fB_destination_recipient_limit = 1\fR
39 /* .fi
40 /*
41 /*	in the Postfix \fBmain.cf\fR file, where \fItransport\fR
42 /*	is the name in the first column of the Postfix \fBmaster.cf\fR
43 /*	entry for the pipe-based delivery transport.
44 /* COMMAND ATTRIBUTE SYNTAX
45 /* .ad
46 /* .fi
47 /*	The external command attributes are given in the \fBmaster.cf\fR
48 /*	file at the end of a service definition.  The syntax is as follows:
49 /* .IP "\fBchroot=\fIpathname\fR (optional)"
50 /*	Change the process root directory and working directory to
51 /*	the named directory. This happens before switching to the
52 /*	privileges specified with the \fBuser\fR attribute, and
53 /*	before executing the optional \fBdirectory=\fIpathname\fR
54 /*	directive. Delivery is deferred in case of failure.
55 /* .sp
56 /*	This feature is available as of Postfix 2.3.
57 /* .IP "\fBdirectory=\fIpathname\fR (optional)"
58 /*	Change to the named directory before executing the external command.
59 /*	The directory must be accessible for the user specified with the
60 /*	\fBuser\fR attribute (see below).
61 /*	The default working directory is \fB$queue_directory\fR.
62 /*	Delivery is deferred in case of failure.
63 /* .sp
64 /*	This feature is available as of Postfix 2.2.
65 /* .IP "\fBeol=\fIstring\fR (optional, default: \fB\en\fR)"
66 /*	The output record delimiter. Typically one would use either
67 /*	\fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
68 /*	sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
69 /*	\e\fIddd\fR (up to three octal digits) and \fB\e\e\fR.
70 /* .IP "\fBflags=BDFORXhqu.>\fR (optional)"
71 /*	Optional message processing flags. By default, a message is
72 /*	copied unchanged.
73 /* .RS
74 /* .IP \fBB\fR
75 /*	Append a blank line at the end of each message. This is required
76 /*	by some mail user agents that recognize "\fBFrom \fR" lines only
77 /*	when preceded by a blank line.
78 /* .IP \fBD\fR
79 /*	Prepend a "\fBDelivered-To: \fIrecipient\fR" message header with the
80 /*	envelope recipient address. Note: for this to work, the
81 /*	\fItransport\fB_destination_recipient_limit\fR must be 1
82 /*	(see SINGLE-RECIPIENT DELIVERY above for details).
83 /* .sp
84 /*	The \fBD\fR flag also enforces loop detection (Postfix 2.5 and later):
85 /*	if a message already contains a \fBDelivered-To:\fR header
86 /*	with the same recipient address, then the message is
87 /*	returned as undeliverable. The address comparison is case
88 /*	insensitive.
89 /* .sp
90 /*	This feature is available as of Postfix 2.0.
91 /* .IP \fBF\fR
92 /*	Prepend a "\fBFrom \fIsender time_stamp\fR" envelope header to
93 /*	the message content.
94 /*	This is expected by, for example, \fBUUCP\fR software.
95 /* .IP \fBO\fR
96 /*	Prepend an "\fBX-Original-To: \fIrecipient\fR" message header
97 /*	with the recipient address as given to Postfix. Note: for this to
98 /*	work, the \fItransport\fB_destination_recipient_limit\fR must be 1
99 /*	(see SINGLE-RECIPIENT DELIVERY above for details).
100 /* .sp
101 /*	This feature is available as of Postfix 2.0.
102 /* .IP \fBR\fR
103 /*	Prepend a \fBReturn-Path:\fR message header with the envelope sender
104 /*	address.
105 /* .IP \fBX\fR
106 /*	Indicate that the external command performs final delivery.
107 /*	This flag affects the status reported in "success" DSN
108 /*	(delivery status notification) messages, and changes it
109 /*	from "relayed" into "delivered".
110 /* .sp
111 /*	This feature is available as of Postfix 2.5.
112 /* .IP \fBh\fR
113 /*	Fold the command-line \fB$original_recipient\fR and
114 /*	\fB$recipient\fR address domain part
115 /*	(text to the right of the right-most \fB@\fR character) to
116 /*	lower case; fold the entire command-line \fB$domain\fR and
117 /*	\fB$nexthop\fR host or domain information to lower case.
118 /*	This is recommended for delivery via \fBUUCP\fR.
119 /* .IP \fBq\fR
120 /*	Quote white space and other special characters in the command-line
121 /*	\fB$sender\fR, \fB$original_recipient\fR and \fB$recipient\fR
122 /*	address localparts (text to the
123 /*	left of the right-most \fB@\fR character), according to an 8-bit
124 /*	transparent version of RFC 822.
125 /*	This is recommended for delivery via \fBUUCP\fR or \fBBSMTP\fR.
126 /* .sp
127 /*	The result is compatible with the address parsing of command-line
128 /*	recipients by the Postfix \fBsendmail\fR(1) mail submission command.
129 /* .sp
130 /*	The \fBq\fR flag affects only entire addresses, not the partial
131 /*	address information from the \fB$user\fR, \fB$extension\fR or
132 /*	\fB$mailbox\fR command-line macros.
133 /* .IP \fBu\fR
134 /*	Fold the command-line \fB$original_recipient\fR and
135 /*	\fB$recipient\fR address localpart (text to
136 /*	the left of the right-most \fB@\fR character) to lower case.
137 /*	This is recommended for delivery via \fBUUCP\fR.
138 /* .IP \fB.\fR
139 /*	Prepend "\fB.\fR" to lines starting with "\fB.\fR". This is needed
140 /*	by, for example, \fBBSMTP\fR software.
141 /* .IP \fB>\fR
142 /*	Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected
143 /*	by, for example, \fBUUCP\fR software.
144 /* .RE
145 /* .IP "\fBnull_sender\fR=\fIreplacement\fR (default: MAILER-DAEMON)"
146 /*	Replace the null sender address (typically used for delivery
147 /*	status notifications) with the specified text
148 /*	when expanding the \fB$sender\fR command-line macro, and
149 /*	when generating a From_ or Return-Path: message header.
150 /*
151 /*	If the null sender replacement text is a non-empty string
152 /*	then it is affected by the \fBq\fR flag for address quoting
153 /*	in command-line arguments.
154 /*
155 /*	The null sender replacement text may be empty; this form
156 /*	is recommended for content filters that feed mail back into
157 /*	Postfix. The empty sender address is not affected by the
158 /*	\fBq\fR flag for address quoting in command-line arguments.
159 /* .sp
160 /*	Caution: a null sender address is easily mis-parsed by
161 /*	naive software. For example, when the \fBpipe\fR(8) daemon
162 /*	executes a command such as:
163 /* .sp
164 /* .nf
165 /*	    \fIWrong\fR: command -f$sender -- $recipient
166 /* .fi
167 /* .IP
168 /*	the command will mis-parse the -f option value when the
169 /*	sender address is a null string.  For correct parsing,
170 /*	specify \fB$sender\fR as an argument by itself:
171 /* .sp
172 /* .nf
173 /*	    \fIRight\fR: command -f $sender -- $recipient
174 /* .fi
175 /* .IP
176 /*	This feature is available as of Postfix 2.3.
177 /* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
178 /*	Don't deliver messages that exceed this size limit (in
179 /*	bytes); return them to the sender instead.
180 /* .IP "\fBuser\fR=\fIusername\fR (required)"
181 /* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
182 /*	Execute the external command with the user ID and group ID of the
183 /*	specified \fIusername\fR.  The software refuses to execute
184 /*	commands with root privileges, or with the privileges of the
185 /*	mail system owner. If \fIgroupname\fR is specified, the
186 /*	corresponding group ID is used instead of the group ID of
187 /*	\fIusername\fR.
188 /* .IP "\fBargv\fR=\fIcommand\fR... (required)"
189 /*	The command to be executed. This must be specified as the
190 /*	last command attribute.
191 /*	The command is executed directly, i.e. without interpretation of
192 /*	shell meta characters by a shell command interpreter.
193 /* .sp
194 /*	In the command argument vector, the following macros are recognized
195 /*	and replaced with corresponding information from the Postfix queue
196 /*	manager delivery request.
197 /* .sp
198 /*	In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
199 /*	$(\fIname\fR) are also recognized.  Specify \fB$$\fR where a single
200 /*	\fB$\fR is wanted.
201 /* .RS
202 /* .IP \fB${\fBclient_address\fR}\fR
203 /*	This macro expands to the remote client network address.
204 /* .sp
205 /*	This feature is available as of Postfix 2.2.
206 /* .IP \fB${\fBclient_helo\fR}\fR
207 /*	This macro expands to the remote client HELO command parameter.
208 /* .sp
209 /*	This feature is available as of Postfix 2.2.
210 /* .IP \fB${\fBclient_hostname\fR}\fR
211 /*	This macro expands to the remote client hostname.
212 /* .sp
213 /*	This feature is available as of Postfix 2.2.
214 /* .IP \fB${\fBclient_port\fR}\fR
215 /*	This macro expands to the remote client TCP port number.
216 /* .sp
217 /*	This feature is available as of Postfix 2.5.
218 /* .IP \fB${\fBclient_protocol\fR}\fR
219 /*	This macro expands to the remote client protocol.
220 /* .sp
221 /*	This feature is available as of Postfix 2.2.
222 /* .IP \fB${\fBdomain\fR}\fR
223 /*	This macro expands to the domain portion of the recipient
224 /*	address.  For example, with an address \fIuser+foo@domain\fR
225 /*	the domain is \fIdomain\fR.
226 /* .sp
227 /*	This information is modified by the \fBh\fR flag for case folding.
228 /* .sp
229 /*	This feature is available as of Postfix 2.5.
230 /* .IP \fB${\fBextension\fR}\fR
231 /*	This macro expands to the extension part of a recipient address.
232 /*	For example, with an address \fIuser+foo@domain\fR the extension is
233 /*	\fIfoo\fR.
234 /* .sp
235 /*	A command-line argument that contains \fB${\fBextension\fR}\fR expands
236 /*	into as many command-line arguments as there are recipients.
237 /* .sp
238 /*	This information is modified by the \fBu\fR flag for case folding.
239 /* .IP \fB${\fBmailbox\fR}\fR
240 /*	This macro expands to the complete local part of a recipient address.
241 /*	For example, with an address \fIuser+foo@domain\fR the mailbox is
242 /*	\fIuser+foo\fR.
243 /* .sp
244 /*	A command-line argument that contains \fB${\fBmailbox\fR}\fR
245 /*	expands to as many command-line arguments as there are recipients.
246 /* .sp
247 /*	This information is modified by the \fBu\fR flag for case folding.
248 /* .IP \fB${\fBnexthop\fR}\fR
249 /*	This macro expands to the next-hop hostname.
250 /* .sp
251 /*	This information is modified by the \fBh\fR flag for case folding.
252 /* .IP \fB${\fBoriginal_recipient\fR}\fR
253 /*	This macro expands to the complete recipient address before any
254 /*	address rewriting or aliasing.
255 /* .sp
256 /*	A command-line argument that contains
257 /*	\fB${\fBoriginal_recipient\fR}\fR expands to as many
258 /*	command-line arguments as there are recipients.
259 /* .sp
260 /*	This information is modified by the \fBhqu\fR flags for quoting
261 /*	and case folding.
262 /* .sp
263 /*	This feature is available as of Postfix 2.5.
264 /* .IP \fB${\fBrecipient\fR}\fR
265 /*	This macro expands to the complete recipient address.
266 /* .sp
267 /*	A command-line argument that contains \fB${\fBrecipient\fR}\fR
268 /*	expands to as many command-line arguments as there are recipients.
269 /* .sp
270 /*	This information is modified by the \fBhqu\fR flags for quoting
271 /*	and case folding.
272 /* .IP \fB${\fBsasl_method\fR}\fR
273 /*	This macro expands to the name of the SASL authentication
274 /*	mechanism in the AUTH command when the Postfix SMTP server
275 /*	received the message.
276 /* .sp
277 /*	This feature is available as of Postfix 2.2.
278 /* .IP \fB${\fBsasl_sender\fR}\fR
279 /*	This macro expands to the SASL sender name (i.e. the original
280 /*	submitter as per RFC 4954) in the MAIL FROM command when
281 /*	the Postfix SMTP server received the message.
282 /* .sp
283 /*	This feature is available as of Postfix 2.2.
284 /* .IP \fB${\fBsasl_username\fR}\fR
285 /*	This macro expands to the SASL user name in the AUTH command
286 /*	when the Postfix SMTP server received the message.
287 /* .sp
288 /*	This feature is available as of Postfix 2.2.
289 /* .IP \fB${\fBsender\fR}\fR
290 /*	This macro expands to the envelope sender address. By default,
291 /*	the null sender address expands to MAILER-DAEMON; this can
292 /*	be changed with the \fBnull_sender\fR attribute, as described
293 /*	above.
294 /* .sp
295 /*	This information is modified by the \fBq\fR flag for quoting.
296 /* .IP \fB${\fBsize\fR}\fR
297 /*	This macro expands to Postfix's idea of the message size, which
298 /*	is an approximation of the size of the message as delivered.
299 /* .IP \fB${\fBuser\fR}\fR
300 /*	This macro expands to the username part of a recipient address.
301 /*	For example, with an address \fIuser+foo@domain\fR the username
302 /*	part is \fIuser\fR.
303 /* .sp
304 /*	A command-line argument that contains \fB${\fBuser\fR}\fR expands
305 /*	into as many command-line arguments as there are recipients.
306 /* .sp
307 /*	This information is modified by the \fBu\fR flag for case folding.
308 /* .RE
309 /* STANDARDS
310 /*	RFC 3463 (Enhanced status codes)
311 /* DIAGNOSTICS
312 /*	Command exit status codes are expected to
313 /*	follow the conventions defined in <\fBsysexits.h\fR>.
314 /*	Exit status 0 means normal successful completion.
315 /*
316 /*	In the case of a non-zero exit status, a limited amount of
317 /*	command output is reported in an delivery status notification.
318 /*	When the output begins with a 4.X.X or 5.X.X enhanced status
319 /*	code, the status code takes precedence over the non-zero
320 /*	exit status (Postfix version 2.3 and later).
321 /*
322 /*	Problems and transactions are logged to \fBsyslogd\fR(8).
323 /*	Corrupted message files are marked so that the queue manager
324 /*	can move them to the \fBcorrupt\fR queue for further inspection.
325 /* SECURITY
326 /* .fi
327 /* .ad
328 /*	This program needs a dual personality 1) to access the private
329 /*	Postfix queue and IPC mechanisms, and 2) to execute external
330 /*	commands as the specified user. It is therefore security sensitive.
331 /* CONFIGURATION PARAMETERS
332 /* .ad
333 /* .fi
334 /*	Changes to \fBmain.cf\fR are picked up automatically as \fBpipe\fR(8)
335 /*	processes run for only a limited amount of time. Use the command
336 /*	"\fBpostfix reload\fR" to speed up a change.
337 /*
338 /*	The text below provides only a parameter summary. See
339 /*	\fBpostconf\fR(5) for more details including examples.
340 /* RESOURCE AND RATE CONTROLS
341 /* .ad
342 /* .fi
343 /*	In the text below, \fItransport\fR is the first field in a
344 /*	\fBmaster.cf\fR entry.
345 /* .IP "\fItransport\fB_destination_concurrency_limit ($default_destination_concurrency_limit)\fR"
346 /*	Limit the number of parallel deliveries to the same destination,
347 /*	for delivery via the named \fItransport\fR.
348 /*	The limit is enforced by the Postfix queue manager.
349 /* .IP "\fItransport\fB_destination_recipient_limit ($default_destination_recipient_limit)\fR"
350 /*	Limit the number of recipients per message delivery, for delivery
351 /*	via the named \fItransport\fR.
352 /*	The limit is enforced by the Postfix queue manager.
353 /* .IP "\fItransport\fB_time_limit ($command_time_limit)\fR"
354 /*	Limit the time for delivery to external command, for delivery via
355 /*	the named \fItransport\fR.
356 /*	The limit is enforced by the pipe delivery agent.
357 /*
358 /*	Postfix 2.4 and later support a suffix that specifies the
359 /*	time unit: s (seconds), m (minutes), h (hours), d (days),
360 /*	w (weeks). The default time unit is seconds.
361 /* MISCELLANEOUS CONTROLS
362 /* .ad
363 /* .fi
364 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
365 /*	The default location of the Postfix main.cf and master.cf
366 /*	configuration files.
367 /* .IP "\fBdaemon_timeout (18000s)\fR"
368 /*	How much time a Postfix daemon process may take to handle a
369 /*	request before it is terminated by a built-in watchdog timer.
370 /* .IP "\fBdelay_logging_resolution_limit (2)\fR"
371 /*	The maximal number of digits after the decimal point when logging
372 /*	sub-second delay values.
373 /* .IP "\fBexport_environment (see 'postconf -d' output)\fR"
374 /*	The list of environment variables that a Postfix process will export
375 /*	to non-Postfix processes.
376 /* .IP "\fBipc_timeout (3600s)\fR"
377 /*	The time limit for sending or receiving information over an internal
378 /*	communication channel.
379 /* .IP "\fBmail_owner (postfix)\fR"
380 /*	The UNIX system account that owns the Postfix queue and most Postfix
381 /*	daemon processes.
382 /* .IP "\fBmax_idle (100s)\fR"
383 /*	The maximum amount of time that an idle Postfix daemon process waits
384 /*	for an incoming connection before terminating voluntarily.
385 /* .IP "\fBmax_use (100)\fR"
386 /*	The maximal number of incoming connections that a Postfix daemon
387 /*	process will service before terminating voluntarily.
388 /* .IP "\fBprocess_id (read-only)\fR"
389 /*	The process ID of a Postfix command or daemon process.
390 /* .IP "\fBprocess_name (read-only)\fR"
391 /*	The process name of a Postfix command or daemon process.
392 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
393 /*	The location of the Postfix top-level queue directory.
394 /* .IP "\fBrecipient_delimiter (empty)\fR"
395 /*	The separator between user names and address extensions (user+foo).
396 /* .IP "\fBsyslog_facility (mail)\fR"
397 /*	The syslog facility of Postfix logging.
398 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
399 /*	The mail system name that is prepended to the process name in syslog
400 /*	records, so that "smtpd" becomes, for example, "postfix/smtpd".
401 /* SEE ALSO
402 /*	qmgr(8), queue manager
403 /*	bounce(8), delivery status reports
404 /*	postconf(5), configuration parameters
405 /*	master(5), generic daemon options
406 /*	master(8), process manager
407 /*	syslogd(8), system logging
408 /* LICENSE
409 /* .ad
410 /* .fi
411 /*	The Secure Mailer license must be distributed with this software.
412 /* AUTHOR(S)
413 /*	Wietse Venema
414 /*	IBM T.J. Watson Research
415 /*	P.O. Box 704
416 /*	Yorktown Heights, NY 10598, USA
417 /*--*/
418 
419 /* System library. */
420 
421 #include <sys_defs.h>
422 #include <unistd.h>
423 #include <stdlib.h>
424 #include <string.h>
425 #include <pwd.h>
426 #include <grp.h>
427 #include <fcntl.h>
428 #include <ctype.h>
429 
430 #ifdef STRCASECMP_IN_STRINGS_H
431 #include <strings.h>
432 #endif
433 
434 /* Utility library. */
435 
436 #include <msg.h>
437 #include <vstream.h>
438 #include <vstring.h>
439 #include <argv.h>
440 #include <htable.h>
441 #include <dict.h>
442 #include <iostuff.h>
443 #include <mymalloc.h>
444 #include <mac_parse.h>
445 #include <set_eugid.h>
446 #include <split_at.h>
447 #include <stringops.h>
448 
449 /* Global library. */
450 
451 #include <recipient_list.h>
452 #include <deliver_request.h>
453 #include <mail_params.h>
454 #include <mail_version.h>
455 #include <mail_conf.h>
456 #include <bounce.h>
457 #include <defer.h>
458 #include <deliver_completed.h>
459 #include <sent.h>
460 #include <pipe_command.h>
461 #include <mail_copy.h>
462 #include <mail_addr.h>
463 #include <canon_addr.h>
464 #include <split_addr.h>
465 #include <off_cvt.h>
466 #include <quote_822_local.h>
467 #include <flush_clnt.h>
468 #include <dsn_util.h>
469 #include <dsn_buf.h>
470 #include <sys_exits.h>
471 #include <delivered_hdr.h>
472 #include <fold_addr.h>
473 
474 /* Single server skeleton. */
475 
476 #include <mail_server.h>
477 
478 /* Application-specific. */
479 
480  /*
481   * The mini symbol table name and keys used for expanding macros in
482   * command-line arguments.
483   *
484   * XXX Update  the parse_callback() routine when something gets added here,
485   * even when the macro is not recipient dependent.
486   */
487 #define PIPE_DICT_TABLE		"pipe_command"	/* table name */
488 #define PIPE_DICT_NEXTHOP	"nexthop"	/* key */
489 #define PIPE_DICT_RCPT		"recipient"	/* key */
490 #define PIPE_DICT_ORIG_RCPT	"original_recipient"	/* key */
491 #define PIPE_DICT_SENDER	"sender"/* key */
492 #define PIPE_DICT_USER		"user"	/* key */
493 #define PIPE_DICT_EXTENSION	"extension"	/* key */
494 #define PIPE_DICT_MAILBOX	"mailbox"	/* key */
495 #define PIPE_DICT_DOMAIN	"domain"/* key */
496 #define PIPE_DICT_SIZE		"size"	/* key */
497 #define PIPE_DICT_CLIENT_ADDR	"client_address"	/* key */
498 #define PIPE_DICT_CLIENT_NAME	"client_hostname"	/* key */
499 #define PIPE_DICT_CLIENT_PORT	"client_port"	/* key */
500 #define PIPE_DICT_CLIENT_PROTO	"client_protocol"	/* key */
501 #define PIPE_DICT_CLIENT_HELO	"client_helo"	/* key */
502 #define PIPE_DICT_SASL_METHOD	"sasl_method"	/* key */
503 #define PIPE_DICT_SASL_USERNAME	"sasl_username"	/* key */
504 #define PIPE_DICT_SASL_SENDER	"sasl_sender"	/* key */
505 
506  /*
507   * Flags used to pass back the type of special parameter found by
508   * parse_callback.
509   */
510 #define PIPE_FLAG_RCPT		(1<<0)
511 #define PIPE_FLAG_USER		(1<<1)
512 #define PIPE_FLAG_EXTENSION	(1<<2)
513 #define PIPE_FLAG_MAILBOX	(1<<3)
514 #define PIPE_FLAG_DOMAIN	(1<<4)
515 #define PIPE_FLAG_ORIG_RCPT	(1<<5)
516 
517  /*
518   * Additional flags. These are colocated with mail_copy() flags. Allow some
519   * space for extension of the mail_copy() interface.
520   */
521 #define PIPE_OPT_FOLD_BASE	(16)
522 #define PIPE_OPT_FOLD_USER	(FOLD_ADDR_USER << PIPE_OPT_FOLD_BASE)
523 #define PIPE_OPT_FOLD_HOST	(FOLD_ADDR_HOST << PIPE_OPT_FOLD_BASE)
524 #define PIPE_OPT_QUOTE_LOCAL	(1 << (PIPE_OPT_FOLD_BASE + 2))
525 #define PIPE_OPT_FINAL_DELIVERY	(1 << (PIPE_OPT_FOLD_BASE + 3))
526 
527 #define PIPE_OPT_FOLD_ALL	(FOLD_ADDR_ALL << PIPE_OPT_FOLD_BASE)
528 #define PIPE_OPT_FOLD_FLAGS(f) \
529 	(((f) & PIPE_OPT_FOLD_ALL) >> PIPE_OPT_FOLD_BASE)
530 
531  /*
532   * Tunable parameters. Values are taken from the config file, after
533   * prepending the service name to _name, and so on.
534   */
535 int     var_command_maxtime;		/* system-wide */
536 
537  /*
538   * For convenience. Instead of passing around lists of parameters, bundle
539   * them up in convenient structures.
540   */
541 
542  /*
543   * Structure for service-specific configuration parameters.
544   */
545 typedef struct {
546     int     time_limit;			/* per-service time limit */
547 } PIPE_PARAMS;
548 
549  /*
550   * Structure for command-line parameters.
551   */
552 typedef struct {
553     char  **command;			/* argument vector */
554     uid_t   uid;			/* command privileges */
555     gid_t   gid;			/* command privileges */
556     int     flags;			/* mail_copy() flags */
557     char   *exec_dir;			/* working directory */
558     char   *chroot_dir;			/* chroot directory */
559     VSTRING *eol;			/* output record delimiter */
560     VSTRING *null_sender;		/* null sender expansion */
561     off_t   size_limit;			/* max size in bytes we will accept */
562 } PIPE_ATTR;
563 
564  /*
565   * Structure for command-line parameter macro expansion.
566   */
567 typedef struct {
568     const char *service;		/* for warnings */
569     int     expand_flag;		/* callback result */
570 } PIPE_STATE;
571 
572  /*
573   * Silly little macros.
574   */
575 #define STR	vstring_str
576 
577 /* parse_callback - callback for mac_parse() */
578 
579 static int parse_callback(int type, VSTRING *buf, char *context)
580 {
581     PIPE_STATE *state = (PIPE_STATE *) context;
582     struct cmd_flags {
583 	const char *name;
584 	int     flags;
585     };
586     static struct cmd_flags cmd_flags[] = {
587 	PIPE_DICT_NEXTHOP, 0,
588 	PIPE_DICT_RCPT, PIPE_FLAG_RCPT,
589 	PIPE_DICT_ORIG_RCPT, PIPE_FLAG_ORIG_RCPT,
590 	PIPE_DICT_SENDER, 0,
591 	PIPE_DICT_USER, PIPE_FLAG_USER,
592 	PIPE_DICT_EXTENSION, PIPE_FLAG_EXTENSION,
593 	PIPE_DICT_MAILBOX, PIPE_FLAG_MAILBOX,
594 	PIPE_DICT_DOMAIN, PIPE_FLAG_DOMAIN,
595 	PIPE_DICT_SIZE, 0,
596 	PIPE_DICT_CLIENT_ADDR, 0,
597 	PIPE_DICT_CLIENT_NAME, 0,
598 	PIPE_DICT_CLIENT_PORT, 0,
599 	PIPE_DICT_CLIENT_PROTO, 0,
600 	PIPE_DICT_CLIENT_HELO, 0,
601 	PIPE_DICT_SASL_METHOD, 0,
602 	PIPE_DICT_SASL_USERNAME, 0,
603 	PIPE_DICT_SASL_SENDER, 0,
604 	0, 0,
605     };
606     struct cmd_flags *p;
607 
608     /*
609      * See if this command-line argument references a special macro.
610      */
611     if (type == MAC_PARSE_VARNAME) {
612 	for (p = cmd_flags; /* see below */ ; p++) {
613 	    if (p->name == 0) {
614 		msg_warn("file %s/%s: service %s: unknown macro name: \"%s\"",
615 			 var_config_dir, MASTER_CONF_FILE,
616 			 state->service, vstring_str(buf));
617 		return (MAC_PARSE_ERROR);
618 	    } else if (strcmp(vstring_str(buf), p->name) == 0) {
619 		state->expand_flag |= p->flags;
620 		return (0);
621 	    }
622 	}
623     }
624     return (0);
625 }
626 
627 /* morph_recipient - morph a recipient address */
628 
629 static void morph_recipient(VSTRING *buf, const char *address, int flags)
630 {
631 
632     /*
633      * Quote the recipient address as appropriate.
634      */
635     if (flags & PIPE_OPT_QUOTE_LOCAL)
636 	quote_822_local(buf, address);
637     else
638 	vstring_strcpy(buf, address);
639 
640     /*
641      * Fold the recipient address as appropriate.
642      */
643     if (flags & PIPE_OPT_FOLD_ALL)
644 	fold_addr(STR(buf), PIPE_OPT_FOLD_FLAGS(flags));
645 }
646 
647 /* expand_argv - expand macros in the argument vector */
648 
649 static ARGV *expand_argv(const char *service, char **argv,
650 			         RECIPIENT_LIST *rcpt_list, int flags)
651 {
652     VSTRING *buf = vstring_alloc(100);
653     ARGV   *result;
654     char  **cpp;
655     PIPE_STATE state;
656     int     i;
657     char   *ext;
658     char   *dom;
659 
660     /*
661      * This appears to be simple operation (replace $name by its expansion).
662      * However, it becomes complex because a command-line argument that
663      * references $recipient must expand to as many command-line arguments as
664      * there are recipients (that's wat programs called by sendmail expect).
665      * So we parse each command-line argument, and depending on what we find,
666      * we either expand the argument just once, or we expand it once for each
667      * recipient. In either case we end up parsing the command-line argument
668      * twice. The amount of CPU time wasted will be negligible.
669      *
670      * Note: we can't use recursive macro expansion here, because recursion
671      * would screw up mail addresses that contain $ characters.
672      */
673 #define NO	0
674 #define EARLY_RETURN(x) { argv_free(result); vstring_free(buf); return (x); }
675 
676     result = argv_alloc(1);
677     for (cpp = argv; *cpp; cpp++) {
678 	state.service = service;
679 	state.expand_flag = 0;
680 	if (mac_parse(*cpp, parse_callback, (char *) &state) & MAC_PARSE_ERROR)
681 	    EARLY_RETURN(0);
682 	if (state.expand_flag == 0) {		/* no $recipient etc. */
683 	    argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
684 	} else {				/* contains $recipient etc. */
685 	    for (i = 0; i < rcpt_list->len; i++) {
686 
687 		/*
688 		 * This argument contains $recipient.
689 		 */
690 		if (state.expand_flag & PIPE_FLAG_RCPT) {
691 		    morph_recipient(buf, rcpt_list->info[i].address, flags);
692 		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT, STR(buf));
693 		}
694 
695 		/*
696 		 * This argument contains $original_recipient.
697 		 */
698 		if (state.expand_flag & PIPE_FLAG_ORIG_RCPT) {
699 		    morph_recipient(buf, rcpt_list->info[i].orig_addr, flags);
700 		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_ORIG_RCPT, STR(buf));
701 		}
702 
703 		/*
704 		 * This argument contains $user. Extract the plain user name.
705 		 * Either anything to the left of the extension delimiter or,
706 		 * in absence of the latter, anything to the left of the
707 		 * rightmost @.
708 		 *
709 		 * Beware: if the user name is blank (e.g. +user@host), the
710 		 * argument is suppressed. This is necessary to allow for
711 		 * cyrus bulletin-board (global mailbox) delivery. XXX But,
712 		 * skipping empty user parts will also prevent other
713 		 * expansions of this specific command-line argument.
714 		 */
715 		if (state.expand_flag & PIPE_FLAG_USER) {
716 		    morph_recipient(buf, rcpt_list->info[i].address,
717 				    flags & PIPE_OPT_FOLD_ALL);
718 		    if (split_at_right(STR(buf), '@') == 0)
719 			msg_warn("no @ in recipient address: %s",
720 				 rcpt_list->info[i].address);
721 		    if (*var_rcpt_delim)
722 			split_addr(STR(buf), *var_rcpt_delim);
723 		    if (*STR(buf) == 0)
724 			continue;
725 		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
726 		}
727 
728 		/*
729 		 * This argument contains $extension. Extract the recipient
730 		 * extension: anything between the leftmost extension
731 		 * delimiter and the rightmost @. The extension may be blank.
732 		 */
733 		if (state.expand_flag & PIPE_FLAG_EXTENSION) {
734 		    morph_recipient(buf, rcpt_list->info[i].address,
735 				    flags & PIPE_OPT_FOLD_ALL);
736 		    if (split_at_right(STR(buf), '@') == 0)
737 			msg_warn("no @ in recipient address: %s",
738 				 rcpt_list->info[i].address);
739 		    if (*var_rcpt_delim == 0
740 		      || (ext = split_addr(STR(buf), *var_rcpt_delim)) == 0)
741 			ext = "";		/* insert null arg */
742 		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext);
743 		}
744 
745 		/*
746 		 * This argument contains $mailbox. Extract the mailbox name:
747 		 * anything to the left of the rightmost @.
748 		 */
749 		if (state.expand_flag & PIPE_FLAG_MAILBOX) {
750 		    morph_recipient(buf, rcpt_list->info[i].address,
751 				    flags & PIPE_OPT_FOLD_ALL);
752 		    if (split_at_right(STR(buf), '@') == 0)
753 			msg_warn("no @ in recipient address: %s",
754 				 rcpt_list->info[i].address);
755 		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf));
756 		}
757 
758 		/*
759 		 * This argument contains $domain. Extract the domain name:
760 		 * anything to the right of the rightmost @.
761 		 */
762 		if (state.expand_flag & PIPE_FLAG_DOMAIN) {
763 		    morph_recipient(buf, rcpt_list->info[i].address,
764 				    flags & PIPE_OPT_FOLD_ALL);
765 		    dom = split_at_right(STR(buf), '@');
766 		    if (dom == 0) {
767 			msg_warn("no @ in recipient address: %s",
768 				 rcpt_list->info[i].address);
769 			dom = "";		/* insert null arg */
770 		    }
771 		    dict_update(PIPE_DICT_TABLE, PIPE_DICT_DOMAIN, dom);
772 		}
773 
774 		/*
775 		 * Done.
776 		 */
777 		argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
778 	    }
779 	}
780     }
781     argv_terminate(result);
782     vstring_free(buf);
783     return (result);
784 }
785 
786 /* get_service_params - get service-name dependent config information */
787 
788 static void get_service_params(PIPE_PARAMS *config, char *service)
789 {
790     const char *myname = "get_service_params";
791 
792     /*
793      * Figure out the command time limit for this transport.
794      */
795     config->time_limit =
796 	get_mail_conf_time2(service, _MAXTIME, var_command_maxtime, 's', 1, 0);
797 
798     /*
799      * Give the poor tester a clue of what is going on.
800      */
801     if (msg_verbose)
802 	msg_info("%s: time_limit %d", myname, config->time_limit);
803 }
804 
805 /* get_service_attr - get command-line attributes */
806 
807 static void get_service_attr(PIPE_ATTR *attr, char **argv)
808 {
809     const char *myname = "get_service_attr";
810     struct passwd *pwd;
811     struct group *grp;
812     char   *user;			/* user name */
813     char   *group;			/* group name */
814     char   *size;			/* max message size */
815     char   *cp;
816 
817     /*
818      * Initialize.
819      */
820     user = 0;
821     group = 0;
822     attr->command = 0;
823     attr->flags = 0;
824     attr->exec_dir = 0;
825     attr->chroot_dir = 0;
826     attr->eol = vstring_strcpy(vstring_alloc(1), "\n");
827     attr->null_sender = vstring_strcpy(vstring_alloc(1), MAIL_ADDR_MAIL_DAEMON);
828     attr->size_limit = 0;
829 
830     /*
831      * Iterate over the command-line attribute list.
832      */
833     for ( /* void */ ; *argv != 0; argv++) {
834 
835 	/*
836 	 * flags=stuff
837 	 */
838 	if (strncasecmp("flags=", *argv, sizeof("flags=") - 1) == 0) {
839 	    for (cp = *argv + sizeof("flags=") - 1; *cp; cp++) {
840 		switch (*cp) {
841 		case 'B':
842 		    attr->flags |= MAIL_COPY_BLANK;
843 		    break;
844 		case 'D':
845 		    attr->flags |= MAIL_COPY_DELIVERED;
846 		    break;
847 		case 'F':
848 		    attr->flags |= MAIL_COPY_FROM;
849 		    break;
850 		case 'O':
851 		    attr->flags |= MAIL_COPY_ORIG_RCPT;
852 		    break;
853 		case 'R':
854 		    attr->flags |= MAIL_COPY_RETURN_PATH;
855 		    break;
856 		case 'X':
857 		    attr->flags |= PIPE_OPT_FINAL_DELIVERY;
858 		    break;
859 		case '.':
860 		    attr->flags |= MAIL_COPY_DOT;
861 		    break;
862 		case '>':
863 		    attr->flags |= MAIL_COPY_QUOTE;
864 		    break;
865 		case 'h':
866 		    attr->flags |= PIPE_OPT_FOLD_HOST;
867 		    break;
868 		case 'q':
869 		    attr->flags |= PIPE_OPT_QUOTE_LOCAL;
870 		    break;
871 		case 'u':
872 		    attr->flags |= PIPE_OPT_FOLD_USER;
873 		    break;
874 		default:
875 		    msg_fatal("unknown flag: %c (ignored)", *cp);
876 		    break;
877 		}
878 	    }
879 	}
880 
881 	/*
882 	 * user=username[:groupname]
883 	 */
884 	else if (strncasecmp("user=", *argv, sizeof("user=") - 1) == 0) {
885 	    user = *argv + sizeof("user=") - 1;
886 	    if ((group = split_at(user, ':')) != 0)	/* XXX clobbers argv */
887 		if (*group == 0)
888 		    group = 0;
889 	    if ((pwd = getpwnam(user)) == 0)
890 		msg_fatal("%s: unknown username: %s", myname, user);
891 	    attr->uid = pwd->pw_uid;
892 	    if (group != 0) {
893 		if ((grp = getgrnam(group)) == 0)
894 		    msg_fatal("%s: unknown group: %s", myname, group);
895 		attr->gid = grp->gr_gid;
896 	    } else {
897 		attr->gid = pwd->pw_gid;
898 	    }
899 	}
900 
901 	/*
902 	 * directory=string
903 	 */
904 	else if (strncasecmp("directory=", *argv, sizeof("directory=") - 1) == 0) {
905 	    attr->exec_dir = mystrdup(*argv + sizeof("directory=") - 1);
906 	}
907 
908 	/*
909 	 * chroot=string
910 	 */
911 	else if (strncasecmp("chroot=", *argv, sizeof("chroot=") - 1) == 0) {
912 	    attr->chroot_dir = mystrdup(*argv + sizeof("chroot=") - 1);
913 	}
914 
915 	/*
916 	 * eol=string
917 	 */
918 	else if (strncasecmp("eol=", *argv, sizeof("eol=") - 1) == 0) {
919 	    unescape(attr->eol, *argv + sizeof("eol=") - 1);
920 	}
921 
922 	/*
923 	 * null_sender=string
924 	 */
925 	else if (strncasecmp("null_sender=", *argv, sizeof("null_sender=") - 1) == 0) {
926 	    vstring_strcpy(attr->null_sender, *argv + sizeof("null_sender=") - 1);
927 	}
928 
929 	/*
930 	 * size=max_message_size (in bytes)
931 	 */
932 	else if (strncasecmp("size=", *argv, sizeof("size=") - 1) == 0) {
933 	    size = *argv + sizeof("size=") - 1;
934 	    if ((attr->size_limit = off_cvt_string(size)) < 0)
935 		msg_fatal("%s: bad size= value: %s", myname, size);
936 	}
937 
938 	/*
939 	 * argv=command...
940 	 */
941 	else if (strncasecmp("argv=", *argv, sizeof("argv=") - 1) == 0) {
942 	    *argv += sizeof("argv=") - 1;	/* XXX clobbers argv */
943 	    attr->command = argv;
944 	    break;
945 	}
946 
947 	/*
948 	 * Bad.
949 	 */
950 	else
951 	    msg_fatal("unknown attribute name: %s", *argv);
952     }
953 
954     /*
955      * Sanity checks. Verify that every member has an acceptable value.
956      */
957     if (user == 0)
958 	msg_fatal("missing user= command-line attribute");
959     if (attr->command == 0)
960 	msg_fatal("missing argv= command-line attribute");
961     if (attr->uid == 0)
962 	msg_fatal("user= command-line attribute specifies root privileges");
963     if (attr->uid == var_owner_uid)
964 	msg_fatal("user= command-line attribute specifies mail system owner %s",
965 		  var_mail_owner);
966     if (attr->gid == 0)
967 	msg_fatal("user= command-line attribute specifies privileged group id 0");
968     if (attr->gid == var_owner_gid)
969 	msg_fatal("user= command-line attribute specifies mail system owner %s group id %ld",
970 		  var_mail_owner, (long) attr->gid);
971     if (attr->gid == var_sgid_gid)
972 	msg_fatal("user= command-line attribute specifies mail system %s group id %ld",
973 		  var_sgid_group, (long) attr->gid);
974 
975     /*
976      * Give the poor tester a clue of what is going on.
977      */
978     if (msg_verbose)
979 	msg_info("%s: uid %ld, gid %ld, flags %d, size %ld",
980 		 myname, (long) attr->uid, (long) attr->gid,
981 		 attr->flags, (long) attr->size_limit);
982 }
983 
984 /* eval_command_status - do something with command completion status */
985 
986 static int eval_command_status(int command_status, char *service,
987 			          DELIVER_REQUEST *request, PIPE_ATTR *attr,
988 			               DSN_BUF *why)
989 {
990     RECIPIENT *rcpt;
991     int     status;
992     int     result = 0;
993     int     n;
994 
995     /*
996      * Depending on the result, bounce or defer the message, and mark the
997      * recipient as done where appropriate.
998      */
999     switch (command_status) {
1000     case PIPE_STAT_OK:
1001 	dsb_update(why, "2.0.0", (attr->flags & PIPE_OPT_FINAL_DELIVERY) ?
1002 		   "delivered" : "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
1003 		   "delivered via %s service", service);
1004 	(void) DSN_FROM_DSN_BUF(why);
1005 	for (n = 0; n < request->rcpt_list.len; n++) {
1006 	    rcpt = request->rcpt_list.info + n;
1007 	    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
1008 			  request->queue_id, &request->msg_stats, rcpt,
1009 			  service, &why->dsn);
1010 	    if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
1011 		deliver_completed(request->fp, rcpt->offset);
1012 	    result |= status;
1013 	}
1014 	break;
1015     case PIPE_STAT_BOUNCE:
1016     case PIPE_STAT_DEFER:
1017 	(void) DSN_FROM_DSN_BUF(why);
1018 	if (STR(why->status)[0] != '4') {
1019 	    for (n = 0; n < request->rcpt_list.len; n++) {
1020 		rcpt = request->rcpt_list.info + n;
1021 		status = bounce_append(DEL_REQ_TRACE_FLAGS(request->flags),
1022 				       request->queue_id,
1023 				       &request->msg_stats, rcpt,
1024 				       service, &why->dsn);
1025 		if (status == 0)
1026 		    deliver_completed(request->fp, rcpt->offset);
1027 		result |= status;
1028 	    }
1029 	} else {
1030 	    for (n = 0; n < request->rcpt_list.len; n++) {
1031 		rcpt = request->rcpt_list.info + n;
1032 		result |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
1033 				       request->queue_id,
1034 				       &request->msg_stats, rcpt,
1035 				       service, &why->dsn);
1036 	    }
1037 	}
1038 	break;
1039     case PIPE_STAT_CORRUPT:
1040 	/* XXX DSN should we send something? */
1041 	result |= DEL_STAT_DEFER;
1042 	break;
1043     default:
1044 	msg_panic("eval_command_status: bad status %d", command_status);
1045 	/* NOTREACHED */
1046     }
1047 
1048     return (result);
1049 }
1050 
1051 /* deliver_message - deliver message with extreme prejudice */
1052 
1053 static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
1054 {
1055     const char *myname = "deliver_message";
1056     static PIPE_PARAMS conf;
1057     static PIPE_ATTR attr;
1058     RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
1059     DSN_BUF *why = dsb_create();
1060     VSTRING *buf;
1061     ARGV   *expanded_argv = 0;
1062     int     deliver_status;
1063     int     command_status;
1064     ARGV   *export_env;
1065     const char *sender;
1066 
1067 #define DELIVER_MSG_CLEANUP() { \
1068 	dsb_free(why); \
1069 	if (expanded_argv) argv_free(expanded_argv); \
1070     }
1071 
1072     if (msg_verbose)
1073 	msg_info("%s: from <%s>", myname, request->sender);
1074 
1075     /*
1076      * Sanity checks. The get_service_params() and get_service_attr()
1077      * routines also do some sanity checks. Look up service attributes and
1078      * config information only once. This is safe since the information comes
1079      * from a trusted source, not from the delivery request.
1080      */
1081     if (request->nexthop[0] == 0)
1082 	msg_fatal("empty nexthop hostname");
1083     if (rcpt_list->len <= 0)
1084 	msg_fatal("recipient count: %d", rcpt_list->len);
1085     if (attr.command == 0) {
1086 	get_service_params(&conf, service);
1087 	get_service_attr(&attr, argv);
1088     }
1089 
1090     /*
1091      * The D flag cannot be specified for multi-recipient deliveries.
1092      */
1093     if ((attr.flags & MAIL_COPY_DELIVERED) && (rcpt_list->len > 1)) {
1094 	dsb_simple(why, "4.3.5", "mail system configuration error");
1095 	deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
1096 					     request, &attr, why);
1097 	msg_warn("pipe flag `D' requires %s_destination_recipient_limit = 1",
1098 		 service);
1099 	DELIVER_MSG_CLEANUP();
1100 	return (deliver_status);
1101     }
1102 
1103     /*
1104      * The O flag cannot be specified for multi-recipient deliveries.
1105      */
1106     if ((attr.flags & MAIL_COPY_ORIG_RCPT) && (rcpt_list->len > 1)) {
1107 	dsb_simple(why, "4.3.5", "mail system configuration error");
1108 	deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
1109 					     request, &attr, why);
1110 	msg_warn("pipe flag `O' requires %s_destination_recipient_limit = 1",
1111 		 service);
1112 	DELIVER_MSG_CLEANUP();
1113 	return (deliver_status);
1114     }
1115 
1116     /*
1117      * Check that this agent accepts messages this large.
1118      */
1119     if (attr.size_limit != 0 && request->data_size > attr.size_limit) {
1120 	if (msg_verbose)
1121 	    msg_info("%s: too big: size_limit = %ld, request->data_size = %ld",
1122 		     myname, (long) attr.size_limit, request->data_size);
1123 	dsb_simple(why, "5.2.3", "message too large");
1124 	deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
1125 					     request, &attr, why);
1126 	DELIVER_MSG_CLEANUP();
1127 	return (deliver_status);
1128     }
1129 
1130     /*
1131      * Don't deliver a trace-only request.
1132      */
1133     if (DEL_REQ_TRACE_ONLY(request->flags)) {
1134 	RECIPIENT *rcpt;
1135 	int     status;
1136 	int     n;
1137 
1138 	deliver_status = 0;
1139 	dsb_simple(why, "2.0.0", "delivers to command: %s", attr.command[0]);
1140 	(void) DSN_FROM_DSN_BUF(why);
1141 	for (n = 0; n < request->rcpt_list.len; n++) {
1142 	    rcpt = request->rcpt_list.info + n;
1143 	    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
1144 			  request->queue_id, &request->msg_stats,
1145 			  rcpt, service, &why->dsn);
1146 	    if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
1147 		deliver_completed(request->fp, rcpt->offset);
1148 	    deliver_status |= status;
1149 	}
1150 	DELIVER_MSG_CLEANUP();
1151 	return (deliver_status);
1152     }
1153 
1154     /*
1155      * Report mail delivery loops. By definition, this requires
1156      * single-recipient delivery. Don't silently lose recipients.
1157      */
1158     if (attr.flags & MAIL_COPY_DELIVERED) {
1159 	DELIVERED_HDR_INFO *info;
1160 	RECIPIENT *rcpt;
1161 	int     loop_found;
1162 
1163 	if (request->rcpt_list.len > 1)
1164 	    msg_panic("%s: delivered-to enabled with multi-recipient request",
1165 		      myname);
1166 	info = delivered_hdr_init(request->fp, request->data_offset,
1167 				  FOLD_ADDR_ALL);
1168 	rcpt = request->rcpt_list.info;
1169 	loop_found = delivered_hdr_find(info, rcpt->address);
1170 	delivered_hdr_free(info);
1171 	if (loop_found) {
1172 	    dsb_simple(why, "5.4.6", "mail forwarding loop for %s",
1173 		       rcpt->address);
1174 	    deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
1175 						 request, &attr, why);
1176 	    DELIVER_MSG_CLEANUP();
1177 	    return (deliver_status);
1178 	}
1179     }
1180 
1181     /*
1182      * Deliver. Set the nexthop and sender variables, and expand the command
1183      * argument vector. Recipients will be expanded on the fly. XXX Rewrite
1184      * envelope and header addresses according to transport-specific
1185      * rewriting rules.
1186      */
1187     if (vstream_fseek(request->fp, request->data_offset, SEEK_SET) < 0)
1188 	msg_fatal("seek queue file %s: %m", VSTREAM_PATH(request->fp));
1189 
1190     /*
1191      * A non-empty null sender replacement is subject to the 'q' flag.
1192      */
1193     buf = vstring_alloc(10);
1194     sender = *request->sender ? request->sender : STR(attr.null_sender);
1195     if (*sender && (attr.flags & PIPE_OPT_QUOTE_LOCAL)) {
1196 	quote_822_local(buf, sender);
1197 	dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, STR(buf));
1198     } else
1199 	dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, sender);
1200     if (attr.flags & PIPE_OPT_FOLD_HOST) {
1201 	vstring_strcpy(buf, request->nexthop);
1202 	lowercase(STR(buf));
1203 	dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, STR(buf));
1204     } else
1205 	dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
1206     vstring_sprintf(buf, "%ld", (long) request->data_size);
1207     dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf));
1208     dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_ADDR,
1209 		request->client_addr);
1210     dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_HELO,
1211 		request->client_helo);
1212     dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_NAME,
1213 		request->client_name);
1214     dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PORT,
1215 		request->client_port);
1216     dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PROTO,
1217 		request->client_proto);
1218     dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_METHOD,
1219 		request->sasl_method);
1220     dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_USERNAME,
1221 		request->sasl_username);
1222     dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_SENDER,
1223 		request->sasl_sender);
1224     vstring_free(buf);
1225 
1226     if ((expanded_argv = expand_argv(service, attr.command,
1227 				     rcpt_list, attr.flags)) == 0) {
1228 	dsb_simple(why, "4.3.5", "mail system configuration error");
1229 	deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
1230 					     request, &attr, why);
1231 	DELIVER_MSG_CLEANUP();
1232 	return (deliver_status);
1233     }
1234     export_env = argv_split(var_export_environ, ", \t\r\n");
1235 
1236     command_status = pipe_command(request->fp, why,
1237 				  PIPE_CMD_UID, attr.uid,
1238 				  PIPE_CMD_GID, attr.gid,
1239 				  PIPE_CMD_SENDER, sender,
1240 				  PIPE_CMD_COPY_FLAGS, attr.flags,
1241 				  PIPE_CMD_ARGV, expanded_argv->argv,
1242 				  PIPE_CMD_TIME_LIMIT, conf.time_limit,
1243 				  PIPE_CMD_EOL, STR(attr.eol),
1244 				  PIPE_CMD_EXPORT, export_env->argv,
1245 				  PIPE_CMD_CWD, attr.exec_dir,
1246 				  PIPE_CMD_CHROOT, attr.chroot_dir,
1247 			   PIPE_CMD_ORIG_RCPT, rcpt_list->info[0].orig_addr,
1248 			     PIPE_CMD_DELIVERED, rcpt_list->info[0].address,
1249 				  PIPE_CMD_END);
1250     argv_free(export_env);
1251 
1252     deliver_status = eval_command_status(command_status, service, request,
1253 					 &attr, why);
1254 
1255     /*
1256      * Clean up.
1257      */
1258     DELIVER_MSG_CLEANUP();
1259 
1260     return (deliver_status);
1261 }
1262 
1263 /* pipe_service - perform service for client */
1264 
1265 static void pipe_service(VSTREAM *client_stream, char *service, char **argv)
1266 {
1267     DELIVER_REQUEST *request;
1268     int     status;
1269 
1270     /*
1271      * This routine runs whenever a client connects to the UNIX-domain socket
1272      * dedicated to delivery via external command. What we see below is a
1273      * little protocol to (1) tell the queue manager that we are ready, (2)
1274      * read a request from the queue manager, and (3) report the completion
1275      * status of that request. All connection-management stuff is handled by
1276      * the common code in single_server.c.
1277      */
1278     if ((request = deliver_request_read(client_stream)) != 0) {
1279 	status = deliver_message(request, service, argv);
1280 	deliver_request_done(client_stream, request, status);
1281     }
1282 }
1283 
1284 /* pre_accept - see if tables have changed */
1285 
1286 static void pre_accept(char *unused_name, char **unused_argv)
1287 {
1288     const char *table;
1289 
1290     if ((table = dict_changed_name()) != 0) {
1291 	msg_info("table %s has changed -- restarting", table);
1292 	exit(0);
1293     }
1294 }
1295 
1296 /* drop_privileges - drop privileges most of the time */
1297 
1298 static void drop_privileges(char *unused_name, char **unused_argv)
1299 {
1300     set_eugid(var_owner_uid, var_owner_gid);
1301 }
1302 
1303 /* pre_init - initialize */
1304 
1305 static void pre_init(char *unused_name, char **unused_argv)
1306 {
1307     flush_init();
1308 }
1309 
1310 MAIL_VERSION_STAMP_DECLARE;
1311 
1312 /* main - pass control to the single-threaded skeleton */
1313 
1314 int     main(int argc, char **argv)
1315 {
1316     static const CONFIG_TIME_TABLE time_table[] = {
1317 	VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
1318 	0,
1319     };
1320 
1321     /*
1322      * Fingerprint executables and core dumps.
1323      */
1324     MAIL_VERSION_STAMP_ALLOCATE;
1325 
1326     single_server_main(argc, argv, pipe_service,
1327 		       MAIL_SERVER_TIME_TABLE, time_table,
1328 		       MAIL_SERVER_PRE_INIT, pre_init,
1329 		       MAIL_SERVER_POST_INIT, drop_privileges,
1330 		       MAIL_SERVER_PRE_ACCEPT, pre_accept,
1331 		       MAIL_SERVER_PRIVILEGED,
1332 		       0);
1333 }
1334