parse_v2.c revision 1.3 1 /* $NetBSD: parse_v2.c,v 1.3 2021/08/30 17:32:23 rillig 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.3 2021/08/30 17:32:23 rillig 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 protocol_handler(struct servtab *, vlist);
79 static hresult recv_buf_handler(struct servtab *, vlist);
80 static hresult send_buf_handler(struct servtab *, vlist);
81 static hresult socket_type_handler(struct servtab *, vlist);
82 static hresult unknown_handler(struct servtab *, vlist);
83 static hresult user_handler(struct servtab *, vlist);
84 static hresult wait_handler(struct servtab *, vlist);
85
86 #ifdef IPSEC
87 static hresult ipsec_handler(struct servtab *, vlist);
88 #endif
89
90 static invoke_result parse_invoke_handler(bool *, char **, struct servtab *);
91 static bool fill_default_values(struct servtab *);
92 static bool parse_quotes(char **);
93 static bool skip_whitespace(char **);
94 static int size_to_bytes(char *);
95 static bool infer_protocol_ip_version(struct servtab *);
96 static bool setup_internal(struct servtab *);
97 static void try_infer_socktype(struct servtab *);
98 int hex_to_bits(char);
99 #ifdef IPSEC
100 static void setup_ipsec(struct servtab *);
101 #endif
102 static 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 */
107 typedef hresult (*key_handler_func)(struct servtab *, vlist);
108
109 /* List of v2 syntax key handlers */
110 static 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 Arguemnts (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
148 parse_v2_result
149 parse_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 invoke_result result;
157
158 for (;;) {
159 switch(result =
160 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 int 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 %d",
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 && quote) {
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 temp, bits;
409 if (((bits = hex_to_bits(*(cp + 1))) == -1)
410 || ((temp = hex_to_bits(*(cp + 2))) == -1)) {
411 ERR("Invalid hexcode sequence '%.4s'",
412 start);
413 return false;
414 }
415 bits <<= 4;
416 bits |= temp;
417 *start = bits;
418 strmove(cp, 3);
419 continue;
420 }
421 case '\\':
422 *start = '\\';
423 break;
424 case 'n':
425 *start = '\n';
426 break;
427 case 't':
428 *start = '\t';
429 break;
430 case 'r':
431 *start = '\r';
432 break;
433 case '\'':
434 *start = '\'';
435 break;
436 case '"':
437 *start = '"';
438 break;
439 case '\0':
440 ERR("Dangling escape sequence backslash");
441 return false;
442 default:
443 ERR("Unknown escape sequence '\\%c'", *cp);
444 return false;
445 }
446 strmove(cp, 1);
447 continue;
448 }
449
450 /* Regular character, advance to the next one. */
451 cp++;
452 }
453
454 if (*cp == '\0' && quote) {
455 ERR("Unclosed quote");
456 return false;
457 }
458 *cpp = cp;
459 return true;
460 }
461
462 int
463 hex_to_bits(char in)
464 {
465 switch(in) {
466 case '0'...'9':
467 return in - '0';
468 case 'a'...'f':
469 return in - 'a' + 10;
470 case 'A'...'F':
471 return in - 'A' + 10;
472 default:
473 return -1;
474 }
475 }
476
477 /*
478 * Parse the next value for a key handler and advance list->cp past the found
479 * value. Return NULL if there are no more values or there was an error
480 * during parsing, and set the list->state to the appropriate value.
481 */
482 static char *
483 next_value(vlist list)
484 {
485 char *cp = list->cp;
486
487 if (list->state != VALS_PARSING) {
488 /* Already at the end of a values list, or there was an error.*/
489 return NULL;
490 }
491
492 if (!skip_whitespace(&cp)) {
493 list->state = VALS_ERROR;
494 return NULL;
495 }
496
497 if (*cp == ',' || *cp == ';') {
498 /* Found end of args, but not immediately after value */
499 list->state = (*cp == ',' ? VALS_END_KEY : VALS_END_DEF);
500 list->cp = cp + 1;
501 return NULL;
502 }
503
504 /* Check for end of line */
505 if (!skip_whitespace(&cp)) {
506 list->state = VALS_ERROR;
507 return NULL;
508 }
509
510 /*
511 * Found the start of a potential value. Advance one character
512 * past the end of the value.
513 */
514 char * start = cp;
515 while (!isblank((unsigned char)*cp) && *cp != '#' &&
516 *cp != ',' && *cp != ';' && *cp != '\0' ) {
517 if (*cp == '"' || *cp == '\'') {
518 /* Found a quoted segment */
519 if (!parse_quotes(&cp)) {
520 list->state = VALS_ERROR;
521 return NULL;
522 }
523 } else {
524 /* Find the end of the value */
525 cp++;
526 }
527 }
528
529 /* Handle comments next to unquoted values */
530 if (*cp == '#') {
531 *cp = '\0';
532 list->cp = cp;
533 return start;
534 }
535
536 if (*cp == '\0') {
537 /*
538 * Value ends with end of line, so it is already NUL-terminated
539 */
540 list->cp = cp;
541 return start;
542 }
543
544 if (*cp == ',') {
545 list->state = VALS_END_KEY;
546 } else if (*cp == ';') {
547 list->state = VALS_END_DEF;
548 }
549
550 *cp = '\0';
551 /* Advance past null so we don't skip the rest of the line */
552 list->cp = cp + 1;
553 return start;
554 }
555
556 /* Parse key name and invoke associated handler */
557 static invoke_result
558 parse_invoke_handler(bool *is_valid_definition, char **cpp, struct servtab *sep)
559 {
560 char *key_name, save, *cp = *cpp;
561 int is_blank;
562 key_handler_func handler;
563 val_parse_info info;
564
565 /* Skip any whitespace if it exists, otherwise do nothing */
566 if (!skip_whitespace(&cp)) {
567 return INVOKE_ERROR;
568 }
569
570 /* Starting character of key */
571 key_name = cp;
572
573
574 /* alphabetical or underscore allowed in name */
575 while (isalpha((unsigned char)*cp) || *cp == '_') {
576 cp++;
577 }
578
579 is_blank = isblank((unsigned char)*cp);
580
581 /* Get key handler and move to start of values */
582 if (*cp != '=' && !is_blank && *cp != '#') {
583 ERR("Expected '=' but found '%c'", *cp);
584 return INVOKE_ERROR;
585 }
586
587 save = *cp;
588 *cp = '\0';
589 cp++;
590
591 handler = get_handler(key_name);
592
593 if (handler == NULL) {
594 ERR("Unknown option '%s'", key_name);
595 handler = unknown_handler;
596 }
597
598 /* If blank or new line, still need to find the '=' or throw error */
599 if (is_blank || save == '#') {
600 if (save == '#') {
601 cp = nextline(fconfig);
602 }
603
604 skip_whitespace(&cp);
605 if (*cp != '=') {
606 ERR("Expected '=' but found '%c'", *cp);
607 return INVOKE_ERROR;
608 }
609 cp++;
610 }
611
612 /* Skip whitespace to start of values */
613 if (!skip_whitespace(&cp)) {
614 return INVOKE_ERROR;
615 }
616
617 info = (val_parse_info) {cp, VALS_PARSING};
618
619 /*
620 * Read values for key and write into sep.
621 * If parsing is successful, all values for key must be read.
622 */
623 if (handler(sep, &info) == KEY_HANDLER_FAILURE) {
624 /*
625 * Eat remaining values if an error happened
626 * so more errors can be caught.
627 */
628 while (next_value(&info) != NULL)
629 continue;
630 *is_valid_definition = false;
631 }
632
633 if (info.state == VALS_END_DEF) {
634 /*
635 * Exit definition handling for(;;).
636 * Set the position to the end of the definition,
637 * for multi-definition lines.
638 */
639 *cpp = info.cp;
640 return INVOKE_FINISH;
641 }
642 if (info.state == VALS_ERROR) {
643 /* Parse error, stop reading config */
644 return INVOKE_ERROR;
645 }
646
647 *cpp = info.cp;
648 return INVOKE_SUCCESS;
649 }
650
651 /* Return true if sep must be a built-in service */
652 static bool
653 is_internal(struct servtab *sep)
654 {
655 return sep->se_bi != NULL;
656 }
657
658 /*
659 * Key-values handlers
660 */
661
662 static hresult
663 unknown_handler(struct servtab *sep, vlist values)
664 {
665 /* Return failure for an unknown service name. */
666 return KEY_HANDLER_FAILURE;
667 }
668
669 /* Set listen address for this service */
670 static hresult
671 bind_handler(struct servtab *sep, vlist values)
672 {
673 if (sep->se_hostaddr != NULL) {
674 TMD("bind");
675 return KEY_HANDLER_FAILURE;
676 }
677
678 char *val = next_value(values);
679 sep->se_hostaddr = newstr(val);
680 if (next_value(values) != NULL) {
681 TMA("bind");
682 return KEY_HANDLER_FAILURE;
683 }
684 return KEY_HANDLER_SUCCESS;
685 }
686
687 static hresult
688 socket_type_handler(struct servtab *sep, vlist values)
689 {
690 char *type = next_value(values);
691 if (type == NULL) {
692 TFA("socktype");
693 return KEY_HANDLER_FAILURE;
694 }
695
696 parse_socktype(type, sep);
697
698 if (sep->se_socktype == -1) {
699 ERR("Invalid socket type '%s'. Valid: " VALID_SOCKET_TYPES,
700 type);
701 return KEY_HANDLER_FAILURE;
702 }
703
704 if (next_value(values) != NULL) {
705 TMA("socktype");
706 return KEY_HANDLER_FAILURE;
707 }
708
709 return KEY_HANDLER_SUCCESS;
710 }
711
712 /* Set accept filter SO_ACCEPTFILTER */
713 static hresult
714 filter_handler(struct servtab *sep, vlist values)
715 {
716 /*
717 * See: SO_ACCEPTFILTER https://man.netbsd.org/setsockopt.2
718 * An accept filter can have one other argument.
719 * This code currently only supports one accept filter
720 * Also see parse_accept_filter(char* arg, struct servtab*sep)
721 */
722
723 char *af_name, *af_arg;
724
725 af_name = next_value(values);
726
727 if (af_name == NULL) {
728 TFA("filter");
729 return KEY_HANDLER_FAILURE;
730 }
731
732 /* Store af_name in se_accf.af_name, no newstr call */
733 strlcpy(sep->se_accf.af_name, af_name, sizeof(sep->se_accf.af_name));
734
735 af_arg = next_value(values);
736
737 if (af_arg != NULL) {
738 strlcpy(sep->se_accf.af_arg, af_arg,
739 sizeof(sep->se_accf.af_arg));
740 if (next_value(values) != NULL) {
741 TMA("filter");
742 return KEY_HANDLER_FAILURE;
743 }
744 } else {
745 /* Store null string */
746 sep->se_accf.af_arg[0] = '\0';
747 }
748
749 return KEY_HANDLER_SUCCESS;
750 }
751
752 /* Set protocol (udp, tcp, unix, etc.) */
753 static hresult
754 protocol_handler(struct servtab *sep, vlist values)
755 {
756 char *val;
757
758 if ((val = next_value(values)) == NULL) {
759 TFA("protocol");
760 return KEY_HANDLER_FAILURE;
761 }
762
763 if (sep->se_type == NORM_TYPE &&
764 strncmp(val, "faith/", strlen("faith/")) == 0) {
765 val += strlen("faith/");
766 sep->se_type = FAITH_TYPE;
767 }
768 sep->se_proto = newstr(val);
769
770 if (parse_protocol(sep))
771 return KEY_HANDLER_FAILURE;
772
773 if ((val = next_value(values)) != NULL) {
774 TMA("protocol");
775 return KEY_HANDLER_FAILURE;
776 }
777 return KEY_HANDLER_SUCCESS;
778 }
779
780 /*
781 * Convert a string number possible ending with k or m to an integer.
782 * Based on MALFORMED, GETVAL, and ASSIGN in getconfigent(void).
783 */
784 static int
785 size_to_bytes(char *arg)
786 {
787 char *tail;
788 int rstatus, count;
789
790 count = (int)strtoi(arg, &tail, 10, 0, INT_MAX, &rstatus);
791
792 if (rstatus && rstatus != ENOTSUP) {
793 ERR("Invalid buffer size '%s': %s", arg, strerror(rstatus));
794 return -1;
795 }
796
797 switch(tail[0]) {
798 case 'm':
799 if (__builtin_smul_overflow((int)count, 1024, &count)) {
800 ERR("Invalid buffer size '%s': Result too large", arg);
801 return -1;
802 }
803 /* FALLTHROUGH */
804 case 'k':
805 if (__builtin_smul_overflow((int)count, 1024, &count)) {
806 ERR("Invalid buffer size '%s': Result too large", arg);
807 return -1;
808 }
809 /* FALLTHROUGH */
810 case '\0':
811 return count;
812 default:
813 ERR("Invalid buffer size unit prefix");
814 return -1;
815 }
816 }
817
818 /* sndbuf size */
819 static hresult
820 send_buf_handler(struct servtab *sep, vlist values)
821 {
822 char *arg;
823 int buffer_size;
824
825 if (ISMUX(sep)) {
826 ERR("%s: can't specify buffer sizes for tcpmux services",
827 sep->se_service);
828 return KEY_HANDLER_FAILURE;
829 }
830
831
832 if ((arg = next_value(values)) == NULL) {
833 TFA("sndbuf");
834 return KEY_HANDLER_FAILURE;
835 }
836
837 buffer_size = size_to_bytes(arg);
838
839 if (buffer_size == -1) {
840 return KEY_HANDLER_FAILURE;
841 }
842
843 if ((arg = next_value(values)) != NULL) {
844 TMA("sndbuf");
845 return KEY_HANDLER_FAILURE;
846 }
847
848 sep->se_sndbuf = buffer_size;
849
850 return KEY_HANDLER_SUCCESS;
851 }
852
853 /* recvbuf size */
854 static hresult
855 recv_buf_handler(struct servtab *sep, vlist values)
856 {
857 char *arg;
858 int buffer_size;
859
860 if (ISMUX(sep)) {
861 ERR("%s: Cannot specify buffer sizes for tcpmux services",
862 sep->se_service);
863 return KEY_HANDLER_FAILURE;
864 }
865
866 if ((arg = next_value(values)) == NULL){
867 TFA("recvbuf");
868 return KEY_HANDLER_FAILURE;
869 }
870
871 buffer_size = size_to_bytes(arg);
872
873 if (buffer_size == -1) {
874 return KEY_HANDLER_FAILURE;
875 }
876
877 if ((arg = next_value(values)) != NULL) {
878 TMA("recvbuf");
879 return KEY_HANDLER_FAILURE;
880 }
881
882 sep->se_rcvbuf = buffer_size;
883
884 return KEY_HANDLER_SUCCESS;
885
886 }
887
888 /* Same as wait in positional */
889 static hresult
890 wait_handler(struct servtab *sep, vlist values)
891 {
892 char *val;
893 pid_t wait;
894
895 /* If 'wait' is specified after internal exec */
896
897 if (!is_internal(sep) && sep->se_wait != SERVTAB_UNSPEC_VAL) {
898 /* Prevent duplicate wait keys */
899 TMD("wait");
900 return KEY_HANDLER_FAILURE;
901 }
902
903 val = next_value(values);
904
905 if (val == NULL) {
906 TFA("wait");
907 return KEY_HANDLER_FAILURE;
908 }
909
910 if (strcmp(val, "yes") == 0) {
911 wait = true;
912 } else if (strcmp(val, "no") == 0) {
913 wait = false;
914 } else {
915 ERR("Invalid value '%s' for wait. Valid: yes, no", val);
916 return KEY_HANDLER_FAILURE;
917 }
918
919 if (is_internal(sep) && wait != sep->se_wait) {
920 /* If wait was set for internal service check for correctness */
921 WRN(WAIT_WRN, sep->se_service);
922 } else if (parse_wait(sep, wait)) {
923 return KEY_HANDLER_FAILURE;
924 }
925
926 if ((val = next_value(values)) != NULL) {
927 TMA("wait");
928 return KEY_HANDLER_FAILURE;
929 }
930
931 return KEY_HANDLER_SUCCESS;
932 }
933
934 /* Set max connections in interval rate-limit, same as max in positional */
935 static hresult
936 service_max_handler(struct servtab *sep, vlist values)
937 {
938 char *count_str;
939 int rstatus;
940
941 if (sep->se_service_max != SERVTAB_UNSPEC_SIZE_T) {
942 TMD("service_max");
943 return KEY_HANDLER_FAILURE;
944 }
945
946 count_str = next_value(values);
947
948 if (count_str == NULL) {
949 TFA("service_max");
950 return KEY_HANDLER_FAILURE;
951 }
952
953 size_t count = (size_t)strtou(count_str, NULL, 10, 0,
954 SERVTAB_COUNT_MAX, &rstatus);
955
956 if (rstatus) {
957 ERR("Invalid service_max '%s': %s", count_str,
958 strerror(rstatus));
959 return KEY_HANDLER_FAILURE;
960 }
961
962 if (next_value(values) != NULL) {
963 TMA("service_max");
964 return KEY_HANDLER_FAILURE;
965 }
966
967 sep->se_service_max = count;
968
969 return KEY_HANDLER_SUCCESS;
970 }
971
972 static hresult
973 ip_max_handler(struct servtab *sep, vlist values)
974 {
975 char *count_str;
976 int rstatus;
977
978 if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
979 TMD("ip_max");
980 return KEY_HANDLER_FAILURE;
981 }
982
983 count_str = next_value(values);
984
985 if (count_str == NULL) {
986 TFA("ip_max");
987 return KEY_HANDLER_FAILURE;
988 }
989
990 size_t count = (size_t)strtou(count_str, NULL, 10, 0,
991 SERVTAB_COUNT_MAX, &rstatus);
992
993 if (rstatus) {
994 ERR("Invalid ip_max '%s': %s", count_str, strerror(rstatus));
995 return KEY_HANDLER_FAILURE;
996 }
997
998 if (next_value(values) != NULL) {
999 TMA("ip_max");
1000 return KEY_HANDLER_FAILURE;
1001 }
1002
1003 sep->se_ip_max = count;
1004
1005 return KEY_HANDLER_SUCCESS;
1006 }
1007
1008 /* Set user to execute as */
1009 static hresult
1010 user_handler(struct servtab *sep, vlist values)
1011 {
1012 if (sep->se_user != NULL) {
1013 TMD("user");
1014 return KEY_HANDLER_FAILURE;
1015 }
1016
1017 char *name = next_value(values);
1018
1019 if (name == NULL) {
1020 TFA("user");
1021 return KEY_HANDLER_FAILURE;
1022 }
1023
1024 sep->se_user = newstr(name);
1025
1026 if (next_value(values) != NULL) {
1027 TMA("user");
1028 return KEY_HANDLER_FAILURE;
1029 }
1030
1031 return KEY_HANDLER_SUCCESS;
1032 }
1033
1034 /* Set group to execute as */
1035 static hresult
1036 group_handler(struct servtab *sep, vlist values)
1037 {
1038 char *name = next_value(values);
1039
1040 if (name == NULL) {
1041 TFA("group");
1042 return KEY_HANDLER_FAILURE;
1043 }
1044
1045 sep->se_group = newstr(name);
1046
1047 if (next_value(values) != NULL) {
1048 TMA("group");
1049 return KEY_HANDLER_FAILURE;
1050 }
1051
1052 return KEY_HANDLER_SUCCESS;
1053 }
1054
1055 /* Handle program path or "internal" */
1056 static hresult
1057 exec_handler(struct servtab *sep, vlist values)
1058 {
1059 char *val;
1060
1061 if ((val = next_value(values)) == NULL) {
1062 TFA("exec");
1063 return KEY_HANDLER_FAILURE;
1064 }
1065
1066 pid_t wait_prev = sep->se_wait;
1067 if (parse_server(sep, val))
1068 return KEY_HANDLER_FAILURE;
1069 if (is_internal(sep) && wait_prev != SERVTAB_UNSPEC_VAL) {
1070 /*
1071 * Warn if the user specifies a value for an internal which
1072 * is different
1073 */
1074 if (wait_prev != sep->se_wait) {
1075 WRN(WAIT_WRN, sep->se_service);
1076 }
1077 }
1078
1079 if ((val = next_value(values)) != NULL) {
1080 TMA("exec");
1081 return KEY_HANDLER_FAILURE;
1082 }
1083
1084 return KEY_HANDLER_SUCCESS;
1085 }
1086
1087 /* Handle program arguments */
1088 static hresult
1089 args_handler(struct servtab *sep, vlist values)
1090 {
1091 char *val;
1092 int argc;
1093
1094 if (sep->se_argv[0] != NULL) {
1095 TMD("args");
1096 return KEY_HANDLER_FAILURE;
1097 }
1098
1099 argc = 0;
1100 for (val = next_value(values); val != NULL; val = next_value(values)) {
1101 if (argc >= MAXARGV) {
1102 ERR("Must be fewer than " TOSTRING(MAXARGV)
1103 " arguments");
1104 return KEY_HANDLER_FAILURE;
1105 }
1106 sep->se_argv[argc++] = newstr(val);
1107 }
1108 while (argc <= MAXARGV)
1109 sep->se_argv[argc++] = NULL;
1110
1111 return KEY_HANDLER_SUCCESS;
1112
1113 }
1114
1115 #ifdef IPSEC
1116 /*
1117 * ipsec_handler currently uses the ipsec.h utilities for parsing, requiring
1118 * all policies as a single value. This handler could potentially allow multiple
1119 * policies as separate values in the future, but strings would need to be
1120 * concatenated so the existing ipsec.h functions continue to work and policies
1121 * can continue to be stored in sep->policy.
1122 */
1123 static hresult
1124 ipsec_handler(struct servtab *sep, vlist values)
1125 {
1126 if (sep->se_policy != NULL) {
1127 TMD("ipsec");
1128 return KEY_HANDLER_FAILURE;
1129 }
1130
1131 char *ipsecstr = next_value(values);
1132
1133 if (ipsecstr != NULL && ipsecsetup_test(ipsecstr) < 0) {
1134 ERR("IPsec policy '%s' is invalid", ipsecstr);
1135 return KEY_HANDLER_FAILURE;
1136 }
1137
1138 /*
1139 * Use 'ipsec=' with no argument to disable ipsec for this service
1140 * An empty string indicates that IPsec was disabled, handled in
1141 * fill_default_values.
1142 */
1143 sep->se_policy = policy ? newstr(ipsecstr) : newstr("");
1144
1145 if (next_value(values) != NULL) {
1146 TMA("ipsec");
1147 /* Currently only one semicolon separated string is allowed */
1148 return KEY_HANDLER_FAILURE;
1149 }
1150
1151 return KEY_HANDLER_SUCCESS;
1152 }
1153 #endif
1154