parse.y revision 1.1
1/*	$Id: parse.y,v 1.1 2005/04/04 22:19:50 hshoexer 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 * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
9 *
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23%{
24#include <sys/types.h>
25#include <sys/queue.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29
30#include <ctype.h>
31#include <err.h>
32#include <errno.h>
33#include <limits.h>
34#include <stdarg.h>
35#include <stdio.h>
36#include <string.h>
37#include <syslog.h>
38
39#include "ipsecctl.h"
40
41static struct ipsecctl	*ipsec = NULL;
42static FILE		*fin = NULL;
43static int		 lineno = 1;
44static int		 errors = 0;
45static int		 debug = 0;
46
47int			 yyerror(const char *, ...);
48int			 yyparse(void);
49int			 kw_cmp(const void *, const void *);
50int			 lookup(char *);
51int			 lgetc(FILE *);
52int			 lungetc(int);
53int			 findeol(void);
54int			 yylex(void);
55
56TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
57struct sym {
58	TAILQ_ENTRY(sym)	 entries;
59	int		 used;
60	int		 persist;
61	char		*nam;
62	char		*val;
63};
64
65int			 symset(const char *, const char *, int);
66int			 cmdline_symset(char *);
67char			*symget(const char *);
68int			 atoul(char *, u_long *);
69struct ipsec_addr	*host(const char *);
70struct ipsec_addr	*copyhost(const struct ipsec_addr *);
71struct ipsec_rule	*create_rule(u_int8_t, struct ipsec_addr *, struct
72			     ipsec_addr *, struct ipsec_addr *, u_int8_t,
73			     char *, char *, u_int16_t);
74struct ipsec_rule	*reverse_rule(struct ipsec_rule *);
75
76typedef struct {
77	union {
78		u_int32_t	 number;
79		u_int8_t	 dir;
80		char		*string;
81		int		 log;
82		u_int8_t	 protocol;
83		struct {
84			struct ipsec_addr *src;
85			struct ipsec_addr *dst;
86		} hosts;
87		struct ipsec_addr *peer;
88		struct ipsec_addr *host;
89		struct {
90			char *srcid;
91			char *dstid;
92		} ids;
93		char		*id;
94		u_int16_t	 authtype;
95	} v;
96	int lineno;
97} YYSTYPE;
98
99%}
100
101%token	FLOW FROM ESP AH IN PEER ON OUT TO SRCID DSTID RSA PSK
102%token	ERROR
103%token	<v.string>		STRING
104%type	<v.dir>			dir
105%type	<v.protocol>		protocol
106%type	<v.number>		number
107%type	<v.hosts>		hosts
108%type	<v.peer>		peer
109%type	<v.host>		host
110%type	<v.ids>			ids
111%type	<v.id>			id
112%type	<v.authtype>		authtype
113%%
114
115grammar		: /* empty */
116		| grammar '\n'
117		| grammar flowrule '\n'
118		| grammar error '\n'		{ errors++; }
119		;
120
121number		: STRING			{
122			unsigned long	ulval;
123
124			if (atoul($1, &ulval) == -1) {
125				yyerror("%s is not a number", $1);
126				free($1);
127				YYERROR;
128			} else
129				$$ = ulval;
130			free($1);
131		}
132
133flowrule	: FLOW ipsecrule		{ }
134		;
135
136ipsecrule	: protocol dir hosts peer ids authtype	{
137			struct ipsec_rule	*r;
138
139			r = create_rule($2, $3.src, $3.dst, $4, $1, $5.srcid,
140			    $5.dstid, $6);
141			if (r == NULL)
142				YYERROR;
143			r->nr = ipsec->rule_nr++;
144
145			if (ipsecctl_add_rule(ipsec, r))
146				errx(1, "esprule: ipsecctl_add_rule");
147
148			/* Create and add reverse rule. */
149			if ($2 == IPSEC_INOUT) {
150				r = reverse_rule(r);
151				r->nr = ipsec->rule_nr++;
152
153				if (ipsecctl_add_rule(ipsec, r))
154					errx(1, "esprule: ipsecctl_add_rule");
155			}
156		}
157		;
158
159protocol	: /* empty */			{ $$ = IPSEC_ESP; }
160		| ESP				{ $$ = IPSEC_ESP; }
161		| AH				{ $$ = IPSEC_AH; }
162		;
163
164dir		: /* empty */			{ $$ = IPSEC_INOUT; }
165		| IN				{ $$ = IPSEC_IN; }
166		| OUT				{ $$ = IPSEC_OUT; }
167		;
168
169hosts		: FROM host TO host		{
170			$$.src = $2;
171			$$.dst = $4;
172		}
173		;
174
175peer		: /* empty */			{ $$ = NULL; }
176		| PEER STRING			{
177			if (($$ = host($2)) == NULL) {
178				free($2);
179				yyerror("could not parse host specification");
180				YYERROR;
181			}
182			free($2);
183		}
184		;
185
186host		: STRING			{
187			if (($$ = host($1)) == NULL) {
188				free($1);
189				yyerror("could not parse host specification");
190				YYERROR;
191			}
192			free($1);
193		}
194		| STRING '/' number		{
195			char	*buf;
196
197			if (asprintf(&buf, "%s/%u", $1, $3) == -1)
198				err(1, "host: asprintf");
199			free($1);
200			if (($$ = host(buf)) == NULL)	{
201				free(buf);
202				yyerror("could not parse host specification");
203				YYERROR;
204			}
205			free(buf);
206		}
207		;
208
209ids		: /* empty */			{
210			$$.srcid = NULL;
211			$$.dstid = NULL;
212		}
213		| SRCID id DSTID id		{
214			$$.srcid = $2;
215			$$.dstid = $4;
216		}
217		| SRCID id			{
218			$$.srcid = $2;
219			$$.dstid = NULL;
220		}
221		| DSTID id			{
222			$$.srcid = NULL;
223			$$.dstid = $2;
224		}
225		;
226
227id		: STRING			{ $$ = $1; }
228		;
229
230authtype	: /* empty */			{ $$ = 0; }
231		| RSA				{ $$ = AUTH_RSA; }
232		| PSK				{ $$ = AUTH_PSK; }
233		;
234%%
235
236struct keywords {
237	const char	*k_name;
238	int		 k_val;
239};
240
241int
242yyerror(const char *fmt, ...)
243{
244	va_list		 ap;
245	extern char 	*infile;
246
247	errors = 1;
248	va_start(ap, fmt);
249	fprintf(stderr, "%s: %d: ", infile, yyval.lineno);
250	vfprintf(stderr, fmt, ap);
251	fprintf(stderr, "\n");
252	va_end(ap);
253	return (0);
254}
255
256int
257kw_cmp(const void *k, const void *e)
258{
259	return (strcmp(k, ((const struct keywords *)e)->k_name));
260}
261
262int
263lookup(char *s)
264{
265	/* this has to be sorted always */
266	static const struct keywords keywords[] = {
267		{ "ah",			AH},
268		{ "dstid",		DSTID},
269		{ "esp",		ESP},
270		{ "flow",		FLOW},
271		{ "from",		FROM},
272		{ "in",			IN},
273		{ "out",		OUT},
274		{ "peer",		PEER},
275		{ "psk",		PSK},
276		{ "rsa",		RSA},
277		{ "srcid",		SRCID},
278		{ "to",			TO},
279	};
280	const struct keywords	*p;
281
282	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
283	    sizeof(keywords[0]), kw_cmp);
284
285	if (p) {
286		if (debug > 1)
287			fprintf(stderr, "%s: %d\n", s, p->k_val);
288		return (p->k_val);
289	} else {
290		if (debug > 1)
291			fprintf(stderr, "string: %s\n", s);
292		return (STRING);
293	}
294}
295
296#define MAXPUSHBACK	128
297
298char	*parsebuf;
299int	 parseindex;
300char	 pushback_buffer[MAXPUSHBACK];
301int	 pushback_index = 0;
302
303int
304lgetc(FILE *f)
305{
306	int	c, next;
307
308	if (parsebuf) {
309		/* Read character from the parsebuffer instead of input. */
310		if (parseindex >= 0) {
311			c = parsebuf[parseindex++];
312			if (c != '\0')
313				return (c);
314			parsebuf = NULL;
315		} else
316			parseindex++;
317	}
318
319	if (pushback_index)
320		return (pushback_buffer[--pushback_index]);
321
322	while ((c = getc(f)) == '\\') {
323		next = getc(f);
324		if (next != '\n') {
325			if (isspace(next))
326				yyerror("whitespace after \\");
327			ungetc(next, f);
328			break;
329		}
330		yylval.lineno = lineno;
331		lineno++;
332	}
333	if (c == '\t' || c == ' ') {
334		/* Compress blanks to a single space. */
335		do {
336			c = getc(f);
337		} while (c == '\t' || c == ' ');
338		ungetc(c, f);
339		c = ' ';
340	}
341
342	return (c);
343}
344
345int
346lungetc(int c)
347{
348	if (c == EOF)
349		return (EOF);
350	if (parsebuf) {
351		parseindex--;
352		if (parseindex >= 0)
353			return (c);
354	}
355	if (pushback_index < MAXPUSHBACK-1)
356		return (pushback_buffer[pushback_index++] = c);
357	else
358		return (EOF);
359}
360
361int
362findeol(void)
363{
364	int	c;
365
366	parsebuf = NULL;
367	pushback_index = 0;
368
369	/* skip to either EOF or the first real EOL */
370	while (1) {
371		c = lgetc(fin);
372		if (c == '\n') {
373			lineno++;
374			break;
375		}
376		if (c == EOF)
377			break;
378	}
379	return (ERROR);
380}
381
382int
383yylex(void)
384{
385	char	 buf[8096];
386	char	*p, *val;
387	int	 endc, c;
388	int	 token;
389
390top:
391	p = buf;
392	while ((c = lgetc(fin)) == ' ')
393		; /* nothing */
394
395	yylval.lineno = lineno;
396	if (c == '#')
397		while ((c = lgetc(fin)) != '\n' && c != EOF)
398			; /* nothing */
399	if (c == '$' && parsebuf == NULL) {
400		while (1) {
401			if ((c = lgetc(fin)) == EOF)
402				return (0);
403
404			if (p + 1 >= buf + sizeof(buf) - 1) {
405				yyerror("string too long");
406				return (findeol());
407			}
408			if (isalnum(c) || c == '_') {
409				*p++ = (char)c;
410				continue;
411			}
412			*p = '\0';
413			lungetc(c);
414			break;
415		}
416		val = symget(buf);
417		if (val == NULL) {
418			yyerror("macro \"%s\" not defined", buf);
419			return (findeol());
420		}
421		parsebuf = val;
422		parseindex = 0;
423		goto top;
424	}
425
426	switch (c) {
427	case '\'':
428	case '"':
429		endc = c;
430		while (1) {
431			if ((c = lgetc(fin)) == EOF)
432				return (0);
433			if (c == endc) {
434				*p = '\0';
435				break;
436			}
437			if (c == '\n') {
438				lineno++;
439				continue;
440			}
441			if (p + 1 >= buf + sizeof(buf) - 1) {
442				yyerror("string too long");
443				return (findeol());
444			}
445			*p++ = (char)c;
446		}
447		yylval.v.string = strdup(buf);
448		if (yylval.v.string == NULL)
449			err(1, "yylex: strdup");
450		return (STRING);
451	}
452
453#define allowed_in_string(x) \
454	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
455	x != '{' && x != '}' && x != '<' && x != '>' && \
456	x != '!' && x != '=' && x != '/' && x != '#' && \
457	x != ','))
458
459	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
460		do {
461			*p++ = c;
462			if ((unsigned)(p-buf) >= sizeof(buf)) {
463				yyerror("string too long");
464				return (findeol());
465			}
466		} while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
467		lungetc(c);
468		*p = '\0';
469		if ((token = lookup(buf)) == STRING)
470			if ((yylval.v.string = strdup(buf)) == NULL)
471				err(1, "yylex: strdup");
472		return (token);
473	}
474	if (c == '\n') {
475		yylval.lineno = lineno;
476		lineno++;
477	}
478	if (c == EOF)
479		return (0);
480	return (c);
481}
482
483int
484parse_rules(FILE *input, struct ipsecctl *ipsecx)
485{
486	struct sym	*sym, *next;
487
488	ipsec = ipsecx;
489	fin = input;
490	lineno = 1;
491	errors = 0;
492
493	yyparse();
494
495	/* Free macros and check which have not been used. */
496	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
497		next = TAILQ_NEXT(sym, entries);
498		free(sym->nam);
499		free(sym->val);
500		TAILQ_REMOVE(&symhead, sym, entries);
501		free(sym);
502	}
503
504	return (errors ? -1 : 0);
505}
506
507int
508symset(const char *nam, const char *val, int persist)
509{
510	struct sym	*sym;
511
512	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
513	    sym = TAILQ_NEXT(sym, entries))
514		;	/* nothing */
515
516	if (sym != NULL) {
517		if (sym->persist == 1)
518			return (0);
519		else {
520			free(sym->nam);
521			free(sym->val);
522			TAILQ_REMOVE(&symhead, sym, entries);
523			free(sym);
524		}
525	}
526	if ((sym = calloc(1, sizeof(*sym))) == NULL)
527		return (-1);
528
529	sym->nam = strdup(nam);
530	if (sym->nam == NULL) {
531		free(sym);
532		return (-1);
533	}
534	sym->val = strdup(val);
535	if (sym->val == NULL) {
536		free(sym->nam);
537		free(sym);
538		return (-1);
539	}
540	sym->used = 0;
541	sym->persist = persist;
542	TAILQ_INSERT_TAIL(&symhead, sym, entries);
543	return (0);
544}
545
546int
547cmdline_symset(char *s)
548{
549	char	*sym, *val;
550	int	ret;
551	size_t	len;
552
553	if ((val = strrchr(s, '=')) == NULL)
554		return (-1);
555
556	len = strlen(s) - strlen(val) + 1;
557	if ((sym = malloc(len)) == NULL)
558		err(1, "cmdline_symset: malloc");
559
560	strlcpy(sym, s, len);
561
562	ret = symset(sym, val + 1, 1);
563	free(sym);
564
565	return (ret);
566}
567
568char *
569symget(const char *nam)
570{
571	struct sym	*sym;
572
573	TAILQ_FOREACH(sym, &symhead, entries)
574		if (strcmp(nam, sym->nam) == 0) {
575			sym->used = 1;
576			return (sym->val);
577		}
578	return (NULL);
579}
580
581int
582atoul(char *s, u_long *ulvalp)
583{
584	u_long	 ulval;
585	char	*ep;
586
587	errno = 0;
588	ulval = strtoul(s, &ep, 0);
589	if (s[0] == '\0' || *ep != '\0')
590		return (-1);
591	if (errno == ERANGE && ulval == ULONG_MAX)
592		return (-1);
593	*ulvalp = ulval;
594	return (0);
595}
596
597struct ipsec_addr *
598host(const char *s)
599{
600	struct ipsec_addr	*ipa;
601	int			 i, bits = 32;
602
603	/* XXX for now only AF_INET. */
604
605	ipa = calloc(1, sizeof(struct ipsec_addr));
606	if (ipa == NULL)
607		err(1, "calloc");
608
609	if (strrchr(s, '/') != NULL) {
610		bits = inet_net_pton(AF_INET, s, &ipa->v4, sizeof(ipa->v4));
611		if (bits == -1 || bits > 32) {
612			free(ipa);
613			return(NULL);
614		}
615	} else {
616		if (inet_pton(AF_INET, s, &ipa->v4) != 1) {
617			free(ipa);
618			return NULL;
619		}
620	}
621
622	memset(&ipa->v4mask, 0, sizeof(ipa->v4mask));
623	if (bits == 32) {
624		ipa->v4mask.mask32 = 0xffffffff;
625		ipa->netaddress = 0;
626	} else {
627		for (i = 31; i > 31 - bits; i--)
628			ipa->v4mask.mask32 |= (1 << i);
629		ipa->v4mask.mask32 = htonl(ipa->v4mask.mask32);
630		ipa->netaddress = 1;
631	}
632
633	ipa->af = AF_INET;
634
635	return ipa;
636}
637
638struct ipsec_addr *
639copyhost(const struct ipsec_addr *src)
640{
641	struct ipsec_addr *dst;
642
643	dst = calloc(1, sizeof(struct ipsec_addr));
644	if (dst == NULL)
645		err(1, "calloc");
646
647	memcpy(dst, src, sizeof(struct ipsec_addr));
648	return dst;
649}
650
651struct ipsec_rule *
652create_rule(u_int8_t dir, struct ipsec_addr *src, struct ipsec_addr *dst,
653    struct ipsec_addr *peer, u_int8_t proto, char *srcid, char *dstid,
654    u_int16_t authtype)
655{
656	struct ipsec_rule *r;
657
658	r = calloc(1, sizeof(struct ipsec_rule));
659	if (r == NULL)
660		err(1, "calloc");
661
662	if (dir == IPSEC_INOUT)
663		r->direction = IPSEC_OUT;
664	else
665		r->direction = dir;
666
667	r->src = src;
668	r->dst = dst;
669
670	if (peer == NULL) {
671		/* Set peer to remote host.  Must be a host address. */
672		if (r->direction == IPSEC_IN) {
673			if (r->src->netaddress) {
674				yyerror("no peer specified");
675				goto errout;
676			}
677			r->peer = copyhost(r->src);
678		} else {
679			if (r->dst->netaddress) {
680				yyerror("no peer specified");
681				goto errout;
682			}
683			r->peer = copyhost(r->dst);
684		}
685	} else
686		r->peer = peer;
687
688	r->proto = proto;
689	r->auth.srcid = srcid;
690	r->auth.dstid = dstid;
691	r->auth.idtype = ID_FQDN;	/* XXX For now only FQDN. */
692#ifdef notyet
693	r->auth.type = authtype;
694#endif
695
696	return r;
697
698errout:
699	free(r);
700	if (srcid)
701		free(srcid);
702	if (dstid)
703		free(dstid);
704	free(src);
705	free(dst);
706
707	return NULL;
708}
709
710struct ipsec_rule *
711reverse_rule(struct ipsec_rule *rule)
712{
713	struct ipsec_rule *reverse;
714
715	reverse = calloc(1, sizeof(struct ipsec_rule));
716	if (reverse == NULL)
717		err(1, "calloc");
718
719	if (rule->direction == (u_int8_t)IPSEC_OUT)
720		reverse->direction = (u_int8_t)IPSEC_IN;
721	else
722		reverse->direction = (u_int8_t)IPSEC_OUT;
723
724	reverse->src = copyhost(rule->dst);
725	reverse->dst = copyhost(rule->src);
726	reverse->peer = copyhost(rule->peer);
727	reverse->proto = (u_int8_t)rule->proto;
728
729	if (rule->auth.dstid && (reverse->auth.srcid =
730	    strdup(rule->auth.dstid)) == NULL)
731		err(1, "strdup");
732	if (rule->auth.srcid && (reverse->auth.dstid =
733	    strdup(rule->auth.srcid)) == NULL)
734		err(1, "strdup");
735	reverse->auth.idtype = rule->auth.idtype;
736	reverse->auth.type = rule->auth.type;
737
738	return reverse;
739}
740