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