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