xref: /openbsd-src/usr.sbin/sasyncd/conf.y (revision b7041c0781c8668129da8084451ded41b0c43954)
1 /*	$OpenBSD: conf.y,v 1.22 2021/10/24 21:24:19 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 H�kan Olsson.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /* Definitions */
29 %{
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <ctype.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <pwd.h>
40 
41 #include "sasyncd.h"
42 #include "net.h"
43 
44 /* Global configuration context.  */
45 struct cfgstate	cfgstate;
46 
47 /* Local variables */
48 int	conflen = 0;
49 char	*confbuf, *confptr;
50 
51 int	yyparse(void);
52 int	yylex(void);
53 void	yyerror(const char *);
54 unsigned char x2i(unsigned char *);
55 %}
56 
57 %union {
58 	char	*string;
59 	int	 val;
60 	struct {
61 		unsigned char	*data;
62 		int	 len;
63 	} hex;
64 }
65 
66 %token MODE INTERFACE INTERVAL LISTEN ON PORT PEER SHAREDKEY
67 %token Y_SLAVE Y_MASTER INET INET6 FLUSHMODE STARTUP NEVER SYNC
68 %token GROUP SKIPSLAVE CONTROL
69 %token <string> STRING
70 %token <hex>	HEX
71 %token <val>	VALUE
72 %type  <val>	af port mode flushmode ctlmode
73 
74 %%
75 /* Rules */
76 
77 settings	: /* empty */
78 		| settings setting
79 		;
80 
81 af		: /* empty */		{ $$ = AF_UNSPEC; }
82 		| INET			{ $$ = AF_INET; }
83 		| INET6			{ $$ = AF_INET6; }
84 		;
85 
86 port		: /* empty */		{ $$ = SASYNCD_DEFAULT_PORT; }
87 		| PORT VALUE		{ $$ = $2; }
88 		;
89 
90 mode		: Y_MASTER		{ $$ = MASTER; }
91 		| Y_SLAVE		{ $$ = SLAVE; }
92 		;
93 
94 modes		: SKIPSLAVE
95 		{
96 			cfgstate.flags |= SKIP_LOCAL_SAS;
97 			log_msg(2, "config: not syncing SA to peers");
98 		}
99 		| mode
100 		{
101 			const char *m[] = CARPSTATES;
102 			cfgstate.lockedstate = $1;
103 			log_msg(2, "config: mode set to %s", m[$1]);
104 		}
105 		;
106 
107 flushmode	: STARTUP		{ $$ = FM_STARTUP; }
108 		| NEVER			{ $$ = FM_NEVER; }
109 		| SYNC			{ $$ = FM_SYNC; }
110 		;
111 
112 key		: STRING
113 		{
114 			if (cfgstate.sharedkey)
115 				free(cfgstate.sharedkey);
116 			cfgstate.sharedkey = $1;
117 			cfgstate.sharedkey_len = strlen($1) * 8;
118 			log_msg(2, "config: shared ascii key");
119 		}
120 		| HEX
121 		{
122 			if (cfgstate.sharedkey)
123 				free(cfgstate.sharedkey);
124 			cfgstate.sharedkey = $1.data;
125 			cfgstate.sharedkey_len = $1.len * 8;
126 			log_msg(2, "config: %d byte shared hex key", $1.len);
127 		}
128 
129 ctlmode		: STRING
130 		{
131 			/* Compare strings to avoid keywords for daemons */
132 			if (strcmp("isakmpd", $1) == 0)
133 				$$ = CTL_ISAKMPD;
134 			else if (strcmp("iked", $1) == 0)
135 				$$ = CTL_IKED;
136 			else if (strcmp("all", $1) == 0)
137 				$$ = CTL_MASK;
138 			else if (strcmp("none", $1) == 0)
139 				$$ = CTL_NONE;
140 			else {
141 				log_err("config: invalid control mode");
142 				free($1);
143 				YYERROR;
144 			}
145 			log_msg(2, "config: control mode set to %s", $1);
146 			free($1);
147 		}
148 		;
149 
150 setting		: INTERFACE STRING
151 		{
152 			if (cfgstate.carp_ifname)
153 				free(cfgstate.carp_ifname);
154 			cfgstate.carp_ifname = $2;
155 			log_msg(2, "config: interface %s",
156 			    cfgstate.carp_ifname);
157 		}
158 		| GROUP STRING
159 		{
160 			if (cfgstate.carp_ifgroup)
161 				free(cfgstate.carp_ifgroup);
162 			cfgstate.carp_ifgroup = $2;
163 			log_msg(2, "config: group %s",
164 			    cfgstate.carp_ifgroup);
165 		}
166 		| FLUSHMODE flushmode
167 		{
168 			const char *fm[] = { "STARTUP", "NEVER", "SYNC" };
169 			cfgstate.flags |= $2;
170 			log_msg(2, "config: flush mode set to %s", fm[$2]);
171 		}
172 		| PEER STRING
173 		{
174 			struct syncpeer	*peer;
175 			int		 duplicate = 0;
176 
177 			for (peer = LIST_FIRST(&cfgstate.peerlist); peer;
178 			     peer = LIST_NEXT(peer, link))
179 				if (strcmp($2, peer->name) == 0) {
180 					duplicate++;
181 					break;
182 				}
183 			if (duplicate)
184 				free($2);
185 			else {
186 				peer = calloc(1, sizeof *peer);
187 				if (!peer) {
188 					log_err("config: calloc(1, %lu) "
189 					    "failed", sizeof *peer);
190 					free($2);
191 					YYERROR;
192 				}
193 				peer->name = $2;
194 			}
195 			LIST_INSERT_HEAD(&cfgstate.peerlist, peer, link);
196 			cfgstate.peercnt++;
197 			log_msg(2, "config: add peer %s", peer->name);
198 		}
199 		| LISTEN ON STRING af port
200 		{
201 			char pstr[20];
202 
203 			if (cfgstate.listen_on)
204 				free(cfgstate.listen_on);
205 			cfgstate.listen_on = $3;
206 			cfgstate.listen_family = $4;
207 			cfgstate.listen_port = $5;
208 			if ($5 < 1 || $5 > IPPORT_HILASTAUTO) {
209 				cfgstate.listen_port = SASYNCD_DEFAULT_PORT;
210 				log_msg(0, "config: bad port, listen-port "
211 				    "reset to %u", SASYNCD_DEFAULT_PORT);
212 			}
213 			if ($5 != SASYNCD_DEFAULT_PORT)
214 				snprintf(pstr, sizeof pstr, "port %d",$5);
215 			log_msg(2, "config: listen on %s %s%s",
216 			    cfgstate.listen_on, $4 == AF_INET6 ? "(IPv6) " :
217 			    ($4 == AF_INET ? "(IPv4) " : ""),
218 			    $5 != SASYNCD_DEFAULT_PORT ? pstr : "");
219 		}
220 		| MODE modes
221 		| SHAREDKEY key
222 		{
223 			int bits;
224 
225 			bits = cfgstate.sharedkey_len;
226 			if (bits != 128 && bits != 192 && bits != 256) {
227 				log_err("config: bad shared key length %d, "
228 				    "should be 128, 192 or 256 bits\n", bits);
229 				YYERROR;
230 			}
231 			log_msg(2, "config: shared key set");
232 		}
233 		| CONTROL ctlmode
234 		{
235 			cfgstate.flags &= ~CTL_MASK;
236 			cfgstate.flags |= $2;
237 		}
238 		;
239 
240 %%
241 /* Program */
242 
243 struct keyword {
244 	char *name;
245 	int   value;
246 };
247 
248 static int
match_cmp(const void * a,const void * b)249 match_cmp(const void *a, const void *b)
250 {
251 	return strcmp(a, ((const struct keyword *)b)->name);
252 }
253 
254 static int
match(char * token)255 match(char *token)
256 {
257 	/* Sorted */
258 	static const struct keyword keywords[] = {
259 		{ "control", CONTROL },
260 		{ "flushmode", FLUSHMODE },
261 		{ "group", GROUP },
262 		{ "inet", INET },
263 		{ "inet6", INET6 },
264 		{ "interface", INTERFACE },
265 		{ "listen", LISTEN },
266 		{ "master", Y_MASTER },
267 		{ "mode", MODE },
268 		{ "never", NEVER },
269 		{ "on", ON },
270 		{ "peer", PEER },
271 		{ "port", PORT },
272 		{ "sharedkey", SHAREDKEY },
273 		{ "skipslave", SKIPSLAVE },
274 		{ "slave", Y_SLAVE },
275 		{ "startup", STARTUP },
276 		{ "sync", SYNC },
277 	};
278 	const struct keyword *k;
279 
280 	k = bsearch(token, keywords, sizeof keywords / sizeof keywords[0],
281 	    sizeof keywords[0], match_cmp);
282 
283 	return k ? k->value : STRING;
284 }
285 
286 int
yylex(void)287 yylex(void)
288 {
289 	char *p;
290 	int v, i, len;
291 
292 	/* Locate next token */
293 	if (!confptr)
294 		confptr = confbuf;
295 	else {
296 		for (p = confptr; p < confbuf + conflen && *p; p++)
297 			;
298 		if (p == confbuf + conflen)
299 			return 0;
300 		p++;
301 		if (!*p)
302 			return 0;
303 		confptr = p;
304 	}
305 
306 	/* Hex token? */
307 	p = confptr;
308 	if (!strncmp(p, "0x", 2)) {
309 		for (p = confptr + 2; *p; p++)
310 			if (!isxdigit(*p))
311 				goto is_string;
312 		p = confptr + 2;
313 		len = strlen(p) / 2;
314 		if ((yylval.hex.data = calloc(len, sizeof(unsigned char)))
315 		    == NULL) {
316 			log_err("yylex: calloc()");
317 			exit(1);
318 		}
319 		for (i = 0; i < len; i++)
320 			yylval.hex.data[i] = x2i(p + 2 * i);
321 		yylval.hex.len = len;
322 		return HEX;
323 	}
324 
325 	/* Numerical token? */
326 	if (isdigit(*confptr)) {
327 		for (p = confptr; *p; p++)
328 			if (*p == '.' || *p == ':') /* IP address, or bad input */
329 				goto is_string;
330 		v = (int)strtol(confptr, (char **)NULL, 10);
331 		yylval.val = v;
332 		return VALUE;
333 	}
334 
335   is_string:
336 	v = match(confptr);
337 	if (v == STRING) {
338 		yylval.string = strdup(confptr);
339 		if (!yylval.string) {
340 			log_err("yylex: strdup()");
341 			exit(1);
342 		}
343 	}
344 	return v;
345 }
346 
347 int
conf_parse_file(char * cfgfile)348 conf_parse_file(char *cfgfile)
349 {
350 	struct stat	st;
351 	int		fd, r;
352 	char		*buf, *s, *d;
353 	struct passwd	*pw;
354 
355 	if (stat(cfgfile, &st) != 0)
356 		goto bad;
357 
358 	pw = getpwnam(SASYNCD_USER);
359 	if (pw == NULL) {
360 		log_err("getpwnam(%s) failed", SASYNCD_USER);
361 		return 1;
362 	}
363 
364 	/* Valid file? */
365 	if ((st.st_uid && st.st_uid != pw->pw_uid) ||
366 	    ((st.st_mode & S_IFMT) != S_IFREG) ||
367 	    ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0)) {
368 		log_msg(0, "configuration file has bad owner, type or mode");
369 		goto bad;
370 	}
371 
372 	fd = open(cfgfile, O_RDONLY);
373 	if (fd == -1)
374 		goto bad;
375 
376 	conflen = st.st_size;
377 	buf = malloc(conflen + 1);
378 	if (!buf) {
379 		log_err("malloc(%d) failed", conflen + 1);
380 		close(fd);
381 		return 1;
382 	}
383 
384 	if (read(fd, buf, conflen) != conflen) {
385 		log_err("read() failed");
386 		free(buf);
387 		close(fd);
388 		return 1;
389 	}
390 	close(fd);
391 
392 	/* Prepare the buffer somewhat in the way of strsep() */
393 	buf[conflen] = (char)0;
394 	for (s = buf, d = s; s < buf + conflen && *s; s++) {
395 		if (isspace(*s) && isspace(*(s+1)))
396 			continue;
397 		if (*s == '#') {
398 			while (*s != '\n' && s < buf + conflen)
399 				s++;
400 			while (*s == '\n' && s < buf + conflen)
401 				s++;
402 			s--;
403 			continue;
404 		}
405 		if (d == buf && isspace(*s))
406 			continue;
407 		*d++ = *s;
408 	}
409 	*d = (char)0;
410 	for (s = buf; s <= d; s++)
411 		if (isspace(*s))
412 			*s = (char)0;
413 
414 	confbuf = buf;
415 	confptr = NULL;
416 	r = yyparse();
417 	free(buf);
418 
419 	if (!cfgstate.carp_ifgroup)
420 		cfgstate.carp_ifgroup = strdup("carp");
421 
422 	return r;
423 
424   bad:
425 	log_msg(0, "failed to open \"%s\"", cfgfile);
426 	return 1;
427 }
428 
429 unsigned char
x2i(unsigned char * s)430 x2i(unsigned char *s)
431 {
432         char    ss[3];
433 
434         ss[0] = s[0];
435         ss[1] = s[1];
436         ss[2] = 0;
437 
438         return ((unsigned char)strtoul(ss, NULL, 16));
439 }
440 
441 void
yyerror(const char * s)442 yyerror(const char *s)
443 {
444 	fprintf(stderr, "config: %s\n", s);
445 }
446