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