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 
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 static 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 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 
148 parse_v2_result
parse_syntax_v2(struct servtab * sep,char ** cpp)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 
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  */
201 static bool
fill_default_values(struct servtab * sep)202 fill_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
257 static void
setup_ipsec(struct servtab * sep)258 setup_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 
271 static void
try_infer_socktype(struct servtab * sep)272 try_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 
285 static bool
setup_internal(struct servtab * sep)286 setup_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 
301 static bool
infer_protocol_ip_version(struct servtab * sep)302 infer_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  */
333 static bool
skip_whitespace(char ** cpp)334 skip_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 */
363 static key_handler_func
get_handler(char * name)364 get_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 
375 static inline void
strmove(char * buf,size_t off)376 strmove(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  */
386 static bool
parse_quotes(char ** cpp)387 parse_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 
458 static int
hex_to_bits(char in)459 hex_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  */
478 static char *
next_value(vlist list)479 next_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 */
553 static invoke_result
parse_invoke_handler(bool * is_valid_definition,char ** cpp,struct servtab * sep)554 parse_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 */
648 static bool
is_internal(struct servtab * sep)649 is_internal(struct servtab *sep)
650 {
651           return sep->se_bi != NULL;
652 }
653 
654 /*
655  * Key-values handlers
656  */
657 
658 static hresult
659 /*ARGSUSED*/
unknown_handler(struct servtab * sep,vlist values)660 unknown_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 */
667 static hresult
bind_handler(struct servtab * sep,vlist values)668 bind_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 
684 static hresult
socket_type_handler(struct servtab * sep,vlist values)685 socket_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 */
710 static hresult
filter_handler(struct servtab * sep,vlist values)711 filter_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.) */
750 static hresult
protocol_handler(struct servtab * sep,vlist values)751 protocol_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  */
781 static int
size_to_bytes(char * arg)782 size_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 */
816 static hresult
send_buf_handler(struct servtab * sep,vlist values)817 send_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 */
851 static hresult
recv_buf_handler(struct servtab * sep,vlist values)852 recv_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 */
886 static hresult
wait_handler(struct servtab * sep,vlist values)887 wait_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 */
932 static hresult
service_max_handler(struct servtab * sep,vlist values)933 service_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 
969 static hresult
ip_max_handler(struct servtab * sep,vlist values)970 ip_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 */
1006 static hresult
user_handler(struct servtab * sep,vlist values)1007 user_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 */
1032 static hresult
group_handler(struct servtab * sep,vlist values)1033 group_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" */
1053 static hresult
exec_handler(struct servtab * sep,vlist values)1054 exec_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 */
1085 static hresult
args_handler(struct servtab * sep,vlist values)1086 args_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  */
1120 static hresult
ipsec_handler(struct servtab * sep,vlist values)1121 ipsec_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