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