summaryrefslogtreecommitdiff
path: root/gnu/dist/postfix/src/spawn/spawn.c
diff options
context:
space:
mode:
authoritojun <itojun@NetBSD.org>2001-03-13 17:45:02 +0000
committeritojun <itojun@NetBSD.org>2001-03-13 17:45:02 +0000
commit173b2f6655e38657f15bb57fe26dc23e8d054d4e (patch)
tree04af30df8438e7c54ceb4e5f4bf0dff83788ce54 /gnu/dist/postfix/src/spawn/spawn.c
parentc7871f80bf97592f2ca4dd16d6d7708bec2c33a2 (diff)
postfix release-20010228
Diffstat (limited to 'gnu/dist/postfix/src/spawn/spawn.c')
-rw-r--r--gnu/dist/postfix/src/spawn/spawn.c311
1 files changed, 311 insertions, 0 deletions
diff --git a/gnu/dist/postfix/src/spawn/spawn.c b/gnu/dist/postfix/src/spawn/spawn.c
new file mode 100644
index 00000000000..b63a63b7837
--- /dev/null
+++ b/gnu/dist/postfix/src/spawn/spawn.c
@@ -0,0 +1,311 @@
+/*++
+/* NAME
+/* spawn 8
+/* SUMMARY
+/* Postfix external command spawner
+/* SYNOPSIS
+/* \fBspawn\fR [generic Postfix daemon options] command_attributes...
+/* DESCRIPTION
+/* The \fBspawn\fR daemon provides the Postfix equivalent of \fBinetd\fR.
+/* It listens on a port as specified in the Postfix \fBmaster.cf\fR file
+/* and spawns an external command whenever a connection is established.
+/* The connection can be made over local IPC (such as UNIX-domain
+/* sockets) or over non-local IPC (such as TCP sockets).
+/* The command\'s standard input, output and error streams are connected
+/* directly to the communication endpoint.
+/*
+/* This daemon expects to be run from the \fBmaster\fR(8) process
+/* manager.
+/* 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 "\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.
+/* BUGS
+/* In order to enforce standard Postfix process resource controls,
+/* the \fBspawn\fR daemon runs only one external command at a time.
+/* As such, it presents a noticeable overhead by wasting precious
+/* process resources. The \fBspawn\fR daemon is expected to be
+/* replaced by a more structural solution.
+/* DIAGNOSTICS
+/* The \fBspawn\fR daemon reports abnormal child exits.
+/* Problems are logged to \fBsyslogd\fR(8).
+/* SECURITY
+/* .fi
+/* .ad
+/* This program needs root privilege in order to execute external
+/* commands as the specified user. It is therefore security sensitive.
+/* However the \fBspawn\fR daemon does not talk to the external command
+/* and thus is not vulnerable to data-driven attacks.
+/* 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 \fBexport_environment\fR
+/* List of names of environment parameters that can be exported
+/* to non-Postfix processes.
+/* .IP \fBmail_owner\fR
+/* The process privileges used while not running an external command.
+/* .SH Resource control
+/* .ad
+/* .fi
+/* .IP \fIservice\fB_command_time_limit\fR
+/* The amount of time the command is allowed to run before it is
+/* killed with force. The \fIservice\fR name is the name of the entry
+/* in the \fBmaster.cf\fR file. The default time limit is given by the
+/* global \fBcommand_time_limit\fR configuration parameter.
+/* SEE ALSO
+/* master(8) process 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 <sys/wait.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 <argv.h>
+#include <dict.h>
+#include <mymalloc.h>
+#include <spawn_command.h>
+#include <split_at.h>
+#include <timed_wait.h>
+#include <set_eugid.h>
+
+/* Single server skeleton. */
+
+#include <mail_params.h>
+#include <mail_server.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+ /*
+ * 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.
+ */
+typedef struct {
+ char **argv; /* argument vector */
+ uid_t uid; /* command privileges */
+ gid_t gid; /* command privileges */
+ int time_limit; /* per-service time limit */
+} SPAWN_ATTR;
+
+/* get_service_attr - get service attributes */
+
+static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv)
+{
+ char *myname = "get_service_attr";
+ struct passwd *pwd;
+ struct group *grp;
+ char *user; /* user name */
+ char *group; /* group name */
+
+ /*
+ * Initialize.
+ */
+ user = 0;
+ group = 0;
+ attr->argv = 0;
+
+ /*
+ * Figure out the command time limit for this transport.
+ */
+ attr->time_limit =
+ get_mail_conf_int2(service, "_time_limit", var_command_maxtime, 1, 0);
+
+ /*
+ * Iterate over the command-line attribute list.
+ */
+ for ( /* void */ ; *argv != 0; argv++) {
+
+ /*
+ * user=username[:groupname]
+ */
+ 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->argv = 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->argv == 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 %ld", (long) attr->gid);
+ if (attr->gid == var_owner_gid)
+ msg_fatal("request to use mail system owner group id %ld", (long) attr->gid);
+
+ /*
+ * Give the poor tester a clue of what is going on.
+ */
+ if (msg_verbose)
+ msg_info("%s: uid %ld, gid %ld; time %d",
+ myname, (long) attr->uid, (long) attr->gid, attr->time_limit);
+}
+
+/* spawn_service - perform service for client */
+
+static void spawn_service(VSTREAM *client_stream, char *service, char **argv)
+{
+ char *myname = "spawn_service";
+ static SPAWN_ATTR attr;
+ WAIT_STATUS_T status;
+ ARGV *export_env;
+
+ /*
+ * This routine runs whenever a client connects to the UNIX-domain socket
+ * dedicated to running an external command.
+ */
+ if (msg_verbose)
+ msg_info("%s: service=%s, command=%s...", myname, service, argv[0]);
+
+ /*
+ * Look up service attributes and config information only once. This is
+ * safe since the information comes from a trusted source.
+ */
+ if (attr.argv == 0) {
+ get_service_attr(&attr, service, argv);
+ }
+
+ /*
+ * Execute the command.
+ */
+ export_env = argv_split(var_export_environ, ", \t\r\n");
+ status = spawn_command(SPAWN_CMD_STDIN, vstream_fileno(client_stream),
+ SPAWN_CMD_STDOUT, vstream_fileno(client_stream),
+ SPAWN_CMD_STDERR, vstream_fileno(client_stream),
+ SPAWN_CMD_UID, attr.uid,
+ SPAWN_CMD_GID, attr.gid,
+ SPAWN_CMD_ARGV, attr.argv,
+ SPAWN_CMD_TIME_LIMIT, attr.time_limit,
+ SPAWN_CMD_EXPORT, export_env->argv,
+ SPAWN_CMD_END);
+ argv_free(export_env);
+
+ /*
+ * Warn about unsuccessful completion.
+ */
+ if (!NORMAL_EXIT_STATUS(status)) {
+ if (WIFEXITED(status))
+ msg_warn("command %s exit status %d",
+ attr.argv[0], WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ msg_warn("command %s killed by signal %d",
+ attr.argv[0], WTERMSIG(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_TIME_TABLE time_table[] = {
+ VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
+ 0,
+ };
+
+ single_server_main(argc, argv, spawn_service,
+ MAIL_SERVER_TIME_TABLE, time_table,
+ MAIL_SERVER_POST_INIT, drop_privileges,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ 0);
+}