697 lines
22 KiB
C
697 lines
22 KiB
C
/*++
|
|
/* NAME
|
|
/* pipe 8
|
|
/* SUMMARY
|
|
/* Postfix delivery to external command
|
|
/* SYNOPSIS
|
|
/* \fBpipe\fR [generic Postfix daemon options] command_attributes...
|
|
/* DESCRIPTION
|
|
/* The \fBpipe\fR daemon processes requests from the Postfix queue
|
|
/* manager to deliver messages to external commands. Each delivery
|
|
/* request specifies a queue file, a sender address, a domain or host
|
|
/* to deliver to, and one or more recipients.
|
|
/* This program expects to be run from the \fBmaster\fR(8) process
|
|
/* manager.
|
|
/*
|
|
/* The \fBpipe\fR daemon updates queue files and marks recipients
|
|
/* as finished, or it informs the queue manager that delivery should
|
|
/* be tried again at a later time. Delivery problem reports are sent
|
|
/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate.
|
|
/* COMMAND ATTRIBUTE SYNTAX
|
|
/* .ad
|
|
/* .fi
|
|
/* The external command attributes are given in the \fBmaster.cf\fR
|
|
/* file at the end of a service definition. The syntax is as follows:
|
|
/* .IP "\fBflags=FR.>\fR (optional)"
|
|
/* Optional message processing flags. By default, a message is
|
|
/* copied unchanged.
|
|
/* .RS
|
|
/* .IP \fBF\fR
|
|
/* Prepend a "\fBFrom \fIsender time_stamp\fR" envelope header to
|
|
/* the message content.
|
|
/* This is expected by, for example, \fBUUCP\fR software. The \fBF\fR
|
|
/* flag also causes an empty line to be appended to the message.
|
|
/* .IP \fBR\fR
|
|
/* Prepend a \fBReturn-Path:\fR message header with the envelope sender
|
|
/* address.
|
|
/* .IP \fB.\fR
|
|
/* Prepend \fB.\fR to lines starting with "\fB.\fR". This is needed
|
|
/* by, for example, \fBBSMTP\fR software.
|
|
/* .IP \fB>\fR
|
|
/* Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected
|
|
/* by, for example, \fBUUCP\fR software.
|
|
/* .RE
|
|
/* .IP "\fBuser\fR=\fIusername\fR (required)"
|
|
/* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
|
|
/* The external command is executed with the rights of the
|
|
/* specified \fIusername\fR. The software refuses to execute
|
|
/* commands with root privileges, or with the privileges of the
|
|
/* mail system owner. If \fIgroupname\fR is specified, the
|
|
/* corresponding group ID is used instead of the group ID of
|
|
/* of \fIusername\fR.
|
|
/* .IP "\fBargv\fR=\fIcommand\fR... (required)"
|
|
/* The command to be executed. This must be specified as the
|
|
/* last command attribute.
|
|
/* The command is executed directly, i.e. without interpretation of
|
|
/* shell meta characters by a shell command interpreter.
|
|
/* .sp
|
|
/* In the command argument vector, the following macros are recognized
|
|
/* and replaced with corresponding information from the Postfix queue
|
|
/* manager delivery request:
|
|
/* .RS
|
|
/* .IP \fB${\fBextension\fR}\fR
|
|
/* This macro expands to the extension part of a recipient address.
|
|
/* For example, with an address \fIuser+foo@domain\fR the extension is
|
|
/* \fIfoo\fR.
|
|
/* A command-line argument that contains \fB${\fBextension\fR}\fR expands
|
|
/* into as many command-line arguments as there are recipients.
|
|
/* .IP \fB${\fBmailbox\fR}\fR
|
|
/* This macro expands to the complete local part of a recipient address.
|
|
/* For example, with an address \fIuser+foo@domain\fR the mailbox is
|
|
/* \fIuser+foo\fR.
|
|
/* A command-line argument that contains \fB${\fBmailbox\fR}\fR
|
|
/* expands into as many command-line arguments as there are recipients.
|
|
/* .IP \fB${\fBnexthop\fR}\fR
|
|
/* This macro expands to the next-hop hostname.
|
|
/* .IP \fB${\fBrecipient\fR}\fR
|
|
/* This macro expands to the complete recipient address.
|
|
/* A command-line argument that contains \fB${\fBrecipient\fR}\fR
|
|
/* expands into as many command-line arguments as there are recipients.
|
|
/* .IP \fB${\fBsender\fR}\fR
|
|
/* This macro expands to the envelope sender address.
|
|
/* .IP \fB${\fBuser\fR}\fR
|
|
/* This macro expands to the username part of a recipient address.
|
|
/* For example, with an address \fIuser+foo@domain\fR the username
|
|
/* part is \fIuser\fR.
|
|
/* A command-line argument that contains \fB${\fBuser\fR}\fR expands
|
|
/* into as many command-line arguments as there are recipients.
|
|
/* .RE
|
|
/* .PP
|
|
/* In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
|
|
/* $(\fIname\fR) are also recognized. Specify \fB$$\fR where a single
|
|
/* \fB$\fR is wanted.
|
|
/* DIAGNOSTICS
|
|
/* Command exit status codes are expected to
|
|
/* follow the conventions defined in <\fBsysexits.h\fR>.
|
|
/*
|
|
/* Problems and transactions are logged to \fBsyslogd\fR(8).
|
|
/* Corrupted message files are marked so that the queue manager
|
|
/* can move them to the \fBcorrupt\fR queue for further inspection.
|
|
/* SECURITY
|
|
/* .fi
|
|
/* .ad
|
|
/* This program needs a dual personality 1) to access the private
|
|
/* Postfix queue and IPC mechanisms, and 2) to execute external
|
|
/* commands as the specified user. It is therefore security sensitive.
|
|
/* CONFIGURATION PARAMETERS
|
|
/* .ad
|
|
/* .fi
|
|
/* The following \fBmain.cf\fR parameters are especially relevant to
|
|
/* this program. See the Postfix \fBmain.cf\fR file for syntax details
|
|
/* and for default values. Use the \fBpostfix reload\fR command after
|
|
/* a configuration change.
|
|
/* .SH Miscellaneous
|
|
/* .ad
|
|
/* .fi
|
|
/* .IP \fBmail_owner\fR
|
|
/* The process privileges used while not running an external command.
|
|
/* .SH "Resource controls"
|
|
/* .ad
|
|
/* .fi
|
|
/* In the text below, \fItransport\fR is the first field in a
|
|
/* \fBmaster.cf\fR entry.
|
|
/* .IP \fItransport\fB_destination_concurrency_limit\fR
|
|
/* Limit the number of parallel deliveries to the same destination,
|
|
/* for delivery via the named \fItransport\fR. The default limit is
|
|
/* taken from the \fBdefault_destination_concurrency_limit\fR parameter.
|
|
/* The limit is enforced by the Postfix queue manager.
|
|
/* .IP \fItransport\fB_destination_recipient_limit\fR
|
|
/* Limit the number of recipients per message delivery, for delivery
|
|
/* via the named \fItransport\fR. The default limit is taken from
|
|
/* the \fBdefault_destination_recipient_limit\fR parameter.
|
|
/* The limit is enforced by the Postfix queue manager.
|
|
/* .IP \fItransport\fB_time_limit\fR
|
|
/* Limit the time for delivery to external command, for delivery via
|
|
/* the named \fBtransport\fR. The default limit is taken from the
|
|
/* \fBcommand_time_limit\fR parameter.
|
|
/* The limit is enforced by the Postfix queue manager.
|
|
/* SEE ALSO
|
|
/* bounce(8) non-delivery status reports
|
|
/* master(8) process manager
|
|
/* qmgr(8) queue manager
|
|
/* syslogd(8) system logging
|
|
/* LICENSE
|
|
/* .ad
|
|
/* .fi
|
|
/* The Secure Mailer license must be distributed with this software.
|
|
/* AUTHOR(S)
|
|
/* Wietse Venema
|
|
/* IBM T.J. Watson Research
|
|
/* P.O. Box 704
|
|
/* Yorktown Heights, NY 10598, USA
|
|
/*--*/
|
|
|
|
/* System library. */
|
|
|
|
#include <sys_defs.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef STRCASECMP_IN_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
/* Utility library. */
|
|
|
|
#include <msg.h>
|
|
#include <vstream.h>
|
|
#include <vstring.h>
|
|
#include <argv.h>
|
|
#include <htable.h>
|
|
#include <dict.h>
|
|
#include <iostuff.h>
|
|
#include <mymalloc.h>
|
|
#include <mac_parse.h>
|
|
#include <set_eugid.h>
|
|
#include <split_at.h>
|
|
#include <stringops.h>
|
|
|
|
/* Global library. */
|
|
|
|
#include <recipient_list.h>
|
|
#include <deliver_request.h>
|
|
#include <mail_params.h>
|
|
#include <mail_conf.h>
|
|
#include <bounce.h>
|
|
#include <defer.h>
|
|
#include <deliver_completed.h>
|
|
#include <sent.h>
|
|
#include <pipe_command.h>
|
|
#include <mail_copy.h>
|
|
#include <mail_addr.h>
|
|
#include <canon_addr.h>
|
|
#include <split_addr.h>
|
|
|
|
/* Single server skeleton. */
|
|
|
|
#include <mail_server.h>
|
|
|
|
/* Application-specific. */
|
|
|
|
/*
|
|
* The mini symbol table name and keys used for expanding macros in
|
|
* command-line arguments.
|
|
*/
|
|
#define PIPE_DICT_TABLE "pipe_command" /* table name */
|
|
#define PIPE_DICT_NEXTHOP "nexthop" /* key */
|
|
#define PIPE_DICT_RCPT "recipient" /* key */
|
|
#define PIPE_DICT_SENDER "sender"/* key */
|
|
#define PIPE_DICT_USER "user" /* key */
|
|
#define PIPE_DICT_EXTENSION "extension" /* key */
|
|
#define PIPE_DICT_MAILBOX "mailbox" /* key */
|
|
|
|
/*
|
|
* Flags used to pass back the type of special parameter found by
|
|
* parse_callback.
|
|
*/
|
|
#define PIPE_FLAG_RCPT (1<<0)
|
|
#define PIPE_FLAG_USER (1<<1)
|
|
#define PIPE_FLAG_EXTENSION (1<<2)
|
|
#define PIPE_FLAG_MAILBOX (1<<3)
|
|
|
|
/*
|
|
* Tunable parameters. Values are taken from the config file, after
|
|
* prepending the service name to _name, and so on.
|
|
*/
|
|
int var_command_maxtime; /* system-wide */
|
|
|
|
/*
|
|
* For convenience. Instead of passing around lists of parameters, bundle
|
|
* them up in convenient structures.
|
|
*/
|
|
|
|
/*
|
|
* Structure for service-specific configuration parameters.
|
|
*/
|
|
typedef struct {
|
|
int time_limit; /* per-service time limit */
|
|
} PIPE_PARAMS;
|
|
|
|
/*
|
|
* Structure for command-line parameters.
|
|
*/
|
|
typedef struct {
|
|
char **command; /* argument vector */
|
|
uid_t uid; /* command privileges */
|
|
gid_t gid; /* command privileges */
|
|
int flags; /* mail_copy() flags */
|
|
} PIPE_ATTR;
|
|
|
|
/* parse_callback - callback for mac_parse() */
|
|
|
|
static int parse_callback(int type, VSTRING *buf, char *context)
|
|
{
|
|
int *expand_flag = (int *) context;
|
|
|
|
/*
|
|
* See if this command-line argument references a special macro.
|
|
*/
|
|
if (type == MAC_PARSE_VARNAME) {
|
|
if (strcmp(vstring_str(buf), PIPE_DICT_RCPT) == 0)
|
|
*expand_flag |= PIPE_FLAG_RCPT;
|
|
else if (strcmp(vstring_str(buf), PIPE_DICT_USER) == 0)
|
|
*expand_flag |= PIPE_FLAG_USER;
|
|
else if (strcmp(vstring_str(buf), PIPE_DICT_EXTENSION) == 0)
|
|
*expand_flag |= PIPE_FLAG_EXTENSION;
|
|
else if (strcmp(vstring_str(buf), PIPE_DICT_MAILBOX) == 0)
|
|
*expand_flag |= PIPE_FLAG_MAILBOX;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* expand_argv - expand macros in the argument vector */
|
|
|
|
static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
|
|
{
|
|
VSTRING *buf = vstring_alloc(100);
|
|
ARGV *result;
|
|
char **cpp;
|
|
int expand_flag;
|
|
int i;
|
|
char *ext;
|
|
|
|
/*
|
|
* This appears to be simple operation (replace $name by its expansion).
|
|
* However, it becomes complex because a command-line argument that
|
|
* references $recipient must expand to as many command-line arguments as
|
|
* there are recipients (that's wat programs called by sendmail expect).
|
|
* So we parse each command-line argument, and depending on what we find,
|
|
* we either expand the argument just once, or we expand it once for each
|
|
* recipient. In either case we end up parsing the command-line argument
|
|
* twice. The amount of CPU time wasted will be negligible.
|
|
*
|
|
* Note: we can't use recursive macro expansion here, because recursion
|
|
* would screw up mail addresses that contain $ characters.
|
|
*/
|
|
#define NO 0
|
|
#define STR vstring_str
|
|
|
|
result = argv_alloc(1);
|
|
for (cpp = argv; *cpp; cpp++) {
|
|
expand_flag = 0;
|
|
mac_parse(*cpp, parse_callback, (char *) &expand_flag);
|
|
if (expand_flag == 0) { /* no $recipient etc. */
|
|
argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
|
|
} else { /* contains $recipient etc. */
|
|
for (i = 0; i < rcpt_list->len; i++) {
|
|
|
|
/*
|
|
* This argument contains $recipient.
|
|
*/
|
|
if (expand_flag & PIPE_FLAG_RCPT) {
|
|
dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT,
|
|
rcpt_list->info[i].address);
|
|
}
|
|
|
|
/*
|
|
* This argument contains $user. Extract the plain user name.
|
|
* Either anything to the left of the extension delimiter or,
|
|
* in absence of the latter, anything to the left of the
|
|
* rightmost @.
|
|
*
|
|
* Beware: if the user name is blank (e.g. +user@host), the
|
|
* argument is suppressed. This is necessary to allow for
|
|
* cyrus bulletin-board (global mailbox) delivery. XXX But,
|
|
* skipping empty user parts will also prevent other
|
|
* expansions of this specific command-line argument.
|
|
*/
|
|
if (expand_flag & PIPE_FLAG_USER) {
|
|
vstring_strcpy(buf, rcpt_list->info[i].address);
|
|
if (split_at_right(STR(buf), '@') == 0)
|
|
msg_warn("no @ in recipient address: %s",
|
|
rcpt_list->info[i].address);
|
|
if (*var_rcpt_delim)
|
|
split_addr(STR(buf), *var_rcpt_delim);
|
|
if (*STR(buf) == 0)
|
|
continue;
|
|
lowercase(STR(buf));
|
|
dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
|
|
}
|
|
|
|
/*
|
|
* This argument contains $extension. Extract the recipient
|
|
* extension: anything between the leftmost extension
|
|
* delimiter and the rightmost @. The extension may be blank.
|
|
*/
|
|
if (expand_flag & PIPE_FLAG_EXTENSION) {
|
|
vstring_strcpy(buf, rcpt_list->info[i].address);
|
|
if (split_at_right(STR(buf), '@') == 0)
|
|
msg_warn("no @ in recipient address: %s",
|
|
rcpt_list->info[i].address);
|
|
if (*var_rcpt_delim == 0
|
|
|| (ext = split_addr(STR(buf), *var_rcpt_delim)) == 0)
|
|
ext = ""; /* insert null arg */
|
|
else
|
|
lowercase(ext);
|
|
dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext);
|
|
}
|
|
|
|
/*
|
|
* This argument contains $mailbox. Extract the mailbox name:
|
|
* anything to the left of the rightmost @.
|
|
*/
|
|
if (expand_flag & PIPE_FLAG_MAILBOX) {
|
|
vstring_strcpy(buf, rcpt_list->info[i].address);
|
|
if (split_at_right(STR(buf), '@') == 0)
|
|
msg_warn("no @ in recipient address: %s",
|
|
rcpt_list->info[i].address);
|
|
lowercase(STR(buf));
|
|
dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf));
|
|
}
|
|
argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
|
|
}
|
|
}
|
|
}
|
|
argv_terminate(result);
|
|
vstring_free(buf);
|
|
return (result);
|
|
}
|
|
|
|
/* get_service_params - get service-name dependent config information */
|
|
|
|
static void get_service_params(PIPE_PARAMS *config, char *service)
|
|
{
|
|
char *myname = "get_service_params";
|
|
|
|
/*
|
|
* Figure out the command time limit for this transport.
|
|
*/
|
|
config->time_limit =
|
|
get_mail_conf_int2(service, "_time_limit", var_command_maxtime, 1, 0);
|
|
|
|
/*
|
|
* Give the poor tester a clue of what is going on.
|
|
*/
|
|
if (msg_verbose)
|
|
msg_info("%s: time_limit %d", myname, config->time_limit);
|
|
}
|
|
|
|
/* get_service_attr - get command-line attributes */
|
|
|
|
static void get_service_attr(PIPE_ATTR *attr, char **argv)
|
|
{
|
|
char *myname = "get_service_attr";
|
|
struct passwd *pwd;
|
|
struct group *grp;
|
|
char *user; /* user name */
|
|
char *group; /* group name */
|
|
char *cp;
|
|
|
|
/*
|
|
* Initialize.
|
|
*/
|
|
user = 0;
|
|
group = 0;
|
|
attr->command = 0;
|
|
attr->flags = 0;
|
|
|
|
/*
|
|
* Iterate over the command-line attribute list.
|
|
*/
|
|
for ( /* void */ ; *argv != 0; argv++) {
|
|
|
|
/*
|
|
* flags=stuff
|
|
*/
|
|
if (strncasecmp("flags=", *argv, sizeof("flags=") - 1) == 0) {
|
|
for (cp = *argv + sizeof("flags=") - 1; *cp; cp++) {
|
|
switch (*cp) {
|
|
case 'F':
|
|
attr->flags |= MAIL_COPY_FROM;
|
|
break;
|
|
case '.':
|
|
attr->flags |= MAIL_COPY_DOT;
|
|
break;
|
|
case '>':
|
|
attr->flags |= MAIL_COPY_QUOTE;
|
|
break;
|
|
case 'R':
|
|
attr->flags |= MAIL_COPY_RETURN_PATH;
|
|
break;
|
|
default:
|
|
msg_fatal("unknown flag: %c (ignored)", *cp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* user=username[:groupname]
|
|
*/
|
|
else if (strncasecmp("user=", *argv, sizeof("user=") - 1) == 0) {
|
|
user = *argv + sizeof("user=") - 1;
|
|
if ((group = split_at(user, ':')) != 0) /* XXX clobbers argv */
|
|
if (*group == 0)
|
|
group = 0;
|
|
if ((pwd = getpwnam(user)) == 0)
|
|
msg_fatal("%s: unknown username: %s", myname, user);
|
|
attr->uid = pwd->pw_uid;
|
|
if (group != 0) {
|
|
if ((grp = getgrnam(group)) == 0)
|
|
msg_fatal("%s: unknown group: %s", myname, group);
|
|
attr->gid = grp->gr_gid;
|
|
} else {
|
|
attr->gid = pwd->pw_gid;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* argv=command...
|
|
*/
|
|
else if (strncasecmp("argv=", *argv, sizeof("argv=") - 1) == 0) {
|
|
*argv += sizeof("argv=") - 1; /* XXX clobbers argv */
|
|
attr->command = argv;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Bad.
|
|
*/
|
|
else
|
|
msg_fatal("unknown attribute name: %s", *argv);
|
|
}
|
|
|
|
/*
|
|
* Sanity checks. Verify that every member has an acceptable value.
|
|
*/
|
|
if (user == 0)
|
|
msg_fatal("missing user= attribute");
|
|
if (attr->command == 0)
|
|
msg_fatal("missing argv= attribute");
|
|
if (attr->uid == 0)
|
|
msg_fatal("request to deliver as root");
|
|
if (attr->uid == var_owner_uid)
|
|
msg_fatal("request to deliver as mail system owner");
|
|
if (attr->gid == 0)
|
|
msg_fatal("request to use privileged group id %d", attr->gid);
|
|
if (attr->gid == var_owner_gid)
|
|
msg_fatal("request to use mail system owner group id %d", attr->gid);
|
|
|
|
/*
|
|
* Give the poor tester a clue of what is going on.
|
|
*/
|
|
if (msg_verbose)
|
|
msg_info("%s: uid %d, gid %d. flags %d",
|
|
myname, attr->uid, attr->gid, attr->flags);
|
|
}
|
|
|
|
/* eval_command_status - do something with command completion status */
|
|
|
|
static int eval_command_status(int command_status, char *service,
|
|
DELIVER_REQUEST *request, VSTREAM *src,
|
|
char *why)
|
|
{
|
|
RECIPIENT *rcpt;
|
|
int status;
|
|
int result = 0;
|
|
int n;
|
|
|
|
/*
|
|
* Depending on the result, bounce or defer the message, and mark the
|
|
* recipient as done where appropriate.
|
|
*/
|
|
switch (command_status) {
|
|
case PIPE_STAT_OK:
|
|
for (n = 0; n < request->rcpt_list.len; n++) {
|
|
rcpt = request->rcpt_list.info + n;
|
|
sent(request->queue_id, rcpt->address, service,
|
|
request->arrival_time, "%s", request->nexthop);
|
|
deliver_completed(src, rcpt->offset);
|
|
}
|
|
break;
|
|
case PIPE_STAT_BOUNCE:
|
|
for (n = 0; n < request->rcpt_list.len; n++) {
|
|
rcpt = request->rcpt_list.info + n;
|
|
status = bounce_append(BOUNCE_FLAG_KEEP,
|
|
request->queue_id, rcpt->address,
|
|
service, request->arrival_time, "%s", why);
|
|
if (status == 0)
|
|
deliver_completed(src, rcpt->offset);
|
|
result |= status;
|
|
}
|
|
break;
|
|
case PIPE_STAT_DEFER:
|
|
for (n = 0; n < request->rcpt_list.len; n++) {
|
|
rcpt = request->rcpt_list.info + n;
|
|
result |= defer_append(BOUNCE_FLAG_KEEP,
|
|
request->queue_id, rcpt->address,
|
|
service, request->arrival_time, "%s", why);
|
|
}
|
|
break;
|
|
default:
|
|
msg_panic("eval_command_status: bad status %d", command_status);
|
|
/* NOTREACHED */
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/* deliver_message - deliver message with extreme prejudice */
|
|
|
|
static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
|
|
{
|
|
char *myname = "deliver_message";
|
|
static PIPE_PARAMS conf;
|
|
static PIPE_ATTR attr;
|
|
RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
|
|
VSTRING *why = vstring_alloc(100);
|
|
VSTRING *buf;
|
|
ARGV *expanded_argv;
|
|
int deliver_status;
|
|
int command_status;
|
|
|
|
if (msg_verbose)
|
|
msg_info("%s: from <%s>", myname, request->sender);
|
|
|
|
/*
|
|
* First of all, replace an empty sender address by the mailer daemon
|
|
* address. The resolver already fixes empty recipient addresses.
|
|
*
|
|
* XXX Should sender and recipient be transformed into external (i.e.
|
|
* quoted) form? Problem is that the quoting rules are transport
|
|
* specific. Such information must evidently not be hard coded into
|
|
* Postfix, but would have to be provided in the form of lookup tables.
|
|
*/
|
|
if (request->sender[0] == 0) {
|
|
buf = vstring_alloc(100);
|
|
canon_addr_internal(buf, MAIL_ADDR_MAIL_DAEMON);
|
|
myfree(request->sender);
|
|
request->sender = vstring_export(buf);
|
|
}
|
|
|
|
/*
|
|
* Sanity checks. The get_service_params() and get_service_attr()
|
|
* routines also do some sanity checks. Look up service attributes and
|
|
* config information only once. This is safe since the information comes
|
|
* from a trusted source, not from the delivery request.
|
|
*/
|
|
if (request->nexthop[0] == 0)
|
|
msg_fatal("empty nexthop hostname");
|
|
if (rcpt_list->len <= 0)
|
|
msg_fatal("recipient count: %d", rcpt_list->len);
|
|
if (attr.command == 0) {
|
|
get_service_params(&conf, service);
|
|
get_service_attr(&attr, argv);
|
|
}
|
|
|
|
/*
|
|
* Deliver. Set the nexthop and sender variables, and expand the command
|
|
* argument vector. Recipients will be expanded on the fly. XXX Rewrite
|
|
* envelope and header addresses according to transport-specific
|
|
* rewriting rules.
|
|
*/
|
|
if (vstream_fseek(request->fp, request->data_offset, SEEK_SET) < 0)
|
|
msg_fatal("seek queue file %s: %m", VSTREAM_PATH(request->fp));
|
|
|
|
dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, request->sender);
|
|
dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
|
|
expanded_argv = expand_argv(attr.command, rcpt_list);
|
|
|
|
command_status = pipe_command(request->fp, why,
|
|
PIPE_CMD_UID, attr.uid,
|
|
PIPE_CMD_GID, attr.gid,
|
|
PIPE_CMD_SENDER, request->sender,
|
|
PIPE_CMD_COPY_FLAGS, attr.flags,
|
|
PIPE_CMD_ARGV, expanded_argv->argv,
|
|
PIPE_CMD_TIME_LIMIT, conf.time_limit,
|
|
PIPE_CMD_END);
|
|
|
|
deliver_status = eval_command_status(command_status, service, request,
|
|
request->fp, vstring_str(why));
|
|
|
|
/*
|
|
* Clean up.
|
|
*/
|
|
vstring_free(why);
|
|
argv_free(expanded_argv);
|
|
|
|
return (deliver_status);
|
|
}
|
|
|
|
/* pipe_service - perform service for client */
|
|
|
|
static void pipe_service(VSTREAM *client_stream, char *service, char **argv)
|
|
{
|
|
DELIVER_REQUEST *request;
|
|
int status;
|
|
|
|
/*
|
|
* This routine runs whenever a client connects to the UNIX-domain socket
|
|
* dedicated to delivery via external command. What we see below is a
|
|
* little protocol to (1) tell the queue manager that we are ready, (2)
|
|
* read a request from the queue manager, and (3) report the completion
|
|
* status of that request. All connection-management stuff is handled by
|
|
* the common code in single_server.c.
|
|
*/
|
|
if ((request = deliver_request_read(client_stream)) != 0) {
|
|
status = deliver_message(request, service, argv);
|
|
deliver_request_done(client_stream, request, status);
|
|
}
|
|
}
|
|
|
|
/* pre_accept - see if tables have changed */
|
|
|
|
static void pre_accept(char *unused_name, char **unused_argv)
|
|
{
|
|
if (dict_changed()) {
|
|
msg_info("table has changed -- exiting");
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
/* drop_privileges - drop privileges most of the time */
|
|
|
|
static void drop_privileges(char *unused_name, char **unused_argv)
|
|
{
|
|
set_eugid(var_owner_uid, var_owner_gid);
|
|
}
|
|
|
|
/* main - pass control to the single-threaded skeleton */
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
static CONFIG_INT_TABLE int_table[] = {
|
|
VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
|
|
0,
|
|
};
|
|
|
|
single_server_main(argc, argv, pipe_service,
|
|
MAIL_SERVER_INT_TABLE, int_table,
|
|
MAIL_SERVER_POST_INIT, drop_privileges,
|
|
MAIL_SERVER_PRE_ACCEPT, pre_accept,
|
|
0);
|
|
}
|