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