1 /* $NetBSD: parse_v2.c,v 1.8 2025/12/27 08:06:38 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (c) 2021 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by James Browning, Gabe Coffland, Alex Gavin, and Solomon Ritzow. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: parse_v2.c,v 1.8 2025/12/27 08:06:38 mlelstv Exp $"); 34 35 #include <ctype.h> 36 #include <errno.h> 37 #include <limits.h> 38 #include <stdbool.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <syslog.h> 43 #include <err.h> 44 45 #include "inetd.h" 46 #include "ipsec.h" 47 48 typedef enum values_state { 49 VALS_PARSING, VALS_END_KEY, VALS_END_DEF, VALS_ERROR 50 } values_state; 51 52 /* Values parsing state */ 53 typedef struct val_parse_info { 54 char *cp; 55 /* Used so we can null-terminate values by overwriting ',' and ';' */ 56 //char terminal; 57 values_state state; 58 } val_parse_info, *vlist; 59 60 /* The result of a call to parse_invoke_handler */ 61 typedef enum invoke_result { 62 INVOKE_SUCCESS, INVOKE_FINISH, INVOKE_ERROR 63 } invoke_result; 64 65 /* The result of a parse of key handler values */ 66 typedef enum hresult { 67 KEY_HANDLER_FAILURE, KEY_HANDLER_SUCCESS 68 } hresult; 69 70 /* v2 syntax key-value parsers */ 71 static hresult args_handler(struct servtab *, vlist); 72 static hresult bind_handler(struct servtab *, vlist); 73 static hresult exec_handler(struct servtab *, vlist); 74 static hresult filter_handler(struct servtab *, vlist); 75 static hresult group_handler(struct servtab *, vlist); 76 static hresult service_max_handler(struct servtab *, vlist); 77 static hresult ip_max_handler(struct servtab *, vlist); 78 static hresult accept_max_handler(struct servtab *, vlist); 79 static hresult protocol_handler(struct servtab *, vlist); 80 static hresult recv_buf_handler(struct servtab *, vlist); 81 static hresult send_buf_handler(struct servtab *, vlist); 82 static hresult socket_type_handler(struct servtab *, vlist); 83 static hresult unknown_handler(struct servtab *, vlist); 84 static hresult user_handler(struct servtab *, vlist); 85 static hresult wait_handler(struct servtab *, vlist); 86 87 #ifdef IPSEC 88 static hresult ipsec_handler(struct servtab *, vlist); 89 #endif 90 91 static invoke_result parse_invoke_handler(bool *, char **, struct servtab *); 92 static bool fill_default_values(struct servtab *); 93 static bool parse_quotes(char **); 94 static bool skip_whitespace(char **); 95 static int size_to_bytes(char *); 96 static bool infer_protocol_ip_version(struct servtab *); 97 static bool setup_internal(struct servtab *); 98 static void try_infer_socktype(struct servtab *); 99 static int hex_to_bits(char); 100 #ifdef IPSEC 101 static void setup_ipsec(struct servtab *); 102 #endif 103 static inline void strmove(char *, size_t); 104 105 /* v2 Key handlers infrastructure */ 106 107 /* v2 syntax Handler function, which must parse all values for its key */ 108 typedef hresult (*key_handler_func)(struct servtab *, vlist); 109 110 /* List of v2 syntax key handlers */ 111 static struct key_handler { 112 const char *name; 113 key_handler_func handler; 114 } key_handlers[] = { 115 { "bind", bind_handler }, 116 { "socktype", socket_type_handler }, 117 { "acceptfilter", filter_handler }, 118 { "protocol", protocol_handler }, 119 { "sndbuf", send_buf_handler }, 120 { "recvbuf", recv_buf_handler }, 121 { "wait", wait_handler }, 122 { "service_max", service_max_handler }, 123 { "user", user_handler }, 124 { "group", group_handler }, 125 { "exec", exec_handler }, 126 { "args", args_handler }, 127 { "ip_max", ip_max_handler }, 128 { "accept_max", accept_max_handler }, 129 #ifdef IPSEC 130 { "ipsec", ipsec_handler } 131 #endif 132 }; 133 134 /* Error Not Initialized */ 135 #define ENI(key) ERR("Required option '%s' not specified", (key)) 136 137 #define WAIT_WRN "Option 'wait' for internal service '%s' was inferred" 138 139 /* Too Few Arguments (values) */ 140 #define TFA(key) ERR("Option '%s' has too few arguments", (key)) 141 142 /* Too Many Arguments (values) */ 143 #define TMA(key) ERR("Option '%s' has too many arguments", (key)) 144 145 /* Too Many Definitions */ 146 #define TMD(key) ERR("Option '%s' is already specified", (key)) 147 148 #define VALID_SOCKET_TYPES "stream, dgram, rdm, seqpacket, raw" 149 150 parse_v2_result 151 parse_syntax_v2(struct servtab *sep, char **cpp) 152 { 153 154 /* Catch multiple semantic errors instead of skipping after one */ 155 bool is_valid_definition = true; 156 /* Line number of service for error logging. */ 157 size_t line_number_start = line_number; 158 159 for (;;) { 160 switch(parse_invoke_handler(&is_valid_definition, cpp, sep)) { 161 case INVOKE_SUCCESS: 162 /* Keep reading more options in. */ 163 continue; 164 case INVOKE_FINISH: 165 /* 166 * Found a semicolon, do final checks and defaults 167 * and return. 168 * Skip whitespace after semicolon to end of line. 169 */ 170 while (isspace((unsigned char)**cpp)) { 171 (*cpp)++; 172 } 173 174 if (is_valid_definition && fill_default_values(sep)) { 175 if (**cpp == '\0') { 176 *cpp = nextline(fconfig); 177 } 178 return V2_SUCCESS; 179 } 180 181 DPRINTCONF("Ignoring invalid definition."); 182 /* Log the error for the starting line of the service */ 183 syslog(LOG_ERR, CONF_ERROR_FMT 184 "Ignoring invalid definition.", CONFIG, 185 line_number_start); 186 if (**cpp == '\0') { 187 *cpp = nextline(fconfig); 188 } 189 return V2_SKIP; 190 case INVOKE_ERROR: 191 DPRINTCONF("Syntax error; Exiting '%s'", CONFIG); 192 return V2_ERROR; 193 } 194 } 195 } 196 197 /* 198 * Fill in any remaining values that should be inferred 199 * Log an error if a required parameter that isn't 200 * provided by user can't be inferred from other servtab data. 201 * Return true on success, false on failure. 202 */ 203 static bool 204 fill_default_values(struct servtab *sep) 205 { 206 bool is_valid = true; 207 208 if (sep->se_service_max == SERVTAB_UNSPEC_SIZE_T) { 209 /* Set default to same as in v1 syntax. */ 210 sep->se_service_max = TOOMANY; 211 } 212 213 if (sep->se_hostaddr == NULL) { 214 /* Set hostaddr to default */ 215 sep->se_hostaddr = newstr(defhost); 216 } 217 218 try_infer_socktype(sep); 219 220 if (sep->se_server == NULL) { 221 /* If an executable is not specified, assume internal. */ 222 is_valid = setup_internal(sep) && is_valid; 223 } 224 225 if (sep->se_socktype == SERVTAB_UNSPEC_VAL) { 226 /* Ensure socktype is specified (either set or inferred) */ 227 ENI("socktype"); 228 is_valid = false; 229 } 230 231 if (sep->se_wait == SERVTAB_UNSPEC_VAL) { 232 /* Ensure wait is specified */ 233 ENI("wait"); 234 is_valid = false; 235 } 236 237 if (sep->se_user == NULL) { 238 /* Ensure user is specified */ 239 ENI("user"); 240 is_valid = false; 241 } 242 243 if (sep->se_proto == NULL) { 244 /* Ensure protocol is specified */ 245 ENI("protocol"); 246 is_valid = false; 247 } else { 248 is_valid = infer_protocol_ip_version(sep) && is_valid; 249 } 250 251 #ifdef IPSEC 252 setup_ipsec(sep); 253 #endif 254 return is_valid; 255 } 256 257 /* fill_default_values related functions */ 258 #ifdef IPSEC 259 static void 260 setup_ipsec(struct servtab *sep) 261 { 262 if (sep->se_policy == NULL) { 263 /* Set to default global policy */ 264 sep->se_policy = policy; 265 } else if (*sep->se_policy == '\0') { 266 /* IPsec was intentionally disabled. */ 267 free(sep->se_policy); 268 sep->se_policy = NULL; 269 } 270 } 271 #endif 272 273 static void 274 try_infer_socktype(struct servtab *sep) { 275 if (sep->se_socktype != SERVTAB_UNSPEC_VAL || sep->se_proto == NULL) { 276 return; 277 } 278 279 /* Check values of se_proto udp, udp6, tcp, tcp6 to set dgram/stream */ 280 if (strncmp(sep->se_proto, "udp", 3) == 0) { 281 sep->se_socktype = SOCK_DGRAM; 282 } else if (strncmp(sep->se_proto, "tcp", 3) == 0) { 283 sep->se_socktype = SOCK_STREAM; 284 } 285 } 286 287 static bool 288 setup_internal(struct servtab *sep) 289 { 290 pid_t wait_prev = sep->se_wait; 291 if (parse_server(sep, "internal") != 0) { 292 ENI("exec"); 293 return false; 294 } 295 296 if (wait_prev != SERVTAB_UNSPEC_VAL && wait_prev != sep->se_wait) { 297 /* If wait was already specified throw an error. */ 298 WRN(WAIT_WRN, sep->se_service); 299 } 300 return true; 301 } 302 303 static bool 304 infer_protocol_ip_version(struct servtab *sep) 305 { 306 struct in_addr tmp; 307 308 if (strcmp("tcp", sep->se_proto) != 0 309 && strcmp("udp", sep->se_proto) != 0 310 && strcmp("rpc/tcp", sep->se_proto) != 0 311 && strcmp("rpc/udp", sep->se_proto) != 0) { 312 return true; 313 } 314 315 if (inet_pton(AF_INET, sep->se_hostaddr, &tmp)) { 316 sep->se_family = AF_INET; 317 return true; 318 } 319 320 if (inet_pton(AF_INET6, sep->se_hostaddr, &tmp)) { 321 sep->se_family = AF_INET6; 322 return true; 323 } 324 325 ERR("Address family of %s is ambigous or invalid. " 326 "Explicitly specify protocol", sep->se_hostaddr); 327 return false; 328 } 329 330 /* 331 * Skips whitespaces, newline characters, and comments, 332 * and returns the next token. Returns false and logs error if an EOF is 333 * encountered. 334 */ 335 static bool 336 skip_whitespace(char **cpp) 337 { 338 char *cp = *cpp; 339 340 size_t line_start = line_number; 341 342 for (;;) { 343 while (isblank((unsigned char)*cp)) 344 cp++; 345 346 if (*cp == '\0' || *cp == '#') { 347 cp = nextline(fconfig); 348 349 /* Should never expect EOF when skipping whitespace */ 350 if (cp == NULL) { 351 ERR("Early end of file after line %zu", 352 line_start); 353 return false; 354 } 355 continue; 356 } 357 break; 358 } 359 360 *cpp = cp; 361 return true; 362 } 363 364 /* Get the key handler function pointer for the given name */ 365 static key_handler_func 366 get_handler(char *name) 367 { 368 /* Call function to handle option parsing. */ 369 for (size_t i = 0; i < __arraycount(key_handlers); i++) { 370 if (strcmp(key_handlers[i].name, name) == 0) { 371 return key_handlers[i].handler; 372 } 373 } 374 return NULL; 375 } 376 377 static inline void 378 strmove(char *buf, size_t off) 379 { 380 memmove(buf, buf + off, strlen(buf + off) + 1); 381 } 382 383 /* 384 * Perform an in-place parse of a single-line quoted string 385 * with escape sequences. Sets *cpp to the position after the quoted characters. 386 * Uses shell-style quote parsing. 387 */ 388 static bool 389 parse_quotes(char **cpp) 390 { 391 char *cp = *cpp; 392 char quote = *cp; 393 394 strmove(cp, 1); 395 while (*cp != '\0' && quote != '\0') { 396 if (*cp == quote) { 397 quote = '\0'; 398 strmove(cp, 1); 399 continue; 400 } 401 402 if (*cp == '\\') { 403 /* start is location of backslash */ 404 char *start = cp; 405 cp++; 406 switch (*cp) { 407 case 'x': { 408 int hi, lo; 409 if ((hi = hex_to_bits(cp[1])) == -1 410 || (lo = hex_to_bits(cp[2])) == -1) { 411 ERR("Invalid hexcode sequence '%.4s'", 412 start); 413 return false; 414 } 415 *start = (char)((hi << 4) | lo); 416 strmove(cp, 3); 417 continue; 418 } 419 case '\\': 420 *start = '\\'; 421 break; 422 case 'n': 423 *start = '\n'; 424 break; 425 case 't': 426 *start = '\t'; 427 break; 428 case 'r': 429 *start = '\r'; 430 break; 431 case '\'': 432 *start = '\''; 433 break; 434 case '"': 435 *start = '"'; 436 break; 437 case '\0': 438 ERR("Dangling escape sequence backslash"); 439 return false; 440 default: 441 ERR("Unknown escape sequence '\\%c'", *cp); 442 return false; 443 } 444 strmove(cp, 1); 445 continue; 446 } 447 448 /* Regular character, advance to the next one. */ 449 cp++; 450 } 451 452 if (*cp == '\0' && quote != '\0') { 453 ERR("Unclosed quote"); 454 return false; 455 } 456 *cpp = cp; 457 return true; 458 } 459 460 static int 461 hex_to_bits(char in) 462 { 463 switch(in) { 464 case '0'...'9': 465 return in - '0'; 466 case 'a'...'f': 467 return in - 'a' + 10; 468 case 'A'...'F': 469 return in - 'A' + 10; 470 default: 471 return -1; 472 } 473 } 474 475 /* 476 * Parse the next value for a key handler and advance list->cp past the found 477 * value. Return NULL if there are no more values or there was an error 478 * during parsing, and set the list->state to the appropriate value. 479 */ 480 static char * 481 next_value(vlist list) 482 { 483 char *cp = list->cp; 484 485 if (list->state != VALS_PARSING) { 486 /* Already at the end of a values list, or there was an error.*/ 487 return NULL; 488 } 489 490 if (!skip_whitespace(&cp)) { 491 list->state = VALS_ERROR; 492 return NULL; 493 } 494 495 if (*cp == ',' || *cp == ';') { 496 /* Found end of args, but not immediately after value */ 497 list->state = (*cp == ',' ? VALS_END_KEY : VALS_END_DEF); 498 list->cp = cp + 1; 499 return NULL; 500 } 501 502 /* Check for end of line */ 503 if (!skip_whitespace(&cp)) { 504 list->state = VALS_ERROR; 505 return NULL; 506 } 507 508 /* 509 * Found the start of a potential value. Advance one character 510 * past the end of the value. 511 */ 512 char *start = cp; 513 while (!isblank((unsigned char)*cp) && *cp != '#' && 514 *cp != ',' && *cp != ';' && *cp != '\0' ) { 515 if (*cp == '"' || *cp == '\'') { 516 /* Found a quoted segment */ 517 if (!parse_quotes(&cp)) { 518 list->state = VALS_ERROR; 519 return NULL; 520 } 521 } else { 522 /* Find the end of the value */ 523 cp++; 524 } 525 } 526 527 /* Handle comments next to unquoted values */ 528 if (*cp == '#') { 529 *cp = '\0'; 530 list->cp = cp; 531 return start; 532 } 533 534 if (*cp == '\0') { 535 /* 536 * Value ends with end of line, so it is already NUL-terminated 537 */ 538 list->cp = cp; 539 return start; 540 } 541 542 if (*cp == ',') { 543 list->state = VALS_END_KEY; 544 } else if (*cp == ';') { 545 list->state = VALS_END_DEF; 546 } 547 548 *cp = '\0'; 549 /* Advance past null so we don't skip the rest of the line */ 550 list->cp = cp + 1; 551 return start; 552 } 553 554 /* Parse key name and invoke associated handler */ 555 static invoke_result 556 parse_invoke_handler(bool *is_valid_definition, char **cpp, struct servtab *sep) 557 { 558 char *key_name, save, *cp = *cpp; 559 int is_blank; 560 key_handler_func handler; 561 val_parse_info info; 562 563 /* Skip any whitespace if it exists, otherwise do nothing */ 564 if (!skip_whitespace(&cp)) { 565 return INVOKE_ERROR; 566 } 567 568 /* Starting character of key */ 569 key_name = cp; 570 571 572 /* alphabetical or underscore allowed in name */ 573 while (isalpha((unsigned char)*cp) || *cp == '_') { 574 cp++; 575 } 576 577 is_blank = isblank((unsigned char)*cp); 578 579 /* Get key handler and move to start of values */ 580 if (*cp != '=' && !is_blank && *cp != '#') { 581 ERR("Expected '=' but found '%c'", *cp); 582 return INVOKE_ERROR; 583 } 584 585 save = *cp; 586 *cp = '\0'; 587 cp++; 588 589 handler = get_handler(key_name); 590 591 if (handler == NULL) { 592 ERR("Unknown option '%s'", key_name); 593 handler = unknown_handler; 594 } 595 596 /* If blank or new line, still need to find the '=' or throw error */ 597 if (is_blank || save == '#') { 598 if (save == '#') { 599 cp = nextline(fconfig); 600 } 601 602 skip_whitespace(&cp); 603 if (*cp != '=') { 604 ERR("Expected '=' but found '%c'", *cp); 605 return INVOKE_ERROR; 606 } 607 cp++; 608 } 609 610 /* Skip whitespace to start of values */ 611 if (!skip_whitespace(&cp)) { 612 return INVOKE_ERROR; 613 } 614 615 info = (val_parse_info) {cp, VALS_PARSING}; 616 617 /* 618 * Read values for key and write into sep. 619 * If parsing is successful, all values for key must be read. 620 */ 621 if (handler(sep, &info) == KEY_HANDLER_FAILURE) { 622 /* 623 * Eat remaining values if an error happened 624 * so more errors can be caught. 625 */ 626 while (next_value(&info) != NULL) 627 continue; 628 *is_valid_definition = false; 629 } 630 631 if (info.state == VALS_END_DEF) { 632 /* 633 * Exit definition handling for(;;). 634 * Set the position to the end of the definition, 635 * for multi-definition lines. 636 */ 637 *cpp = info.cp; 638 return INVOKE_FINISH; 639 } 640 if (info.state == VALS_ERROR) { 641 /* Parse error, stop reading config */ 642 return INVOKE_ERROR; 643 } 644 645 *cpp = info.cp; 646 return INVOKE_SUCCESS; 647 } 648 649 /* Return true if sep must be a built-in service */ 650 static bool 651 is_internal(struct servtab *sep) 652 { 653 return sep->se_bi != NULL; 654 } 655 656 /* 657 * Key-values handlers 658 */ 659 660 static hresult 661 /*ARGSUSED*/ 662 unknown_handler(struct servtab *sep, vlist values) 663 { 664 /* Return failure for an unknown service name. */ 665 return KEY_HANDLER_FAILURE; 666 } 667 668 /* Set listen address for this service */ 669 static hresult 670 bind_handler(struct servtab *sep, vlist values) 671 { 672 if (sep->se_hostaddr != NULL) { 673 TMD("bind"); 674 return KEY_HANDLER_FAILURE; 675 } 676 677 char *val = next_value(values); 678 sep->se_hostaddr = newstr(val); 679 if (next_value(values) != NULL) { 680 TMA("bind"); 681 return KEY_HANDLER_FAILURE; 682 } 683 return KEY_HANDLER_SUCCESS; 684 } 685 686 static hresult 687 socket_type_handler(struct servtab *sep, vlist values) 688 { 689 char *type = next_value(values); 690 if (type == NULL) { 691 TFA("socktype"); 692 return KEY_HANDLER_FAILURE; 693 } 694 695 parse_socktype(type, sep); 696 697 if (sep->se_socktype == -1) { 698 ERR("Invalid socket type '%s'. Valid: " VALID_SOCKET_TYPES, 699 type); 700 return KEY_HANDLER_FAILURE; 701 } 702 703 if (next_value(values) != NULL) { 704 TMA("socktype"); 705 return KEY_HANDLER_FAILURE; 706 } 707 708 return KEY_HANDLER_SUCCESS; 709 } 710 711 /* Set accept filter SO_ACCEPTFILTER */ 712 static hresult 713 filter_handler(struct servtab *sep, vlist values) 714 { 715 /* 716 * See: SO_ACCEPTFILTER https://man.netbsd.org/setsockopt.2 717 * An accept filter can have one other argument. 718 * This code currently only supports one accept filter 719 * Also see parse_accept_filter(char* arg, struct servtab*sep) 720 */ 721 722 char *af_name, *af_arg; 723 724 af_name = next_value(values); 725 726 if (af_name == NULL) { 727 TFA("filter"); 728 return KEY_HANDLER_FAILURE; 729 } 730 731 /* Store af_name in se_accf.af_name, no newstr call */ 732 strlcpy(sep->se_accf.af_name, af_name, sizeof(sep->se_accf.af_name)); 733 734 af_arg = next_value(values); 735 736 if (af_arg != NULL) { 737 strlcpy(sep->se_accf.af_arg, af_arg, 738 sizeof(sep->se_accf.af_arg)); 739 if (next_value(values) != NULL) { 740 TMA("filter"); 741 return KEY_HANDLER_FAILURE; 742 } 743 } else { 744 /* Store null string */ 745 sep->se_accf.af_arg[0] = '\0'; 746 } 747 748 return KEY_HANDLER_SUCCESS; 749 } 750 751 /* Set protocol (udp, tcp, unix, etc.) */ 752 static hresult 753 protocol_handler(struct servtab *sep, vlist values) 754 { 755 char *val; 756 757 if ((val = next_value(values)) == NULL) { 758 TFA("protocol"); 759 return KEY_HANDLER_FAILURE; 760 } 761 762 if (sep->se_type == NORM_TYPE && 763 strncmp(val, "faith/", strlen("faith/")) == 0) { 764 val += strlen("faith/"); 765 sep->se_type = FAITH_TYPE; 766 } 767 sep->se_proto = newstr(val); 768 769 if (parse_protocol(sep)) 770 return KEY_HANDLER_FAILURE; 771 772 if ((val = next_value(values)) != NULL) { 773 TMA("protocol"); 774 return KEY_HANDLER_FAILURE; 775 } 776 return KEY_HANDLER_SUCCESS; 777 } 778 779 /* 780 * Convert a string number possible ending with k or m to an integer. 781 * Based on MALFORMED, GETVAL, and ASSIGN in getconfigent(void). 782 */ 783 static int 784 size_to_bytes(char *arg) 785 { 786 char *tail; 787 int rstatus, count; 788 789 count = (int)strtoi(arg, &tail, 10, 0, INT_MAX, &rstatus); 790 791 if (rstatus != 0 && rstatus != ENOTSUP) { 792 ERR("Invalid buffer size '%s': %s", arg, strerror(rstatus)); 793 return -1; 794 } 795 796 switch(tail[0]) { 797 case 'm': 798 if (__builtin_smul_overflow((int)count, 1024, &count)) { 799 ERR("Invalid buffer size '%s': Result too large", arg); 800 return -1; 801 } 802 /* FALLTHROUGH */ 803 case 'k': 804 if (__builtin_smul_overflow((int)count, 1024, &count)) { 805 ERR("Invalid buffer size '%s': Result too large", arg); 806 return -1; 807 } 808 /* FALLTHROUGH */ 809 case '\0': 810 return count; 811 default: 812 ERR("Invalid buffer size unit prefix"); 813 return -1; 814 } 815 } 816 817 /* sndbuf size */ 818 static hresult 819 send_buf_handler(struct servtab *sep, vlist values) 820 { 821 char *arg; 822 int buffer_size; 823 824 if (ISMUX(sep)) { 825 ERR("%s: can't specify buffer sizes for tcpmux services", 826 sep->se_service); 827 return KEY_HANDLER_FAILURE; 828 } 829 830 831 if ((arg = next_value(values)) == NULL) { 832 TFA("sndbuf"); 833 return KEY_HANDLER_FAILURE; 834 } 835 836 buffer_size = size_to_bytes(arg); 837 838 if (buffer_size == -1) { 839 return KEY_HANDLER_FAILURE; 840 } 841 842 if ((arg = next_value(values)) != NULL) { 843 TMA("sndbuf"); 844 return KEY_HANDLER_FAILURE; 845 } 846 847 sep->se_sndbuf = buffer_size; 848 849 return KEY_HANDLER_SUCCESS; 850 } 851 852 /* recvbuf size */ 853 static hresult 854 recv_buf_handler(struct servtab *sep, vlist values) 855 { 856 char *arg; 857 int buffer_size; 858 859 if (ISMUX(sep)) { 860 ERR("%s: Cannot specify buffer sizes for tcpmux services", 861 sep->se_service); 862 return KEY_HANDLER_FAILURE; 863 } 864 865 if ((arg = next_value(values)) == NULL){ 866 TFA("recvbuf"); 867 return KEY_HANDLER_FAILURE; 868 } 869 870 buffer_size = size_to_bytes(arg); 871 872 if (buffer_size == -1) { 873 return KEY_HANDLER_FAILURE; 874 } 875 876 if ((arg = next_value(values)) != NULL) { 877 TMA("recvbuf"); 878 return KEY_HANDLER_FAILURE; 879 } 880 881 sep->se_rcvbuf = buffer_size; 882 883 return KEY_HANDLER_SUCCESS; 884 885 } 886 887 /* Same as wait in positional */ 888 static hresult 889 wait_handler(struct servtab *sep, vlist values) 890 { 891 char *val; 892 pid_t wait; 893 894 /* If 'wait' is specified after internal exec */ 895 896 if (!is_internal(sep) && sep->se_wait != SERVTAB_UNSPEC_VAL) { 897 /* Prevent duplicate wait keys */ 898 TMD("wait"); 899 return KEY_HANDLER_FAILURE; 900 } 901 902 val = next_value(values); 903 904 if (val == NULL) { 905 TFA("wait"); 906 return KEY_HANDLER_FAILURE; 907 } 908 909 if (strcmp(val, "yes") == 0) { 910 wait = true; 911 } else if (strcmp(val, "no") == 0) { 912 wait = false; 913 } else { 914 ERR("Invalid value '%s' for wait. Valid: yes, no", val); 915 return KEY_HANDLER_FAILURE; 916 } 917 918 if (is_internal(sep) && wait != sep->se_wait) { 919 /* If wait was set for internal service check for correctness */ 920 WRN(WAIT_WRN, sep->se_service); 921 } else if (parse_wait(sep, wait)) { 922 return KEY_HANDLER_FAILURE; 923 } 924 925 if ((val = next_value(values)) != NULL) { 926 TMA("wait"); 927 return KEY_HANDLER_FAILURE; 928 } 929 930 return KEY_HANDLER_SUCCESS; 931 } 932 933 /* Set max connections in interval rate-limit, same as max in positional */ 934 static hresult 935 service_max_handler(struct servtab *sep, vlist values) 936 { 937 char *count_str; 938 int rstatus; 939 940 if (sep->se_service_max != SERVTAB_UNSPEC_SIZE_T) { 941 TMD("service_max"); 942 return KEY_HANDLER_FAILURE; 943 } 944 945 count_str = next_value(values); 946 947 if (count_str == NULL) { 948 TFA("service_max"); 949 return KEY_HANDLER_FAILURE; 950 } 951 952 size_t count = (size_t)strtou(count_str, NULL, 10, 0, 953 SERVTAB_COUNT_MAX, &rstatus); 954 955 if (rstatus != 0) { 956 ERR("Invalid service_max '%s': %s", count_str, 957 strerror(rstatus)); 958 return KEY_HANDLER_FAILURE; 959 } 960 961 if (next_value(values) != NULL) { 962 TMA("service_max"); 963 return KEY_HANDLER_FAILURE; 964 } 965 966 sep->se_service_max = count; 967 968 return KEY_HANDLER_SUCCESS; 969 } 970 971 static hresult 972 ip_max_handler(struct servtab *sep, vlist values) 973 { 974 char *count_str; 975 int rstatus; 976 977 if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) { 978 TMD("ip_max"); 979 return KEY_HANDLER_FAILURE; 980 } 981 982 count_str = next_value(values); 983 984 if (count_str == NULL) { 985 TFA("ip_max"); 986 return KEY_HANDLER_FAILURE; 987 } 988 989 size_t count = (size_t)strtou(count_str, NULL, 10, 0, 990 SERVTAB_COUNT_MAX, &rstatus); 991 992 if (rstatus != 0) { 993 ERR("Invalid ip_max '%s': %s", count_str, strerror(rstatus)); 994 return KEY_HANDLER_FAILURE; 995 } 996 997 if (next_value(values) != NULL) { 998 TMA("ip_max"); 999 return KEY_HANDLER_FAILURE; 1000 } 1001 1002 sep->se_ip_max = count; 1003 1004 return KEY_HANDLER_SUCCESS; 1005 } 1006 1007 /* Set max connections to accept */ 1008 static hresult 1009 accept_max_handler(struct servtab *sep, vlist values) 1010 { 1011 char *count_str; 1012 int rstatus; 1013 1014 if (sep->se_accept_max != SERVTAB_UNSPEC_SIZE_T) { 1015 TMD("accept_max"); 1016 return KEY_HANDLER_FAILURE; 1017 } 1018 1019 count_str = next_value(values); 1020 1021 if (count_str == NULL) { 1022 TFA("accept_max"); 1023 return KEY_HANDLER_FAILURE; 1024 } 1025 1026 size_t count = (size_t)strtou(count_str, NULL, 10, 0, 1027 SERVTAB_COUNT_MAX, &rstatus); 1028 1029 if (rstatus != 0) { 1030 ERR("Invalid accept_max '%s': %s", count_str, 1031 strerror(rstatus)); 1032 return KEY_HANDLER_FAILURE; 1033 } 1034 1035 if (next_value(values) != NULL) { 1036 TMA("accept_max"); 1037 return KEY_HANDLER_FAILURE; 1038 } 1039 1040 sep->se_accept_max = count; 1041 1042 return KEY_HANDLER_SUCCESS; 1043 } 1044 1045 /* Set user to execute as */ 1046 static hresult 1047 user_handler(struct servtab *sep, vlist values) 1048 { 1049 if (sep->se_user != NULL) { 1050 TMD("user"); 1051 return KEY_HANDLER_FAILURE; 1052 } 1053 1054 char *name = next_value(values); 1055 1056 if (name == NULL) { 1057 TFA("user"); 1058 return KEY_HANDLER_FAILURE; 1059 } 1060 1061 sep->se_user = newstr(name); 1062 1063 if (next_value(values) != NULL) { 1064 TMA("user"); 1065 return KEY_HANDLER_FAILURE; 1066 } 1067 1068 return KEY_HANDLER_SUCCESS; 1069 } 1070 1071 /* Set group to execute as */ 1072 static hresult 1073 group_handler(struct servtab *sep, vlist values) 1074 { 1075 char *name = next_value(values); 1076 1077 if (name == NULL) { 1078 TFA("group"); 1079 return KEY_HANDLER_FAILURE; 1080 } 1081 1082 sep->se_group = newstr(name); 1083 1084 if (next_value(values) != NULL) { 1085 TMA("group"); 1086 return KEY_HANDLER_FAILURE; 1087 } 1088 1089 return KEY_HANDLER_SUCCESS; 1090 } 1091 1092 /* Handle program path or "internal" */ 1093 static hresult 1094 exec_handler(struct servtab *sep, vlist values) 1095 { 1096 char *val; 1097 1098 if ((val = next_value(values)) == NULL) { 1099 TFA("exec"); 1100 return KEY_HANDLER_FAILURE; 1101 } 1102 1103 pid_t wait_prev = sep->se_wait; 1104 if (parse_server(sep, val)) 1105 return KEY_HANDLER_FAILURE; 1106 if (is_internal(sep) && wait_prev != SERVTAB_UNSPEC_VAL) { 1107 /* 1108 * Warn if the user specifies a value for an internal which 1109 * is different 1110 */ 1111 if (wait_prev != sep->se_wait) { 1112 WRN(WAIT_WRN, sep->se_service); 1113 } 1114 } 1115 1116 if ((val = next_value(values)) != NULL) { 1117 TMA("exec"); 1118 return KEY_HANDLER_FAILURE; 1119 } 1120 1121 return KEY_HANDLER_SUCCESS; 1122 } 1123 1124 /* Handle program arguments */ 1125 static hresult 1126 args_handler(struct servtab *sep, vlist values) 1127 { 1128 char *val; 1129 int argc; 1130 1131 if (sep->se_argv[0] != NULL) { 1132 TMD("args"); 1133 return KEY_HANDLER_FAILURE; 1134 } 1135 1136 argc = 0; 1137 for (val = next_value(values); val != NULL; val = next_value(values)) { 1138 if (argc >= MAXARGV) { 1139 ERR("Must be fewer than " TOSTRING(MAXARGV) 1140 " arguments"); 1141 return KEY_HANDLER_FAILURE; 1142 } 1143 sep->se_argv[argc++] = newstr(val); 1144 } 1145 while (argc <= MAXARGV) 1146 sep->se_argv[argc++] = NULL; 1147 1148 return KEY_HANDLER_SUCCESS; 1149 1150 } 1151 1152 #ifdef IPSEC 1153 /* 1154 * ipsec_handler currently uses the ipsec.h utilities for parsing, requiring 1155 * all policies as a single value. This handler could potentially allow multiple 1156 * policies as separate values in the future, but strings would need to be 1157 * concatenated so the existing ipsec.h functions continue to work and policies 1158 * can continue to be stored in sep->policy. 1159 */ 1160 static hresult 1161 ipsec_handler(struct servtab *sep, vlist values) 1162 { 1163 if (sep->se_policy != NULL) { 1164 TMD("ipsec"); 1165 return KEY_HANDLER_FAILURE; 1166 } 1167 1168 char *ipsecstr = next_value(values); 1169 1170 if (ipsecstr != NULL && ipsecsetup_test(ipsecstr) < 0) { 1171 ERR("IPsec policy '%s' is invalid", ipsecstr); 1172 return KEY_HANDLER_FAILURE; 1173 } 1174 1175 /* 1176 * Use 'ipsec=' with no argument to disable ipsec for this service 1177 * An empty string indicates that IPsec was disabled, handled in 1178 * fill_default_values. 1179 */ 1180 sep->se_policy = policy != NULL ? newstr(ipsecstr) : newstr(""); 1181 1182 if (next_value(values) != NULL) { 1183 TMA("ipsec"); 1184 /* Currently only one semicolon separated string is allowed */ 1185 return KEY_HANDLER_FAILURE; 1186 } 1187 1188 return KEY_HANDLER_SUCCESS; 1189 } 1190 #endif 1191