parse.c revision 1.4 1 1.4 dholland /* $NetBSD: parse.c,v 1.4 2022/08/10 03:35:38 dholland Exp $ */
2 1.1 christos
3 1.1 christos /*-
4 1.1 christos * Copyright (c) 1998, 2003 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 Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 1.1 christos * NASA Ames Research Center and by Matthias Scheler.
10 1.1 christos *
11 1.1 christos * Redistribution and use in source and binary forms, with or without
12 1.1 christos * modification, are permitted provided that the following conditions
13 1.1 christos * are met:
14 1.1 christos * 1. Redistributions of source code must retain the above copyright
15 1.1 christos * notice, this list of conditions and the following disclaimer.
16 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
17 1.1 christos * notice, this list of conditions and the following disclaimer in the
18 1.1 christos * documentation and/or other materials provided with the distribution.
19 1.1 christos *
20 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 1.1 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 1.1 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 1.1 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 1.1 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 1.1 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 1.1 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 1.1 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 1.1 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 1.1 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 1.1 christos * POSSIBILITY OF SUCH DAMAGE.
31 1.1 christos */
32 1.1 christos
33 1.1 christos /*
34 1.1 christos * Copyright (c) 1983, 1991, 1993, 1994
35 1.1 christos * The Regents of the University of California. All rights reserved.
36 1.1 christos *
37 1.1 christos * Redistribution and use in source and binary forms, with or without
38 1.1 christos * modification, are permitted provided that the following conditions
39 1.1 christos * are met:
40 1.1 christos * 1. Redistributions of source code must retain the above copyright
41 1.1 christos * notice, this list of conditions and the following disclaimer.
42 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
43 1.1 christos * notice, this list of conditions and the following disclaimer in the
44 1.1 christos * documentation and/or other materials provided with the distribution.
45 1.1 christos * 3. Neither the name of the University nor the names of its contributors
46 1.1 christos * may be used to endorse or promote products derived from this software
47 1.1 christos * without specific prior written permission.
48 1.1 christos *
49 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 1.1 christos * SUCH DAMAGE.
60 1.1 christos */
61 1.1 christos
62 1.1 christos #include <sys/cdefs.h>
63 1.1 christos #ifndef lint
64 1.1 christos #if 0
65 1.1 christos static char sccsid[] = "@(#)inetd.c 8.4 (Berkeley) 4/13/94";
66 1.1 christos #else
67 1.4 dholland __RCSID("$NetBSD: parse.c,v 1.4 2022/08/10 03:35:38 dholland Exp $");
68 1.1 christos #endif
69 1.1 christos #endif /* not lint */
70 1.1 christos
71 1.1 christos /*
72 1.1 christos * This file contains code and state for loading and managing servtabs.
73 1.1 christos * The "positional" syntax parsing is performed in this file. See parse_v2.c
74 1.1 christos * for "key-values" syntax parsing.
75 1.1 christos */
76 1.1 christos
77 1.1 christos #include <sys/param.h>
78 1.1 christos #include <sys/stat.h>
79 1.1 christos #include <sys/socket.h>
80 1.1 christos #include <sys/queue.h>
81 1.1 christos
82 1.1 christos #include <ctype.h>
83 1.1 christos #include <err.h>
84 1.1 christos #include <errno.h>
85 1.1 christos #include <fcntl.h>
86 1.1 christos #include <glob.h>
87 1.1 christos #include <libgen.h>
88 1.1 christos #include <stdio.h>
89 1.1 christos #include <stdlib.h>
90 1.1 christos #include <string.h>
91 1.1 christos #include <syslog.h>
92 1.1 christos #include <unistd.h>
93 1.1 christos
94 1.4 dholland #ifdef RPC
95 1.4 dholland #include <rpc/rpc.h>
96 1.4 dholland #endif
97 1.4 dholland
98 1.1 christos #include "inetd.h"
99 1.1 christos
100 1.1 christos static void config(void);
101 1.1 christos static void endconfig(void);
102 1.1 christos static struct servtab *enter(struct servtab *);
103 1.1 christos static struct servtab *getconfigent(char **);
104 1.1 christos #ifdef DEBUG_ENABLE
105 1.1 christos static void print_service(const char *, struct servtab *);
106 1.1 christos #endif
107 1.1 christos static struct servtab init_servtab(void);
108 1.1 christos static void include_configs(char *);
109 1.1 christos static int glob_error(const char *, int);
110 1.1 christos static void read_glob_configs(char *);
111 1.1 christos static void prepare_next_config(const char*);
112 1.1 christos static bool is_same_service(const struct servtab *, const struct servtab *);
113 1.1 christos static char *gen_file_pattern(const char *, const char *);
114 1.1 christos static bool check_no_reinclude(const char *);
115 1.1 christos static void include_matched_path(char *);
116 1.1 christos static void purge_unchecked(void);
117 1.1 christos static void freeconfig(struct servtab *);
118 1.1 christos static char *skip(char **);
119 1.1 christos
120 1.1 christos size_t line_number;
121 1.1 christos FILE *fconfig;
122 1.1 christos /* Temporary storage for new servtab */
123 1.1 christos static struct servtab serv;
124 1.1 christos /* Current line from current config file */
125 1.1 christos static char line[LINE_MAX];
126 1.1 christos char *defhost;
127 1.1 christos #ifdef IPSEC
128 1.1 christos char *policy;
129 1.1 christos #endif
130 1.1 christos
131 1.1 christos /*
132 1.1 christos * Recursively merge loaded service definitions with any defined
133 1.1 christos * in the current or included config files.
134 1.1 christos */
135 1.1 christos static void
136 1.1 christos config(void)
137 1.1 christos {
138 1.1 christos struct servtab *sep, *cp;
139 1.1 christos /*
140 1.1 christos * Current position in line, used with key-values notation,
141 1.1 christos * saves cp across getconfigent calls.
142 1.1 christos */
143 1.1 christos char *current_pos;
144 1.1 christos size_t n;
145 1.1 christos
146 1.1 christos /* open config file from beginning */
147 1.1 christos fconfig = fopen(CONFIG, "r");
148 1.1 christos if (fconfig == NULL) {
149 1.1 christos DPRINTF("Could not open file \"%s\": %s",
150 1.1 christos CONFIG, strerror(errno));
151 1.1 christos syslog(LOG_ERR, "%s: %m", CONFIG);
152 1.1 christos return;
153 1.1 christos }
154 1.1 christos
155 1.1 christos /* First call to nextline will advance line_number to 1 */
156 1.1 christos line_number = 0;
157 1.1 christos
158 1.1 christos /* Start parsing at the beginning of the first line */
159 1.1 christos current_pos = nextline(fconfig);
160 1.1 christos
161 1.1 christos while ((cp = getconfigent(¤t_pos)) != NULL) {
162 1.1 christos /* Find an already existing service definition */
163 1.1 christos for (sep = servtab; sep != NULL; sep = sep->se_next)
164 1.1 christos if (is_same_service(sep, cp))
165 1.1 christos break;
166 1.1 christos if (sep != NULL) {
167 1.1 christos int i;
168 1.1 christos
169 1.1 christos #define SWAP(type, a, b) {type c = a; a = b; b = c;}
170 1.1 christos
171 1.1 christos /*
172 1.1 christos * sep->se_wait may be holding the pid of a daemon
173 1.1 christos * that we're waiting for. If so, don't overwrite
174 1.1 christos * it unless the config file explicitly says don't
175 1.1 christos * wait.
176 1.1 christos */
177 1.1 christos if (cp->se_bi == 0 &&
178 1.1 christos (sep->se_wait == 1 || cp->se_wait == 0))
179 1.1 christos sep->se_wait = cp->se_wait;
180 1.1 christos SWAP(char *, sep->se_user, cp->se_user);
181 1.1 christos SWAP(char *, sep->se_group, cp->se_group);
182 1.1 christos SWAP(char *, sep->se_server, cp->se_server);
183 1.1 christos for (i = 0; i < MAXARGV; i++)
184 1.1 christos SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
185 1.1 christos #ifdef IPSEC
186 1.1 christos SWAP(char *, sep->se_policy, cp->se_policy);
187 1.1 christos #endif
188 1.1 christos SWAP(service_type, cp->se_type, sep->se_type);
189 1.1 christos SWAP(size_t, cp->se_service_max, sep->se_service_max);
190 1.1 christos SWAP(size_t, cp->se_ip_max, sep->se_ip_max);
191 1.1 christos #undef SWAP
192 1.1 christos if (isrpcservice(sep))
193 1.1 christos unregister_rpc(sep);
194 1.1 christos sep->se_rpcversl = cp->se_rpcversl;
195 1.1 christos sep->se_rpcversh = cp->se_rpcversh;
196 1.1 christos freeconfig(cp);
197 1.1 christos #ifdef DEBUG_ENABLE
198 1.1 christos if (debug)
199 1.1 christos print_service("REDO", sep);
200 1.1 christos #endif
201 1.1 christos } else {
202 1.1 christos sep = enter(cp);
203 1.1 christos #ifdef DEBUG_ENABLE
204 1.1 christos if (debug)
205 1.1 christos print_service("ADD ", sep);
206 1.1 christos #endif
207 1.1 christos }
208 1.1 christos sep->se_checked = 1;
209 1.1 christos
210 1.1 christos /*
211 1.1 christos * Remainder of config(void) checks validity of servtab options
212 1.1 christos * and sets up the service by setting up sockets
213 1.1 christos * (in setup(servtab)).
214 1.1 christos */
215 1.1 christos switch (sep->se_family) {
216 1.1 christos case AF_LOCAL:
217 1.1 christos if (sep->se_fd != -1)
218 1.1 christos break;
219 1.1 christos n = strlen(sep->se_service);
220 1.1 christos if (n >= sizeof(sep->se_ctrladdr_un.sun_path)) {
221 1.1 christos syslog(LOG_ERR, "%s/%s: address too long",
222 1.1 christos sep->se_service, sep->se_proto);
223 1.1 christos sep->se_checked = 0;
224 1.1 christos continue;
225 1.1 christos }
226 1.1 christos (void)unlink(sep->se_service);
227 1.1 christos strlcpy(sep->se_ctrladdr_un.sun_path,
228 1.1 christos sep->se_service, n + 1);
229 1.1 christos sep->se_ctrladdr_un.sun_family = AF_LOCAL;
230 1.1 christos sep->se_ctrladdr_size = (socklen_t)(n +
231 1.1 christos sizeof(sep->se_ctrladdr_un) -
232 1.1 christos sizeof(sep->se_ctrladdr_un.sun_path));
233 1.1 christos if (!ISMUX(sep))
234 1.1 christos setup(sep);
235 1.1 christos break;
236 1.1 christos case AF_INET:
237 1.1 christos #ifdef INET6
238 1.1 christos case AF_INET6:
239 1.1 christos #endif
240 1.1 christos {
241 1.1 christos struct addrinfo hints, *res;
242 1.1 christos char *host;
243 1.1 christos const char *port;
244 1.1 christos int error;
245 1.1 christos int s;
246 1.1 christos
247 1.1 christos /* check if the family is supported */
248 1.1 christos s = socket(sep->se_family, SOCK_DGRAM, 0);
249 1.1 christos if (s < 0) {
250 1.1 christos syslog(LOG_WARNING,
251 1.1 christos "%s/%s: %s: the address family is not "
252 1.1 christos "supported by the kernel",
253 1.1 christos sep->se_service, sep->se_proto,
254 1.1 christos sep->se_hostaddr);
255 1.1 christos sep->se_checked = false;
256 1.1 christos continue;
257 1.1 christos }
258 1.1 christos close(s);
259 1.1 christos
260 1.1 christos memset(&hints, 0, sizeof(hints));
261 1.1 christos hints.ai_family = sep->se_family;
262 1.1 christos hints.ai_socktype = sep->se_socktype;
263 1.1 christos hints.ai_flags = AI_PASSIVE;
264 1.1 christos if (strcmp(sep->se_hostaddr, "*") == 0)
265 1.1 christos host = NULL;
266 1.1 christos else
267 1.1 christos host = sep->se_hostaddr;
268 1.1 christos if (isrpcservice(sep) || ISMUX(sep))
269 1.1 christos port = "0";
270 1.1 christos else
271 1.1 christos port = sep->se_service;
272 1.1 christos error = getaddrinfo(host, port, &hints, &res);
273 1.1 christos if (error != 0) {
274 1.1 christos if (error == EAI_SERVICE) {
275 1.1 christos /* gai_strerror not friendly enough */
276 1.1 christos syslog(LOG_WARNING, SERV_FMT ": "
277 1.1 christos "unknown service",
278 1.1 christos SERV_PARAMS(sep));
279 1.1 christos } else {
280 1.1 christos syslog(LOG_ERR, SERV_FMT ": %s: %s",
281 1.1 christos SERV_PARAMS(sep),
282 1.1 christos sep->se_hostaddr,
283 1.1 christos gai_strerror(error));
284 1.1 christos }
285 1.1 christos sep->se_checked = false;
286 1.1 christos continue;
287 1.1 christos }
288 1.1 christos if (res->ai_next != NULL) {
289 1.1 christos syslog(LOG_ERR, SERV_FMT
290 1.1 christos ": %s: resolved to multiple addr",
291 1.1 christos SERV_PARAMS(sep),
292 1.1 christos sep->se_hostaddr);
293 1.1 christos sep->se_checked = false;
294 1.1 christos freeaddrinfo(res);
295 1.1 christos continue;
296 1.1 christos }
297 1.1 christos memcpy(&sep->se_ctrladdr, res->ai_addr,
298 1.1 christos res->ai_addrlen);
299 1.1 christos if (ISMUX(sep)) {
300 1.1 christos sep->se_fd = -1;
301 1.1 christos freeaddrinfo(res);
302 1.1 christos continue;
303 1.1 christos }
304 1.1 christos sep->se_ctrladdr_size = res->ai_addrlen;
305 1.1 christos freeaddrinfo(res);
306 1.1 christos #ifdef RPC
307 1.1 christos if (isrpcservice(sep)) {
308 1.1 christos struct rpcent *rp;
309 1.1 christos
310 1.1 christos sep->se_rpcprog = atoi(sep->se_service);
311 1.1 christos if (sep->se_rpcprog == 0) {
312 1.1 christos rp = getrpcbyname(sep->se_service);
313 1.1 christos if (rp == 0) {
314 1.1 christos syslog(LOG_ERR,
315 1.1 christos SERV_FMT
316 1.1 christos ": unknown service",
317 1.1 christos SERV_PARAMS(sep));
318 1.1 christos sep->se_checked = false;
319 1.1 christos continue;
320 1.1 christos }
321 1.1 christos sep->se_rpcprog = rp->r_number;
322 1.1 christos }
323 1.1 christos if (sep->se_fd == -1 && !ISMUX(sep))
324 1.1 christos setup(sep);
325 1.1 christos if (sep->se_fd != -1)
326 1.1 christos register_rpc(sep);
327 1.1 christos } else
328 1.4 dholland #endif /* RPC */
329 1.1 christos {
330 1.1 christos if (sep->se_fd >= 0)
331 1.1 christos close_sep(sep);
332 1.1 christos if (sep->se_fd == -1 && !ISMUX(sep))
333 1.1 christos setup(sep);
334 1.1 christos }
335 1.1 christos }
336 1.1 christos }
337 1.1 christos }
338 1.1 christos endconfig();
339 1.1 christos }
340 1.1 christos
341 1.1 christos static struct servtab *
342 1.1 christos enter(struct servtab *cp)
343 1.1 christos {
344 1.1 christos struct servtab *sep;
345 1.1 christos
346 1.1 christos sep = malloc(sizeof (*sep));
347 1.1 christos if (sep == NULL) {
348 1.1 christos syslog(LOG_ERR, "Out of memory.");
349 1.1 christos exit(EXIT_FAILURE);
350 1.1 christos }
351 1.1 christos *sep = *cp;
352 1.1 christos sep->se_fd = -1;
353 1.1 christos sep->se_rpcprog = -1;
354 1.1 christos sep->se_next = servtab;
355 1.1 christos servtab = sep;
356 1.1 christos return (sep);
357 1.1 christos }
358 1.1 christos
359 1.1 christos static void
360 1.1 christos endconfig(void)
361 1.1 christos {
362 1.1 christos if (fconfig != NULL) {
363 1.1 christos (void) fclose(fconfig);
364 1.1 christos fconfig = NULL;
365 1.1 christos }
366 1.1 christos if (defhost != NULL) {
367 1.1 christos free(defhost);
368 1.1 christos defhost = NULL;
369 1.1 christos }
370 1.1 christos
371 1.1 christos #ifdef IPSEC
372 1.1 christos if (policy != NULL) {
373 1.1 christos free(policy);
374 1.1 christos policy = NULL;
375 1.1 christos }
376 1.1 christos #endif
377 1.1 christos
378 1.1 christos }
379 1.1 christos
380 1.1 christos #define LOG_EARLY_ENDCONF() \
381 1.1 christos ERR("Exiting %s early. Some services will be unavailable", CONFIG)
382 1.1 christos
383 1.1 christos #define LOG_TOO_FEW_ARGS() \
384 1.1 christos ERR("Expected more arguments")
385 1.1 christos
386 1.1 christos /* Parse the next service and apply any directives, and returns it as servtab */
387 1.1 christos static struct servtab *
388 1.1 christos getconfigent(char **current_pos)
389 1.1 christos {
390 1.1 christos struct servtab *sep = &serv;
391 1.1 christos int argc, val;
392 1.1 christos char *cp, *cp0, *arg, *buf0, *buf1, *sz0, *sz1;
393 1.1 christos static char TCPMUX_TOKEN[] = "tcpmux/";
394 1.1 christos #define MUX_LEN (sizeof(TCPMUX_TOKEN)-1)
395 1.1 christos char *hostdelim;
396 1.1 christos
397 1.1 christos /*
398 1.1 christos * Pre-condition: current_pos points into line,
399 1.1 christos * line contains config line. Continue where the last getconfigent
400 1.1 christos * left off. Allows for multiple service definitions per line.
401 1.1 christos */
402 1.1 christos cp = *current_pos;
403 1.1 christos
404 1.1 christos if (/*CONSTCOND*/false) {
405 1.1 christos /*
406 1.3 andvar * Go to the next line, but only after attempting to read the
407 1.1 christos * current one! Keep reading until we find a valid definition
408 1.1 christos * or EOF.
409 1.1 christos */
410 1.1 christos more:
411 1.1 christos cp = nextline(fconfig);
412 1.1 christos }
413 1.1 christos
414 1.1 christos if (cp == NULL) {
415 1.1 christos /* EOF or I/O error, let config() know to exit the file */
416 1.1 christos return NULL;
417 1.1 christos }
418 1.1 christos
419 1.1 christos /* Comments and IPsec policies */
420 1.1 christos if (cp[0] == '#') {
421 1.1 christos #ifdef IPSEC
422 1.1 christos /* lines starting with #@ is not a comment, but the policy */
423 1.1 christos if (cp[1] == '@') {
424 1.1 christos char *p;
425 1.1 christos for (p = cp + 2; isspace((unsigned char)*p); p++)
426 1.1 christos ;
427 1.1 christos if (*p == '\0') {
428 1.1 christos if (policy)
429 1.1 christos free(policy);
430 1.1 christos policy = NULL;
431 1.1 christos } else {
432 1.1 christos if (ipsecsetup_test(p) < 0) {
433 1.1 christos ERR("Invalid IPsec policy \"%s\"", p);
434 1.1 christos LOG_EARLY_ENDCONF();
435 1.1 christos /*
436 1.1 christos * Stop reading the current config to
437 1.1 christos * prevent services from being run
438 1.1 christos * without IPsec.
439 1.1 christos */
440 1.1 christos return NULL;
441 1.1 christos } else {
442 1.1 christos if (policy)
443 1.1 christos free(policy);
444 1.1 christos policy = newstr(p);
445 1.1 christos }
446 1.1 christos }
447 1.1 christos }
448 1.1 christos #endif
449 1.1 christos
450 1.1 christos goto more;
451 1.1 christos }
452 1.1 christos
453 1.1 christos /* Parse next token: listen-addr/hostname, service-spec, .include */
454 1.1 christos arg = skip(&cp);
455 1.1 christos
456 1.1 christos if (cp == NULL) {
457 1.1 christos goto more;
458 1.1 christos }
459 1.1 christos
460 1.2 rillig if (arg[0] == '.') {
461 1.1 christos if (strcmp(&arg[1], "include") == 0) {
462 1.1 christos /* include directive */
463 1.1 christos arg = skip(&cp);
464 1.2 rillig if (arg == NULL) {
465 1.1 christos LOG_TOO_FEW_ARGS();
466 1.1 christos return NULL;
467 1.1 christos }
468 1.1 christos include_configs(arg);
469 1.1 christos goto more;
470 1.1 christos } else {
471 1.1 christos ERR("Unknown directive '%s'", &arg[1]);
472 1.1 christos goto more;
473 1.1 christos }
474 1.1 christos }
475 1.1 christos
476 1.1 christos /* After this point, we might need to store data in a servtab */
477 1.1 christos *sep = init_servtab();
478 1.1 christos
479 1.1 christos /* Check for a host name. */
480 1.1 christos hostdelim = strrchr(arg, ':');
481 1.1 christos if (hostdelim != NULL) {
482 1.1 christos *hostdelim = '\0';
483 1.1 christos if (arg[0] == '[' && hostdelim > arg && hostdelim[-1] == ']') {
484 1.1 christos hostdelim[-1] = '\0';
485 1.1 christos sep->se_hostaddr = newstr(arg + 1);
486 1.1 christos } else
487 1.1 christos sep->se_hostaddr = newstr(arg);
488 1.1 christos arg = hostdelim + 1;
489 1.1 christos /*
490 1.1 christos * If the line is of the form `host:', then just change the
491 1.1 christos * default host for the following lines.
492 1.1 christos */
493 1.1 christos if (*arg == '\0') {
494 1.1 christos arg = skip(&cp);
495 1.1 christos if (cp == NULL) {
496 1.1 christos free(defhost);
497 1.1 christos defhost = sep->se_hostaddr;
498 1.1 christos goto more;
499 1.1 christos }
500 1.1 christos }
501 1.1 christos } else {
502 1.1 christos /* No host address found, set it to NULL to indicate absence */
503 1.1 christos sep->se_hostaddr = NULL;
504 1.1 christos }
505 1.1 christos if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
506 1.1 christos char *c = arg + MUX_LEN;
507 1.1 christos if (*c == '+') {
508 1.1 christos sep->se_type = MUXPLUS_TYPE;
509 1.1 christos c++;
510 1.1 christos } else
511 1.1 christos sep->se_type = MUX_TYPE;
512 1.1 christos sep->se_service = newstr(c);
513 1.1 christos } else {
514 1.1 christos sep->se_service = newstr(arg);
515 1.1 christos sep->se_type = NORM_TYPE;
516 1.1 christos }
517 1.1 christos
518 1.1 christos DPRINTCONF("Found service definition '%s'", sep->se_service);
519 1.1 christos
520 1.1 christos /* on/off/socktype */
521 1.1 christos arg = skip(&cp);
522 1.1 christos if (arg == NULL) {
523 1.1 christos LOG_TOO_FEW_ARGS();
524 1.1 christos freeconfig(sep);
525 1.1 christos goto more;
526 1.1 christos }
527 1.1 christos
528 1.1 christos /* Check for new v2 syntax */
529 1.1 christos if (strcmp(arg, "on") == 0 || strncmp(arg, "on#", 3) == 0) {
530 1.1 christos
531 1.1 christos if (arg[2] == '#') {
532 1.1 christos cp = nextline(fconfig);
533 1.1 christos }
534 1.1 christos
535 1.1 christos switch(parse_syntax_v2(sep, &cp)) {
536 1.1 christos case V2_SUCCESS:
537 1.1 christos *current_pos = cp;
538 1.1 christos return sep;
539 1.1 christos case V2_SKIP:
540 1.1 christos /*
541 1.1 christos * Skip invalid definitions, freeconfig is called in
542 1.1 christos * parse_v2.c
543 1.1 christos */
544 1.1 christos *current_pos = cp;
545 1.1 christos freeconfig(sep);
546 1.1 christos goto more;
547 1.1 christos case V2_ERROR:
548 1.1 christos /*
549 1.1 christos * Unrecoverable error, stop reading. freeconfig
550 1.1 christos * is called in parse_v2.c
551 1.1 christos */
552 1.1 christos LOG_EARLY_ENDCONF();
553 1.1 christos freeconfig(sep);
554 1.1 christos return NULL;
555 1.1 christos }
556 1.1 christos } else if (strcmp(arg, "off") == 0 || strncmp(arg, "off#", 4) == 0) {
557 1.1 christos
558 1.1 christos if (arg[3] == '#') {
559 1.1 christos cp = nextline(fconfig);
560 1.1 christos }
561 1.1 christos
562 1.1 christos /* Parse syntax the same as with 'on', but ignore the result */
563 1.1 christos switch(parse_syntax_v2(sep, &cp)) {
564 1.1 christos case V2_SUCCESS:
565 1.1 christos case V2_SKIP:
566 1.1 christos *current_pos = cp;
567 1.1 christos freeconfig(sep);
568 1.1 christos goto more;
569 1.1 christos case V2_ERROR:
570 1.1 christos /* Unrecoverable error, stop reading */
571 1.1 christos LOG_EARLY_ENDCONF();
572 1.1 christos freeconfig(sep);
573 1.1 christos return NULL;
574 1.1 christos }
575 1.1 christos } else {
576 1.1 christos /* continue parsing v1 */
577 1.1 christos parse_socktype(arg, sep);
578 1.1 christos if (sep->se_socktype == SOCK_STREAM) {
579 1.1 christos parse_accept_filter(arg, sep);
580 1.1 christos }
581 1.1 christos if (sep->se_hostaddr == NULL) {
582 1.1 christos /* Set host to current default */
583 1.1 christos sep->se_hostaddr = newstr(defhost);
584 1.1 christos }
585 1.1 christos }
586 1.1 christos
587 1.1 christos /* protocol */
588 1.1 christos arg = skip(&cp);
589 1.1 christos if (arg == NULL) {
590 1.1 christos LOG_TOO_FEW_ARGS();
591 1.1 christos freeconfig(sep);
592 1.1 christos goto more;
593 1.1 christos }
594 1.1 christos if (sep->se_type == NORM_TYPE &&
595 1.1 christos strncmp(arg, "faith/", strlen("faith/")) == 0) {
596 1.1 christos arg += strlen("faith/");
597 1.1 christos sep->se_type = FAITH_TYPE;
598 1.1 christos }
599 1.1 christos sep->se_proto = newstr(arg);
600 1.1 christos
601 1.1 christos #define MALFORMED(arg) \
602 1.1 christos do { \
603 1.1 christos ERR("%s: malformed buffer size option `%s'", \
604 1.1 christos sep->se_service, (arg)); \
605 1.1 christos freeconfig(sep); \
606 1.1 christos goto more; \
607 1.1 christos } while (false)
608 1.1 christos
609 1.1 christos #define GETVAL(arg) \
610 1.1 christos do { \
611 1.1 christos if (!isdigit((unsigned char)*(arg))) \
612 1.1 christos MALFORMED(arg); \
613 1.1 christos val = (int)strtol((arg), &cp0, 10); \
614 1.1 christos if (cp0 != NULL) { \
615 1.1 christos if (cp0[1] != '\0') \
616 1.1 christos MALFORMED((arg)); \
617 1.1 christos if (cp0[0] == 'k') \
618 1.1 christos val *= 1024; \
619 1.1 christos if (cp0[0] == 'm') \
620 1.1 christos val *= 1024 * 1024; \
621 1.1 christos } \
622 1.1 christos if (val < 1) { \
623 1.1 christos ERR("%s: invalid buffer size `%s'", \
624 1.1 christos sep->se_service, (arg)); \
625 1.1 christos freeconfig(sep); \
626 1.1 christos goto more; \
627 1.1 christos } \
628 1.1 christos } while (false)
629 1.1 christos
630 1.1 christos #define ASSIGN(arg) \
631 1.1 christos do { \
632 1.1 christos if (strcmp((arg), "sndbuf") == 0) \
633 1.1 christos sep->se_sndbuf = val; \
634 1.1 christos else if (strcmp((arg), "rcvbuf") == 0) \
635 1.1 christos sep->se_rcvbuf = val; \
636 1.1 christos else \
637 1.1 christos MALFORMED((arg)); \
638 1.1 christos } while (false)
639 1.1 christos
640 1.1 christos /*
641 1.1 christos * Extract the send and receive buffer sizes before parsing
642 1.1 christos * the protocol.
643 1.1 christos */
644 1.1 christos sep->se_sndbuf = sep->se_rcvbuf = 0;
645 1.1 christos buf0 = buf1 = sz0 = sz1 = NULL;
646 1.1 christos if ((buf0 = strchr(sep->se_proto, ',')) != NULL) {
647 1.1 christos /* Not meaningful for Tcpmux services. */
648 1.1 christos if (ISMUX(sep)) {
649 1.1 christos ERR("%s: can't specify buffer sizes for "
650 1.1 christos "tcpmux services", sep->se_service);
651 1.1 christos goto more;
652 1.1 christos }
653 1.1 christos
654 1.1 christos /* Skip the , */
655 1.1 christos *buf0++ = '\0';
656 1.1 christos
657 1.1 christos /* Check to see if another socket buffer size was specified. */
658 1.1 christos if ((buf1 = strchr(buf0, ',')) != NULL) {
659 1.1 christos /* Skip the , */
660 1.1 christos *buf1++ = '\0';
661 1.1 christos
662 1.1 christos /* Make sure a 3rd one wasn't specified. */
663 1.1 christos if (strchr(buf1, ',') != NULL) {
664 1.1 christos ERR("%s: too many buffer sizes",
665 1.1 christos sep->se_service);
666 1.1 christos goto more;
667 1.1 christos }
668 1.1 christos
669 1.1 christos /* Locate the size. */
670 1.1 christos if ((sz1 = strchr(buf1, '=')) == NULL)
671 1.1 christos MALFORMED(buf1);
672 1.1 christos
673 1.1 christos /* Skip the = */
674 1.1 christos *sz1++ = '\0';
675 1.1 christos }
676 1.1 christos
677 1.1 christos /* Locate the size. */
678 1.1 christos if ((sz0 = strchr(buf0, '=')) == NULL)
679 1.1 christos MALFORMED(buf0);
680 1.1 christos
681 1.1 christos /* Skip the = */
682 1.1 christos *sz0++ = '\0';
683 1.1 christos
684 1.1 christos GETVAL(sz0);
685 1.1 christos ASSIGN(buf0);
686 1.1 christos
687 1.1 christos if (buf1 != NULL) {
688 1.1 christos GETVAL(sz1);
689 1.1 christos ASSIGN(buf1);
690 1.1 christos }
691 1.1 christos }
692 1.1 christos
693 1.1 christos #undef ASSIGN
694 1.1 christos #undef GETVAL
695 1.1 christos #undef MALFORMED
696 1.1 christos
697 1.1 christos if (parse_protocol(sep)) {
698 1.1 christos freeconfig(sep);
699 1.1 christos goto more;
700 1.1 christos }
701 1.1 christos
702 1.1 christos /* wait/nowait:max */
703 1.1 christos arg = skip(&cp);
704 1.1 christos if (arg == NULL) {
705 1.1 christos LOG_TOO_FEW_ARGS();
706 1.1 christos freeconfig(sep);
707 1.1 christos goto more;
708 1.1 christos }
709 1.1 christos
710 1.1 christos /* Rate limiting parsing */ {
711 1.1 christos char *cp1;
712 1.1 christos if ((cp1 = strchr(arg, ':')) == NULL)
713 1.1 christos cp1 = strchr(arg, '.');
714 1.1 christos if (cp1 != NULL) {
715 1.1 christos int rstatus;
716 1.1 christos *cp1++ = '\0';
717 1.1 christos sep->se_service_max = (size_t)strtou(cp1, NULL, 10, 0,
718 1.1 christos SERVTAB_COUNT_MAX, &rstatus);
719 1.1 christos
720 1.1 christos if (rstatus != 0) {
721 1.1 christos if (rstatus != ERANGE) {
722 1.1 christos /* For compatibility w/ atoi parsing */
723 1.1 christos sep->se_service_max = 0;
724 1.1 christos }
725 1.1 christos
726 1.1 christos WRN("Improper \"max\" value '%s', "
727 1.1 christos "using '%zu' instead: %s",
728 1.1 christos cp1,
729 1.1 christos sep->se_service_max,
730 1.1 christos strerror(rstatus));
731 1.1 christos }
732 1.1 christos
733 1.1 christos } else
734 1.1 christos sep->se_service_max = TOOMANY;
735 1.1 christos }
736 1.1 christos if (parse_wait(sep, strcmp(arg, "wait") == 0)) {
737 1.1 christos freeconfig(sep);
738 1.1 christos goto more;
739 1.1 christos }
740 1.1 christos
741 1.1 christos /* Parse user:group token */
742 1.1 christos arg = skip(&cp);
743 1.2 rillig if (arg == NULL) {
744 1.1 christos LOG_TOO_FEW_ARGS();
745 1.1 christos freeconfig(sep);
746 1.1 christos goto more;
747 1.1 christos }
748 1.1 christos char* separator = strchr(arg, ':');
749 1.1 christos if (separator == NULL) {
750 1.1 christos /* Backwards compatibility, allow dot instead of colon */
751 1.1 christos separator = strchr(arg, '.');
752 1.1 christos }
753 1.1 christos
754 1.1 christos if (separator == NULL) {
755 1.1 christos /* Only user was specified */
756 1.1 christos sep->se_group = NULL;
757 1.1 christos } else {
758 1.1 christos *separator = '\0';
759 1.1 christos sep->se_group = newstr(separator + 1);
760 1.1 christos }
761 1.1 christos
762 1.1 christos sep->se_user = newstr(arg);
763 1.1 christos
764 1.1 christos /* Parser server-program (path to binary or "internal") */
765 1.1 christos arg = skip(&cp);
766 1.1 christos if (arg == NULL) {
767 1.1 christos LOG_TOO_FEW_ARGS();
768 1.1 christos freeconfig(sep);
769 1.1 christos goto more;
770 1.1 christos }
771 1.1 christos if (parse_server(sep, arg)) {
772 1.1 christos freeconfig(sep);
773 1.1 christos goto more;
774 1.1 christos }
775 1.1 christos
776 1.1 christos argc = 0;
777 1.1 christos for (arg = skip(&cp); cp != NULL; arg = skip(&cp)) {
778 1.1 christos if (argc < MAXARGV)
779 1.1 christos sep->se_argv[argc++] = newstr(arg);
780 1.1 christos }
781 1.1 christos while (argc <= MAXARGV)
782 1.1 christos sep->se_argv[argc++] = NULL;
783 1.1 christos #ifdef IPSEC
784 1.1 christos sep->se_policy = policy != NULL ? newstr(policy) : NULL;
785 1.1 christos #endif
786 1.1 christos /* getconfigent read a positional service def, move to next line */
787 1.1 christos *current_pos = nextline(fconfig);
788 1.1 christos return (sep);
789 1.1 christos }
790 1.1 christos
791 1.1 christos void
792 1.1 christos freeconfig(struct servtab *cp)
793 1.1 christos {
794 1.1 christos int i;
795 1.1 christos
796 1.1 christos free(cp->se_hostaddr);
797 1.1 christos free(cp->se_service);
798 1.1 christos free(cp->se_proto);
799 1.1 christos free(cp->se_user);
800 1.1 christos free(cp->se_group);
801 1.1 christos free(cp->se_server);
802 1.1 christos for (i = 0; i < MAXARGV; i++)
803 1.1 christos free(cp->se_argv[i]);
804 1.1 christos #ifdef IPSEC
805 1.1 christos free(cp->se_policy);
806 1.1 christos #endif
807 1.1 christos }
808 1.1 christos
809 1.1 christos /*
810 1.1 christos * Get next token *in the current service definition* from config file.
811 1.1 christos * Allows multi-line parse if single space or single tab-indented.
812 1.1 christos * Things in quotes are considered single token.
813 1.1 christos * Advances cp to next token.
814 1.1 christos */
815 1.1 christos static char *
816 1.1 christos skip(char **cpp)
817 1.1 christos {
818 1.1 christos char *cp = *cpp;
819 1.1 christos char *start;
820 1.1 christos char quote;
821 1.1 christos
822 1.1 christos if (*cpp == NULL)
823 1.1 christos return (NULL);
824 1.1 christos
825 1.1 christos again:
826 1.1 christos while (*cp == ' ' || *cp == '\t')
827 1.1 christos cp++;
828 1.1 christos if (*cp == '\0') {
829 1.1 christos int c;
830 1.1 christos
831 1.1 christos c = getc(fconfig);
832 1.1 christos (void) ungetc(c, fconfig);
833 1.1 christos if (c == ' ' || c == '\t')
834 1.1 christos if ((cp = nextline(fconfig)) != NULL)
835 1.1 christos goto again;
836 1.1 christos *cpp = NULL;
837 1.1 christos return (NULL);
838 1.1 christos }
839 1.1 christos start = cp;
840 1.1 christos /* Parse shell-style quotes */
841 1.1 christos quote = '\0';
842 1.1 christos while (*cp != '\0' && (quote != '\0' || (*cp != ' ' && *cp != '\t'))) {
843 1.1 christos if (*cp == '\'' || *cp == '"') {
844 1.1 christos if (quote != '\0' && *cp != quote)
845 1.1 christos cp++;
846 1.1 christos else {
847 1.1 christos if (quote != '\0')
848 1.1 christos quote = '\0';
849 1.1 christos else
850 1.1 christos quote = *cp;
851 1.1 christos memmove(cp, cp+1, strlen(cp));
852 1.1 christos }
853 1.1 christos } else
854 1.1 christos cp++;
855 1.1 christos }
856 1.1 christos if (*cp != '\0')
857 1.1 christos *cp++ = '\0';
858 1.1 christos *cpp = cp;
859 1.1 christos return (start);
860 1.1 christos }
861 1.1 christos
862 1.1 christos char *
863 1.1 christos nextline(FILE *fd)
864 1.1 christos {
865 1.1 christos char *cp;
866 1.1 christos
867 1.1 christos if (fgets(line, (int)sizeof(line), fd) == NULL) {
868 1.1 christos if (ferror(fd) != 0) {
869 1.1 christos ERR("Error when reading next line: %s",
870 1.1 christos strerror(errno));
871 1.1 christos }
872 1.1 christos return NULL;
873 1.1 christos }
874 1.1 christos cp = strchr(line, '\n');
875 1.1 christos if (cp != NULL)
876 1.1 christos *cp = '\0';
877 1.1 christos line_number++;
878 1.1 christos return line;
879 1.1 christos }
880 1.1 christos
881 1.1 christos char *
882 1.1 christos newstr(const char *cp)
883 1.1 christos {
884 1.1 christos char *dp;
885 1.1 christos if ((dp = strdup((cp != NULL) ? cp : "")) != NULL)
886 1.1 christos return (dp);
887 1.1 christos syslog(LOG_ERR, "strdup: %m");
888 1.1 christos exit(EXIT_FAILURE);
889 1.1 christos /*NOTREACHED*/
890 1.1 christos }
891 1.1 christos
892 1.1 christos #ifdef DEBUG_ENABLE
893 1.1 christos /*
894 1.1 christos * print_service:
895 1.1 christos * Dump relevant information to stderr
896 1.1 christos */
897 1.1 christos static void
898 1.1 christos print_service(const char *action, struct servtab *sep)
899 1.1 christos {
900 1.1 christos
901 1.1 christos if (isrpcservice(sep))
902 1.1 christos fprintf(stderr,
903 1.1 christos "%s: %s rpcprog=%d, rpcvers = %d/%d, proto=%s, "
904 1.1 christos "wait.max=%d.%zu, "
905 1.1 christos "user:group=%s:%s builtin=%lx server=%s"
906 1.1 christos #ifdef IPSEC
907 1.1 christos " policy=\"%s\""
908 1.1 christos #endif
909 1.1 christos "\n",
910 1.1 christos action, sep->se_service,
911 1.1 christos sep->se_rpcprog, sep->se_rpcversh, sep->se_rpcversl,
912 1.1 christos sep->se_proto, sep->se_wait, sep->se_service_max,
913 1.1 christos sep->se_user, sep->se_group,
914 1.1 christos (long)sep->se_bi, sep->se_server
915 1.1 christos #ifdef IPSEC
916 1.1 christos , (sep->se_policy != NULL ? sep->se_policy : "")
917 1.1 christos #endif
918 1.1 christos );
919 1.1 christos else
920 1.1 christos fprintf(stderr,
921 1.1 christos "%s: %s:%s proto=%s%s, wait.max=%d.%zu, user:group=%s:%s "
922 1.1 christos "builtin=%lx "
923 1.1 christos "server=%s"
924 1.1 christos #ifdef IPSEC
925 1.1 christos " policy=%s"
926 1.1 christos #endif
927 1.1 christos "\n",
928 1.1 christos action, sep->se_hostaddr, sep->se_service,
929 1.1 christos sep->se_type == FAITH_TYPE ? "faith/" : "",
930 1.1 christos sep->se_proto,
931 1.1 christos sep->se_wait, sep->se_service_max, sep->se_user,
932 1.1 christos sep->se_group, (long)sep->se_bi, sep->se_server
933 1.1 christos #ifdef IPSEC
934 1.1 christos , (sep->se_policy != NULL ? sep->se_policy : "")
935 1.1 christos #endif
936 1.1 christos );
937 1.1 christos }
938 1.1 christos #endif
939 1.1 christos
940 1.1 christos void
941 1.1 christos config_root(void)
942 1.1 christos {
943 1.1 christos struct servtab *sep;
944 1.1 christos /* Uncheck services */
945 1.1 christos for (sep = servtab; sep != NULL; sep = sep->se_next) {
946 1.1 christos sep->se_checked = false;
947 1.1 christos }
948 1.1 christos defhost = newstr("*");
949 1.1 christos #ifdef IPSEC
950 1.1 christos policy = NULL;
951 1.1 christos #endif
952 1.1 christos fconfig = NULL;
953 1.1 christos config();
954 1.1 christos purge_unchecked();
955 1.1 christos }
956 1.1 christos
957 1.1 christos static void
958 1.1 christos purge_unchecked(void)
959 1.1 christos {
960 1.1 christos struct servtab *sep, **sepp = &servtab;
961 1.1 christos int servtab_count = 0;
962 1.1 christos while ((sep = *sepp) != NULL) {
963 1.1 christos if (sep->se_checked) {
964 1.1 christos sepp = &sep->se_next;
965 1.1 christos servtab_count++;
966 1.1 christos continue;
967 1.1 christos }
968 1.1 christos *sepp = sep->se_next;
969 1.1 christos if (sep->se_fd >= 0)
970 1.1 christos close_sep(sep);
971 1.1 christos if (isrpcservice(sep))
972 1.1 christos unregister_rpc(sep);
973 1.1 christos if (sep->se_family == AF_LOCAL)
974 1.1 christos (void)unlink(sep->se_service);
975 1.1 christos #ifdef DEBUG_ENABLE
976 1.1 christos if (debug)
977 1.1 christos print_service("FREE", sep);
978 1.1 christos #endif
979 1.1 christos freeconfig(sep);
980 1.1 christos free(sep);
981 1.1 christos }
982 1.1 christos DPRINTF("%d service(s) loaded.", servtab_count);
983 1.1 christos }
984 1.1 christos
985 1.1 christos static bool
986 1.1 christos is_same_service(const struct servtab *sep, const struct servtab *cp)
987 1.1 christos {
988 1.1 christos return
989 1.1 christos strcmp(sep->se_service, cp->se_service) == 0 &&
990 1.1 christos strcmp(sep->se_hostaddr, cp->se_hostaddr) == 0 &&
991 1.1 christos strcmp(sep->se_proto, cp->se_proto) == 0 &&
992 1.1 christos sep->se_family == cp->se_family &&
993 1.1 christos ISMUX(sep) == ISMUX(cp);
994 1.1 christos }
995 1.1 christos
996 1.1 christos int
997 1.1 christos parse_protocol(struct servtab *sep)
998 1.1 christos {
999 1.1 christos int val;
1000 1.1 christos
1001 1.1 christos if (strcmp(sep->se_proto, "unix") == 0) {
1002 1.1 christos sep->se_family = AF_LOCAL;
1003 1.1 christos } else {
1004 1.1 christos val = (int)strlen(sep->se_proto);
1005 1.1 christos if (val == 0) {
1006 1.1 christos ERR("%s: invalid protocol specified",
1007 1.1 christos sep->se_service);
1008 1.1 christos return -1;
1009 1.1 christos }
1010 1.1 christos val = sep->se_proto[val - 1];
1011 1.1 christos switch (val) {
1012 1.1 christos case '4': /*tcp4 or udp4*/
1013 1.1 christos sep->se_family = AF_INET;
1014 1.1 christos break;
1015 1.1 christos #ifdef INET6
1016 1.1 christos case '6': /*tcp6 or udp6*/
1017 1.1 christos sep->se_family = AF_INET6;
1018 1.1 christos break;
1019 1.1 christos #endif
1020 1.1 christos default:
1021 1.1 christos /*
1022 1.1 christos * Use 'default' IP version which is IPv4, may
1023 1.1 christos * eventually be changed to AF_INET6
1024 1.1 christos */
1025 1.1 christos sep->se_family = AF_INET;
1026 1.1 christos break;
1027 1.1 christos }
1028 1.1 christos if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
1029 1.1 christos #ifdef RPC
1030 1.1 christos char *cp1, *ccp;
1031 1.1 christos cp1 = strchr(sep->se_service, '/');
1032 1.1 christos if (cp1 == 0) {
1033 1.1 christos ERR("%s: no rpc version",
1034 1.1 christos sep->se_service);
1035 1.1 christos return -1;
1036 1.1 christos }
1037 1.1 christos *cp1++ = '\0';
1038 1.1 christos sep->se_rpcversl = sep->se_rpcversh =
1039 1.1 christos (int)strtol(cp1, &ccp, 0);
1040 1.1 christos if (ccp == cp1) {
1041 1.1 christos badafterall:
1042 1.1 christos ERR("%s/%s: bad rpc version",
1043 1.1 christos sep->se_service, cp1);
1044 1.1 christos return -1;
1045 1.1 christos }
1046 1.1 christos if (*ccp == '-') {
1047 1.1 christos cp1 = ccp + 1;
1048 1.1 christos sep->se_rpcversh = (int)strtol(cp1, &ccp, 0);
1049 1.1 christos if (ccp == cp1)
1050 1.1 christos goto badafterall;
1051 1.1 christos }
1052 1.1 christos #else
1053 1.1 christos ERR("%s: rpc services not supported",
1054 1.1 christos sep->se_service);
1055 1.1 christos return -1;
1056 1.1 christos #endif /* RPC */
1057 1.1 christos }
1058 1.1 christos }
1059 1.1 christos return 0;
1060 1.1 christos }
1061 1.1 christos
1062 1.1 christos int
1063 1.1 christos parse_wait(struct servtab *sep, int wait)
1064 1.1 christos {
1065 1.1 christos if (!ISMUX(sep)) {
1066 1.1 christos sep->se_wait = wait;
1067 1.1 christos return 0;
1068 1.1 christos }
1069 1.1 christos /*
1070 1.1 christos * Silently enforce "nowait" for TCPMUX services since
1071 1.1 christos * they don't have an assigned port to listen on.
1072 1.1 christos */
1073 1.1 christos sep->se_wait = 0;
1074 1.1 christos
1075 1.1 christos if (strncmp(sep->se_proto, "tcp", 3)) {
1076 1.1 christos ERR("bad protocol for tcpmux service %s",
1077 1.1 christos sep->se_service);
1078 1.1 christos return -1;
1079 1.1 christos }
1080 1.1 christos if (sep->se_socktype != SOCK_STREAM) {
1081 1.1 christos ERR("bad socket type for tcpmux service %s",
1082 1.1 christos sep->se_service);
1083 1.1 christos return -1;
1084 1.1 christos }
1085 1.1 christos return 0;
1086 1.1 christos }
1087 1.1 christos
1088 1.1 christos int
1089 1.1 christos parse_server(struct servtab *sep, const char *arg)
1090 1.1 christos {
1091 1.1 christos sep->se_server = newstr(arg);
1092 1.1 christos if (strcmp(sep->se_server, "internal") != 0) {
1093 1.1 christos sep->se_bi = NULL;
1094 1.1 christos return 0;
1095 1.1 christos }
1096 1.1 christos
1097 1.1 christos if (!try_biltin(sep)) {
1098 1.1 christos ERR("Internal service %s unknown", sep->se_service);
1099 1.1 christos return -1;
1100 1.1 christos }
1101 1.1 christos return 0;
1102 1.1 christos }
1103 1.1 christos
1104 1.1 christos /* TODO test to make sure accept filter still works */
1105 1.1 christos void
1106 1.1 christos parse_accept_filter(char *arg, struct servtab *sep)
1107 1.1 christos {
1108 1.1 christos char *accf, *accf_arg;
1109 1.1 christos /* one and only one accept filter */
1110 1.1 christos accf = strchr(arg, ':');
1111 1.1 christos if (accf == NULL)
1112 1.1 christos return;
1113 1.1 christos if (accf != strrchr(arg, ':') || *(accf + 1) == '\0') {
1114 1.1 christos /* more than one || nothing beyond */
1115 1.1 christos sep->se_socktype = -1;
1116 1.1 christos return;
1117 1.1 christos }
1118 1.1 christos
1119 1.1 christos accf++; /* skip delimiter */
1120 1.1 christos strlcpy(sep->se_accf.af_name, accf, sizeof(sep->se_accf.af_name));
1121 1.1 christos accf_arg = strchr(accf, ',');
1122 1.1 christos if (accf_arg == NULL) /* zero or one arg, no more */
1123 1.1 christos return;
1124 1.1 christos
1125 1.1 christos if (strrchr(accf, ',') != accf_arg) {
1126 1.1 christos sep->se_socktype = -1;
1127 1.1 christos } else {
1128 1.1 christos accf_arg++;
1129 1.1 christos strlcpy(sep->se_accf.af_arg, accf_arg,
1130 1.1 christos sizeof(sep->se_accf.af_arg));
1131 1.1 christos }
1132 1.1 christos }
1133 1.1 christos
1134 1.1 christos void
1135 1.1 christos parse_socktype(char* arg, struct servtab* sep)
1136 1.1 christos {
1137 1.1 christos /* stream socket may have an accept filter, only check first chars */
1138 1.1 christos if (strncmp(arg, "stream", sizeof("stream") - 1) == 0)
1139 1.1 christos sep->se_socktype = SOCK_STREAM;
1140 1.1 christos else if (strcmp(arg, "dgram") == 0)
1141 1.1 christos sep->se_socktype = SOCK_DGRAM;
1142 1.1 christos else if (strcmp(arg, "rdm") == 0)
1143 1.1 christos sep->se_socktype = SOCK_RDM;
1144 1.1 christos else if (strcmp(arg, "seqpacket") == 0)
1145 1.1 christos sep->se_socktype = SOCK_SEQPACKET;
1146 1.1 christos else if (strcmp(arg, "raw") == 0)
1147 1.1 christos sep->se_socktype = SOCK_RAW;
1148 1.1 christos else
1149 1.1 christos sep->se_socktype = -1;
1150 1.1 christos }
1151 1.1 christos
1152 1.1 christos static struct servtab
1153 1.1 christos init_servtab(void)
1154 1.1 christos {
1155 1.1 christos /* This does not set every field to default. See enter() as well */
1156 1.1 christos return (struct servtab) {
1157 1.1 christos /*
1158 1.1 christos * Set se_max to non-zero so uninitialized value is not
1159 1.1 christos * a valid value. Useful in v2 syntax parsing.
1160 1.1 christos */
1161 1.1 christos .se_service_max = SERVTAB_UNSPEC_SIZE_T,
1162 1.1 christos .se_ip_max = SERVTAB_UNSPEC_SIZE_T,
1163 1.1 christos .se_wait = SERVTAB_UNSPEC_VAL,
1164 1.1 christos .se_socktype = SERVTAB_UNSPEC_VAL,
1165 1.1 christos .se_rl_ip_list = SLIST_HEAD_INITIALIZER(se_ip_list_head)
1166 1.1 christos /* All other fields initialized to 0 or null */
1167 1.1 christos };
1168 1.1 christos }
1169 1.1 christos
1170 1.1 christos /* Include directives bookkeeping structure */
1171 1.1 christos struct file_list {
1172 1.1 christos /* Absolute path used for checking for circular references */
1173 1.1 christos char *abs;
1174 1.1 christos /* Pointer to the absolute path of the parent config file,
1175 1.1 christos * on the stack */
1176 1.1 christos struct file_list *next;
1177 1.1 christos } *file_list_head;
1178 1.1 christos
1179 1.1 christos static void
1180 1.1 christos include_configs(char *pattern)
1181 1.1 christos {
1182 1.1 christos /* Allocate global per-config state on the thread stack */
1183 1.1 christos const char* save_CONFIG;
1184 1.1 christos FILE *save_fconfig;
1185 1.1 christos size_t save_line_number;
1186 1.1 christos char *save_defhost;
1187 1.1 christos struct file_list new_file;
1188 1.1 christos #ifdef IPSEC
1189 1.1 christos char *save_policy;
1190 1.1 christos #endif
1191 1.1 christos
1192 1.1 christos /* Store current globals on the stack */
1193 1.1 christos save_CONFIG = CONFIG;
1194 1.1 christos save_fconfig = fconfig;
1195 1.1 christos save_line_number = line_number;
1196 1.1 christos save_defhost = defhost;
1197 1.1 christos new_file.abs = realpath(CONFIG, NULL);
1198 1.1 christos new_file.next = file_list_head;
1199 1.1 christos #ifdef IPSEC
1200 1.1 christos save_policy = policy;
1201 1.1 christos #endif
1202 1.1 christos /* Put new_file at the top of the config stack */
1203 1.1 christos file_list_head = &new_file;
1204 1.1 christos read_glob_configs(pattern);
1205 1.1 christos free(new_file.abs);
1206 1.1 christos /* Pop new_file off the stack */
1207 1.1 christos file_list_head = new_file.next;
1208 1.1 christos
1209 1.1 christos /* Restore global per-config state */
1210 1.1 christos CONFIG = save_CONFIG;
1211 1.1 christos fconfig = save_fconfig;
1212 1.1 christos line_number = save_line_number;
1213 1.1 christos defhost = save_defhost;
1214 1.1 christos #ifdef IPSEC
1215 1.1 christos policy = save_policy;
1216 1.1 christos #endif
1217 1.1 christos }
1218 1.1 christos
1219 1.1 christos static void
1220 1.1 christos prepare_next_config(const char *file_name)
1221 1.1 christos {
1222 1.1 christos /* Setup new state that is normally only done in main */
1223 1.1 christos CONFIG = file_name;
1224 1.1 christos
1225 1.1 christos /* Inherit default host and IPsec policy */
1226 1.1 christos defhost = newstr(defhost);
1227 1.1 christos
1228 1.1 christos #ifdef IPSEC
1229 1.1 christos policy = (policy == NULL) ? NULL : newstr(policy);
1230 1.1 christos #endif
1231 1.1 christos }
1232 1.1 christos
1233 1.1 christos static void
1234 1.1 christos read_glob_configs(char *pattern)
1235 1.1 christos {
1236 1.1 christos glob_t results;
1237 1.1 christos char *full_pattern;
1238 1.1 christos int glob_result;
1239 1.1 christos full_pattern = gen_file_pattern(CONFIG, pattern);
1240 1.1 christos
1241 1.1 christos DPRINTCONF("Found include directive '%s'", full_pattern);
1242 1.1 christos
1243 1.1 christos glob_result = glob(full_pattern, GLOB_NOSORT, glob_error, &results);
1244 1.1 christos switch(glob_result) {
1245 1.1 christos case 0:
1246 1.1 christos /* No glob errors */
1247 1.1 christos break;
1248 1.1 christos case GLOB_ABORTED:
1249 1.1 christos ERR("Error while searching for include files");
1250 1.1 christos break;
1251 1.1 christos case GLOB_NOMATCH:
1252 1.1 christos /* It's fine if no files were matched. */
1253 1.1 christos DPRINTCONF("No files matched pattern '%s'", full_pattern);
1254 1.1 christos break;
1255 1.1 christos case GLOB_NOSPACE:
1256 1.1 christos ERR("Error when searching for include files: %s",
1257 1.1 christos strerror(errno));
1258 1.1 christos break;
1259 1.1 christos default:
1260 1.1 christos ERR("Unknown glob(3) error %d", errno);
1261 1.1 christos break;
1262 1.1 christos }
1263 1.1 christos free(full_pattern);
1264 1.1 christos
1265 1.1 christos for (size_t i = 0; i < results.gl_pathc; i++) {
1266 1.1 christos include_matched_path(results.gl_pathv[i]);
1267 1.1 christos }
1268 1.1 christos
1269 1.1 christos globfree(&results);
1270 1.1 christos }
1271 1.1 christos
1272 1.1 christos static void
1273 1.1 christos include_matched_path(char *glob_path)
1274 1.1 christos {
1275 1.1 christos struct stat sb;
1276 1.1 christos char *tmp;
1277 1.1 christos
1278 1.1 christos if (lstat(glob_path, &sb) != 0) {
1279 1.1 christos ERR("Error calling stat on path '%s': %s", glob_path,
1280 1.1 christos strerror(errno));
1281 1.1 christos return;
1282 1.1 christos }
1283 1.1 christos
1284 1.1 christos if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode)) {
1285 1.1 christos DPRINTCONF("'%s' is not a file.", glob_path);
1286 1.1 christos ERR("The matched path '%s' is not a regular file", glob_path);
1287 1.1 christos return;
1288 1.1 christos }
1289 1.1 christos
1290 1.1 christos DPRINTCONF("Include '%s'", glob_path);
1291 1.1 christos
1292 1.1 christos if (S_ISLNK(sb.st_mode)) {
1293 1.1 christos tmp = glob_path;
1294 1.1 christos glob_path = realpath(tmp, NULL);
1295 1.1 christos }
1296 1.1 christos
1297 1.1 christos /* Ensure the file is not being reincluded .*/
1298 1.1 christos if (check_no_reinclude(glob_path)) {
1299 1.1 christos prepare_next_config(glob_path);
1300 1.1 christos config();
1301 1.1 christos } else {
1302 1.1 christos DPRINTCONF("File '%s' already included in current include "
1303 1.1 christos "chain", glob_path);
1304 1.1 christos WRN("Including file '%s' would cause a circular "
1305 1.1 christos "dependency", glob_path);
1306 1.1 christos }
1307 1.1 christos
1308 1.1 christos if (S_ISLNK(sb.st_mode)) {
1309 1.1 christos free(glob_path);
1310 1.1 christos glob_path = tmp;
1311 1.1 christos }
1312 1.1 christos }
1313 1.1 christos
1314 1.1 christos static bool
1315 1.1 christos check_no_reinclude(const char *glob_path)
1316 1.1 christos {
1317 1.1 christos struct file_list *cur = file_list_head;
1318 1.1 christos char *abs_path = realpath(glob_path, NULL);
1319 1.1 christos
1320 1.1 christos if (abs_path == NULL) {
1321 1.1 christos ERR("Error checking real path for '%s': %s",
1322 1.1 christos glob_path, strerror(errno));
1323 1.1 christos return false;
1324 1.1 christos }
1325 1.1 christos
1326 1.1 christos DPRINTCONF("Absolute path '%s'", abs_path);
1327 1.1 christos
1328 1.1 christos for (cur = file_list_head; cur != NULL; cur = cur->next) {
1329 1.1 christos if (strcmp(cur->abs, abs_path) == 0) {
1330 1.1 christos /* file included more than once */
1331 1.1 christos /* TODO relative or abs path for logging error? */
1332 1.1 christos free(abs_path);
1333 1.1 christos return false;
1334 1.1 christos }
1335 1.1 christos }
1336 1.1 christos free(abs_path);
1337 1.1 christos return true;
1338 1.1 christos }
1339 1.1 christos
1340 1.1 christos /* Resolve the pattern relative to the config file the pattern is from */
1341 1.1 christos static char *
1342 1.1 christos gen_file_pattern(const char *cur_config, const char *pattern)
1343 1.1 christos {
1344 1.1 christos if (pattern[0] == '/') {
1345 1.1 christos /* Absolute paths don't need any normalization */
1346 1.1 christos return newstr(pattern);
1347 1.1 christos }
1348 1.1 christos
1349 1.1 christos /* pattern is relative */
1350 1.1 christos /* Find the end of the file's directory */
1351 1.1 christos size_t i, last = 0;
1352 1.1 christos for (i = 0; cur_config[i] != '\0'; i++) {
1353 1.1 christos if (cur_config[i] == '/') {
1354 1.1 christos last = i;
1355 1.1 christos }
1356 1.1 christos }
1357 1.1 christos
1358 1.1 christos if (last == 0) {
1359 1.1 christos /* cur_config is just a filename, pattern already correct */
1360 1.1 christos return newstr(pattern);
1361 1.1 christos }
1362 1.1 christos
1363 1.1 christos /* Relativize pattern to cur_config file's directory */
1364 1.1 christos char *full_pattern = malloc(last + 1 + strlen(pattern) + 1);
1365 1.1 christos if (full_pattern == NULL) {
1366 1.1 christos syslog(LOG_ERR, "Out of memory.");
1367 1.1 christos exit(EXIT_FAILURE);
1368 1.1 christos }
1369 1.1 christos memcpy(full_pattern, cur_config, last);
1370 1.1 christos full_pattern[last] = '/';
1371 1.1 christos strcpy(&full_pattern[last + 1], pattern);
1372 1.1 christos return full_pattern;
1373 1.1 christos }
1374 1.1 christos
1375 1.1 christos static int
1376 1.1 christos glob_error(const char *path, int error)
1377 1.1 christos {
1378 1.1 christos WRN("Error while resolving path '%s': %s", path, strerror(error));
1379 1.1 christos return 0;
1380 1.1 christos }
1381