xref: /netbsd/usr.sbin/inetd/parse_v2.c (revision a8ab4c4c)
1 /*	$NetBSD: parse_v2.c,v 1.6 2021/10/12 19:08:04 christos 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.6 2021/10/12 19:08:04 christos 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 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
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