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