parse_v2.c revision 1.3 1 1.3 rillig /* $NetBSD: parse_v2.c,v 1.3 2021/08/30 17:32:23 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.3 rillig __RCSID("$NetBSD: parse_v2.c,v 1.3 2021/08/30 17:32:23 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.2 tih 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.1 christos int 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.1 christos ERR("Early end of file after line %d",
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.1 christos while (*cp && quote) {
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.2 tih int temp, bits;
409 1.3 rillig if (((bits = hex_to_bits(*(cp + 1))) == -1)
410 1.1 christos || ((temp = 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.1 christos bits <<= 4;
416 1.1 christos bits |= temp;
417 1.1 christos *start = bits;
418 1.1 christos strmove(cp, 3);
419 1.1 christos continue;
420 1.1 christos }
421 1.1 christos case '\\':
422 1.1 christos *start = '\\';
423 1.1 christos break;
424 1.1 christos case 'n':
425 1.1 christos *start = '\n';
426 1.1 christos break;
427 1.1 christos case 't':
428 1.1 christos *start = '\t';
429 1.1 christos break;
430 1.1 christos case 'r':
431 1.1 christos *start = '\r';
432 1.1 christos break;
433 1.1 christos case '\'':
434 1.1 christos *start = '\'';
435 1.1 christos break;
436 1.1 christos case '"':
437 1.1 christos *start = '"';
438 1.1 christos break;
439 1.1 christos case '\0':
440 1.1 christos ERR("Dangling escape sequence backslash");
441 1.1 christos return false;
442 1.1 christos default:
443 1.1 christos ERR("Unknown escape sequence '\\%c'", *cp);
444 1.1 christos return false;
445 1.1 christos }
446 1.1 christos strmove(cp, 1);
447 1.1 christos continue;
448 1.1 christos }
449 1.1 christos
450 1.1 christos /* Regular character, advance to the next one. */
451 1.1 christos cp++;
452 1.1 christos }
453 1.1 christos
454 1.1 christos if (*cp == '\0' && quote) {
455 1.1 christos ERR("Unclosed quote");
456 1.1 christos return false;
457 1.1 christos }
458 1.1 christos *cpp = cp;
459 1.1 christos return true;
460 1.1 christos }
461 1.1 christos
462 1.2 tih int
463 1.1 christos hex_to_bits(char in)
464 1.1 christos {
465 1.1 christos switch(in) {
466 1.1 christos case '0'...'9':
467 1.1 christos return in - '0';
468 1.1 christos case 'a'...'f':
469 1.1 christos return in - 'a' + 10;
470 1.1 christos case 'A'...'F':
471 1.1 christos return in - 'A' + 10;
472 1.1 christos default:
473 1.1 christos return -1;
474 1.1 christos }
475 1.1 christos }
476 1.1 christos
477 1.3 rillig /*
478 1.1 christos * Parse the next value for a key handler and advance list->cp past the found
479 1.3 rillig * value. Return NULL if there are no more values or there was an error
480 1.1 christos * during parsing, and set the list->state to the appropriate value.
481 1.1 christos */
482 1.3 rillig static char *
483 1.1 christos next_value(vlist list)
484 1.1 christos {
485 1.1 christos char *cp = list->cp;
486 1.3 rillig
487 1.1 christos if (list->state != VALS_PARSING) {
488 1.1 christos /* Already at the end of a values list, or there was an error.*/
489 1.1 christos return NULL;
490 1.1 christos }
491 1.1 christos
492 1.1 christos if (!skip_whitespace(&cp)) {
493 1.1 christos list->state = VALS_ERROR;
494 1.1 christos return NULL;
495 1.1 christos }
496 1.1 christos
497 1.1 christos if (*cp == ',' || *cp == ';') {
498 1.1 christos /* Found end of args, but not immediately after value */
499 1.1 christos list->state = (*cp == ',' ? VALS_END_KEY : VALS_END_DEF);
500 1.1 christos list->cp = cp + 1;
501 1.1 christos return NULL;
502 1.1 christos }
503 1.3 rillig
504 1.1 christos /* Check for end of line */
505 1.1 christos if (!skip_whitespace(&cp)) {
506 1.1 christos list->state = VALS_ERROR;
507 1.1 christos return NULL;
508 1.1 christos }
509 1.1 christos
510 1.3 rillig /*
511 1.1 christos * Found the start of a potential value. Advance one character
512 1.3 rillig * past the end of the value.
513 1.1 christos */
514 1.1 christos char * start = cp;
515 1.3 rillig while (!isblank((unsigned char)*cp) && *cp != '#' &&
516 1.1 christos *cp != ',' && *cp != ';' && *cp != '\0' ) {
517 1.1 christos if (*cp == '"' || *cp == '\'') {
518 1.1 christos /* Found a quoted segment */
519 1.1 christos if (!parse_quotes(&cp)) {
520 1.1 christos list->state = VALS_ERROR;
521 1.1 christos return NULL;
522 1.1 christos }
523 1.1 christos } else {
524 1.1 christos /* Find the end of the value */
525 1.1 christos cp++;
526 1.1 christos }
527 1.1 christos }
528 1.3 rillig
529 1.1 christos /* Handle comments next to unquoted values */
530 1.1 christos if (*cp == '#') {
531 1.1 christos *cp = '\0';
532 1.1 christos list->cp = cp;
533 1.1 christos return start;
534 1.1 christos }
535 1.1 christos
536 1.1 christos if (*cp == '\0') {
537 1.3 rillig /*
538 1.1 christos * Value ends with end of line, so it is already NUL-terminated
539 1.1 christos */
540 1.1 christos list->cp = cp;
541 1.1 christos return start;
542 1.1 christos }
543 1.1 christos
544 1.1 christos if (*cp == ',') {
545 1.1 christos list->state = VALS_END_KEY;
546 1.1 christos } else if (*cp == ';') {
547 1.1 christos list->state = VALS_END_DEF;
548 1.1 christos }
549 1.1 christos
550 1.1 christos *cp = '\0';
551 1.1 christos /* Advance past null so we don't skip the rest of the line */
552 1.1 christos list->cp = cp + 1;
553 1.1 christos return start;
554 1.1 christos }
555 1.1 christos
556 1.1 christos /* Parse key name and invoke associated handler */
557 1.1 christos static invoke_result
558 1.1 christos parse_invoke_handler(bool *is_valid_definition, char **cpp, struct servtab *sep)
559 1.1 christos {
560 1.1 christos char *key_name, save, *cp = *cpp;
561 1.1 christos int is_blank;
562 1.1 christos key_handler_func handler;
563 1.1 christos val_parse_info info;
564 1.1 christos
565 1.1 christos /* Skip any whitespace if it exists, otherwise do nothing */
566 1.1 christos if (!skip_whitespace(&cp)) {
567 1.1 christos return INVOKE_ERROR;
568 1.1 christos }
569 1.1 christos
570 1.1 christos /* Starting character of key */
571 1.1 christos key_name = cp;
572 1.1 christos
573 1.3 rillig
574 1.1 christos /* alphabetical or underscore allowed in name */
575 1.1 christos while (isalpha((unsigned char)*cp) || *cp == '_') {
576 1.1 christos cp++;
577 1.1 christos }
578 1.3 rillig
579 1.1 christos is_blank = isblank((unsigned char)*cp);
580 1.1 christos
581 1.1 christos /* Get key handler and move to start of values */
582 1.1 christos if (*cp != '=' && !is_blank && *cp != '#') {
583 1.1 christos ERR("Expected '=' but found '%c'", *cp);
584 1.1 christos return INVOKE_ERROR;
585 1.3 rillig }
586 1.3 rillig
587 1.1 christos save = *cp;
588 1.1 christos *cp = '\0';
589 1.1 christos cp++;
590 1.1 christos
591 1.1 christos handler = get_handler(key_name);
592 1.1 christos
593 1.1 christos if (handler == NULL) {
594 1.1 christos ERR("Unknown option '%s'", key_name);
595 1.1 christos handler = unknown_handler;
596 1.1 christos }
597 1.1 christos
598 1.1 christos /* If blank or new line, still need to find the '=' or throw error */
599 1.1 christos if (is_blank || save == '#') {
600 1.1 christos if (save == '#') {
601 1.1 christos cp = nextline(fconfig);
602 1.1 christos }
603 1.1 christos
604 1.1 christos skip_whitespace(&cp);
605 1.1 christos if (*cp != '=') {
606 1.1 christos ERR("Expected '=' but found '%c'", *cp);
607 1.1 christos return INVOKE_ERROR;
608 1.1 christos }
609 1.1 christos cp++;
610 1.1 christos }
611 1.1 christos
612 1.1 christos /* Skip whitespace to start of values */
613 1.1 christos if (!skip_whitespace(&cp)) {
614 1.1 christos return INVOKE_ERROR;
615 1.1 christos }
616 1.1 christos
617 1.1 christos info = (val_parse_info) {cp, VALS_PARSING};
618 1.1 christos
619 1.3 rillig /*
620 1.1 christos * Read values for key and write into sep.
621 1.3 rillig * If parsing is successful, all values for key must be read.
622 1.1 christos */
623 1.1 christos if (handler(sep, &info) == KEY_HANDLER_FAILURE) {
624 1.1 christos /*
625 1.1 christos * Eat remaining values if an error happened
626 1.1 christos * so more errors can be caught.
627 1.1 christos */
628 1.1 christos while (next_value(&info) != NULL)
629 1.1 christos continue;
630 1.1 christos *is_valid_definition = false;
631 1.1 christos }
632 1.1 christos
633 1.1 christos if (info.state == VALS_END_DEF) {
634 1.3 rillig /*
635 1.1 christos * Exit definition handling for(;;).
636 1.3 rillig * Set the position to the end of the definition,
637 1.1 christos * for multi-definition lines.
638 1.1 christos */
639 1.1 christos *cpp = info.cp;
640 1.1 christos return INVOKE_FINISH;
641 1.3 rillig }
642 1.1 christos if (info.state == VALS_ERROR) {
643 1.1 christos /* Parse error, stop reading config */
644 1.1 christos return INVOKE_ERROR;
645 1.1 christos }
646 1.1 christos
647 1.1 christos *cpp = info.cp;
648 1.1 christos return INVOKE_SUCCESS;
649 1.1 christos }
650 1.1 christos
651 1.1 christos /* Return true if sep must be a built-in service */
652 1.1 christos static bool
653 1.1 christos is_internal(struct servtab *sep)
654 1.1 christos {
655 1.1 christos return sep->se_bi != NULL;
656 1.1 christos }
657 1.1 christos
658 1.3 rillig /*
659 1.3 rillig * Key-values handlers
660 1.1 christos */
661 1.1 christos
662 1.1 christos static hresult
663 1.1 christos unknown_handler(struct servtab *sep, vlist values)
664 1.1 christos {
665 1.1 christos /* Return failure for an unknown service name. */
666 1.1 christos return KEY_HANDLER_FAILURE;
667 1.1 christos }
668 1.1 christos
669 1.1 christos /* Set listen address for this service */
670 1.3 rillig static hresult
671 1.3 rillig bind_handler(struct servtab *sep, vlist values)
672 1.1 christos {
673 1.1 christos if (sep->se_hostaddr != NULL) {
674 1.1 christos TMD("bind");
675 1.1 christos return KEY_HANDLER_FAILURE;
676 1.1 christos }
677 1.1 christos
678 1.1 christos char *val = next_value(values);
679 1.1 christos sep->se_hostaddr = newstr(val);
680 1.1 christos if (next_value(values) != NULL) {
681 1.1 christos TMA("bind");
682 1.1 christos return KEY_HANDLER_FAILURE;
683 1.1 christos }
684 1.1 christos return KEY_HANDLER_SUCCESS;
685 1.1 christos }
686 1.1 christos
687 1.3 rillig static hresult
688 1.1 christos socket_type_handler(struct servtab *sep, vlist values)
689 1.1 christos {
690 1.1 christos char *type = next_value(values);
691 1.1 christos if (type == NULL) {
692 1.1 christos TFA("socktype");
693 1.1 christos return KEY_HANDLER_FAILURE;
694 1.1 christos }
695 1.1 christos
696 1.1 christos parse_socktype(type, sep);
697 1.1 christos
698 1.1 christos if (sep->se_socktype == -1) {
699 1.1 christos ERR("Invalid socket type '%s'. Valid: " VALID_SOCKET_TYPES,
700 1.1 christos type);
701 1.1 christos return KEY_HANDLER_FAILURE;
702 1.1 christos }
703 1.1 christos
704 1.1 christos if (next_value(values) != NULL) {
705 1.1 christos TMA("socktype");
706 1.1 christos return KEY_HANDLER_FAILURE;
707 1.1 christos }
708 1.1 christos
709 1.1 christos return KEY_HANDLER_SUCCESS;
710 1.1 christos }
711 1.1 christos
712 1.1 christos /* Set accept filter SO_ACCEPTFILTER */
713 1.3 rillig static hresult
714 1.1 christos filter_handler(struct servtab *sep, vlist values)
715 1.1 christos {
716 1.3 rillig /*
717 1.1 christos * See: SO_ACCEPTFILTER https://man.netbsd.org/setsockopt.2
718 1.1 christos * An accept filter can have one other argument.
719 1.1 christos * This code currently only supports one accept filter
720 1.3 rillig * Also see parse_accept_filter(char* arg, struct servtab*sep)
721 1.1 christos */
722 1.1 christos
723 1.1 christos char *af_name, *af_arg;
724 1.1 christos
725 1.1 christos af_name = next_value(values);
726 1.1 christos
727 1.1 christos if (af_name == NULL) {
728 1.1 christos TFA("filter");
729 1.1 christos return KEY_HANDLER_FAILURE;
730 1.1 christos }
731 1.1 christos
732 1.1 christos /* Store af_name in se_accf.af_name, no newstr call */
733 1.1 christos strlcpy(sep->se_accf.af_name, af_name, sizeof(sep->se_accf.af_name));
734 1.1 christos
735 1.1 christos af_arg = next_value(values);
736 1.1 christos
737 1.1 christos if (af_arg != NULL) {
738 1.1 christos strlcpy(sep->se_accf.af_arg, af_arg,
739 1.1 christos sizeof(sep->se_accf.af_arg));
740 1.1 christos if (next_value(values) != NULL) {
741 1.1 christos TMA("filter");
742 1.1 christos return KEY_HANDLER_FAILURE;
743 1.1 christos }
744 1.1 christos } else {
745 1.1 christos /* Store null string */
746 1.1 christos sep->se_accf.af_arg[0] = '\0';
747 1.1 christos }
748 1.1 christos
749 1.1 christos return KEY_HANDLER_SUCCESS;
750 1.1 christos }
751 1.1 christos
752 1.1 christos /* Set protocol (udp, tcp, unix, etc.) */
753 1.3 rillig static hresult
754 1.1 christos protocol_handler(struct servtab *sep, vlist values)
755 1.1 christos {
756 1.1 christos char *val;
757 1.1 christos
758 1.1 christos if ((val = next_value(values)) == NULL) {
759 1.1 christos TFA("protocol");
760 1.1 christos return KEY_HANDLER_FAILURE;
761 1.1 christos }
762 1.3 rillig
763 1.1 christos if (sep->se_type == NORM_TYPE &&
764 1.1 christos strncmp(val, "faith/", strlen("faith/")) == 0) {
765 1.1 christos val += strlen("faith/");
766 1.1 christos sep->se_type = FAITH_TYPE;
767 1.1 christos }
768 1.1 christos sep->se_proto = newstr(val);
769 1.1 christos
770 1.1 christos if (parse_protocol(sep))
771 1.1 christos return KEY_HANDLER_FAILURE;
772 1.1 christos
773 1.1 christos if ((val = next_value(values)) != NULL) {
774 1.1 christos TMA("protocol");
775 1.1 christos return KEY_HANDLER_FAILURE;
776 1.1 christos }
777 1.1 christos return KEY_HANDLER_SUCCESS;
778 1.1 christos }
779 1.1 christos
780 1.3 rillig /*
781 1.1 christos * Convert a string number possible ending with k or m to an integer.
782 1.1 christos * Based on MALFORMED, GETVAL, and ASSIGN in getconfigent(void).
783 1.1 christos */
784 1.1 christos static int
785 1.1 christos size_to_bytes(char *arg)
786 1.1 christos {
787 1.1 christos char *tail;
788 1.1 christos int rstatus, count;
789 1.1 christos
790 1.1 christos count = (int)strtoi(arg, &tail, 10, 0, INT_MAX, &rstatus);
791 1.1 christos
792 1.1 christos if (rstatus && rstatus != ENOTSUP) {
793 1.1 christos ERR("Invalid buffer size '%s': %s", arg, strerror(rstatus));
794 1.1 christos return -1;
795 1.1 christos }
796 1.1 christos
797 1.1 christos switch(tail[0]) {
798 1.1 christos case 'm':
799 1.1 christos if (__builtin_smul_overflow((int)count, 1024, &count)) {
800 1.1 christos ERR("Invalid buffer size '%s': Result too large", arg);
801 1.1 christos return -1;
802 1.1 christos }
803 1.1 christos /* FALLTHROUGH */
804 1.1 christos case 'k':
805 1.1 christos if (__builtin_smul_overflow((int)count, 1024, &count)) {
806 1.1 christos ERR("Invalid buffer size '%s': Result too large", arg);
807 1.1 christos return -1;
808 1.1 christos }
809 1.1 christos /* FALLTHROUGH */
810 1.1 christos case '\0':
811 1.1 christos return count;
812 1.1 christos default:
813 1.1 christos ERR("Invalid buffer size unit prefix");
814 1.1 christos return -1;
815 1.1 christos }
816 1.1 christos }
817 1.1 christos
818 1.1 christos /* sndbuf size */
819 1.3 rillig static hresult
820 1.1 christos send_buf_handler(struct servtab *sep, vlist values)
821 1.1 christos {
822 1.1 christos char *arg;
823 1.1 christos int buffer_size;
824 1.1 christos
825 1.1 christos if (ISMUX(sep)) {
826 1.3 rillig ERR("%s: can't specify buffer sizes for tcpmux services",
827 1.1 christos sep->se_service);
828 1.1 christos return KEY_HANDLER_FAILURE;
829 1.1 christos }
830 1.3 rillig
831 1.1 christos
832 1.1 christos if ((arg = next_value(values)) == NULL) {
833 1.1 christos TFA("sndbuf");
834 1.1 christos return KEY_HANDLER_FAILURE;
835 1.1 christos }
836 1.1 christos
837 1.1 christos buffer_size = size_to_bytes(arg);
838 1.1 christos
839 1.1 christos if (buffer_size == -1) {
840 1.1 christos return KEY_HANDLER_FAILURE;
841 1.1 christos }
842 1.1 christos
843 1.1 christos if ((arg = next_value(values)) != NULL) {
844 1.1 christos TMA("sndbuf");
845 1.1 christos return KEY_HANDLER_FAILURE;
846 1.1 christos }
847 1.1 christos
848 1.1 christos sep->se_sndbuf = buffer_size;
849 1.1 christos
850 1.1 christos return KEY_HANDLER_SUCCESS;
851 1.1 christos }
852 1.1 christos
853 1.1 christos /* recvbuf size */
854 1.3 rillig static hresult
855 1.1 christos recv_buf_handler(struct servtab *sep, vlist values)
856 1.1 christos {
857 1.1 christos char *arg;
858 1.1 christos int buffer_size;
859 1.1 christos
860 1.1 christos if (ISMUX(sep)) {
861 1.3 rillig ERR("%s: Cannot specify buffer sizes for tcpmux services",
862 1.1 christos sep->se_service);
863 1.1 christos return KEY_HANDLER_FAILURE;
864 1.1 christos }
865 1.1 christos
866 1.1 christos if ((arg = next_value(values)) == NULL){
867 1.1 christos TFA("recvbuf");
868 1.1 christos return KEY_HANDLER_FAILURE;
869 1.3 rillig }
870 1.3 rillig
871 1.1 christos buffer_size = size_to_bytes(arg);
872 1.1 christos
873 1.1 christos if (buffer_size == -1) {
874 1.1 christos return KEY_HANDLER_FAILURE;
875 1.1 christos }
876 1.1 christos
877 1.1 christos if ((arg = next_value(values)) != NULL) {
878 1.1 christos TMA("recvbuf");
879 1.1 christos return KEY_HANDLER_FAILURE;
880 1.1 christos }
881 1.1 christos
882 1.1 christos sep->se_rcvbuf = buffer_size;
883 1.1 christos
884 1.1 christos return KEY_HANDLER_SUCCESS;
885 1.1 christos
886 1.1 christos }
887 1.1 christos
888 1.1 christos /* Same as wait in positional */
889 1.3 rillig static hresult
890 1.1 christos wait_handler(struct servtab *sep, vlist values)
891 1.1 christos {
892 1.1 christos char *val;
893 1.1 christos pid_t wait;
894 1.1 christos
895 1.1 christos /* If 'wait' is specified after internal exec */
896 1.1 christos
897 1.1 christos if (!is_internal(sep) && sep->se_wait != SERVTAB_UNSPEC_VAL) {
898 1.1 christos /* Prevent duplicate wait keys */
899 1.1 christos TMD("wait");
900 1.1 christos return KEY_HANDLER_FAILURE;
901 1.1 christos }
902 1.1 christos
903 1.1 christos val = next_value(values);
904 1.1 christos
905 1.1 christos if (val == NULL) {
906 1.1 christos TFA("wait");
907 1.1 christos return KEY_HANDLER_FAILURE;
908 1.1 christos }
909 1.1 christos
910 1.1 christos if (strcmp(val, "yes") == 0) {
911 1.1 christos wait = true;
912 1.1 christos } else if (strcmp(val, "no") == 0) {
913 1.1 christos wait = false;
914 1.1 christos } else {
915 1.1 christos ERR("Invalid value '%s' for wait. Valid: yes, no", val);
916 1.1 christos return KEY_HANDLER_FAILURE;
917 1.1 christos }
918 1.1 christos
919 1.1 christos if (is_internal(sep) && wait != sep->se_wait) {
920 1.1 christos /* If wait was set for internal service check for correctness */
921 1.1 christos WRN(WAIT_WRN, sep->se_service);
922 1.1 christos } else if (parse_wait(sep, wait)) {
923 1.1 christos return KEY_HANDLER_FAILURE;
924 1.1 christos }
925 1.3 rillig
926 1.1 christos if ((val = next_value(values)) != NULL) {
927 1.1 christos TMA("wait");
928 1.1 christos return KEY_HANDLER_FAILURE;
929 1.1 christos }
930 1.1 christos
931 1.1 christos return KEY_HANDLER_SUCCESS;
932 1.1 christos }
933 1.1 christos
934 1.1 christos /* Set max connections in interval rate-limit, same as max in positional */
935 1.3 rillig static hresult
936 1.1 christos service_max_handler(struct servtab *sep, vlist values)
937 1.1 christos {
938 1.1 christos char *count_str;
939 1.1 christos int rstatus;
940 1.1 christos
941 1.1 christos if (sep->se_service_max != SERVTAB_UNSPEC_SIZE_T) {
942 1.1 christos TMD("service_max");
943 1.1 christos return KEY_HANDLER_FAILURE;
944 1.1 christos }
945 1.3 rillig
946 1.1 christos count_str = next_value(values);
947 1.1 christos
948 1.1 christos if (count_str == NULL) {
949 1.1 christos TFA("service_max");
950 1.1 christos return KEY_HANDLER_FAILURE;
951 1.1 christos }
952 1.1 christos
953 1.3 rillig size_t count = (size_t)strtou(count_str, NULL, 10, 0,
954 1.1 christos SERVTAB_COUNT_MAX, &rstatus);
955 1.1 christos
956 1.1 christos if (rstatus) {
957 1.1 christos ERR("Invalid service_max '%s': %s", count_str,
958 1.1 christos strerror(rstatus));
959 1.1 christos return KEY_HANDLER_FAILURE;
960 1.1 christos }
961 1.1 christos
962 1.1 christos if (next_value(values) != NULL) {
963 1.1 christos TMA("service_max");
964 1.1 christos return KEY_HANDLER_FAILURE;
965 1.1 christos }
966 1.1 christos
967 1.1 christos sep->se_service_max = count;
968 1.1 christos
969 1.1 christos return KEY_HANDLER_SUCCESS;
970 1.1 christos }
971 1.1 christos
972 1.1 christos static hresult
973 1.1 christos ip_max_handler(struct servtab *sep, vlist values)
974 1.1 christos {
975 1.1 christos char *count_str;
976 1.1 christos int rstatus;
977 1.3 rillig
978 1.1 christos if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
979 1.1 christos TMD("ip_max");
980 1.1 christos return KEY_HANDLER_FAILURE;
981 1.1 christos }
982 1.3 rillig
983 1.1 christos count_str = next_value(values);
984 1.1 christos
985 1.1 christos if (count_str == NULL) {
986 1.1 christos TFA("ip_max");
987 1.1 christos return KEY_HANDLER_FAILURE;
988 1.1 christos }
989 1.1 christos
990 1.3 rillig size_t count = (size_t)strtou(count_str, NULL, 10, 0,
991 1.1 christos SERVTAB_COUNT_MAX, &rstatus);
992 1.1 christos
993 1.1 christos if (rstatus) {
994 1.1 christos ERR("Invalid ip_max '%s': %s", count_str, strerror(rstatus));
995 1.1 christos return KEY_HANDLER_FAILURE;
996 1.1 christos }
997 1.1 christos
998 1.1 christos if (next_value(values) != NULL) {
999 1.1 christos TMA("ip_max");
1000 1.1 christos return KEY_HANDLER_FAILURE;
1001 1.1 christos }
1002 1.1 christos
1003 1.1 christos sep->se_ip_max = count;
1004 1.1 christos
1005 1.1 christos return KEY_HANDLER_SUCCESS;
1006 1.1 christos }
1007 1.1 christos
1008 1.1 christos /* Set user to execute as */
1009 1.3 rillig static hresult
1010 1.1 christos user_handler(struct servtab *sep, vlist values)
1011 1.1 christos {
1012 1.1 christos if (sep->se_user != NULL) {
1013 1.1 christos TMD("user");
1014 1.3 rillig return KEY_HANDLER_FAILURE;
1015 1.1 christos }
1016 1.1 christos
1017 1.1 christos char *name = next_value(values);
1018 1.1 christos
1019 1.1 christos if (name == NULL) {
1020 1.1 christos TFA("user");
1021 1.1 christos return KEY_HANDLER_FAILURE;
1022 1.1 christos }
1023 1.1 christos
1024 1.1 christos sep->se_user = newstr(name);
1025 1.1 christos
1026 1.1 christos if (next_value(values) != NULL) {
1027 1.1 christos TMA("user");
1028 1.1 christos return KEY_HANDLER_FAILURE;
1029 1.1 christos }
1030 1.1 christos
1031 1.1 christos return KEY_HANDLER_SUCCESS;
1032 1.1 christos }
1033 1.3 rillig
1034 1.1 christos /* Set group to execute as */
1035 1.3 rillig static hresult
1036 1.1 christos group_handler(struct servtab *sep, vlist values)
1037 1.1 christos {
1038 1.1 christos char *name = next_value(values);
1039 1.1 christos
1040 1.1 christos if (name == NULL) {
1041 1.1 christos TFA("group");
1042 1.1 christos return KEY_HANDLER_FAILURE;
1043 1.1 christos }
1044 1.1 christos
1045 1.1 christos sep->se_group = newstr(name);
1046 1.1 christos
1047 1.1 christos if (next_value(values) != NULL) {
1048 1.1 christos TMA("group");
1049 1.1 christos return KEY_HANDLER_FAILURE;
1050 1.1 christos }
1051 1.1 christos
1052 1.1 christos return KEY_HANDLER_SUCCESS;
1053 1.1 christos }
1054 1.1 christos
1055 1.1 christos /* Handle program path or "internal" */
1056 1.3 rillig static hresult
1057 1.1 christos exec_handler(struct servtab *sep, vlist values)
1058 1.1 christos {
1059 1.1 christos char *val;
1060 1.1 christos
1061 1.1 christos if ((val = next_value(values)) == NULL) {
1062 1.1 christos TFA("exec");
1063 1.1 christos return KEY_HANDLER_FAILURE;
1064 1.1 christos }
1065 1.1 christos
1066 1.1 christos pid_t wait_prev = sep->se_wait;
1067 1.1 christos if (parse_server(sep, val))
1068 1.1 christos return KEY_HANDLER_FAILURE;
1069 1.1 christos if (is_internal(sep) && wait_prev != SERVTAB_UNSPEC_VAL) {
1070 1.1 christos /*
1071 1.1 christos * Warn if the user specifies a value for an internal which
1072 1.1 christos * is different
1073 1.1 christos */
1074 1.1 christos if (wait_prev != sep->se_wait) {
1075 1.1 christos WRN(WAIT_WRN, sep->se_service);
1076 1.1 christos }
1077 1.1 christos }
1078 1.3 rillig
1079 1.1 christos if ((val = next_value(values)) != NULL) {
1080 1.1 christos TMA("exec");
1081 1.1 christos return KEY_HANDLER_FAILURE;
1082 1.1 christos }
1083 1.1 christos
1084 1.1 christos return KEY_HANDLER_SUCCESS;
1085 1.1 christos }
1086 1.1 christos
1087 1.1 christos /* Handle program arguments */
1088 1.3 rillig static hresult
1089 1.1 christos args_handler(struct servtab *sep, vlist values)
1090 1.1 christos {
1091 1.1 christos char *val;
1092 1.1 christos int argc;
1093 1.1 christos
1094 1.1 christos if (sep->se_argv[0] != NULL) {
1095 1.1 christos TMD("args");
1096 1.1 christos return KEY_HANDLER_FAILURE;
1097 1.1 christos }
1098 1.1 christos
1099 1.1 christos argc = 0;
1100 1.1 christos for (val = next_value(values); val != NULL; val = next_value(values)) {
1101 1.1 christos if (argc >= MAXARGV) {
1102 1.1 christos ERR("Must be fewer than " TOSTRING(MAXARGV)
1103 1.1 christos " arguments");
1104 1.1 christos return KEY_HANDLER_FAILURE;
1105 1.1 christos }
1106 1.1 christos sep->se_argv[argc++] = newstr(val);
1107 1.1 christos }
1108 1.1 christos while (argc <= MAXARGV)
1109 1.1 christos sep->se_argv[argc++] = NULL;
1110 1.3 rillig
1111 1.1 christos return KEY_HANDLER_SUCCESS;
1112 1.1 christos
1113 1.1 christos }
1114 1.1 christos
1115 1.1 christos #ifdef IPSEC
1116 1.3 rillig /*
1117 1.1 christos * ipsec_handler currently uses the ipsec.h utilities for parsing, requiring
1118 1.1 christos * all policies as a single value. This handler could potentially allow multiple
1119 1.1 christos * policies as separate values in the future, but strings would need to be
1120 1.1 christos * concatenated so the existing ipsec.h functions continue to work and policies
1121 1.1 christos * can continue to be stored in sep->policy.
1122 1.1 christos */
1123 1.1 christos static hresult
1124 1.1 christos ipsec_handler(struct servtab *sep, vlist values)
1125 1.1 christos {
1126 1.1 christos if (sep->se_policy != NULL) {
1127 1.1 christos TMD("ipsec");
1128 1.1 christos return KEY_HANDLER_FAILURE;
1129 1.1 christos }
1130 1.1 christos
1131 1.1 christos char *ipsecstr = next_value(values);
1132 1.1 christos
1133 1.1 christos if (ipsecstr != NULL && ipsecsetup_test(ipsecstr) < 0) {
1134 1.1 christos ERR("IPsec policy '%s' is invalid", ipsecstr);
1135 1.1 christos return KEY_HANDLER_FAILURE;
1136 1.1 christos }
1137 1.1 christos
1138 1.3 rillig /*
1139 1.1 christos * Use 'ipsec=' with no argument to disable ipsec for this service
1140 1.1 christos * An empty string indicates that IPsec was disabled, handled in
1141 1.1 christos * fill_default_values.
1142 1.1 christos */
1143 1.1 christos sep->se_policy = policy ? newstr(ipsecstr) : newstr("");
1144 1.1 christos
1145 1.1 christos if (next_value(values) != NULL) {
1146 1.1 christos TMA("ipsec");
1147 1.1 christos /* Currently only one semicolon separated string is allowed */
1148 1.1 christos return KEY_HANDLER_FAILURE;
1149 1.1 christos }
1150 1.1 christos
1151 1.1 christos return KEY_HANDLER_SUCCESS;
1152 1.1 christos }
1153 1.1 christos #endif
1154