xref: /openbsd-src/usr.sbin/radiusd/parse.y (revision 3e976a28dfaa0a46b4d43324fde2a02b16362e54)
1 /*	$OpenBSD: parse.y,v 1.27 2024/08/15 07:24:28 yasuoka Exp $	*/
2 
3 /*
4  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
6  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
7  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 %{
23 #include <sys/types.h>
24 #include <sys/queue.h>
25 #include <sys/socket.h>
26 
27 #include <ctype.h>
28 #include <errno.h>
29 #include <limits.h>
30 #include <netdb.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <syslog.h>
34 
35 #include "radiusd.h"
36 #include "radiusd_local.h"
37 #include "log.h"
38 
39 static struct	 radiusd *conf;
40 static struct	 radiusd_authentication  authen;
41 static struct	 radiusd_module		*conf_module = NULL;
42 static struct	 radiusd_client		 client;
43 
44 static struct radiusd_authentication
45 		*create_authen(const char *, char **, int, char **);
46 static struct radiusd_module
47 		*find_module(const char *);
48 static void	 free_str_l(void *);
49 static struct	 radiusd_module_ref *create_module_ref(const char *);
50 static void	 radiusd_authentication_init(struct radiusd_authentication *);
51 static void	 radiusd_client_init(struct radiusd_client *);
52 static const char
53 		*default_module_path(const char *);
54 
55 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
56 static struct file {
57 	TAILQ_ENTRY(file)	 entry;
58 	FILE			*stream;
59 	char			*name;
60 	int			 lineno;
61 	int			 errors;
62 } *file, *topfile;
63 struct file	*pushfile(const char *);
64 int		 popfile(void);
65 int		 yyparse(void);
66 int		 yylex(void);
67 int		 yyerror(const char *, ...)
68     __attribute__((__format__ (printf, 1, 2)))
69     __attribute__((__nonnull__ (1)));
70 int		 kw_cmp(const void *, const void *);
71 int		 lookup(char *);
72 int		 lgetc(int);
73 int		 lungetc(int);
74 int		 findeol(void);
75 
76 typedef struct {
77 	union {
78 		int64_t				  number;
79 		char				 *string;
80 		struct radiusd_listen		  listen;
81 		int				  yesno;
82 		struct {
83 			char			**v;
84 			int			  c;
85 		} str_l;
86 		struct {
87 			int			 af;
88 			struct radiusd_addr	 addr;
89 			struct radiusd_addr	 mask;
90 		} prefix;
91 	} v;
92 	int lineno;
93 } YYSTYPE;
94 
95 %}
96 
97 %token	INCLUDE LISTEN ON PORT CLIENT SECRET LOAD MODULE MSGAUTH_REQUIRED
98 %token	ACCOUNT ACCOUNTING AUTHENTICATE AUTHENTICATE_BY AUTHENTICATION_FILTER
99 %token	BY DECORATE_BY QUICK SET TO ERROR YES NO
100 %token	<v.string>		STRING
101 %token	<v.number>		NUMBER
102 %type	<v.number>		optport optacct
103 %type	<v.listen>		listen_addr
104 %type	<v.str_l>		str_l optdeco
105 %type	<v.prefix>		prefix
106 %type	<v.yesno>		yesno optquick
107 %type	<v.string>		strnum
108 %type	<v.string>		key
109 %type	<v.string>		optstring
110 %%
111 
112 grammar		: /* empty */
113 		| grammar '\n'
114 		| grammar include '\n'
115 		| grammar listen '\n'
116 		| grammar client '\n'
117 		| grammar module '\n'
118 		| grammar authenticate '\n'
119 		| grammar account '\n'
120 		| grammar error '\n'
121 		;
122 
123 include		: INCLUDE STRING		{
124 			struct file	*nfile;
125 
126 			if ((nfile = pushfile($2)) == NULL) {
127 				yyerror("failed to include file %s", $2);
128 				free($2);
129 				YYERROR;
130 			}
131 			free($2);
132 
133 			file = nfile;
134 			lungetc('\n');
135 			nfile->lineno--;
136 		}
137 		;
138 listen		: LISTEN ON listen_addr {
139 			struct radiusd_listen *n;
140 
141 			if ((n = calloc(1, sizeof(struct radiusd_listen)))
142 			    == NULL) {
143 outofmemory:
144 				yyerror("Out of memory: %s", strerror(errno));
145 				YYERROR;
146 			}
147 			*n = $3;
148 			TAILQ_INSERT_TAIL(&conf->listen, n, next);
149 		}
150 listen_addr	: STRING optacct optport {
151 			int		 gai_errno;
152 			struct addrinfo hints, *res;
153 
154 			memset(&hints, 0, sizeof(hints));
155 			hints.ai_family = PF_UNSPEC;
156 			hints.ai_socktype = SOCK_DGRAM;
157 			hints.ai_flags = AI_PASSIVE;
158 			hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
159 
160 			if ((gai_errno =
161 				    getaddrinfo($1, NULL, &hints, &res)) != 0 ||
162 			    res->ai_addrlen > sizeof($$.addr)) {
163 				yyerror("Could not parse the address: %s: %s",
164 				    $1, gai_strerror(gai_errno));
165 				free($1);
166 				YYERROR;
167 			}
168 			free($1);
169 			$$.stype = res->ai_socktype;
170 			$$.sproto = res->ai_protocol;
171 			$$.accounting = $2;
172 			memcpy(&$$.addr, res->ai_addr, res->ai_addrlen);
173 			if ($3 != 0)
174 				$$.addr.ipv4.sin_port = htons($3);
175 			else if ($2)
176 				$$.addr.ipv4.sin_port =
177 				    htons(RADIUS_ACCT_DEFAULT_PORT);
178 			else
179 				$$.addr.ipv4.sin_port =
180 				    htons(RADIUS_DEFAULT_PORT);
181 
182 			freeaddrinfo(res);
183 		}
184 optacct		: ACCOUNTING { $$ = 1; }
185 		| { $$ = 0; }
186 		;
187 optport		: { $$ = 0; }
188 		| PORT NUMBER	{ $$ = $2; }
189 		;
190 client		: CLIENT {
191 			radiusd_client_init(&client);
192 		  } prefix optnl '{' clientopts '}' {
193 			struct radiusd_client *client0;
194 
195 			if (client.secret[0] == '\0') {
196 				yyerror("secret is required for client");
197 				YYERROR;
198 			}
199 
200 			client0 = calloc(1, sizeof(struct radiusd_client));
201 			if (client0 == NULL)
202 				goto outofmemory;
203 			strlcpy(client0->secret, client.secret,
204 			    sizeof(client0->secret));
205 			client0->msgauth_required = client.msgauth_required;
206 			client0->af = $3.af;
207 			client0->addr = $3.addr;
208 			client0->mask = $3.mask;
209 			TAILQ_INSERT_TAIL(&conf->client, client0, next);
210 		}
211 
212 clientopts	: clientopts '\n' clientopt
213 		| clientopt
214 		;
215 
216 clientopt	: SECRET STRING {
217 			if (client.secret[0] != '\0') {
218 				free($2);
219 				yyerror("secret is specified already");
220 				YYERROR;
221 			} else if (strlcpy(client.secret, $2,
222 			    sizeof(client.secret)) >= sizeof(client.secret)) {
223 				free($2);
224 				yyerror("secret is too long");
225 				YYERROR;
226 			}
227 			free($2);
228 		}
229 		| MSGAUTH_REQUIRED yesno {
230 			client.msgauth_required = $2;
231 		}
232 		|
233 		;
234 
235 prefix		: STRING '/' NUMBER {
236 			int		 gai_errno, q, r;
237 			struct addrinfo	 hints, *res;
238 
239 			memset(&hints, 0, sizeof(hints));
240 			hints.ai_family = PF_UNSPEC;
241 			hints.ai_socktype = SOCK_DGRAM;	/* dummy */
242 			hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
243 
244 			if ((gai_errno = getaddrinfo($1, NULL, &hints, &res))
245 			    != 0) {
246 				yyerror("Could not parse the address: %s: %s",
247 				    $1, gai_strerror(gai_errno));
248 				free($1);
249 				YYERROR;
250 			}
251 			free($1);
252 			q = $3 >> 3;
253 			r = $3 & 7;
254 			switch (res->ai_family) {
255 			case AF_INET:
256 				if ($3 < 0 || 32 < $3) {
257 					yyerror("mask len %lld is out of range",
258 					    (long long)$3);
259 					YYERROR;
260 				}
261 				$$.addr.addr.ipv4 = ((struct sockaddr_in *)
262 				    res->ai_addr)->sin_addr;
263 				$$.mask.addr.ipv4.s_addr = htonl((uint32_t)
264 				    ((0xffffffffffULL) << (32 - $3)));
265 				break;
266 			case AF_INET6:
267 				if ($3 < 0 || 128 < $3) {
268 					yyerror("mask len %lld is out of range",
269 					    (long long)$3);
270 					YYERROR;
271 				}
272 				$$.addr.addr.ipv6 = ((struct sockaddr_in6 *)
273 				    res->ai_addr)->sin6_addr;
274 				memset(&$$.mask.addr.ipv6, 0,
275 				    sizeof($$.mask.addr.ipv6));
276 				if (q > 0)
277 					memset(&$$.mask.addr.ipv6, 0xff, q);
278 				if (r > 0)
279 					*((u_char *)&$$.mask.addr.ipv6 + q) =
280 					    (0xff00 >> r) & 0xff;
281 				break;
282 			}
283 			$$.af = res->ai_family;
284 			freeaddrinfo(res);
285 		}
286 		;
287 module		: MODULE STRING optstring {
288 			const char *path = $3;
289 			if (path == NULL && (path = default_module_path($2))
290 			    == NULL) {
291 				yyerror("default path for `%s' is unknown.",
292 				    $2);
293 				free($2);
294 				free($3);
295 				YYERROR;
296 			}
297 			conf_module = radiusd_module_load(conf, path, $2);
298 			free($2);
299 			free($3);
300 			if (conf_module == NULL)
301 				YYERROR;
302 			TAILQ_INSERT_TAIL(&conf->module, conf_module, next);
303 			conf_module = NULL;
304 		}
305 		| MODULE STRING optstring {
306 			const char *path = $3;
307 			if (path == NULL && (path = default_module_path($2))
308 			    == NULL) {
309 				yyerror("default path for `%s' is unknown.",
310 				    $2);
311 				free($2);
312 				free($3);
313 				YYERROR;
314 			}
315 			conf_module = radiusd_module_load(conf, path, $2);
316 			free($2);
317 			free($3);
318 			if (conf_module == NULL)
319 				YYERROR;
320 		} '{' moduleopts '}' {
321 			TAILQ_INSERT_TAIL(&conf->module, conf_module, next);
322 			conf_module = NULL;
323 		}
324 		/* following syntaxes are for backward compatilities */
325 		| MODULE LOAD STRING STRING {
326 			struct radiusd_module *module;
327 			if ((module = radiusd_module_load(conf, $4, $3))
328 			    == NULL) {
329 				free($3);
330 				free($4);
331 				YYERROR;
332 			}
333 			free($3);
334 			free($4);
335 			TAILQ_INSERT_TAIL(&conf->module, module, next);
336 		}
337 		| MODULE SET STRING key str_l {
338 			struct radiusd_module	*module;
339 
340 			module = find_module($3);
341 			if (module == NULL) {
342 				yyerror("module `%s' is not found", $3);
343 setstrerr:
344 				free($3);
345 				free($4);
346 				free_str_l(&$5);
347 				YYERROR;
348 			}
349 			if ($4[0] == '_') {
350 				yyerror("setting `%s' is not allowed", $4);
351 				goto setstrerr;
352 			}
353 			if (radiusd_module_set(module, $4, $5.c, $5.v)) {
354 				yyerror("syntax error by module `%s'", $3);
355 				goto setstrerr;
356 			}
357 			free($3);
358 			free($4);
359 			free_str_l(&$5);
360 		}
361 		;
362 
363 moduleopts	: moduleopts '\n' moduleopt
364 		| moduleopt
365 		;
366 moduleopt	: /* empty */
367 		| SET key str_l {
368 			if ($2[0] == '_') {
369 				yyerror("setting `%s' is not allowed", $2);
370 				free($2);
371 				free_str_l(&$3);
372 				YYERROR;
373 			}
374 			if (radiusd_module_set(conf_module, $2, $3.c, $3.v)) {
375 				yyerror("syntax error by module `%s'",
376 				    conf_module->name);
377 				free($2);
378 				free_str_l(&$3);
379 				YYERROR;
380 			}
381 			free($2);
382 			free_str_l(&$3);
383 		}
384 		;
385 
386 key		: STRING
387 		| SECRET { $$ = strdup("secret"); }
388 		;
389 
390 authenticate	: AUTHENTICATE str_l BY STRING optdeco {
391 			struct radiusd_authentication	*auth;
392 
393 			auth = create_authen($4, $2.v, $5.c, $5.v);
394 			free($4);
395 			free_str_l(&$5);
396 			if (auth == NULL) {
397 				free_str_l(&$2);
398 				YYERROR;
399 			} else
400 				TAILQ_INSERT_TAIL(&conf->authen, auth, next);
401 		}
402 		| AUTHENTICATION_FILTER str_l BY STRING optdeco {
403 			struct radiusd_authentication	*auth;
404 
405 			auth = create_authen($4, $2.v, $5.c, $5.v);
406 			free($4);
407 			free_str_l(&$5);
408 			if (auth == NULL) {
409 				free_str_l(&$2);
410 				YYERROR;
411 			} else {
412 				auth->isfilter = true;
413 				TAILQ_INSERT_TAIL(&conf->authen, auth, next);
414 			}
415 		}
416 		/* the followings are for backward compatibilities */
417 		| AUTHENTICATE str_l optnl '{' {
418 			radiusd_authentication_init(&authen);
419 			authen.username = $2.v;
420 		} authopts '}' {
421 			int				 i;
422 			struct radiusd_authentication	*a;
423 
424 			if (authen.auth == NULL) {
425 				yyerror("no authentication module specified");
426 				for (i = 0; authen.username[i] != NULL; i++)
427 					free(authen.username[i]);
428 				free(authen.username);
429 				YYERROR;
430 			}
431 			if ((a = calloc(1,
432 			    sizeof(struct radiusd_authentication))) == NULL) {
433 				for (i = 0; authen.username[i] != NULL; i++)
434 					free(authen.username[i]);
435 				free(authen.username);
436 				goto outofmemory;
437 			}
438 			a->auth = authen.auth;
439 			authen.auth = NULL;
440 			a->deco = authen.deco;
441 			a->username = authen.username;
442 			TAILQ_INSERT_TAIL(&conf->authen, a, next);
443 		}
444 		;
445 
446 optdeco		: { $$.c = 0; $$.v = NULL; }
447 		| DECORATE_BY str_l { $$ = $2; }
448 		;
449 
450 authopts	: authopts '\n' authopt
451 		| authopt
452 		;
453 
454 authopt		: /* empty */
455 		| AUTHENTICATE_BY STRING {
456 			struct radiusd_module_ref	*modref;
457 
458 			if (authen.auth != NULL) {
459 				free($2);
460 				yyerror("authenticate is specified already");
461 				YYERROR;
462 			}
463 			modref = create_module_ref($2);
464 			free($2);
465 			if (modref == NULL)
466 				YYERROR;
467 			authen.auth = modref;
468 		}
469 		| DECORATE_BY str_l {
470 			int				 i;
471 			struct radiusd_module_ref	*modref;
472 
473 			for (i = 0; i < $2.c; i++) {
474 				if ((modref = create_module_ref($2.v[i]))
475 				    == NULL) {
476 					free_str_l(&$2);
477 					YYERROR;
478 				}
479 				TAILQ_INSERT_TAIL(&authen.deco, modref, next);
480 			}
481 			free_str_l(&$2);
482 		}
483 		;
484 
485 account		: ACCOUNT optquick str_l TO STRING optdeco {
486 			int				 i, error = 1;
487 			struct radiusd_accounting	*acct;
488 			struct radiusd_module_ref	*modref, *modreft;
489 
490 			if ((acct = calloc(1,
491 			    sizeof(struct radiusd_accounting))) == NULL) {
492 				yyerror("Out of memory: %s", strerror(errno));
493 				goto account_error;
494 			}
495 			if ((acct->acct = create_module_ref($5)) == NULL)
496 				goto account_error;
497 			acct->username = $3.v;
498 			acct->quick = $2;
499 			TAILQ_INIT(&acct->deco);
500 			for (i = 0; i < $6.c; i++) {
501 				if ((modref = create_module_ref($6.v[i]))
502 				    == NULL)
503 					goto account_error;
504 				TAILQ_INSERT_TAIL(&acct->deco, modref, next);
505 			}
506 			TAILQ_INSERT_TAIL(&conf->account, acct, next);
507 			acct = NULL;
508 			error = 0;
509  account_error:
510 			if (acct != NULL) {
511 				free(acct->acct);
512 				TAILQ_FOREACH_SAFE(modref, &acct->deco, next,
513 				    modreft) {
514 					TAILQ_REMOVE(&acct->deco, modref, next);
515 					free(modref);
516 				}
517 				free_str_l(&$3);
518 			}
519 			free(acct);
520 			free($5);
521 			free_str_l(&$6);
522 			if (error > 0)
523 				YYERROR;
524 		}
525 		;
526 
527 optquick	: { $$ = 0; }
528 		| QUICK { $$ = 1; }
529 
530 str_l		: str_l strnum {
531 			int	  i;
532 			char	**v;
533 			if ((v = calloc(sizeof(char **), $$.c + 2)) == NULL)
534 				goto outofmemory;
535 			for (i = 0; i < $$.c; i++)
536 				v[i] = $$.v[i];
537 			v[i++] = $2;
538 			v[i] = NULL;
539 			$$.c++;
540 			free($$.v);
541 			$$.v = v;
542 		}
543 		| strnum {
544 			if (($$.v = calloc(sizeof(char **), 2)) == NULL)
545 				goto outofmemory;
546 			$$.v[0] = $1;
547 			$$.v[1] = NULL;
548 			$$.c = 1;
549 		}
550 		;
551 strnum		: STRING	{ $$ = $1; }
552 		| NUMBER {
553 			/* Treat number as a string */
554 			asprintf(&($$), "%jd", (intmax_t)$1);
555 			if ($$ == NULL)
556 				goto outofmemory;
557 		}
558 		;
559 optnl		:
560 		| '\n'
561 		;
562 optstring	: { $$ = NULL; }
563 		| STRING { $$ = $1; }
564 		;
565 yesno		: YES { $$ = true; }
566 		| NO  { $$ = false; }
567 		;
568 %%
569 
570 struct keywords {
571 	const char	*k_name;
572 	int		 k_val;
573 };
574 
575 int
576 yyerror(const char *fmt, ...)
577 {
578 	va_list		 ap;
579 	char		*msg;
580 
581 	file->errors++;
582 	va_start(ap, fmt);
583 	if (vasprintf(&msg, fmt, ap) == -1)
584 		fatalx("yyerror vasprintf");
585 	va_end(ap);
586 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
587 	free(msg);
588 	return (0);
589 }
590 
591 int
592 kw_cmp(const void *k, const void *e)
593 {
594 	return (strcmp(k, ((const struct keywords *)e)->k_name));
595 }
596 
597 int
598 lookup(char *s)
599 {
600 	/* this has to be sorted always */
601 	static const struct keywords keywords[] = {
602 		{ "account",			ACCOUNT},
603 		{ "accounting",			ACCOUNTING},
604 		{ "authenticate",		AUTHENTICATE},
605 		{ "authenticate-by",		AUTHENTICATE_BY},
606 		{ "authentication-filter",	AUTHENTICATION_FILTER},
607 		{ "by",				BY},
608 		{ "client",			CLIENT},
609 		{ "decorate-by",		DECORATE_BY},
610 		{ "include",			INCLUDE},
611 		{ "listen",			LISTEN},
612 		{ "load",			LOAD},
613 		{ "module",			MODULE},
614 		{ "msgauth-required",		MSGAUTH_REQUIRED},
615 		{ "no",				NO},
616 		{ "on",				ON},
617 		{ "port",			PORT},
618 		{ "quick",			QUICK},
619 		{ "secret",			SECRET},
620 		{ "set",			SET},
621 		{ "to",				TO},
622 		{ "yes",			YES},
623 	};
624 	const struct keywords	*p;
625 
626 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
627 	    sizeof(keywords[0]), kw_cmp);
628 
629 	if (p)
630 		return (p->k_val);
631 	else
632 		return (STRING);
633 }
634 
635 #define MAXPUSHBACK	128
636 
637 char	*parsebuf;
638 int	 parseindex;
639 char	 pushback_buffer[MAXPUSHBACK];
640 int	 pushback_index = 0;
641 
642 int
643 lgetc(int quotec)
644 {
645 	int		c, next;
646 
647 	if (parsebuf) {
648 		/* Read character from the parsebuffer instead of input. */
649 		if (parseindex >= 0) {
650 			c = (unsigned char)parsebuf[parseindex++];
651 			if (c != '\0')
652 				return (c);
653 			parsebuf = NULL;
654 		} else
655 			parseindex++;
656 	}
657 
658 	if (pushback_index)
659 		return ((unsigned char)pushback_buffer[--pushback_index]);
660 
661 	if (quotec) {
662 		if ((c = getc(file->stream)) == EOF) {
663 			yyerror("reached end of file while parsing "
664 			    "quoted string");
665 			if (file == topfile || popfile() == EOF)
666 				return (EOF);
667 			return (quotec);
668 		}
669 		return (c);
670 	}
671 
672 	while ((c = getc(file->stream)) == '\\') {
673 		next = getc(file->stream);
674 		if (next != '\n') {
675 			c = next;
676 			break;
677 		}
678 		yylval.lineno = file->lineno;
679 		file->lineno++;
680 	}
681 
682 	while (c == EOF) {
683 		if (file == topfile || popfile() == EOF)
684 			return (EOF);
685 		c = getc(file->stream);
686 	}
687 	return (c);
688 }
689 
690 int
691 lungetc(int c)
692 {
693 	if (c == EOF)
694 		return (EOF);
695 	if (parsebuf) {
696 		parseindex--;
697 		if (parseindex >= 0)
698 			return (c);
699 	}
700 	if (pushback_index + 1 >= MAXPUSHBACK)
701 		return (EOF);
702 	pushback_buffer[pushback_index++] = c;
703 	return (c);
704 }
705 
706 int
707 findeol(void)
708 {
709 	int	c;
710 
711 	parsebuf = NULL;
712 
713 	/* skip to either EOF or the first real EOL */
714 	while (1) {
715 		if (pushback_index)
716 			c = (unsigned char)pushback_buffer[--pushback_index];
717 		else
718 			c = lgetc(0);
719 		if (c == '\n') {
720 			file->lineno++;
721 			break;
722 		}
723 		if (c == EOF)
724 			break;
725 	}
726 	return (ERROR);
727 }
728 
729 int
730 yylex(void)
731 {
732 	char	 buf[8096];
733 	char	*p;
734 	int	 quotec, next, c;
735 	int	 token;
736 
737 	p = buf;
738 	while ((c = lgetc(0)) == ' ' || c == '\t')
739 		; /* nothing */
740 
741 	yylval.lineno = file->lineno;
742 	if (c == '#')
743 		while ((c = lgetc(0)) != '\n' && c != EOF)
744 			; /* nothing */
745 
746 	switch (c) {
747 	case '\'':
748 	case '"':
749 		quotec = c;
750 		while (1) {
751 			if ((c = lgetc(quotec)) == EOF)
752 				return (0);
753 			if (c == '\n') {
754 				file->lineno++;
755 				continue;
756 			} else if (c == '\\') {
757 				if ((next = lgetc(quotec)) == EOF)
758 					return (0);
759 				if (next == quotec || next == ' ' ||
760 				    next == '\t')
761 					c = next;
762 				else if (next == '\n') {
763 					file->lineno++;
764 					continue;
765 				} else
766 					lungetc(next);
767 			} else if (c == quotec) {
768 				*p = '\0';
769 				break;
770 			} else if (c == '\0') {
771 				yyerror("syntax error");
772 				return (findeol());
773 			}
774 			if (p + 1 >= buf + sizeof(buf) - 1) {
775 				yyerror("string too long");
776 				return (findeol());
777 			}
778 			*p++ = c;
779 		}
780 		yylval.v.string = strdup(buf);
781 		if (yylval.v.string == NULL)
782 			fatal("yylex: strdup");
783 		return (STRING);
784 	}
785 
786 #define allowed_to_end_number(x) \
787 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
788 
789 	if (c == '-' || isdigit(c)) {
790 		do {
791 			*p++ = c;
792 			if ((size_t)(p-buf) >= sizeof(buf)) {
793 				yyerror("string too long");
794 				return (findeol());
795 			}
796 		} while ((c = lgetc(0)) != EOF && isdigit(c));
797 		lungetc(c);
798 		if (p == buf + 1 && buf[0] == '-')
799 			goto nodigits;
800 		if (c == EOF || allowed_to_end_number(c)) {
801 			const char *errstr = NULL;
802 
803 			*p = '\0';
804 			yylval.v.number = strtonum(buf, LLONG_MIN,
805 			    LLONG_MAX, &errstr);
806 			if (errstr) {
807 				yyerror("\"%s\" invalid number: %s",
808 				    buf, errstr);
809 				return (findeol());
810 			}
811 			return (NUMBER);
812 		} else {
813 nodigits:
814 			while (p > buf + 1)
815 				lungetc((unsigned char)*--p);
816 			c = (unsigned char)*--p;
817 			if (c == '-')
818 				return (c);
819 		}
820 	}
821 
822 #define allowed_in_string(x) \
823 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
824 	x != '{' && x != '}' && x != '<' && x != '>' && \
825 	x != '!' && x != '=' && x != '/' && x != '#' && \
826 	x != ','))
827 
828 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
829 		do {
830 			*p++ = c;
831 			if ((size_t)(p-buf) >= sizeof(buf)) {
832 				yyerror("string too long");
833 				return (findeol());
834 			}
835 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
836 		lungetc(c);
837 		*p = '\0';
838 		if ((token = lookup(buf)) == STRING)
839 			if ((yylval.v.string = strdup(buf)) == NULL)
840 				fatal("yylex: strdup");
841 		return (token);
842 	}
843 	if (c == '\n') {
844 		yylval.lineno = file->lineno;
845 		file->lineno++;
846 	}
847 	if (c == EOF)
848 		return (0);
849 	return (c);
850 }
851 
852 struct file *
853 pushfile(const char *name)
854 {
855 	struct file	*nfile;
856 
857 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
858 		log_warn("%s", __func__);
859 		return (NULL);
860 	}
861 	if ((nfile->name = strdup(name)) == NULL) {
862 		log_warn("%s", __func__);
863 		free(nfile);
864 		return (NULL);
865 	}
866 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
867 		log_warn("%s: %s", __func__, nfile->name);
868 		free(nfile->name);
869 		free(nfile);
870 		return (NULL);
871 	}
872 	nfile->lineno = 1;
873 	TAILQ_INSERT_TAIL(&files, nfile, entry);
874 	return (nfile);
875 }
876 
877 int
878 popfile(void)
879 {
880 	struct file	*prev;
881 
882 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
883 		prev->errors += file->errors;
884 
885 	TAILQ_REMOVE(&files, file, entry);
886 	fclose(file->stream);
887 	free(file->name);
888 	free(file);
889 	file = prev;
890 	return (file ? 0 : EOF);
891 }
892 
893 int
894 parse_config(const char *filename, struct radiusd *radiusd)
895 {
896 	int				 errors = 0;
897 	struct radiusd_listen		*l;
898 
899 	conf = radiusd;
900 	radiusd_conf_init(conf);
901 	radiusd_authentication_init(&authen);
902 	radiusd_client_init(&client);
903 
904 	if ((file = pushfile(filename)) == NULL) {
905 		errors++;
906 		goto out;
907 	}
908 	topfile = file;
909 
910 	yyparse();
911 	errors = file->errors;
912 	popfile();
913 
914 	if (TAILQ_EMPTY(&conf->listen)) {
915 		if ((l = calloc(1, sizeof(struct radiusd_listen))) == NULL) {
916 			log_warn("Out of memory");
917 			return (-1);
918 		}
919 		l->stype = SOCK_DGRAM;
920 		l->sproto = IPPROTO_UDP;
921 		l->addr.ipv4.sin_family = AF_INET;
922 		l->addr.ipv4.sin_len = sizeof(struct sockaddr_in);
923 		l->addr.ipv4.sin_addr.s_addr = htonl(0x7F000001L);
924 		l->addr.ipv4.sin_port = htons(RADIUS_DEFAULT_PORT);
925 		TAILQ_INSERT_TAIL(&conf->listen, l, next);
926 	}
927 	TAILQ_FOREACH(l, &conf->listen, next) {
928 		l->sock = -1;
929 	}
930 	radiusd_authentication_init(&authen);
931 	if (conf_module != NULL)
932 		radiusd_module_unload(conf_module);
933 out:
934 	conf = NULL;
935 	return (errors ? -1 : 0);
936 }
937 
938 static struct radiusd_authentication *
939 create_authen(const char *byname, char **username, int decoc, char **deco)
940 {
941 	int				 i;
942 	struct radiusd_authentication	*auth;
943 	struct radiusd_module_ref	*modref, *modreft;
944 
945 	if ((auth = calloc(1, sizeof(struct radiusd_authentication)))
946 	    == NULL) {
947 		yyerror("Out of memory: %s", strerror(errno));
948 		return (NULL);
949 	}
950 	if ((auth->auth = create_module_ref(byname)) == NULL)
951 		goto on_error;
952 
953 	auth->username = username;
954 	TAILQ_INIT(&auth->deco);
955 	for (i = 0; i < decoc; i++) {
956 		if ((modref = create_module_ref(deco[i])) == NULL)
957 			goto on_error;
958 		TAILQ_INSERT_TAIL(&auth->deco, modref, next);
959 	}
960 	return (auth);
961  on_error:
962 	TAILQ_FOREACH_SAFE(modref, &auth->deco, next, modreft) {
963 		TAILQ_REMOVE(&auth->deco, modref, next);
964 		free(modref);
965 	}
966 	free(auth);
967 	return (NULL);
968 }
969 
970 static struct radiusd_module *
971 find_module(const char *name)
972 {
973 	struct radiusd_module	*module;
974 
975 	TAILQ_FOREACH(module, &conf->module, next) {
976 		if (strcmp(name, module->name) == 0)
977 			return (module);
978 	}
979 
980 	return (NULL);
981 }
982 
983 static void
984 free_str_l(void *str_l0)
985 {
986 	int				  i;
987 	struct {
988 		char			**v;
989 		int			  c;
990 	}				 *str_l = str_l0;
991 
992 	for (i = 0; i < str_l->c; i++)
993 		free(str_l->v[i]);
994 	free(str_l->v);
995 }
996 
997 static struct radiusd_module_ref *
998 create_module_ref(const char *modulename)
999 {
1000 	struct radiusd_module		*module;
1001 	struct radiusd_module_ref	*modref;
1002 
1003 	if ((module = find_module(modulename)) == NULL) {
1004 		yyerror("module `%s' is not found", modulename);
1005 		return (NULL);
1006 	}
1007 	if ((modref = calloc(1, sizeof(struct radiusd_module_ref))) == NULL) {
1008 		yyerror("Out of memory: %s", strerror(errno));
1009 		return (NULL);
1010 	}
1011 	modref->module = module;
1012 
1013 	return (modref);
1014 }
1015 
1016 static void
1017 radiusd_authentication_init(struct radiusd_authentication *auth)
1018 {
1019 	free(auth->auth);
1020 	memset(auth, 0, sizeof(struct radiusd_authentication));
1021 	TAILQ_INIT(&auth->deco);
1022 }
1023 
1024 static void
1025 radiusd_client_init(struct radiusd_client *clnt)
1026 {
1027 	memset(clnt, 0, sizeof(struct radiusd_client));
1028 	clnt->msgauth_required = true;
1029 }
1030 
1031 static const char *
1032 default_module_path(const char *name)
1033 {
1034 	unsigned i;
1035 	struct {
1036 		const char *name;
1037 		const char *path;
1038 	} module_paths[] = {
1039 		{ "bsdauth",	"/usr/libexec/radiusd/radiusd_bsdauth" },
1040 		{ "eap2mschap",	"/usr/libexec/radiusd/radiusd_eap2mschap" },
1041 		{ "file",	"/usr/libexec/radiusd/radiusd_file" },
1042 		{ "ipcp",	"/usr/libexec/radiusd/radiusd_ipcp" },
1043 		{ "radius",	"/usr/libexec/radiusd/radiusd_radius" },
1044 		{ "standard",	"/usr/libexec/radiusd/radiusd_standard" }
1045 	};
1046 
1047 	for (i = 0; i < nitems(module_paths); i++) {
1048 		if (strcmp(name, module_paths[i].name) == 0)
1049 			return (module_paths[i].path);
1050 	}
1051 
1052 	return (NULL);
1053 }
1054