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