1 |
|
|
/* $OpenBSD: conf.y,v 1.19 2017/04/09 02:40:24 jsg 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 |
249 |
|
|
match_cmp(const void *a, const void *b) |
250 |
|
|
{ |
251 |
|
|
return strcmp(a, ((const struct keyword *)b)->name); |
252 |
|
|
} |
253 |
|
|
|
254 |
|
|
static int |
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 |
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 == '.') /* 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 |
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, 0); |
373 |
|
|
if (fd < 0) |
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 |
|
|
continue; |
401 |
|
|
} |
402 |
|
|
if (d == buf && isspace(*s)) |
403 |
|
|
continue; |
404 |
|
|
*d++ = *s; |
405 |
|
|
} |
406 |
|
|
*d = (char)0; |
407 |
|
|
for (s = buf; s <= d; s++) |
408 |
|
|
if (isspace(*s)) |
409 |
|
|
*s = (char)0; |
410 |
|
|
|
411 |
|
|
confbuf = buf; |
412 |
|
|
confptr = NULL; |
413 |
|
|
r = yyparse(); |
414 |
|
|
free(buf); |
415 |
|
|
|
416 |
|
|
if (!cfgstate.carp_ifgroup) |
417 |
|
|
cfgstate.carp_ifgroup = strdup("carp"); |
418 |
|
|
|
419 |
|
|
return r; |
420 |
|
|
|
421 |
|
|
bad: |
422 |
|
|
log_msg(0, "failed to open \"%s\"", cfgfile); |
423 |
|
|
return 1; |
424 |
|
|
} |
425 |
|
|
|
426 |
|
|
unsigned char |
427 |
|
|
x2i(unsigned char *s) |
428 |
|
|
{ |
429 |
|
|
char ss[3]; |
430 |
|
|
|
431 |
|
|
ss[0] = s[0]; |
432 |
|
|
ss[1] = s[1]; |
433 |
|
|
ss[2] = 0; |
434 |
|
|
|
435 |
|
|
return ((unsigned char)strtoul(ss, NULL, 16)); |
436 |
|
|
} |
437 |
|
|
|
438 |
|
|
void |
439 |
|
|
yyerror(const char *s) |
440 |
|
|
{ |
441 |
|
|
fprintf(stderr, "config: %s\n", s); |
442 |
|
|
} |