xref: /netbsd-src/usr.sbin/inetd/parse_v2.c (revision d3789543466c1fc9b2703e1746ab85d655beccb3)
1*d3789543Sandvar /*	$NetBSD: parse_v2.c,v 1.7 2024/02/08 20:51:25 andvar Exp $	*/
225573806Schristos 
325573806Schristos /*-
425573806Schristos  * Copyright (c) 2021 The NetBSD Foundation, Inc.
525573806Schristos  * All rights reserved.
625573806Schristos  *
725573806Schristos  * This code is derived from software contributed to The NetBSD Foundation
825573806Schristos  * by James Browning, Gabe Coffland, Alex Gavin, and Solomon Ritzow.
925573806Schristos  *
1025573806Schristos  * Redistribution and use in source and binary forms, with or without
1125573806Schristos  * modification, are permitted provided that the following conditions
1225573806Schristos  * are met:
1325573806Schristos  * 1. Redistributions of source code must retain the above copyright
1425573806Schristos  *    notice, this list of conditions and the following disclaimer.
1525573806Schristos  * 2. Redistributions in binary form must reproduce the above copyright
1625573806Schristos  *    notice, this list of conditions and the following disclaimer in the
1725573806Schristos  *    documentation and/or other materials provided with the distribution.
1825573806Schristos  *
1925573806Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2025573806Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2125573806Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2225573806Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2325573806Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2425573806Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2525573806Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2625573806Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2725573806Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2825573806Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2925573806Schristos  * POSSIBILITY OF SUCH DAMAGE.
3025573806Schristos  */
3125573806Schristos 
3225573806Schristos #include <sys/cdefs.h>
33*d3789543Sandvar __RCSID("$NetBSD: parse_v2.c,v 1.7 2024/02/08 20:51:25 andvar Exp $");
3425573806Schristos 
3525573806Schristos #include <ctype.h>
3625573806Schristos #include <errno.h>
3725573806Schristos #include <limits.h>
3825573806Schristos #include <stdbool.h>
3925573806Schristos #include <stdio.h>
4025573806Schristos #include <stdlib.h>
4125573806Schristos #include <string.h>
4225573806Schristos #include <syslog.h>
4325573806Schristos #include <err.h>
4425573806Schristos 
4525573806Schristos #include "inetd.h"
4625573806Schristos #include "ipsec.h"
4725573806Schristos 
4825573806Schristos typedef enum values_state {
4925573806Schristos 	VALS_PARSING, VALS_END_KEY, VALS_END_DEF, VALS_ERROR
5025573806Schristos } values_state;
5125573806Schristos 
5225573806Schristos /* Values parsing state */
5325573806Schristos typedef struct val_parse_info {
5425573806Schristos 	char *cp;
5525573806Schristos 	/* Used so we can null-terminate values by overwriting ',' and ';' */
5625573806Schristos 	//char terminal;
5725573806Schristos 	values_state state;
5825573806Schristos } val_parse_info, *vlist;
5925573806Schristos 
6025573806Schristos /* The result of a call to parse_invoke_handler */
6125573806Schristos typedef enum invoke_result {
6225573806Schristos 	INVOKE_SUCCESS, INVOKE_FINISH, INVOKE_ERROR
6325573806Schristos } invoke_result;
6425573806Schristos 
6525573806Schristos /* The result of a parse of key handler values */
6625573806Schristos typedef enum hresult {
6725573806Schristos 	KEY_HANDLER_FAILURE, KEY_HANDLER_SUCCESS
6825573806Schristos } hresult;
6925573806Schristos 
7025573806Schristos /* v2 syntax key-value parsers */
7125573806Schristos static hresult	args_handler(struct servtab *, vlist);
7225573806Schristos static hresult	bind_handler(struct servtab *, vlist);
7325573806Schristos static hresult	exec_handler(struct servtab *, vlist);
7425573806Schristos static hresult	filter_handler(struct servtab *, vlist);
7525573806Schristos static hresult	group_handler(struct servtab *, vlist);
7625573806Schristos static hresult	service_max_handler(struct servtab *, vlist);
7725573806Schristos static hresult	ip_max_handler(struct servtab *, vlist);
7825573806Schristos static hresult	protocol_handler(struct servtab *, vlist);
7925573806Schristos static hresult	recv_buf_handler(struct servtab *, vlist);
8025573806Schristos static hresult	send_buf_handler(struct servtab *, vlist);
8125573806Schristos static hresult	socket_type_handler(struct servtab *, vlist);
8225573806Schristos static hresult	unknown_handler(struct servtab *, vlist);
8325573806Schristos static hresult	user_handler(struct servtab *, vlist);
8425573806Schristos static hresult	wait_handler(struct servtab *, vlist);
8525573806Schristos 
8625573806Schristos #ifdef IPSEC
8725573806Schristos static hresult	ipsec_handler(struct servtab *, vlist);
8825573806Schristos #endif
8925573806Schristos 
9025573806Schristos static invoke_result	parse_invoke_handler(bool *, char **, struct servtab *);
9125573806Schristos static bool fill_default_values(struct servtab *);
9225573806Schristos static bool parse_quotes(char **);
9325573806Schristos static bool	skip_whitespace(char **);
9425573806Schristos static int	size_to_bytes(char *);
9525573806Schristos static bool infer_protocol_ip_version(struct servtab *);
9625573806Schristos static bool	setup_internal(struct servtab *);
9725573806Schristos static void	try_infer_socktype(struct servtab *);
985d133dbcSrillig static int hex_to_bits(char);
9925573806Schristos #ifdef IPSEC
10025573806Schristos static void	setup_ipsec(struct servtab *);
10125573806Schristos #endif
10225573806Schristos static inline void	strmove(char *, size_t);
10325573806Schristos 
10425573806Schristos /* v2 Key handlers infrastructure */
10525573806Schristos 
10625573806Schristos /* v2 syntax Handler function, which must parse all values for its key */
10725573806Schristos typedef hresult (*key_handler_func)(struct servtab *, vlist);
10825573806Schristos 
10925573806Schristos /* List of v2 syntax key handlers */
11025573806Schristos static struct key_handler {
11125573806Schristos 	const char *name;
11225573806Schristos 	key_handler_func handler;
11325573806Schristos } key_handlers[] = {
11425573806Schristos 	{ "bind", bind_handler },
11525573806Schristos 	{ "socktype", socket_type_handler },
11625573806Schristos 	{ "acceptfilter", filter_handler },
11725573806Schristos 	{ "protocol", protocol_handler },
11825573806Schristos 	{ "sndbuf", send_buf_handler },
11925573806Schristos 	{ "recvbuf", recv_buf_handler },
12025573806Schristos 	{ "wait", wait_handler },
12125573806Schristos 	{ "service_max", service_max_handler },
12225573806Schristos 	{ "user", user_handler },
12325573806Schristos 	{ "group", group_handler },
12425573806Schristos 	{ "exec", exec_handler },
12525573806Schristos 	{ "args", args_handler },
12625573806Schristos 	{ "ip_max", ip_max_handler },
12725573806Schristos #ifdef IPSEC
12825573806Schristos 	{ "ipsec", ipsec_handler }
12925573806Schristos #endif
13025573806Schristos };
13125573806Schristos 
13225573806Schristos /* Error Not Initialized */
13325573806Schristos #define ENI(key) ERR("Required option '%s' not specified", (key))
13425573806Schristos 
13525573806Schristos #define WAIT_WRN "Option 'wait' for internal service '%s' was inferred"
13625573806Schristos 
137*d3789543Sandvar /* Too Few Arguments (values) */
13825573806Schristos #define TFA(key) ERR("Option '%s' has too few arguments", (key))
13925573806Schristos 
14025573806Schristos /* Too Many Arguments (values) */
14125573806Schristos #define TMA(key) ERR("Option '%s' has too many arguments", (key))
14225573806Schristos 
14325573806Schristos /* Too Many Definitions */
14425573806Schristos #define TMD(key) ERR("Option '%s' is already specified", (key))
14525573806Schristos 
14625573806Schristos #define VALID_SOCKET_TYPES "stream, dgram, rdm, seqpacket, raw"
14725573806Schristos 
14825573806Schristos parse_v2_result
parse_syntax_v2(struct servtab * sep,char ** cpp)14925573806Schristos parse_syntax_v2(struct servtab *sep, char **cpp)
15025573806Schristos {
15125573806Schristos 
15225573806Schristos 	/* Catch multiple semantic errors instead of skipping after one */
15325573806Schristos 	bool is_valid_definition = true;
15425573806Schristos 	/* Line number of service for error logging. */
15525573806Schristos 	size_t line_number_start = line_number;
15625573806Schristos 
15725573806Schristos 	for (;;) {
158b19025f3Schristos 		switch(parse_invoke_handler(&is_valid_definition, cpp, sep)) {
15925573806Schristos 		case INVOKE_SUCCESS:
16025573806Schristos 			/* Keep reading more options in. */
16125573806Schristos 			continue;
16225573806Schristos 		case INVOKE_FINISH:
16325573806Schristos 			/*
16425573806Schristos 			 * Found a semicolon, do final checks and defaults
16525573806Schristos 			 * and return.
16625573806Schristos 			 * Skip whitespace after semicolon to end of line.
16725573806Schristos 		         */
16825573806Schristos 			while (isspace((unsigned char)**cpp)) {
16925573806Schristos 				(*cpp)++;
17025573806Schristos 			}
17125573806Schristos 
17225573806Schristos 			if (is_valid_definition && fill_default_values(sep)) {
17325573806Schristos 				if (**cpp == '\0') {
17425573806Schristos 					*cpp = nextline(fconfig);
17525573806Schristos 				}
17625573806Schristos 				return V2_SUCCESS;
17725573806Schristos 			}
17825573806Schristos 
17925573806Schristos 			DPRINTCONF("Ignoring invalid definition.");
18025573806Schristos 			/* Log the error for the starting line of the service */
18125573806Schristos 			syslog(LOG_ERR, CONF_ERROR_FMT
18225573806Schristos 			    "Ignoring invalid definition.", CONFIG,
18325573806Schristos 			    line_number_start);
18425573806Schristos 			if (**cpp == '\0') {
18525573806Schristos 				*cpp = nextline(fconfig);
18625573806Schristos 			}
18725573806Schristos 			return V2_SKIP;
18825573806Schristos 		case INVOKE_ERROR:
18925573806Schristos 			DPRINTCONF("Syntax error; Exiting '%s'", CONFIG);
19025573806Schristos 			return V2_ERROR;
19125573806Schristos 		}
19225573806Schristos 	}
19325573806Schristos }
19425573806Schristos 
19525573806Schristos /*
19625573806Schristos  * Fill in any remaining values that should be inferred
19725573806Schristos  * Log an error if a required parameter that isn't
19825573806Schristos  * provided by user can't be inferred from other servtab data.
19925573806Schristos  * Return true on success, false on failure.
20025573806Schristos  */
20125573806Schristos static bool
fill_default_values(struct servtab * sep)20225573806Schristos fill_default_values(struct servtab *sep)
20325573806Schristos {
20425573806Schristos 	bool is_valid = true;
20525573806Schristos 
20625573806Schristos 	if (sep->se_service_max == SERVTAB_UNSPEC_SIZE_T) {
20725573806Schristos 		/* Set default to same as in v1 syntax. */
20825573806Schristos 		sep->se_service_max = TOOMANY;
20925573806Schristos 	}
21025573806Schristos 
21125573806Schristos 	if (sep->se_hostaddr == NULL) {
21225573806Schristos 		/* Set hostaddr to default */
21325573806Schristos 		sep->se_hostaddr = newstr(defhost);
21425573806Schristos 	}
21525573806Schristos 
21625573806Schristos 	try_infer_socktype(sep);
21725573806Schristos 
21825573806Schristos 	if (sep->se_server == NULL) {
21925573806Schristos 		/* If an executable is not specified, assume internal. */
22025573806Schristos 		is_valid = setup_internal(sep) && is_valid;
22125573806Schristos 	}
22225573806Schristos 
22325573806Schristos 	if (sep->se_socktype == SERVTAB_UNSPEC_VAL) {
22425573806Schristos 		/* Ensure socktype is specified (either set or inferred) */
22525573806Schristos 		ENI("socktype");
22625573806Schristos 		is_valid = false;
22725573806Schristos 	}
22825573806Schristos 
22925573806Schristos 	if (sep->se_wait == SERVTAB_UNSPEC_VAL) {
23025573806Schristos 		/* Ensure wait is specified */
23125573806Schristos 		ENI("wait");
23225573806Schristos 		is_valid = false;
23325573806Schristos 	}
23425573806Schristos 
23525573806Schristos 	if (sep->se_user == NULL) {
23625573806Schristos 		/* Ensure user is specified */
23725573806Schristos 		ENI("user");
23825573806Schristos 		is_valid = false;
23925573806Schristos 	}
24025573806Schristos 
24125573806Schristos 	if (sep->se_proto == NULL) {
24225573806Schristos 		/* Ensure protocol is specified */
24325573806Schristos 		ENI("protocol");
24425573806Schristos 		is_valid = false;
24525573806Schristos 	} else {
24625573806Schristos 		is_valid = infer_protocol_ip_version(sep) && is_valid;
24725573806Schristos 	}
24825573806Schristos 
24925573806Schristos #ifdef IPSEC
25025573806Schristos 	setup_ipsec(sep);
25125573806Schristos #endif
25225573806Schristos 	return is_valid;
25325573806Schristos }
25425573806Schristos 
25525573806Schristos /* fill_default_values related functions */
25625573806Schristos #ifdef IPSEC
25725573806Schristos static void
setup_ipsec(struct servtab * sep)25825573806Schristos setup_ipsec(struct servtab *sep)
25925573806Schristos {
26025573806Schristos 	if (sep->se_policy == NULL) {
26125573806Schristos 		/* Set to default global policy */
26225573806Schristos 		sep->se_policy = policy;
26325573806Schristos 	} else if (*sep->se_policy == '\0') {
26425573806Schristos 		/* IPsec was intentionally disabled. */
26525573806Schristos 		free(sep->se_policy);
26625573806Schristos 		sep->se_policy = NULL;
26725573806Schristos 	}
26825573806Schristos }
26925573806Schristos #endif
27025573806Schristos 
27125573806Schristos static void
try_infer_socktype(struct servtab * sep)27225573806Schristos try_infer_socktype(struct servtab *sep) {
27325573806Schristos 	if (sep->se_socktype != SERVTAB_UNSPEC_VAL || sep->se_proto == NULL) {
27425573806Schristos 		return;
27525573806Schristos 	}
27625573806Schristos 
27725573806Schristos 	/* Check values of se_proto udp, udp6, tcp, tcp6 to set dgram/stream */
27825573806Schristos 	if (strncmp(sep->se_proto, "udp", 3) == 0) {
27925573806Schristos 		sep->se_socktype = SOCK_DGRAM;
28025573806Schristos 	} else if (strncmp(sep->se_proto, "tcp", 3) == 0) {
28125573806Schristos 		sep->se_socktype = SOCK_STREAM;
28225573806Schristos 	}
28325573806Schristos }
28425573806Schristos 
28525573806Schristos static bool
setup_internal(struct servtab * sep)28625573806Schristos setup_internal(struct servtab *sep)
28725573806Schristos {
28825573806Schristos 	pid_t wait_prev = sep->se_wait;
28925573806Schristos 	if (parse_server(sep, "internal") != 0) {
29025573806Schristos 		ENI("exec");
29125573806Schristos 		return false;
29225573806Schristos 	}
29325573806Schristos 
29425573806Schristos 	if (wait_prev != SERVTAB_UNSPEC_VAL && wait_prev != sep->se_wait) {
29525573806Schristos 		/* If wait was already specified throw an error. */
29625573806Schristos 		WRN(WAIT_WRN, sep->se_service);
29725573806Schristos 	}
29825573806Schristos 	return true;
29925573806Schristos }
30025573806Schristos 
30125573806Schristos static bool
infer_protocol_ip_version(struct servtab * sep)30225573806Schristos infer_protocol_ip_version(struct servtab *sep)
30325573806Schristos {
30425573806Schristos 	struct in_addr tmp;
30525573806Schristos 
30625573806Schristos 	if (strcmp("tcp", sep->se_proto) != 0
30725573806Schristos 		&& strcmp("udp", sep->se_proto) != 0
30825573806Schristos 		&& strcmp("rpc/tcp", sep->se_proto) != 0
30925573806Schristos 		&& strcmp("rpc/udp", sep->se_proto) != 0) {
31025573806Schristos 		return true;
31125573806Schristos 	}
31225573806Schristos 
31325573806Schristos 	if (inet_pton(AF_INET, sep->se_hostaddr, &tmp)) {
31425573806Schristos 		sep->se_family = AF_INET;
31525573806Schristos 		return true;
31625573806Schristos 	}
31725573806Schristos 
31825573806Schristos 	if (inet_pton(AF_INET6, sep->se_hostaddr, &tmp)) {
31925573806Schristos 		sep->se_family = AF_INET6;
32025573806Schristos 		return true;
32125573806Schristos 	}
32225573806Schristos 
32325573806Schristos 	ERR("Address family of %s is ambigous or invalid. "
32425573806Schristos 		"Explicitly specify protocol", sep->se_hostaddr);
32525573806Schristos 	return false;
32625573806Schristos }
32725573806Schristos 
32825573806Schristos /*
32925573806Schristos  * Skips whitespaces, newline characters, and comments,
33025573806Schristos  * and returns the next token. Returns false and logs error if an EOF is
33125573806Schristos  * encountered.
33225573806Schristos  */
33325573806Schristos static bool
skip_whitespace(char ** cpp)33425573806Schristos skip_whitespace(char **cpp)
33525573806Schristos {
33625573806Schristos 	char *cp = *cpp;
33725573806Schristos 
3385d133dbcSrillig 	size_t line_start = line_number;
33925573806Schristos 
34025573806Schristos 	for (;;) {
34125573806Schristos 		while (isblank((unsigned char)*cp))
34225573806Schristos 			cp++;
34325573806Schristos 
34425573806Schristos 		if (*cp == '\0' || *cp == '#') {
34525573806Schristos 			cp = nextline(fconfig);
34625573806Schristos 
34725573806Schristos 			/* Should never expect EOF when skipping whitespace */
34825573806Schristos 			if (cp == NULL) {
3495d133dbcSrillig 				ERR("Early end of file after line %zu",
35025573806Schristos 				    line_start);
35125573806Schristos 				return false;
35225573806Schristos 			}
35325573806Schristos 			continue;
35425573806Schristos 		}
35525573806Schristos 		break;
35625573806Schristos 	}
35725573806Schristos 
35825573806Schristos 	*cpp = cp;
35925573806Schristos 	return true;
36025573806Schristos }
36125573806Schristos 
36225573806Schristos /* Get the key handler function pointer for the given name */
36325573806Schristos static key_handler_func
get_handler(char * name)36425573806Schristos get_handler(char *name)
36525573806Schristos {
36625573806Schristos 	/* Call function to handle option parsing. */
36725573806Schristos 	for (size_t i = 0; i < __arraycount(key_handlers); i++) {
36825573806Schristos 		if (strcmp(key_handlers[i].name, name) == 0) {
36925573806Schristos 			return key_handlers[i].handler;
37025573806Schristos 		}
37125573806Schristos 	}
37225573806Schristos 	return NULL;
37325573806Schristos }
37425573806Schristos 
37525573806Schristos static inline void
strmove(char * buf,size_t off)37625573806Schristos strmove(char *buf, size_t off)
37725573806Schristos {
37825573806Schristos 	memmove(buf, buf + off, strlen(buf + off) + 1);
37925573806Schristos }
38025573806Schristos 
38125573806Schristos /*
38225573806Schristos  * Perform an in-place parse of a single-line quoted string
38325573806Schristos  * with escape sequences. Sets *cpp to the position after the quoted characters.
38425573806Schristos  * Uses shell-style quote parsing.
38525573806Schristos  */
38625573806Schristos static bool
parse_quotes(char ** cpp)38725573806Schristos parse_quotes(char **cpp)
38825573806Schristos {
38925573806Schristos 	char *cp = *cpp;
39025573806Schristos 	char quote = *cp;
39125573806Schristos 
39225573806Schristos 	strmove(cp, 1);
393adeed07fSrillig 	while (*cp != '\0' && quote != '\0') {
39425573806Schristos 		if (*cp == quote) {
39525573806Schristos 			quote = '\0';
39625573806Schristos 			strmove(cp, 1);
39725573806Schristos 			continue;
39825573806Schristos 		}
39925573806Schristos 
40025573806Schristos 		if (*cp == '\\') {
40125573806Schristos 			/* start is location of backslash */
40225573806Schristos 			char *start = cp;
40325573806Schristos 			cp++;
40425573806Schristos 			switch (*cp) {
40525573806Schristos 			case 'x': {
4065d133dbcSrillig 				int hi, lo;
4075d133dbcSrillig 				if ((hi = hex_to_bits(cp[1])) == -1
4085d133dbcSrillig 				|| (lo = hex_to_bits(cp[2])) == -1) {
40925573806Schristos 					ERR("Invalid hexcode sequence '%.4s'",
41025573806Schristos 					    start);
41125573806Schristos 					return false;
41225573806Schristos 				}
4135d133dbcSrillig 				*start = (char)((hi << 4) | lo);
41425573806Schristos 				strmove(cp, 3);
41525573806Schristos 				continue;
41625573806Schristos 			}
41725573806Schristos 			case '\\':
41825573806Schristos 				*start = '\\';
41925573806Schristos 				break;
42025573806Schristos 			case 'n':
42125573806Schristos 				*start = '\n';
42225573806Schristos 				break;
42325573806Schristos 			case 't':
42425573806Schristos 				*start = '\t';
42525573806Schristos 				break;
42625573806Schristos 			case 'r':
42725573806Schristos 				*start = '\r';
42825573806Schristos 				break;
42925573806Schristos 			case '\'':
43025573806Schristos 				*start = '\'';
43125573806Schristos 				break;
43225573806Schristos 			case '"':
43325573806Schristos 				*start = '"';
43425573806Schristos 				break;
43525573806Schristos 			case '\0':
43625573806Schristos 				ERR("Dangling escape sequence backslash");
43725573806Schristos 				return false;
43825573806Schristos 			default:
43925573806Schristos 				ERR("Unknown escape sequence '\\%c'", *cp);
44025573806Schristos 				return false;
44125573806Schristos 			}
44225573806Schristos 			strmove(cp, 1);
44325573806Schristos 			continue;
44425573806Schristos 		}
44525573806Schristos 
44625573806Schristos 		/* Regular character, advance to the next one. */
44725573806Schristos 		cp++;
44825573806Schristos 	}
44925573806Schristos 
450adeed07fSrillig 	if (*cp == '\0' && quote != '\0') {
45125573806Schristos 		ERR("Unclosed quote");
45225573806Schristos 		return false;
45325573806Schristos 	}
45425573806Schristos 	*cpp = cp;
45525573806Schristos 	return true;
45625573806Schristos }
45725573806Schristos 
4585d133dbcSrillig static int
hex_to_bits(char in)45925573806Schristos hex_to_bits(char in)
46025573806Schristos {
46125573806Schristos 	switch(in) {
46225573806Schristos 	case '0'...'9':
46325573806Schristos 		return in - '0';
46425573806Schristos 	case 'a'...'f':
46525573806Schristos 		return in - 'a' + 10;
46625573806Schristos 	case 'A'...'F':
46725573806Schristos 		return in - 'A' + 10;
46825573806Schristos 	default:
46925573806Schristos 		return -1;
47025573806Schristos 	}
47125573806Schristos }
47225573806Schristos 
47325573806Schristos /*
47425573806Schristos  * Parse the next value for a key handler and advance list->cp past the found
47525573806Schristos  * value. Return NULL if there are no more values or there was an error
47625573806Schristos  * during parsing, and set the list->state to the appropriate value.
47725573806Schristos  */
47825573806Schristos static char *
next_value(vlist list)47925573806Schristos next_value(vlist list)
48025573806Schristos {
48125573806Schristos 	char *cp = list->cp;
48225573806Schristos 
48325573806Schristos 	if (list->state != VALS_PARSING) {
48425573806Schristos 		/* Already at the end of a values list, or there was an error.*/
48525573806Schristos 		return NULL;
48625573806Schristos 	}
48725573806Schristos 
48825573806Schristos 	if (!skip_whitespace(&cp)) {
48925573806Schristos 		list->state = VALS_ERROR;
49025573806Schristos 		return NULL;
49125573806Schristos 	}
49225573806Schristos 
49325573806Schristos 	if (*cp == ',' || *cp == ';') {
49425573806Schristos 		/* Found end of args, but not immediately after value */
49525573806Schristos 		list->state = (*cp == ',' ? VALS_END_KEY : VALS_END_DEF);
49625573806Schristos 		list->cp = cp + 1;
49725573806Schristos 		return NULL;
49825573806Schristos 	}
49925573806Schristos 
50025573806Schristos 	/* Check for end of line */
50125573806Schristos 	if (!skip_whitespace(&cp)) {
50225573806Schristos 		list->state = VALS_ERROR;
50325573806Schristos 		return NULL;
50425573806Schristos 	}
50525573806Schristos 
50625573806Schristos 	/*
50725573806Schristos 	 * Found the start of a potential value. Advance one character
50825573806Schristos 	 * past the end of the value.
50925573806Schristos 	 */
51025573806Schristos 	char *start = cp;
51125573806Schristos 	while (!isblank((unsigned char)*cp) && *cp != '#' &&
51225573806Schristos 	    *cp != ',' && *cp != ';' && *cp != '\0' ) {
51325573806Schristos 		if (*cp == '"' || *cp == '\'') {
51425573806Schristos 			/* Found a quoted segment */
51525573806Schristos 			if (!parse_quotes(&cp)) {
51625573806Schristos 				list->state = VALS_ERROR;
51725573806Schristos 				return NULL;
51825573806Schristos 			}
51925573806Schristos 		} else {
52025573806Schristos 			/* Find the end of the value */
52125573806Schristos 			cp++;
52225573806Schristos 		}
52325573806Schristos 	}
52425573806Schristos 
52525573806Schristos 	/* Handle comments next to unquoted values */
52625573806Schristos 	if (*cp == '#') {
52725573806Schristos 		*cp = '\0';
52825573806Schristos 		list->cp = cp;
52925573806Schristos 		return start;
53025573806Schristos 	}
53125573806Schristos 
53225573806Schristos 	if (*cp == '\0') {
53325573806Schristos 		/*
53425573806Schristos 		 * Value ends with end of line, so it is already NUL-terminated
53525573806Schristos 		 */
53625573806Schristos 		list->cp = cp;
53725573806Schristos 		return start;
53825573806Schristos 	}
53925573806Schristos 
54025573806Schristos 	if (*cp == ',') {
54125573806Schristos 		list->state = VALS_END_KEY;
54225573806Schristos 	} else if (*cp == ';') {
54325573806Schristos 		list->state = VALS_END_DEF;
54425573806Schristos 	}
54525573806Schristos 
54625573806Schristos 	*cp = '\0';
54725573806Schristos 	/* Advance past null so we don't skip the rest of the line */
54825573806Schristos 	list->cp = cp + 1;
54925573806Schristos 	return start;
55025573806Schristos }
55125573806Schristos 
55225573806Schristos /* Parse key name and invoke associated handler */
55325573806Schristos static invoke_result
parse_invoke_handler(bool * is_valid_definition,char ** cpp,struct servtab * sep)55425573806Schristos parse_invoke_handler(bool *is_valid_definition, char **cpp, struct servtab *sep)
55525573806Schristos {
55625573806Schristos 	char *key_name, save, *cp = *cpp;
55725573806Schristos 	int is_blank;
55825573806Schristos 	key_handler_func handler;
55925573806Schristos 	val_parse_info info;
56025573806Schristos 
56125573806Schristos 	/* Skip any whitespace if it exists, otherwise do nothing */
56225573806Schristos 	if (!skip_whitespace(&cp)) {
56325573806Schristos 		return INVOKE_ERROR;
56425573806Schristos 	}
56525573806Schristos 
56625573806Schristos 	/* Starting character of key */
56725573806Schristos 	key_name = cp;
56825573806Schristos 
56925573806Schristos 
57025573806Schristos 	/* alphabetical or underscore allowed in name */
57125573806Schristos 	while (isalpha((unsigned char)*cp) || *cp == '_') {
57225573806Schristos 		cp++;
57325573806Schristos 	}
57425573806Schristos 
57525573806Schristos 	is_blank = isblank((unsigned char)*cp);
57625573806Schristos 
57725573806Schristos 	/* Get key handler and move to start of values */
57825573806Schristos 	if (*cp != '=' && !is_blank && *cp != '#') {
57925573806Schristos 		ERR("Expected '=' but found '%c'", *cp);
58025573806Schristos 		return INVOKE_ERROR;
58125573806Schristos 	}
58225573806Schristos 
58325573806Schristos 	save = *cp;
58425573806Schristos 	*cp = '\0';
58525573806Schristos 	cp++;
58625573806Schristos 
58725573806Schristos 	handler = get_handler(key_name);
58825573806Schristos 
58925573806Schristos 	if (handler == NULL) {
59025573806Schristos 		ERR("Unknown option '%s'", key_name);
59125573806Schristos 		handler = unknown_handler;
59225573806Schristos 	}
59325573806Schristos 
59425573806Schristos 	/* If blank or new line, still need to find the '=' or throw error */
59525573806Schristos 	if (is_blank || save == '#') {
59625573806Schristos 		if (save == '#') {
59725573806Schristos 			cp = nextline(fconfig);
59825573806Schristos 		}
59925573806Schristos 
60025573806Schristos 		skip_whitespace(&cp);
60125573806Schristos 		if (*cp != '=') {
60225573806Schristos 			ERR("Expected '=' but found '%c'", *cp);
60325573806Schristos 			return INVOKE_ERROR;
60425573806Schristos 		}
60525573806Schristos 		cp++;
60625573806Schristos 	}
60725573806Schristos 
60825573806Schristos 	/* Skip whitespace to start of values */
60925573806Schristos 	if (!skip_whitespace(&cp)) {
61025573806Schristos 		return INVOKE_ERROR;
61125573806Schristos 	}
61225573806Schristos 
61325573806Schristos 	info = (val_parse_info) {cp, VALS_PARSING};
61425573806Schristos 
61525573806Schristos 	/*
61625573806Schristos 	 * Read values for key and write into sep.
61725573806Schristos 	 * If parsing is successful, all values for key must be read.
61825573806Schristos 	 */
61925573806Schristos 	if (handler(sep, &info) == KEY_HANDLER_FAILURE) {
62025573806Schristos 		/*
62125573806Schristos 		 * Eat remaining values if an error happened
62225573806Schristos 	         * so more errors can be caught.
62325573806Schristos 		 */
62425573806Schristos 		while (next_value(&info) != NULL)
62525573806Schristos 			continue;
62625573806Schristos 		*is_valid_definition = false;
62725573806Schristos 	}
62825573806Schristos 
62925573806Schristos 	if (info.state == VALS_END_DEF) {
63025573806Schristos 		/*
63125573806Schristos 		 * Exit definition handling for(;;).
63225573806Schristos 		 * Set the position to the end of the definition,
63325573806Schristos 		 * for multi-definition lines.
63425573806Schristos 		 */
63525573806Schristos 		*cpp = info.cp;
63625573806Schristos 		return INVOKE_FINISH;
63725573806Schristos 	}
63825573806Schristos 	if (info.state == VALS_ERROR) {
63925573806Schristos 		/* Parse error, stop reading config */
64025573806Schristos 		return INVOKE_ERROR;
64125573806Schristos 	}
64225573806Schristos 
64325573806Schristos 	*cpp = info.cp;
64425573806Schristos 	return INVOKE_SUCCESS;
64525573806Schristos }
64625573806Schristos 
64725573806Schristos /* Return true if sep must be a built-in service */
64825573806Schristos static bool
is_internal(struct servtab * sep)64925573806Schristos is_internal(struct servtab *sep)
65025573806Schristos {
65125573806Schristos 	return sep->se_bi != NULL;
65225573806Schristos }
65325573806Schristos 
65425573806Schristos /*
65525573806Schristos  * Key-values handlers
65625573806Schristos  */
65725573806Schristos 
65825573806Schristos static hresult
659b19025f3Schristos /*ARGSUSED*/
unknown_handler(struct servtab * sep,vlist values)66025573806Schristos unknown_handler(struct servtab *sep, vlist values)
66125573806Schristos {
66225573806Schristos 	/* Return failure for an unknown service name. */
66325573806Schristos 	return KEY_HANDLER_FAILURE;
66425573806Schristos }
66525573806Schristos 
66625573806Schristos /* Set listen address for this service */
66725573806Schristos static hresult
bind_handler(struct servtab * sep,vlist values)66825573806Schristos bind_handler(struct servtab *sep, vlist values)
66925573806Schristos {
67025573806Schristos 	if (sep->se_hostaddr != NULL) {
67125573806Schristos 		TMD("bind");
67225573806Schristos 		return KEY_HANDLER_FAILURE;
67325573806Schristos 	}
67425573806Schristos 
67525573806Schristos 	char *val = next_value(values);
67625573806Schristos 	sep->se_hostaddr = newstr(val);
67725573806Schristos 	if (next_value(values) != NULL) {
67825573806Schristos 		TMA("bind");
67925573806Schristos 		return KEY_HANDLER_FAILURE;
68025573806Schristos 	}
68125573806Schristos 	return KEY_HANDLER_SUCCESS;
68225573806Schristos }
68325573806Schristos 
68425573806Schristos static hresult
socket_type_handler(struct servtab * sep,vlist values)68525573806Schristos socket_type_handler(struct servtab *sep, vlist values)
68625573806Schristos {
68725573806Schristos 	char *type = next_value(values);
68825573806Schristos 	if (type == NULL) {
68925573806Schristos 		TFA("socktype");
69025573806Schristos 		return KEY_HANDLER_FAILURE;
69125573806Schristos 	}
69225573806Schristos 
69325573806Schristos 	parse_socktype(type, sep);
69425573806Schristos 
69525573806Schristos 	if (sep->se_socktype == -1) {
69625573806Schristos 		ERR("Invalid socket type '%s'. Valid: " VALID_SOCKET_TYPES,
69725573806Schristos 		    type);
69825573806Schristos 		return KEY_HANDLER_FAILURE;
69925573806Schristos 	}
70025573806Schristos 
70125573806Schristos 	if (next_value(values) != NULL) {
70225573806Schristos 		TMA("socktype");
70325573806Schristos 		return KEY_HANDLER_FAILURE;
70425573806Schristos 	}
70525573806Schristos 
70625573806Schristos 	return KEY_HANDLER_SUCCESS;
70725573806Schristos }
70825573806Schristos 
70925573806Schristos /* Set accept filter SO_ACCEPTFILTER */
71025573806Schristos static hresult
filter_handler(struct servtab * sep,vlist values)71125573806Schristos filter_handler(struct servtab *sep, vlist values)
71225573806Schristos {
71325573806Schristos 	/*
71425573806Schristos 	 * See: SO_ACCEPTFILTER https://man.netbsd.org/setsockopt.2
71525573806Schristos 	 * An accept filter can have one other argument.
71625573806Schristos 	 * This code currently only supports one accept filter
71725573806Schristos 	 * Also see parse_accept_filter(char* arg, struct servtab*sep)
71825573806Schristos 	 */
71925573806Schristos 
72025573806Schristos 	char *af_name, *af_arg;
72125573806Schristos 
72225573806Schristos 	af_name = next_value(values);
72325573806Schristos 
72425573806Schristos 	if (af_name == NULL) {
72525573806Schristos 		TFA("filter");
72625573806Schristos 		return KEY_HANDLER_FAILURE;
72725573806Schristos 	}
72825573806Schristos 
72925573806Schristos 	/* Store af_name in se_accf.af_name, no newstr call */
73025573806Schristos 	strlcpy(sep->se_accf.af_name, af_name, sizeof(sep->se_accf.af_name));
73125573806Schristos 
73225573806Schristos 	af_arg = next_value(values);
73325573806Schristos 
73425573806Schristos 	if (af_arg != NULL) {
73525573806Schristos 		strlcpy(sep->se_accf.af_arg, af_arg,
73625573806Schristos 		    sizeof(sep->se_accf.af_arg));
73725573806Schristos 		if (next_value(values) != NULL) {
73825573806Schristos 			TMA("filter");
73925573806Schristos 			return KEY_HANDLER_FAILURE;
74025573806Schristos 		}
74125573806Schristos 	} else {
74225573806Schristos 		/* Store null string */
74325573806Schristos 		sep->se_accf.af_arg[0] = '\0';
74425573806Schristos 	}
74525573806Schristos 
74625573806Schristos 	return KEY_HANDLER_SUCCESS;
74725573806Schristos }
74825573806Schristos 
74925573806Schristos /* Set protocol (udp, tcp, unix, etc.) */
75025573806Schristos static hresult
protocol_handler(struct servtab * sep,vlist values)75125573806Schristos protocol_handler(struct servtab *sep, vlist values)
75225573806Schristos {
75325573806Schristos 	char *val;
75425573806Schristos 
75525573806Schristos 	if ((val = next_value(values)) == NULL) {
75625573806Schristos 		TFA("protocol");
75725573806Schristos 		return KEY_HANDLER_FAILURE;
75825573806Schristos 	}
75925573806Schristos 
76025573806Schristos 	if (sep->se_type == NORM_TYPE &&
76125573806Schristos 	    strncmp(val, "faith/", strlen("faith/")) == 0) {
76225573806Schristos 		val += strlen("faith/");
76325573806Schristos 		sep->se_type = FAITH_TYPE;
76425573806Schristos 	}
76525573806Schristos 	sep->se_proto = newstr(val);
76625573806Schristos 
76725573806Schristos 	if (parse_protocol(sep))
76825573806Schristos 		return KEY_HANDLER_FAILURE;
76925573806Schristos 
77025573806Schristos 	if ((val = next_value(values)) != NULL) {
77125573806Schristos 		TMA("protocol");
77225573806Schristos 		return KEY_HANDLER_FAILURE;
77325573806Schristos 	}
77425573806Schristos 	return KEY_HANDLER_SUCCESS;
77525573806Schristos }
77625573806Schristos 
77725573806Schristos /*
77825573806Schristos  * Convert a string number possible ending with k or m to an integer.
77925573806Schristos  * Based on MALFORMED, GETVAL, and ASSIGN in getconfigent(void).
78025573806Schristos  */
78125573806Schristos static int
size_to_bytes(char * arg)78225573806Schristos size_to_bytes(char *arg)
78325573806Schristos {
78425573806Schristos 	char *tail;
78525573806Schristos 	int rstatus, count;
78625573806Schristos 
78725573806Schristos 	count = (int)strtoi(arg, &tail, 10, 0, INT_MAX, &rstatus);
78825573806Schristos 
789adeed07fSrillig 	if (rstatus != 0 && rstatus != ENOTSUP) {
79025573806Schristos 		ERR("Invalid buffer size '%s': %s", arg, strerror(rstatus));
79125573806Schristos 		return -1;
79225573806Schristos 	}
79325573806Schristos 
79425573806Schristos 	switch(tail[0]) {
79525573806Schristos 	case 'm':
79625573806Schristos 		if (__builtin_smul_overflow((int)count, 1024, &count)) {
79725573806Schristos 			ERR("Invalid buffer size '%s': Result too large", arg);
79825573806Schristos 			return -1;
79925573806Schristos 		}
80025573806Schristos 		/* FALLTHROUGH */
80125573806Schristos 	case 'k':
80225573806Schristos 		if (__builtin_smul_overflow((int)count, 1024, &count)) {
80325573806Schristos 			ERR("Invalid buffer size '%s': Result too large", arg);
80425573806Schristos 			return -1;
80525573806Schristos 		}
80625573806Schristos 		/* FALLTHROUGH */
80725573806Schristos 	case '\0':
80825573806Schristos 		return count;
80925573806Schristos 	default:
81025573806Schristos 		ERR("Invalid buffer size unit prefix");
81125573806Schristos 		return -1;
81225573806Schristos 	}
81325573806Schristos }
81425573806Schristos 
81525573806Schristos /* sndbuf size */
81625573806Schristos static hresult
send_buf_handler(struct servtab * sep,vlist values)81725573806Schristos send_buf_handler(struct servtab *sep, vlist values)
81825573806Schristos {
81925573806Schristos 	char *arg;
82025573806Schristos 	int buffer_size;
82125573806Schristos 
82225573806Schristos 	if (ISMUX(sep)) {
82325573806Schristos 		ERR("%s: can't specify buffer sizes for tcpmux services",
82425573806Schristos 			sep->se_service);
82525573806Schristos 		return KEY_HANDLER_FAILURE;
82625573806Schristos 	}
82725573806Schristos 
82825573806Schristos 
82925573806Schristos 	if ((arg = next_value(values)) == NULL) {
83025573806Schristos 		TFA("sndbuf");
83125573806Schristos 		return KEY_HANDLER_FAILURE;
83225573806Schristos 	}
83325573806Schristos 
83425573806Schristos 	buffer_size = size_to_bytes(arg);
83525573806Schristos 
83625573806Schristos 	if (buffer_size == -1) {
83725573806Schristos 		return KEY_HANDLER_FAILURE;
83825573806Schristos 	}
83925573806Schristos 
84025573806Schristos 	if ((arg = next_value(values)) != NULL) {
84125573806Schristos 		TMA("sndbuf");
84225573806Schristos 		return KEY_HANDLER_FAILURE;
84325573806Schristos 	}
84425573806Schristos 
84525573806Schristos 	sep->se_sndbuf = buffer_size;
84625573806Schristos 
84725573806Schristos 	return KEY_HANDLER_SUCCESS;
84825573806Schristos }
84925573806Schristos 
85025573806Schristos /* recvbuf size */
85125573806Schristos static hresult
recv_buf_handler(struct servtab * sep,vlist values)85225573806Schristos recv_buf_handler(struct servtab *sep, vlist values)
85325573806Schristos {
85425573806Schristos 	char *arg;
85525573806Schristos 	int buffer_size;
85625573806Schristos 
85725573806Schristos 	if (ISMUX(sep)) {
85825573806Schristos 		ERR("%s: Cannot specify buffer sizes for tcpmux services",
85925573806Schristos 			sep->se_service);
86025573806Schristos 		return KEY_HANDLER_FAILURE;
86125573806Schristos 	}
86225573806Schristos 
86325573806Schristos 	if ((arg = next_value(values)) == NULL){
86425573806Schristos 		TFA("recvbuf");
86525573806Schristos 		return KEY_HANDLER_FAILURE;
86625573806Schristos 	}
86725573806Schristos 
86825573806Schristos 	buffer_size = size_to_bytes(arg);
86925573806Schristos 
87025573806Schristos 	if (buffer_size == -1) {
87125573806Schristos 		return KEY_HANDLER_FAILURE;
87225573806Schristos 	}
87325573806Schristos 
87425573806Schristos 	if ((arg = next_value(values)) != NULL) {
87525573806Schristos 		TMA("recvbuf");
87625573806Schristos 		return KEY_HANDLER_FAILURE;
87725573806Schristos 	}
87825573806Schristos 
87925573806Schristos 	sep->se_rcvbuf = buffer_size;
88025573806Schristos 
88125573806Schristos 	return KEY_HANDLER_SUCCESS;
88225573806Schristos 
88325573806Schristos }
88425573806Schristos 
88525573806Schristos /* Same as wait in positional */
88625573806Schristos static hresult
wait_handler(struct servtab * sep,vlist values)88725573806Schristos wait_handler(struct servtab *sep, vlist values)
88825573806Schristos {
88925573806Schristos 	char *val;
89025573806Schristos 	pid_t wait;
89125573806Schristos 
89225573806Schristos 	/* If 'wait' is specified after internal exec */
89325573806Schristos 
89425573806Schristos 	if (!is_internal(sep) && sep->se_wait != SERVTAB_UNSPEC_VAL) {
89525573806Schristos 		/* Prevent duplicate wait keys */
89625573806Schristos 		TMD("wait");
89725573806Schristos 		return KEY_HANDLER_FAILURE;
89825573806Schristos 	}
89925573806Schristos 
90025573806Schristos 	val = next_value(values);
90125573806Schristos 
90225573806Schristos 	if (val == NULL) {
90325573806Schristos 		TFA("wait");
90425573806Schristos 		return KEY_HANDLER_FAILURE;
90525573806Schristos 	}
90625573806Schristos 
90725573806Schristos 	if (strcmp(val, "yes") == 0) {
90825573806Schristos 		wait = true;
90925573806Schristos 	} else if (strcmp(val, "no") == 0) {
91025573806Schristos 		wait = false;
91125573806Schristos 	} else {
91225573806Schristos 		ERR("Invalid value '%s' for wait. Valid: yes, no", val);
91325573806Schristos 		return KEY_HANDLER_FAILURE;
91425573806Schristos 	}
91525573806Schristos 
91625573806Schristos 	if (is_internal(sep) && wait != sep->se_wait) {
91725573806Schristos 		/* If wait was set for internal service check for correctness */
91825573806Schristos 		WRN(WAIT_WRN, sep->se_service);
91925573806Schristos 	} else if (parse_wait(sep, wait)) {
92025573806Schristos 		return KEY_HANDLER_FAILURE;
92125573806Schristos 	}
92225573806Schristos 
92325573806Schristos 	if ((val = next_value(values)) != NULL) {
92425573806Schristos 		TMA("wait");
92525573806Schristos 		return KEY_HANDLER_FAILURE;
92625573806Schristos 	}
92725573806Schristos 
92825573806Schristos 	return KEY_HANDLER_SUCCESS;
92925573806Schristos }
93025573806Schristos 
93125573806Schristos /* Set max connections in interval rate-limit, same as max in positional */
93225573806Schristos static hresult
service_max_handler(struct servtab * sep,vlist values)93325573806Schristos service_max_handler(struct servtab *sep, vlist values)
93425573806Schristos {
93525573806Schristos 	char *count_str;
93625573806Schristos 	int rstatus;
93725573806Schristos 
93825573806Schristos 	if (sep->se_service_max != SERVTAB_UNSPEC_SIZE_T) {
93925573806Schristos 		TMD("service_max");
94025573806Schristos 		return KEY_HANDLER_FAILURE;
94125573806Schristos 	}
94225573806Schristos 
94325573806Schristos 	count_str = next_value(values);
94425573806Schristos 
94525573806Schristos 	if (count_str == NULL) {
94625573806Schristos 		TFA("service_max");
94725573806Schristos 		return KEY_HANDLER_FAILURE;
94825573806Schristos 	}
94925573806Schristos 
95025573806Schristos 	size_t count = (size_t)strtou(count_str, NULL, 10, 0,
95125573806Schristos 	    SERVTAB_COUNT_MAX, &rstatus);
95225573806Schristos 
953adeed07fSrillig 	if (rstatus != 0) {
95425573806Schristos 		ERR("Invalid service_max '%s': %s", count_str,
95525573806Schristos 		    strerror(rstatus));
95625573806Schristos 		return KEY_HANDLER_FAILURE;
95725573806Schristos 	}
95825573806Schristos 
95925573806Schristos 	if (next_value(values) != NULL) {
96025573806Schristos 		TMA("service_max");
96125573806Schristos 		return KEY_HANDLER_FAILURE;
96225573806Schristos 	}
96325573806Schristos 
96425573806Schristos 	sep->se_service_max = count;
96525573806Schristos 
96625573806Schristos 	return KEY_HANDLER_SUCCESS;
96725573806Schristos }
96825573806Schristos 
96925573806Schristos static hresult
ip_max_handler(struct servtab * sep,vlist values)97025573806Schristos ip_max_handler(struct servtab *sep, vlist values)
97125573806Schristos {
97225573806Schristos 	char *count_str;
97325573806Schristos 	int rstatus;
97425573806Schristos 
97525573806Schristos 	if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
97625573806Schristos 		TMD("ip_max");
97725573806Schristos 		return KEY_HANDLER_FAILURE;
97825573806Schristos 	}
97925573806Schristos 
98025573806Schristos 	count_str = next_value(values);
98125573806Schristos 
98225573806Schristos 	if (count_str == NULL) {
98325573806Schristos 		TFA("ip_max");
98425573806Schristos 		return KEY_HANDLER_FAILURE;
98525573806Schristos 	}
98625573806Schristos 
98725573806Schristos 	size_t count = (size_t)strtou(count_str, NULL, 10, 0,
98825573806Schristos 	    SERVTAB_COUNT_MAX, &rstatus);
98925573806Schristos 
990adeed07fSrillig 	if (rstatus != 0) {
99125573806Schristos 		ERR("Invalid ip_max '%s': %s", count_str, strerror(rstatus));
99225573806Schristos 		return KEY_HANDLER_FAILURE;
99325573806Schristos 	}
99425573806Schristos 
99525573806Schristos 	if (next_value(values) != NULL) {
99625573806Schristos 		TMA("ip_max");
99725573806Schristos 		return KEY_HANDLER_FAILURE;
99825573806Schristos 	}
99925573806Schristos 
100025573806Schristos 	sep->se_ip_max = count;
100125573806Schristos 
100225573806Schristos 	return KEY_HANDLER_SUCCESS;
100325573806Schristos }
100425573806Schristos 
100525573806Schristos /* Set user to execute as */
100625573806Schristos static hresult
user_handler(struct servtab * sep,vlist values)100725573806Schristos user_handler(struct servtab *sep, vlist values)
100825573806Schristos {
100925573806Schristos 	if (sep->se_user != NULL) {
101025573806Schristos 		TMD("user");
101125573806Schristos 		return KEY_HANDLER_FAILURE;
101225573806Schristos 	}
101325573806Schristos 
101425573806Schristos 	char *name = next_value(values);
101525573806Schristos 
101625573806Schristos 	if (name == NULL) {
101725573806Schristos 		TFA("user");
101825573806Schristos 		return KEY_HANDLER_FAILURE;
101925573806Schristos 	}
102025573806Schristos 
102125573806Schristos 	sep->se_user = newstr(name);
102225573806Schristos 
102325573806Schristos 	if (next_value(values) != NULL) {
102425573806Schristos 		TMA("user");
102525573806Schristos 		return KEY_HANDLER_FAILURE;
102625573806Schristos 	}
102725573806Schristos 
102825573806Schristos 	return KEY_HANDLER_SUCCESS;
102925573806Schristos }
103025573806Schristos 
103125573806Schristos /* Set group to execute as */
103225573806Schristos static hresult
group_handler(struct servtab * sep,vlist values)103325573806Schristos group_handler(struct servtab *sep, vlist values)
103425573806Schristos {
103525573806Schristos 	char *name = next_value(values);
103625573806Schristos 
103725573806Schristos 	if (name == NULL) {
103825573806Schristos 		TFA("group");
103925573806Schristos 		return KEY_HANDLER_FAILURE;
104025573806Schristos 	}
104125573806Schristos 
104225573806Schristos 	sep->se_group = newstr(name);
104325573806Schristos 
104425573806Schristos 	if (next_value(values) != NULL) {
104525573806Schristos 		TMA("group");
104625573806Schristos 		return KEY_HANDLER_FAILURE;
104725573806Schristos 	}
104825573806Schristos 
104925573806Schristos 	return KEY_HANDLER_SUCCESS;
105025573806Schristos }
105125573806Schristos 
105225573806Schristos /* Handle program path or "internal" */
105325573806Schristos static hresult
exec_handler(struct servtab * sep,vlist values)105425573806Schristos exec_handler(struct servtab *sep, vlist values)
105525573806Schristos {
105625573806Schristos 	char *val;
105725573806Schristos 
105825573806Schristos 	if ((val = next_value(values)) == NULL) {
105925573806Schristos 		TFA("exec");
106025573806Schristos 		return KEY_HANDLER_FAILURE;
106125573806Schristos 	}
106225573806Schristos 
106325573806Schristos 	pid_t wait_prev = sep->se_wait;
106425573806Schristos 	if (parse_server(sep, val))
106525573806Schristos 		return KEY_HANDLER_FAILURE;
106625573806Schristos 	if (is_internal(sep) && wait_prev != SERVTAB_UNSPEC_VAL) {
106725573806Schristos 		/*
106825573806Schristos 		 * Warn if the user specifies a value for an internal which
106925573806Schristos 		 * is different
107025573806Schristos 		 */
107125573806Schristos 		if (wait_prev != sep->se_wait) {
107225573806Schristos 			WRN(WAIT_WRN, sep->se_service);
107325573806Schristos 		}
107425573806Schristos 	}
107525573806Schristos 
107625573806Schristos 	if ((val = next_value(values)) != NULL) {
107725573806Schristos 		TMA("exec");
107825573806Schristos 		return KEY_HANDLER_FAILURE;
107925573806Schristos 	}
108025573806Schristos 
108125573806Schristos 	return KEY_HANDLER_SUCCESS;
108225573806Schristos }
108325573806Schristos 
108425573806Schristos /* Handle program arguments */
108525573806Schristos static hresult
args_handler(struct servtab * sep,vlist values)108625573806Schristos args_handler(struct servtab *sep, vlist values)
108725573806Schristos {
108825573806Schristos 	char *val;
108925573806Schristos 	int argc;
109025573806Schristos 
109125573806Schristos 	if (sep->se_argv[0] != NULL) {
109225573806Schristos 		TMD("args");
109325573806Schristos 		return KEY_HANDLER_FAILURE;
109425573806Schristos 	}
109525573806Schristos 
109625573806Schristos 	argc = 0;
109725573806Schristos 	for (val = next_value(values); val != NULL; val = next_value(values)) {
109825573806Schristos 		if (argc >= MAXARGV) {
109925573806Schristos 			ERR("Must be fewer than " TOSTRING(MAXARGV)
110025573806Schristos 			    " arguments");
110125573806Schristos 			return KEY_HANDLER_FAILURE;
110225573806Schristos 		}
110325573806Schristos 		sep->se_argv[argc++] = newstr(val);
110425573806Schristos 	}
110525573806Schristos 	while (argc <= MAXARGV)
110625573806Schristos 		sep->se_argv[argc++] = NULL;
110725573806Schristos 
110825573806Schristos 	return KEY_HANDLER_SUCCESS;
110925573806Schristos 
111025573806Schristos }
111125573806Schristos 
111225573806Schristos #ifdef IPSEC
111325573806Schristos /*
111425573806Schristos  * ipsec_handler currently uses the ipsec.h utilities for parsing, requiring
111525573806Schristos  * all policies as a single value. This handler could potentially allow multiple
111625573806Schristos  * policies as separate values in the future, but strings would need to be
111725573806Schristos  * concatenated so the existing ipsec.h functions continue to work and policies
111825573806Schristos  * can continue to be stored in sep->policy.
111925573806Schristos  */
112025573806Schristos static hresult
ipsec_handler(struct servtab * sep,vlist values)112125573806Schristos ipsec_handler(struct servtab *sep, vlist values)
112225573806Schristos {
112325573806Schristos 	if (sep->se_policy != NULL) {
112425573806Schristos 		TMD("ipsec");
112525573806Schristos 		return KEY_HANDLER_FAILURE;
112625573806Schristos 	}
112725573806Schristos 
112825573806Schristos 	char *ipsecstr = next_value(values);
112925573806Schristos 
113025573806Schristos 	if (ipsecstr != NULL && ipsecsetup_test(ipsecstr) < 0) {
113125573806Schristos 		ERR("IPsec policy '%s' is invalid", ipsecstr);
113225573806Schristos 		return KEY_HANDLER_FAILURE;
113325573806Schristos 	}
113425573806Schristos 
113525573806Schristos 	/*
113625573806Schristos 	 * Use 'ipsec=' with no argument to disable ipsec for this service
113725573806Schristos 	 * An empty string indicates that IPsec was disabled, handled in
113825573806Schristos 	 * fill_default_values.
113925573806Schristos 	 */
1140adeed07fSrillig 	sep->se_policy = policy != NULL ? newstr(ipsecstr) : newstr("");
114125573806Schristos 
114225573806Schristos 	if (next_value(values) != NULL) {
114325573806Schristos 		TMA("ipsec");
114425573806Schristos 		/* Currently only one semicolon separated string is allowed */
114525573806Schristos 		return KEY_HANDLER_FAILURE;
114625573806Schristos 	}
114725573806Schristos 
114825573806Schristos 	return KEY_HANDLER_SUCCESS;
114925573806Schristos }
115025573806Schristos #endif
1151