xref: /netbsd-src/external/ibm-public/postfix/dist/src/master/master_ent.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: master_ent.c,v 1.1.1.4 2013/01/02 18:59:01 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	master_ent 3
6 /* SUMMARY
7 /*	Postfix master - config file access
8 /* SYNOPSIS
9 /*	#include "master.h"
10 /*
11 /*	void	fset_master_ent(path)
12 /*	char	*path;
13 /*
14 /*	void	set_master_ent()
15 /*
16 /*	MASTER_SERV *get_master_ent()
17 /*
18 /*	void	end_master_ent()
19 /*
20 /*	void	print_master_ent(entry)
21 /*	MASTER_SERV *entry;
22 /*
23 /*	void	free_master_ent(entry)
24 /*	MASTER_SERV *entry;
25 /* DESCRIPTION
26 /*	This module implements a simple programmatic interface
27 /*	for accessing Postfix master process configuration files.
28 /*
29 /*	fset_master_ent() specifies the location of the master process
30 /*	configuration file.  The pathname is copied.
31 /*
32 /*	set_master_ent() opens the configuration file. It is an error
33 /*	to call this routine while the configuration file is still open.
34 /*	It is an error to open a configuration file without specifying
35 /*	its name to fset_master_ent().
36 /*
37 /*	get_master_ent() reads the next entry from an open configuration
38 /*	file and returns the parsed result. A null result means the end
39 /*	of file was reached.
40 /*
41 /*	print_master_ent() prints the specified service entry.
42 /*
43 /*	end_master_ent() closes an open configuration file. It is an error
44 /*	to call this routine when the configuration file is not open.
45 /*
46 /*	free_master_ent() destroys the memory used for a parsed configuration
47 /*	file entry.
48 /* DIAGNOSTICS
49 /*	Panics: interface violations. Fatal errors: memory allocation
50 /*	failure.
51 /* BUGS
52 /* SEE ALSO
53 /* LICENSE
54 /* .ad
55 /* .fi
56 /*	The Secure Mailer license must be distributed with this software.
57 /* AUTHOR(S)
58 /*	Wietse Venema
59 /*	IBM T.J. Watson Research
60 /*	P.O. Box 704
61 /*	Yorktown Heights, NY 10598, USA
62 /*--*/
63 
64 /* System libraries. */
65 
66 #include <sys_defs.h>
67 #include <netinet/in.h>
68 #include <stdarg.h>
69 #include <string.h>
70 #include <stdlib.h>
71 #include <unistd.h>
72 #include <ctype.h>
73 #include <fcntl.h>
74 
75 #ifdef STRCASECMP_IN_STRINGS_H
76 #include <strings.h>
77 #endif
78 
79 /* Utility libraries. */
80 
81 #include <msg.h>
82 #include <mymalloc.h>
83 #include <vstring.h>
84 #include <vstream.h>
85 #include <argv.h>
86 #include <stringops.h>
87 #include <readlline.h>
88 #include <inet_addr_list.h>
89 #include <host_port.h>
90 #include <inet_addr_host.h>
91 #include <sock_addr.h>
92 #include <inet_proto.h>
93 
94 /* Global library. */
95 
96 #include <match_service.h>
97 #include <mail_proto.h>
98 #include <mail_params.h>
99 #include <own_inet_addr.h>
100 #include <wildcard_inet_addr.h>
101 #include <mail_conf.h>
102 
103 /* Local stuff. */
104 
105 #include "master_proto.h"
106 #include "master.h"
107 
108 static char *master_path;		/* config file name */
109 static VSTREAM *master_fp;		/* config file pointer */
110 static int master_line;			/* config file line number */
111 static ARGV *master_disable;		/* disabled service patterns */
112 
113 static char master_blanks[] = " \t\r\n";/* field delimiters */
114 
115 static NORETURN fatal_invalid_field(char *, char *);
116 static NORETURN fatal_with_context(char *,...);
117 
118 /* fset_master_ent - specify configuration file pathname */
119 
120 void    fset_master_ent(char *path)
121 {
122     if (master_path != 0)
123 	myfree(master_path);
124     master_path = mystrdup(path);
125 }
126 
127 /* set_master_ent - open configuration file */
128 
129 void    set_master_ent()
130 {
131     const char *myname = "set_master_ent";
132     char   *disable;
133 
134     if (master_fp != 0)
135 	msg_panic("%s: configuration file still open", myname);
136     if (master_path == 0)
137 	msg_panic("%s: no configuration file specified", myname);
138     if ((master_fp = vstream_fopen(master_path, O_RDONLY, 0)) == 0)
139 	msg_fatal("open %s: %m", master_path);
140     master_line = 0;
141     if (master_disable != 0)
142 	msg_panic("%s: service disable list still exists", myname);
143     if (inet_proto_info()->ai_family_list[0] == 0) {
144 	msg_warn("all network protocols are disabled (%s = %s)",
145 		 VAR_INET_PROTOCOLS, var_inet_protocols);
146 	msg_warn("disabling all type \"inet\" services in master.cf");
147 	disable = concatenate(MASTER_XPORT_NAME_INET, ",",
148 			      var_master_disable, (char *) 0);
149 	master_disable = match_service_init(disable);
150 	myfree(disable);
151     } else
152 	master_disable = match_service_init(var_master_disable);
153 }
154 
155 /* end_master_ent - close configuration file */
156 
157 void    end_master_ent()
158 {
159     const char *myname = "end_master_ent";
160 
161     if (master_fp == 0)
162 	msg_panic("%s: configuration file not open", myname);
163     if (vstream_fclose(master_fp) != 0)
164 	msg_fatal("%s: close configuration file: %m", myname);
165     master_fp = 0;
166     if (master_disable == 0)
167 	msg_panic("%s: no service disable list", myname);
168     match_service_free(master_disable);
169     master_disable = 0;
170 }
171 
172 /* fatal_with_context - print fatal error with file/line context */
173 
174 static NORETURN fatal_with_context(char *format,...)
175 {
176     const char *myname = "fatal_with_context";
177     VSTRING *vp = vstring_alloc(100);
178     va_list ap;
179 
180     if (master_path == 0)
181 	msg_panic("%s: no configuration file specified", myname);
182 
183     va_start(ap, format);
184     vstring_vsprintf(vp, format, ap);
185     va_end(ap);
186     msg_fatal("%s: line %d: %s", master_path, master_line, vstring_str(vp));
187 }
188 
189 /* fatal_invalid_field - report invalid field value */
190 
191 static NORETURN fatal_invalid_field(char *name, char *value)
192 {
193     fatal_with_context("field \"%s\": bad value: \"%s\"", name, value);
194 }
195 
196 /* get_str_ent - extract string field */
197 
198 static char *get_str_ent(char **bufp, char *name, char *def_val)
199 {
200     char   *value;
201 
202     if ((value = mystrtok(bufp, master_blanks)) == 0)
203 	fatal_with_context("missing \"%s\" field", name);
204     if (strcmp(value, "-") == 0) {
205 	if (def_val == 0)
206 	    fatal_with_context("field \"%s\" has no default value", name);
207 	return (def_val);
208     } else {
209 	return (value);
210     }
211 }
212 
213 /* get_bool_ent - extract boolean field */
214 
215 static int get_bool_ent(char **bufp, char *name, char *def_val)
216 {
217     char   *value;
218 
219     value = get_str_ent(bufp, name, def_val);
220     if (strcmp("y", value) == 0) {
221 	return (1);
222     } else if (strcmp("n", value) == 0) {
223 	return (0);
224     } else {
225 	fatal_invalid_field(name, value);
226     }
227     /* NOTREACHED */
228 }
229 
230 /* get_int_ent - extract integer field */
231 
232 static int get_int_ent(char **bufp, char *name, char *def_val, int min_val)
233 {
234     char   *value;
235     int     n;
236 
237     value = get_str_ent(bufp, name, def_val);
238     if (!ISDIGIT(*value) || (n = atoi(value)) < min_val)
239 	fatal_invalid_field(name, value);
240     return (n);
241 }
242 
243 /* get_master_ent - read entry from configuration file */
244 
245 MASTER_SERV *get_master_ent()
246 {
247     VSTRING *buf = vstring_alloc(100);
248     VSTRING *junk = vstring_alloc(100);
249     MASTER_SERV *serv;
250     char   *cp;
251     char   *name;
252     char   *host = 0;
253     char   *port = 0;
254     char   *transport;
255     int     private;
256     int     unprivileged;		/* passed on to child */
257     int     chroot;			/* passed on to child */
258     char   *command;
259     int     n;
260     char   *bufp;
261     char   *atmp;
262     const char *parse_err;
263     static char *saved_interfaces = 0;
264 
265     if (master_fp == 0)
266 	msg_panic("get_master_ent: config file not open");
267     if (master_disable == 0)
268 	msg_panic("get_master_ent: no service disable list");
269 
270     /*
271      * XXX We cannot change the inet_interfaces setting for a running master
272      * process. Listening sockets are inherited by child processes so that
273      * closing and reopening those sockets in the master does not work.
274      *
275      * Another problem is that library routines still cache results that are
276      * based on the old inet_interfaces setting. It is too much trouble to
277      * recompute everything.
278      *
279      * In order to keep our data structures consistent we ignore changes in
280      * inet_interfaces settings, and issue a warning instead.
281      */
282     if (saved_interfaces == 0)
283 	saved_interfaces = mystrdup(var_inet_interfaces);
284 
285     /*
286      * Skip blank lines and comment lines.
287      */
288     for (;;) {
289 	if (readlline(buf, master_fp, &master_line) == 0) {
290 	    vstring_free(buf);
291 	    vstring_free(junk);
292 	    return (0);
293 	}
294 	bufp = vstring_str(buf);
295 	if ((cp = mystrtok(&bufp, master_blanks)) == 0)
296 	    continue;
297 	name = cp;
298 	transport = get_str_ent(&bufp, "transport type", (char *) 0);
299 	vstring_sprintf(junk, "%s.%s", name, transport);
300 	if (match_service_match(master_disable, vstring_str(junk)) == 0)
301 	    break;
302     }
303 
304     /*
305      * Parse one logical line from the configuration file. Initialize service
306      * structure members in order.
307      */
308     serv = (MASTER_SERV *) mymalloc(sizeof(MASTER_SERV));
309     serv->next = 0;
310 
311     /*
312      * Flags member.
313      */
314     serv->flags = 0;
315 
316     /*
317      * All servers busy warning timer.
318      */
319     serv->busy_warn_time = 0;
320 
321     /*
322      * Service name. Syntax is transport-specific.
323      */
324     serv->ext_name = mystrdup(name);
325 
326     /*
327      * Transport type: inet (wild-card listen or virtual) or unix.
328      */
329 #define STR_SAME	!strcmp
330 
331     if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) {
332 	if (!STR_SAME(saved_interfaces, var_inet_interfaces)) {
333 	    msg_warn("service %s: ignoring %s change",
334 		     serv->ext_name, VAR_INET_INTERFACES);
335 	    msg_warn("to change %s, stop and start Postfix",
336 		     VAR_INET_INTERFACES);
337 	}
338 	serv->type = MASTER_SERV_TYPE_INET;
339 	atmp = mystrdup(name);
340 	if ((parse_err = host_port(atmp, &host, "", &port, (char *) 0)) != 0)
341 	    msg_fatal("%s: line %d: %s in \"%s\"",
342 		      VSTREAM_PATH(master_fp), master_line,
343 		      parse_err, name);
344 	if (*host) {
345 	    serv->flags |= MASTER_FLAG_INETHOST;/* host:port */
346 	    MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *)
347 		mymalloc(sizeof(*MASTER_INET_ADDRLIST(serv)));
348 	    inet_addr_list_init(MASTER_INET_ADDRLIST(serv));
349 	    if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0)
350 		msg_fatal("%s: line %d: bad hostname or network address: %s",
351 			  VSTREAM_PATH(master_fp), master_line, name);
352 	    inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
353 	    serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
354 	} else {
355 	    MASTER_INET_ADDRLIST(serv) =
356 		strcasecmp(saved_interfaces, INET_INTERFACES_ALL) ?
357 		own_inet_addr_list() :		/* virtual */
358 		wildcard_inet_addr_list();	/* wild-card */
359 	    inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
360 	    serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
361 	}
362 	MASTER_INET_PORT(serv) = mystrdup(port);
363 	for (n = 0; /* see below */ ; n++) {
364 	    if (n >= MASTER_INET_ADDRLIST(serv)->used) {
365 		serv->flags |= MASTER_FLAG_LOCAL_ONLY;
366 		break;
367 	    }
368 	    if (!sock_addr_in_loopback(SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n)))
369 		break;
370 	}
371     } else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) {
372 	serv->type = MASTER_SERV_TYPE_UNIX;
373 	serv->listen_fd_count = 1;
374 	serv->flags |= MASTER_FLAG_LOCAL_ONLY;
375     } else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) {
376 	serv->type = MASTER_SERV_TYPE_FIFO;
377 	serv->listen_fd_count = 1;
378 	serv->flags |= MASTER_FLAG_LOCAL_ONLY;
379 #ifdef MASTER_SERV_TYPE_PASS
380     } else if (STR_SAME(transport, MASTER_XPORT_NAME_PASS)) {
381 	serv->type = MASTER_SERV_TYPE_PASS;
382 	serv->listen_fd_count = 1;
383 	/* If this is a connection screener, remote clients are likely. */
384 #endif
385     } else {
386 	fatal_with_context("bad transport type: %s", transport);
387     }
388 
389     /*
390      * Service class: public or private.
391      */
392     private = get_bool_ent(&bufp, "private", "y");
393 
394     /*
395      * Derive an internal service name. The name may depend on service
396      * attributes such as privacy.
397      */
398     if (serv->type == MASTER_SERV_TYPE_INET) {
399 	MAI_HOSTADDR_STR host_addr;
400 	MAI_SERVPORT_STR serv_port;
401 	struct addrinfo *res0;
402 
403 	if (private)
404 	    fatal_with_context("inet service cannot be private");
405 
406 	/*
407 	 * Canonicalize endpoint names so that we correctly handle "reload"
408 	 * requests after someone changes "25" into "smtp" or vice versa.
409 	 */
410 	if (*host == 0)
411 	    host = 0;
412 	/* Canonicalize numeric host and numeric or symbolic service. */
413 	if (hostaddr_to_sockaddr(host, port, 0, &res0) == 0) {
414 	    SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
415 				 host ? &host_addr : (MAI_HOSTADDR_STR *) 0,
416 				 &serv_port, 0);
417 	    serv->name = (host ? concatenate("[", host_addr.buf, "]:",
418 					     serv_port.buf, (char *) 0) :
419 			  mystrdup(serv_port.buf));
420 	    freeaddrinfo(res0);
421 	}
422 	/* Canonicalize numeric or symbolic service. */
423 	else if (hostaddr_to_sockaddr((char *) 0, port, 0, &res0) == 0) {
424 	    SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
425 				 (MAI_HOSTADDR_STR *) 0, &serv_port, 0);
426 	    serv->name = (host ? concatenate("[", host, "]:",
427 					     serv_port.buf, (char *) 0) :
428 			  mystrdup(serv_port.buf));
429 	    freeaddrinfo(res0);
430 	}
431 	/* Bad service name? */
432 	else
433 	    serv->name = mystrdup(name);
434 	myfree(atmp);
435     } else if (serv->type == MASTER_SERV_TYPE_UNIX) {
436 	serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
437 				   MAIL_CLASS_PUBLIC, name);
438     } else if (serv->type == MASTER_SERV_TYPE_FIFO) {
439 	serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
440 				   MAIL_CLASS_PUBLIC, name);
441 #ifdef MASTER_SERV_TYPE_PASS
442     } else if (serv->type == MASTER_SERV_TYPE_PASS) {
443 	serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
444 				   MAIL_CLASS_PUBLIC, name);
445 #endif
446     } else {
447 	msg_panic("bad transport type: %d", serv->type);
448     }
449 
450     /*
451      * Listen socket(s). XXX We pre-allocate storage because the number of
452      * sockets is frozen anyway once we build the command-line vector below.
453      */
454     if (serv->listen_fd_count == 0) {
455 	msg_fatal("%s: line %d: no valid IP address found: %s",
456 		  VSTREAM_PATH(master_fp), master_line, name);
457     }
458     serv->listen_fd = (int *) mymalloc(sizeof(int) * serv->listen_fd_count);
459     for (n = 0; n < serv->listen_fd_count; n++)
460 	serv->listen_fd[n] = -1;
461 
462     /*
463      * Privilege level. Default is to restrict process privileges to those of
464      * the mail owner.
465      */
466     unprivileged = get_bool_ent(&bufp, "unprivileged", "y");
467 
468     /*
469      * Chroot. Default is to restrict file system access to the mail queue.
470      * XXX Chroot cannot imply unprivileged service (for example, the pickup
471      * service runs chrooted but needs privileges to open files as the user).
472      */
473     chroot = get_bool_ent(&bufp, "chroot", "y");
474 
475     /*
476      * Wakeup timer. XXX should we require that var_proc_limit == 1? Right
477      * now, the only services that have a wakeup timer also happen to be the
478      * services that have at most one running instance: local pickup and
479      * local delivery.
480      */
481     serv->wakeup_time = get_int_ent(&bufp, "wakeup_time", "0", 0);
482 
483     /*
484      * Find out if the wakeup time is conditional, i.e., wakeup triggers
485      * should not be sent until the service has actually been used.
486      */
487     if (serv->wakeup_time > 0 && bufp[*bufp ? -2 : -1] == '?')
488 	serv->flags |= MASTER_FLAG_CONDWAKE;
489 
490     /*
491      * Concurrency limit. Zero means no limit.
492      */
493     vstring_sprintf(junk, "%d", var_proc_limit);
494     serv->max_proc = get_int_ent(&bufp, "max_proc", vstring_str(junk), 0);
495 
496     /*
497      * Path to command,
498      */
499     command = get_str_ent(&bufp, "command", (char *) 0);
500     serv->path = concatenate(var_daemon_dir, "/", command, (char *) 0);
501 
502     /*
503      * Idle and total process count.
504      */
505     serv->avail_proc = 0;
506     serv->total_proc = 0;
507 
508     /*
509      * Backoff time in case a service is broken.
510      */
511     serv->throttle_delay = var_throttle_time;
512 
513     /*
514      * Shared channel for child status updates.
515      */
516     serv->status_fd[0] = serv->status_fd[1] = -1;
517 
518     /*
519      * Child process structures.
520      */
521     serv->children = 0;
522 
523     /*
524      * Command-line vector. Add "-n service_name" when the process name
525      * basename differs from the service name. Always add the transport.
526      */
527     serv->args = argv_alloc(0);
528     argv_add(serv->args, command, (char *) 0);
529     if (serv->max_proc == 1)
530 	argv_add(serv->args, "-l", (char *) 0);
531     if (serv->max_proc == 0)
532 	argv_add(serv->args, "-z", (char *) 0);
533     if (strcmp(basename(command), name) != 0)
534 	argv_add(serv->args, "-n", name, (char *) 0);
535     argv_add(serv->args, "-t", transport, (char *) 0);
536     if (master_detach == 0)
537 	argv_add(serv->args, "-d", (char *) 0);
538     if (msg_verbose)
539 	argv_add(serv->args, "-v", (char *) 0);
540     if (unprivileged)
541 	argv_add(serv->args, "-u", (char *) 0);
542     if (chroot)
543 	argv_add(serv->args, "-c", (char *) 0);
544     if ((serv->flags & MASTER_FLAG_LOCAL_ONLY) == 0 && serv->max_proc > 1) {
545 	argv_add(serv->args, "-o", "stress=" CONFIG_BOOL_YES, (char *) 0);
546 	serv->stress_param_val =
547 	    serv->args->argv[serv->args->argc - 1] + sizeof("stress=") - 1;
548 	serv->stress_param_val[0] = 0;
549     } else
550 	serv->stress_param_val = 0;
551     serv->stress_expire_time = 0;
552     if (serv->listen_fd_count > 1)
553 	argv_add(serv->args, "-s",
554 	    vstring_str(vstring_sprintf(junk, "%d", serv->listen_fd_count)),
555 		 (char *) 0);
556     while ((cp = mystrtok(&bufp, master_blanks)) != 0)
557 	argv_add(serv->args, cp, (char *) 0);
558     argv_terminate(serv->args);
559 
560     /*
561      * Cleanup.
562      */
563     vstring_free(buf);
564     vstring_free(junk);
565     return (serv);
566 }
567 
568 /* print_master_ent - show service entry contents */
569 
570 void    print_master_ent(MASTER_SERV *serv)
571 {
572     char  **cpp;
573 
574     msg_info("====start service entry");
575     msg_info("flags: %d", serv->flags);
576     msg_info("name: %s", serv->name);
577     msg_info("type: %s",
578 	     serv->type == MASTER_SERV_TYPE_UNIX ? MASTER_XPORT_NAME_UNIX :
579 	     serv->type == MASTER_SERV_TYPE_FIFO ? MASTER_XPORT_NAME_FIFO :
580 	     serv->type == MASTER_SERV_TYPE_INET ? MASTER_XPORT_NAME_INET :
581 #ifdef MASTER_SERV_TYPE_PASS
582 	     serv->type == MASTER_SERV_TYPE_PASS ? MASTER_XPORT_NAME_PASS :
583 #endif
584 	     "unknown transport type");
585     msg_info("listen_fd_count: %d", serv->listen_fd_count);
586     msg_info("wakeup: %d", serv->wakeup_time);
587     msg_info("max_proc: %d", serv->max_proc);
588     msg_info("path: %s", serv->path);
589     for (cpp = serv->args->argv; *cpp; cpp++)
590 	msg_info("arg[%d]: %s", (int) (cpp - serv->args->argv), *cpp);
591     msg_info("avail_proc: %d", serv->avail_proc);
592     msg_info("total_proc: %d", serv->total_proc);
593     msg_info("throttle_delay: %d", serv->throttle_delay);
594     msg_info("status_fd %d %d", serv->status_fd[0], serv->status_fd[1]);
595     msg_info("children: 0x%lx", (long) serv->children);
596     msg_info("next: 0x%lx", (long) serv->next);
597     msg_info("====end service entry");
598 }
599 
600 /* free_master_ent - destroy process entry */
601 
602 void    free_master_ent(MASTER_SERV *serv)
603 {
604 
605     /*
606      * Undo what get_master_ent() created.
607      */
608     if (serv->flags & MASTER_FLAG_INETHOST) {
609 	inet_addr_list_free(MASTER_INET_ADDRLIST(serv));
610 	myfree((char *) MASTER_INET_ADDRLIST(serv));
611     }
612     if (serv->type == MASTER_SERV_TYPE_INET)
613 	myfree(MASTER_INET_PORT(serv));
614     myfree(serv->ext_name);
615     myfree(serv->name);
616     myfree(serv->path);
617     argv_free(serv->args);
618     myfree((char *) serv->listen_fd);
619     myfree((char *) serv);
620 }
621