Postfix3.3.1
spawn.c
[詳解]
1 /*++
2 /* NAME
3 /* spawn 8
4 /* SUMMARY
5 /* Postfix external command spawner
6 /* SYNOPSIS
7 /* \fBspawn\fR [generic Postfix daemon options] command_attributes...
8 /* DESCRIPTION
9 /* The \fBspawn\fR(8) daemon provides the Postfix equivalent
10 /* of \fBinetd\fR.
11 /* It listens on a port as specified in the Postfix \fBmaster.cf\fR file
12 /* and spawns an external command whenever a connection is established.
13 /* The connection can be made over local IPC (such as UNIX-domain
14 /* sockets) or over non-local IPC (such as TCP sockets).
15 /* The command\'s standard input, output and error streams are connected
16 /* directly to the communication endpoint.
17 /*
18 /* This daemon expects to be run from the \fBmaster\fR(8) process
19 /* manager.
20 /* COMMAND ATTRIBUTE SYNTAX
21 /* .ad
22 /* .fi
23 /* The external command attributes are given in the \fBmaster.cf\fR
24 /* file at the end of a service definition. The syntax is as follows:
25 /* .IP "\fBuser\fR=\fIusername\fR (required)"
26 /* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
27 /* The external command is executed with the rights of the
28 /* specified \fIusername\fR. The software refuses to execute
29 /* commands with root privileges, or with the privileges of the
30 /* mail system owner. If \fIgroupname\fR is specified, the
31 /* corresponding group ID is used instead of the group ID
32 /* of \fIusername\fR.
33 /* .IP "\fBargv\fR=\fIcommand\fR... (required)"
34 /* The command to be executed. This must be specified as the
35 /* last command attribute.
36 /* The command is executed directly, i.e. without interpretation of
37 /* shell meta characters by a shell command interpreter.
38 /* BUGS
39 /* In order to enforce standard Postfix process resource controls,
40 /* the \fBspawn\fR(8) daemon runs only one external command at a time.
41 /* As such, it presents a noticeable overhead by wasting precious
42 /* process resources. The \fBspawn\fR(8) daemon is expected to be
43 /* replaced by a more structural solution.
44 /* DIAGNOSTICS
45 /* The \fBspawn\fR(8) daemon reports abnormal child exits.
46 /* Problems are logged to \fBsyslogd\fR(8).
47 /* SECURITY
48 /* .fi
49 /* .ad
50 /* This program needs root privilege in order to execute external
51 /* commands as the specified user. It is therefore security sensitive.
52 /* However the \fBspawn\fR(8) daemon does not talk to the external command
53 /* and thus is not vulnerable to data-driven attacks.
54 /* CONFIGURATION PARAMETERS
55 /* .ad
56 /* .fi
57 /* Changes to \fBmain.cf\fR are picked up automatically as \fBspawn\fR(8)
58 /* processes run for only a limited amount of time. Use the command
59 /* "\fBpostfix reload\fR" to speed up a change.
60 /*
61 /* The text below provides only a parameter summary. See
62 /* \fBpostconf\fR(5) for more details including examples.
63 /*
64 /* In the text below, \fItransport\fR is the first field of the entry
65 /* in the \fBmaster.cf\fR file.
66 /* RESOURCE AND RATE CONTROL
67 /* .ad
68 /* .fi
69 /* .IP "\fBtransport_time_limit ($command_time_limit)\fR"
70 /* A transport-specific override for the command_time_limit parameter
71 /* value, where \fItransport\fR is the master.cf name of the message
72 /* delivery transport.
73 /* MISCELLANEOUS
74 /* .ad
75 /* .fi
76 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
77 /* The default location of the Postfix main.cf and master.cf
78 /* configuration files.
79 /* .IP "\fBdaemon_timeout (18000s)\fR"
80 /* How much time a Postfix daemon process may take to handle a
81 /* request before it is terminated by a built-in watchdog timer.
82 /* .IP "\fBexport_environment (see 'postconf -d' output)\fR"
83 /* The list of environment variables that a Postfix process will export
84 /* to non-Postfix processes.
85 /* .IP "\fBipc_timeout (3600s)\fR"
86 /* The time limit for sending or receiving information over an internal
87 /* communication channel.
88 /* .IP "\fBmail_owner (postfix)\fR"
89 /* The UNIX system account that owns the Postfix queue and most Postfix
90 /* daemon processes.
91 /* .IP "\fBmax_idle (100s)\fR"
92 /* The maximum amount of time that an idle Postfix daemon process waits
93 /* for an incoming connection before terminating voluntarily.
94 /* .IP "\fBmax_use (100)\fR"
95 /* The maximal number of incoming connections that a Postfix daemon
96 /* process will service before terminating voluntarily.
97 /* .IP "\fBprocess_id (read-only)\fR"
98 /* The process ID of a Postfix command or daemon process.
99 /* .IP "\fBprocess_name (read-only)\fR"
100 /* The process name of a Postfix command or daemon process.
101 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
102 /* The location of the Postfix top-level queue directory.
103 /* .IP "\fBsyslog_facility (mail)\fR"
104 /* The syslog facility of Postfix logging.
105 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
106 /* A prefix that is prepended to the process name in syslog
107 /* records, so that, for example, "smtpd" becomes "prefix/smtpd".
108 /* .PP
109 /* Available in Postfix 3.3 and later:
110 /* .IP "\fBservice_name (read-only)\fR"
111 /* The master.cf service name of a Postfix daemon process.
112 /* SEE ALSO
113 /* postconf(5), configuration parameters
114 /* master(8), process manager
115 /* syslogd(8), system logging
116 /* LICENSE
117 /* .ad
118 /* .fi
119 /* The Secure Mailer license must be distributed with this software.
120 /* AUTHOR(S)
121 /* Wietse Venema
122 /* IBM T.J. Watson Research
123 /* P.O. Box 704
124 /* Yorktown Heights, NY 10598, USA
125 /*
126 /* Wietse Venema
127 /* Google, Inc.
128 /* 111 8th Avenue
129 /* New York, NY 10011, USA
130 /*--*/
131 
132 /* System library. */
133 
134 #include <sys_defs.h>
135 #include <sys/wait.h>
136 #include <unistd.h>
137 #include <stdlib.h>
138 #include <string.h>
139 #include <pwd.h>
140 #include <grp.h>
141 #include <fcntl.h>
142 #ifdef STRCASECMP_IN_STRINGS_H
143 #include <strings.h>
144 #endif
145 
146 /* Utility library. */
147 
148 #include <msg.h>
149 #include <argv.h>
150 #include <dict.h>
151 #include <mymalloc.h>
152 #include <spawn_command.h>
153 #include <split_at.h>
154 #include <timed_wait.h>
155 #include <set_eugid.h>
156 
157 /* Global library. */
158 
159 #include <mail_version.h>
160 
161 /* Single server skeleton. */
162 
163 #include <mail_params.h>
164 #include <mail_server.h>
165 #include <mail_conf.h>
166 #include <mail_parm_split.h>
167 
168 /* Application-specific. */
169 
170  /*
171  * Tunable parameters. Values are taken from the config file, after
172  * prepending the service name to _name, and so on.
173  */
174 int var_command_maxtime; /* system-wide */
175 
176  /*
177  * For convenience. Instead of passing around lists of parameters, bundle
178  * them up in convenient structures.
179  */
180 typedef struct {
181  char **argv; /* argument vector */
182  uid_t uid; /* command privileges */
183  gid_t gid; /* command privileges */
184  int time_limit; /* per-service time limit */
185 } SPAWN_ATTR;
186 
187 /* get_service_attr - get service attributes */
188 
189 static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv)
190 {
191  const char *myname = "get_service_attr";
192  struct passwd *pwd;
193  struct group *grp;
194  char *user; /* user name */
195  char *group; /* group name */
196 
197  /*
198  * Initialize.
199  */
200  user = 0;
201  group = 0;
202  attr->argv = 0;
203 
204  /*
205  * Figure out the command time limit for this transport.
206  */
207  attr->time_limit =
208  get_mail_conf_time2(service, _MAXTIME, var_command_maxtime, 's', 1, 0);
209 
210  /*
211  * Iterate over the command-line attribute list.
212  */
213  for ( /* void */ ; *argv != 0; argv++) {
214 
215  /*
216  * user=username[:groupname]
217  */
218  if (strncasecmp("user=", *argv, sizeof("user=") - 1) == 0) {
219  user = *argv + sizeof("user=") - 1;
220  if ((group = split_at(user, ':')) != 0) /* XXX clobbers argv */
221  if (*group == 0)
222  group = 0;
223  if ((pwd = getpwnam(user)) == 0)
224  msg_fatal("unknown user name: %s", user);
225  attr->uid = pwd->pw_uid;
226  if (group != 0) {
227  if ((grp = getgrnam(group)) == 0)
228  msg_fatal("unknown group name: %s", group);
229  attr->gid = grp->gr_gid;
230  } else {
231  attr->gid = pwd->pw_gid;
232  }
233  }
234 
235  /*
236  * argv=command...
237  */
238  else if (strncasecmp("argv=", *argv, sizeof("argv=") - 1) == 0) {
239  *argv += sizeof("argv=") - 1; /* XXX clobbers argv */
240  attr->argv = argv;
241  break;
242  }
243 
244  /*
245  * Bad.
246  */
247  else
248  msg_fatal("unknown attribute name: %s", *argv);
249  }
250 
251  /*
252  * Sanity checks. Verify that every member has an acceptable value.
253  */
254  if (user == 0)
255  msg_fatal("missing user= attribute");
256  if (attr->argv == 0)
257  msg_fatal("missing argv= attribute");
258  if (attr->uid == 0)
259  msg_fatal("request to deliver as root");
260  if (attr->uid == var_owner_uid)
261  msg_fatal("request to deliver as mail system owner");
262  if (attr->gid == 0)
263  msg_fatal("request to use privileged group id %ld", (long) attr->gid);
264  if (attr->gid == var_owner_gid)
265  msg_fatal("request to use mail system owner group id %ld", (long) attr->gid);
266  if (attr->uid == (uid_t) (-1))
267  msg_fatal("user must not have user ID -1");
268  if (attr->gid == (gid_t) (-1))
269  msg_fatal("user must not have group ID -1");
270 
271  /*
272  * Give the poor tester a clue of what is going on.
273  */
274  if (msg_verbose)
275  msg_info("%s: uid %ld, gid %ld; time %d",
276  myname, (long) attr->uid, (long) attr->gid, attr->time_limit);
277 }
278 
279 /* spawn_service - perform service for client */
280 
281 static void spawn_service(VSTREAM *client_stream, char *service, char **argv)
282 {
283  const char *myname = "spawn_service";
284  static SPAWN_ATTR attr;
285  WAIT_STATUS_T status;
286  ARGV *export_env;
287 
288  /*
289  * This routine runs whenever a client connects to the UNIX-domain socket
290  * dedicated to running an external command.
291  */
292  if (msg_verbose)
293  msg_info("%s: service=%s, command=%s...", myname, service, argv[0]);
294 
295  /*
296  * Look up service attributes and config information only once. This is
297  * safe since the information comes from a trusted source.
298  */
299  if (attr.argv == 0) {
300  get_service_attr(&attr, service, argv);
301  }
302 
303  /*
304  * Execute the command.
305  */
307  status = spawn_command(CA_SPAWN_CMD_STDIN(vstream_fileno(client_stream)),
308  CA_SPAWN_CMD_STDOUT(vstream_fileno(client_stream)),
309  CA_SPAWN_CMD_STDERR(vstream_fileno(client_stream)),
310  CA_SPAWN_CMD_UID(attr.uid),
311  CA_SPAWN_CMD_GID(attr.gid),
312  CA_SPAWN_CMD_ARGV(attr.argv),
314  CA_SPAWN_CMD_EXPORT(export_env->argv),
316  argv_free(export_env);
317 
318  /*
319  * Warn about unsuccessful completion.
320  */
321  if (!NORMAL_EXIT_STATUS(status)) {
322  if (WIFEXITED(status))
323  msg_warn("command %s exit status %d",
324  attr.argv[0], WEXITSTATUS(status));
325  if (WIFSIGNALED(status))
326  msg_warn("command %s killed by signal %d",
327  attr.argv[0], WTERMSIG(status));
328  }
329 }
330 
331 /* pre_accept - see if tables have changed */
332 
333 static void pre_accept(char *unused_name, char **unused_argv)
334 {
335  const char *table;
336 
337  if ((table = dict_changed_name()) != 0) {
338  msg_info("table %s has changed -- restarting", table);
339  exit(0);
340  }
341 }
342 
343 /* drop_privileges - drop privileges most of the time */
344 
345 static void drop_privileges(char *unused_name, char **unused_argv)
346 {
348 }
349 
351 
352 /* main - pass control to the single-threaded skeleton */
353 
354 int main(int argc, char **argv)
355 {
356  static const CONFIG_TIME_TABLE time_table[] = {
358  0,
359  };
360 
361  /*
362  * Fingerprint executables and core dumps.
363  */
365 
366  single_server_main(argc, argv, spawn_service,
367  CA_MAIL_SERVER_TIME_TABLE(time_table),
368  CA_MAIL_SERVER_POST_INIT(drop_privileges),
369  CA_MAIL_SERVER_PRE_ACCEPT(pre_accept),
371  0);
372 }
int time_limit
Definition: spawn.c:184
int msg_verbose
Definition: msg.c:177
#define CA_SPAWN_CMD_GID(v)
Definition: spawn_command.h:41
ARGV * argv_free(ARGV *argvp)
Definition: argv.c:136
Definition: argv.h:17
WAIT_STATUS_T spawn_command(int key,...)
#define CA_SPAWN_CMD_STDIN(v)
Definition: spawn_command.h:37
int var_command_maxtime
Definition: spawn.c:174
char ** argv
Definition: argv.h:20
MAIL_VERSION_STAMP_DECLARE
Definition: spawn.c:350
#define CA_SPAWN_CMD_STDERR(v)
Definition: spawn_command.h:39
int main(int argc, char **argv)
Definition: spawn.c:354
int strncasecmp(const char *s1, const char *s2, size_t n)
Definition: strcasecmp.c:52
#define _MAXTIME
Definition: mail_params.h:546
#define VAR_EXPORT_ENVIRON
Definition: mail_params.h:2510
#define CA_MAIL_SERVER_POST_INIT(v)
Definition: mail_server.h:65
gid_t gid
Definition: spawn.c:183
gid_t var_owner_gid
Definition: mail_params.c:235
ARGV * mail_parm_split(const char *name, const char *value)
uid_t var_owner_uid
Definition: mail_params.c:234
const char * dict_changed_name(void)
Definition: dict.c:583
#define CA_SPAWN_CMD_STDOUT(v)
Definition: spawn_command.h:38
#define DEF_COMMAND_MAXTIME
Definition: mail_params.h:547
#define CA_SPAWN_CMD_UID(v)
Definition: spawn_command.h:40
int get_mail_conf_time2(const char *, const char *, int, int, int, int)
void msg_warn(const char *fmt,...)
Definition: msg.c:215
uid_t uid
Definition: spawn.c:182
#define VAR_COMMAND_MAXTIME
Definition: mail_params.h:545
#define MAIL_VERSION_STAMP_ALLOCATE
Definition: mail_version.h:67
#define CA_MAIL_SERVER_TIME_TABLE(v)
Definition: mail_server.h:59
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
#define CA_MAIL_SERVER_PRIVILEGED
Definition: mail_server.h:72
void set_eugid(uid_t euid, gid_t egid)
Definition: set_eugid.c:54
#define CA_SPAWN_CMD_EXPORT(v)
Definition: spawn_command.h:45
#define CA_SPAWN_CMD_ARGV(v)
Definition: spawn_command.h:35
#define CA_SPAWN_CMD_TIME_LIMIT(v)
Definition: spawn_command.h:42
#define CA_SPAWN_CMD_END
Definition: spawn_command.h:34
NORETURN single_server_main(int, char **, SINGLE_SERVER_FN,...)
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
#define vstream_fileno(vp)
Definition: vstream.h:115
char * var_export_environ
Definition: mail_params.c:297
char ** argv
Definition: spawn.c:181
#define CA_MAIL_SERVER_PRE_ACCEPT(v)
Definition: mail_server.h:68
int WAIT_STATUS_T
Definition: sys_defs.h:1436
#define NORMAL_EXIT_STATUS(status)
Definition: sys_defs.h:1438
void msg_info(const char *fmt,...)
Definition: msg.c:199