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