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