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