1 /* $NetBSD: master_ent.c,v 1.2 2017/02/14 01:16:45 christos 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_last; /* config file line number */ 111 static int master_line; /* config file line number */ 112 static ARGV *master_disable; /* disabled service patterns */ 113 114 static char master_blanks[] = CHARS_SPACE; /* field delimiters */ 115 116 /* fset_master_ent - specify configuration file pathname */ 117 118 void 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 127 void 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_last = 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 155 void 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 /* master_conf_context - plot the target range */ 171 172 static const char *master_conf_context(void) 173 { 174 static VSTRING *context_buf = 0; 175 176 if (context_buf == 0) 177 context_buf = vstring_alloc(100); 178 vstring_sprintf(context_buf, "%s: line %d", master_path, master_line); 179 return (vstring_str(context_buf)); 180 } 181 182 /* fatal_with_context - print fatal error with file/line context */ 183 184 static NORETURN PRINTFLIKE(1, 2) fatal_with_context(char *format,...) 185 { 186 const char *myname = "fatal_with_context"; 187 VSTRING *vp = vstring_alloc(100); 188 va_list ap; 189 190 if (master_path == 0) 191 msg_panic("%s: no configuration file specified", myname); 192 193 va_start(ap, format); 194 vstring_vsprintf(vp, format, ap); 195 va_end(ap); 196 msg_fatal("%s: %s", master_conf_context(), vstring_str(vp)); 197 } 198 199 /* fatal_invalid_field - report invalid field value */ 200 201 static NORETURN fatal_invalid_field(char *name, char *value) 202 { 203 fatal_with_context("field \"%s\": bad value: \"%s\"", name, value); 204 } 205 206 /* get_str_ent - extract string field */ 207 208 static char *get_str_ent(char **bufp, char *name, char *def_val) 209 { 210 char *value; 211 212 if ((value = mystrtok(bufp, master_blanks)) == 0) 213 fatal_with_context("missing \"%s\" field", name); 214 if (strcmp(value, "-") == 0) { 215 if (def_val == 0) 216 fatal_with_context("field \"%s\" has no default value", name); 217 if (warn_compat_break_chroot && strcmp(name, "chroot") == 0) 218 msg_info("%s: using backwards-compatible default setting " 219 "%s=%s", master_conf_context(), name, def_val); 220 return (def_val); 221 } else { 222 return (value); 223 } 224 } 225 226 /* get_bool_ent - extract boolean field */ 227 228 static int get_bool_ent(char **bufp, char *name, char *def_val) 229 { 230 char *value; 231 232 value = get_str_ent(bufp, name, def_val); 233 if (strcmp("y", value) == 0) { 234 return (1); 235 } else if (strcmp("n", value) == 0) { 236 return (0); 237 } else { 238 fatal_invalid_field(name, value); 239 } 240 /* NOTREACHED */ 241 } 242 243 /* get_int_ent - extract integer field */ 244 245 static int get_int_ent(char **bufp, char *name, char *def_val, int min_val) 246 { 247 char *value; 248 int n; 249 250 value = get_str_ent(bufp, name, def_val); 251 if (!ISDIGIT(*value) || (n = atoi(value)) < min_val) 252 fatal_invalid_field(name, value); 253 return (n); 254 } 255 256 /* get_master_ent - read entry from configuration file */ 257 258 MASTER_SERV *get_master_ent() 259 { 260 VSTRING *buf = vstring_alloc(100); 261 VSTRING *junk = vstring_alloc(100); 262 MASTER_SERV *serv; 263 char *cp; 264 char *name; 265 char *host = 0; 266 char *port = 0; 267 char *transport; 268 int private; 269 int unprivileged; /* passed on to child */ 270 int chroot; /* passed on to child */ 271 char *command; 272 int n; 273 char *bufp; 274 char *atmp; 275 const char *parse_err; 276 static char *saved_interfaces = 0; 277 char *err; 278 279 if (master_fp == 0) 280 msg_panic("get_master_ent: config file not open"); 281 if (master_disable == 0) 282 msg_panic("get_master_ent: no service disable list"); 283 284 /* 285 * XXX We cannot change the inet_interfaces setting for a running master 286 * process. Listening sockets are inherited by child processes so that 287 * closing and reopening those sockets in the master does not work. 288 * 289 * Another problem is that library routines still cache results that are 290 * based on the old inet_interfaces setting. It is too much trouble to 291 * recompute everything. 292 * 293 * In order to keep our data structures consistent we ignore changes in 294 * inet_interfaces settings, and issue a warning instead. 295 */ 296 if (saved_interfaces == 0) 297 saved_interfaces = mystrdup(var_inet_interfaces); 298 299 /* 300 * Skip blank lines and comment lines. 301 */ 302 for (;;) { 303 if (readllines(buf, master_fp, &master_line_last, &master_line) == 0) { 304 vstring_free(buf); 305 vstring_free(junk); 306 return (0); 307 } 308 bufp = vstring_str(buf); 309 if ((cp = mystrtok(&bufp, master_blanks)) == 0) 310 continue; 311 name = cp; 312 transport = get_str_ent(&bufp, "transport type", (char *) 0); 313 vstring_sprintf(junk, "%s/%s", name, transport); 314 if (match_service_match(master_disable, vstring_str(junk)) == 0) 315 break; 316 } 317 318 /* 319 * Parse one logical line from the configuration file. Initialize service 320 * structure members in order. 321 */ 322 serv = (MASTER_SERV *) mymalloc(sizeof(MASTER_SERV)); 323 serv->next = 0; 324 325 /* 326 * Flags member. 327 */ 328 serv->flags = 0; 329 330 /* 331 * All servers busy warning timer. 332 */ 333 serv->busy_warn_time = 0; 334 335 /* 336 * Service name. Syntax is transport-specific. 337 */ 338 serv->ext_name = mystrdup(name); 339 340 /* 341 * Transport type: inet (wild-card listen or virtual) or unix. 342 */ 343 #define STR_SAME !strcmp 344 345 if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) { 346 if (!STR_SAME(saved_interfaces, var_inet_interfaces)) { 347 msg_warn("service %s: ignoring %s change", 348 serv->ext_name, VAR_INET_INTERFACES); 349 msg_warn("to change %s, stop and start Postfix", 350 VAR_INET_INTERFACES); 351 } 352 serv->type = MASTER_SERV_TYPE_INET; 353 atmp = mystrdup(name); 354 if ((parse_err = host_port(atmp, &host, "", &port, (char *) 0)) != 0) 355 fatal_with_context("%s in \"%s\"", parse_err, name); 356 if (*host) { 357 serv->flags |= MASTER_FLAG_INETHOST;/* host:port */ 358 MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *) 359 mymalloc(sizeof(*MASTER_INET_ADDRLIST(serv))); 360 inet_addr_list_init(MASTER_INET_ADDRLIST(serv)); 361 if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0) 362 fatal_with_context("bad hostname or network address: %s", name); 363 inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv)); 364 serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; 365 } else { 366 MASTER_INET_ADDRLIST(serv) = 367 strcasecmp(saved_interfaces, INET_INTERFACES_ALL) ? 368 own_inet_addr_list() : /* virtual */ 369 wildcard_inet_addr_list(); /* wild-card */ 370 inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv)); 371 serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; 372 } 373 MASTER_INET_PORT(serv) = mystrdup(port); 374 for (n = 0; /* see below */ ; n++) { 375 if (n >= MASTER_INET_ADDRLIST(serv)->used) { 376 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 377 break; 378 } 379 if (!sock_addr_in_loopback(SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n))) 380 break; 381 } 382 } else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) { 383 serv->type = MASTER_SERV_TYPE_UNIX; 384 serv->listen_fd_count = 1; 385 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 386 } else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) { 387 serv->type = MASTER_SERV_TYPE_FIFO; 388 serv->listen_fd_count = 1; 389 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 390 #ifdef MASTER_SERV_TYPE_PASS 391 } else if (STR_SAME(transport, MASTER_XPORT_NAME_PASS)) { 392 serv->type = MASTER_SERV_TYPE_PASS; 393 serv->listen_fd_count = 1; 394 /* If this is a connection screener, remote clients are likely. */ 395 #endif 396 } else { 397 fatal_with_context("bad transport type: %s", transport); 398 } 399 400 /* 401 * Service class: public or private. 402 */ 403 private = get_bool_ent(&bufp, "private", "y"); 404 405 /* 406 * Derive an internal service name. The name may depend on service 407 * attributes such as privacy. 408 */ 409 if (serv->type == MASTER_SERV_TYPE_INET) { 410 MAI_HOSTADDR_STR host_addr; 411 MAI_SERVPORT_STR serv_port; 412 struct addrinfo *res0; 413 414 if (private) 415 fatal_with_context("inet service cannot be private"); 416 417 /* 418 * Canonicalize endpoint names so that we correctly handle "reload" 419 * requests after someone changes "25" into "smtp" or vice versa. 420 */ 421 if (*host == 0) 422 host = 0; 423 /* Canonicalize numeric host and numeric or symbolic service. */ 424 if (hostaddr_to_sockaddr(host, port, 0, &res0) == 0) { 425 SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen, 426 host ? &host_addr : (MAI_HOSTADDR_STR *) 0, 427 &serv_port, 0); 428 serv->name = (host ? concatenate("[", host_addr.buf, "]:", 429 serv_port.buf, (char *) 0) : 430 mystrdup(serv_port.buf)); 431 freeaddrinfo(res0); 432 } 433 /* Canonicalize numeric or symbolic service. */ 434 else if (hostaddr_to_sockaddr((char *) 0, port, 0, &res0) == 0) { 435 SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen, 436 (MAI_HOSTADDR_STR *) 0, &serv_port, 0); 437 serv->name = (host ? concatenate("[", host, "]:", 438 serv_port.buf, (char *) 0) : 439 mystrdup(serv_port.buf)); 440 freeaddrinfo(res0); 441 } 442 /* Bad service name? */ 443 else 444 serv->name = mystrdup(name); 445 myfree(atmp); 446 } else if (serv->type == MASTER_SERV_TYPE_UNIX) { 447 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 448 MAIL_CLASS_PUBLIC, name); 449 } else if (serv->type == MASTER_SERV_TYPE_FIFO) { 450 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 451 MAIL_CLASS_PUBLIC, name); 452 #ifdef MASTER_SERV_TYPE_PASS 453 } else if (serv->type == MASTER_SERV_TYPE_PASS) { 454 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 455 MAIL_CLASS_PUBLIC, name); 456 #endif 457 } else { 458 msg_panic("bad transport type: %d", serv->type); 459 } 460 461 /* 462 * Listen socket(s). XXX We pre-allocate storage because the number of 463 * sockets is frozen anyway once we build the command-line vector below. 464 */ 465 if (serv->listen_fd_count == 0) { 466 fatal_with_context("no valid IP address found: %s", name); 467 } 468 serv->listen_fd = (int *) mymalloc(sizeof(int) * serv->listen_fd_count); 469 for (n = 0; n < serv->listen_fd_count; n++) 470 serv->listen_fd[n] = -1; 471 472 /* 473 * Privilege level. Default is to restrict process privileges to those of 474 * the mail owner. 475 */ 476 unprivileged = get_bool_ent(&bufp, "unprivileged", "y"); 477 478 /* 479 * Chroot. Default is to restrict file system access to the mail queue. 480 * XXX Chroot cannot imply unprivileged service (for example, the pickup 481 * service runs chrooted but needs privileges to open files as the user). 482 */ 483 chroot = get_bool_ent(&bufp, "chroot", var_compat_level < 1 ? "y" : "n"); 484 485 /* 486 * Wakeup timer. XXX should we require that var_proc_limit == 1? Right 487 * now, the only services that have a wakeup timer also happen to be the 488 * services that have at most one running instance: local pickup and 489 * local delivery. 490 */ 491 serv->wakeup_time = get_int_ent(&bufp, "wakeup_time", "0", 0); 492 493 /* 494 * Find out if the wakeup time is conditional, i.e., wakeup triggers 495 * should not be sent until the service has actually been used. 496 */ 497 if (serv->wakeup_time > 0 && bufp[*bufp ? -2 : -1] == '?') 498 serv->flags |= MASTER_FLAG_CONDWAKE; 499 500 /* 501 * Concurrency limit. Zero means no limit. 502 */ 503 vstring_sprintf(junk, "%d", var_proc_limit); 504 serv->max_proc = get_int_ent(&bufp, "max_proc", vstring_str(junk), 0); 505 506 /* 507 * Path to command, 508 */ 509 command = get_str_ent(&bufp, "command", (char *) 0); 510 serv->path = concatenate(var_daemon_dir, "/", command, (char *) 0); 511 512 /* 513 * Idle and total process count. 514 */ 515 serv->avail_proc = 0; 516 serv->total_proc = 0; 517 518 /* 519 * Backoff time in case a service is broken. 520 */ 521 serv->throttle_delay = var_throttle_time; 522 523 /* 524 * Shared channel for child status updates. 525 */ 526 serv->status_fd[0] = serv->status_fd[1] = -1; 527 528 /* 529 * Child process structures. 530 */ 531 serv->children = 0; 532 533 /* 534 * Command-line vector. Add "-n service_name" when the process name 535 * basename differs from the service name. Always add the transport. 536 */ 537 serv->args = argv_alloc(0); 538 argv_add(serv->args, command, (char *) 0); 539 if (serv->max_proc == 1) 540 argv_add(serv->args, "-l", (char *) 0); 541 if (serv->max_proc == 0) 542 argv_add(serv->args, "-z", (char *) 0); 543 if (strcmp(basename(command), name) != 0) 544 argv_add(serv->args, "-n", name, (char *) 0); 545 argv_add(serv->args, "-t", transport, (char *) 0); 546 if (master_detach == 0) 547 argv_add(serv->args, "-d", (char *) 0); 548 if (msg_verbose) 549 argv_add(serv->args, "-v", (char *) 0); 550 if (unprivileged) 551 argv_add(serv->args, "-u", (char *) 0); 552 if (chroot) 553 argv_add(serv->args, "-c", (char *) 0); 554 if ((serv->flags & MASTER_FLAG_LOCAL_ONLY) == 0 && serv->max_proc > 1) { 555 argv_add(serv->args, "-o", "stress=" CONFIG_BOOL_YES, (char *) 0); 556 serv->stress_param_val = 557 serv->args->argv[serv->args->argc - 1] + sizeof("stress=") - 1; 558 serv->stress_param_val[0] = 0; 559 } else 560 serv->stress_param_val = 0; 561 serv->stress_expire_time = 0; 562 if (serv->listen_fd_count > 1) 563 argv_add(serv->args, "-s", 564 vstring_str(vstring_sprintf(junk, "%d", serv->listen_fd_count)), 565 (char *) 0); 566 while ((cp = mystrtokq(&bufp, master_blanks, CHARS_BRACE)) != 0) { 567 if (*cp == CHARS_BRACE[0] 568 && (err = extpar(&cp, CHARS_BRACE, EXTPAR_FLAG_STRIP)) != 0) 569 fatal_with_context("%s", err); 570 argv_add(serv->args, cp, (char *) 0); 571 } 572 argv_terminate(serv->args); 573 574 /* 575 * Cleanup. 576 */ 577 vstring_free(buf); 578 vstring_free(junk); 579 return (serv); 580 } 581 582 /* print_master_ent - show service entry contents */ 583 584 void print_master_ent(MASTER_SERV *serv) 585 { 586 char **cpp; 587 588 msg_info("====start service entry"); 589 msg_info("flags: %d", serv->flags); 590 msg_info("name: %s", serv->name); 591 msg_info("type: %s", 592 serv->type == MASTER_SERV_TYPE_UNIX ? MASTER_XPORT_NAME_UNIX : 593 serv->type == MASTER_SERV_TYPE_FIFO ? MASTER_XPORT_NAME_FIFO : 594 serv->type == MASTER_SERV_TYPE_INET ? MASTER_XPORT_NAME_INET : 595 #ifdef MASTER_SERV_TYPE_PASS 596 serv->type == MASTER_SERV_TYPE_PASS ? MASTER_XPORT_NAME_PASS : 597 #endif 598 "unknown transport type"); 599 msg_info("listen_fd_count: %d", serv->listen_fd_count); 600 msg_info("wakeup: %d", serv->wakeup_time); 601 msg_info("max_proc: %d", serv->max_proc); 602 msg_info("path: %s", serv->path); 603 for (cpp = serv->args->argv; *cpp; cpp++) 604 msg_info("arg[%d]: %s", (int) (cpp - serv->args->argv), *cpp); 605 msg_info("avail_proc: %d", serv->avail_proc); 606 msg_info("total_proc: %d", serv->total_proc); 607 msg_info("throttle_delay: %d", serv->throttle_delay); 608 msg_info("status_fd %d %d", serv->status_fd[0], serv->status_fd[1]); 609 msg_info("children: 0x%lx", (long) serv->children); 610 msg_info("next: 0x%lx", (long) serv->next); 611 msg_info("====end service entry"); 612 } 613 614 /* free_master_ent - destroy process entry */ 615 616 void free_master_ent(MASTER_SERV *serv) 617 { 618 619 /* 620 * Undo what get_master_ent() created. 621 */ 622 if (serv->flags & MASTER_FLAG_INETHOST) { 623 inet_addr_list_free(MASTER_INET_ADDRLIST(serv)); 624 myfree((void *) MASTER_INET_ADDRLIST(serv)); 625 } 626 if (serv->type == MASTER_SERV_TYPE_INET) 627 myfree(MASTER_INET_PORT(serv)); 628 myfree(serv->ext_name); 629 myfree(serv->name); 630 myfree(serv->path); 631 argv_free(serv->args); 632 myfree((void *) serv->listen_fd); 633 myfree((void *) serv); 634 } 635