/* $OpenBSD: conf.y,v 1.21 2019/06/28 13:32:50 deraadt Exp $ */ /* * Copyright (c) 2005 Håkan Olsson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Definitions */ %{ #include #include #include #include #include #include #include #include #include #include #include "sasyncd.h" #include "net.h" /* Global configuration context. */ struct cfgstate cfgstate; /* Local variables */ int conflen = 0; char *confbuf, *confptr; int yyparse(void); int yylex(void); void yyerror(const char *); unsigned char x2i(unsigned char *); %} %union { char *string; int val; struct { unsigned char *data; int len; } hex; } %token MODE INTERFACE INTERVAL LISTEN ON PORT PEER SHAREDKEY %token Y_SLAVE Y_MASTER INET INET6 FLUSHMODE STARTUP NEVER SYNC %token GROUP SKIPSLAVE CONTROL %token STRING %token HEX %token VALUE %type af port mode flushmode ctlmode %% /* Rules */ settings : /* empty */ | settings setting ; af : /* empty */ { $$ = AF_UNSPEC; } | INET { $$ = AF_INET; } | INET6 { $$ = AF_INET6; } ; port : /* empty */ { $$ = SASYNCD_DEFAULT_PORT; } | PORT VALUE { $$ = $2; } ; mode : Y_MASTER { $$ = MASTER; } | Y_SLAVE { $$ = SLAVE; } ; modes : SKIPSLAVE { cfgstate.flags |= SKIP_LOCAL_SAS; log_msg(2, "config: not syncing SA to peers"); } | mode { const char *m[] = CARPSTATES; cfgstate.lockedstate = $1; log_msg(2, "config: mode set to %s", m[$1]); } ; flushmode : STARTUP { $$ = FM_STARTUP; } | NEVER { $$ = FM_NEVER; } | SYNC { $$ = FM_SYNC; } ; key : STRING { if (cfgstate.sharedkey) free(cfgstate.sharedkey); cfgstate.sharedkey = $1; cfgstate.sharedkey_len = strlen($1) * 8; log_msg(2, "config: shared ascii key"); } | HEX { if (cfgstate.sharedkey) free(cfgstate.sharedkey); cfgstate.sharedkey = $1.data; cfgstate.sharedkey_len = $1.len * 8; log_msg(2, "config: %d byte shared hex key", $1.len); } ctlmode : STRING { /* Compare strings to avoid keywords for daemons */ if (strcmp("isakmpd", $1) == 0) $$ = CTL_ISAKMPD; else if (strcmp("iked", $1) == 0) $$ = CTL_IKED; else if (strcmp("all", $1) == 0) $$ = CTL_MASK; else if (strcmp("none", $1) == 0) $$ = CTL_NONE; else { log_err("config: invalid control mode"); free($1); YYERROR; } log_msg(2, "config: control mode set to %s", $1); free($1); } ; setting : INTERFACE STRING { if (cfgstate.carp_ifname) free(cfgstate.carp_ifname); cfgstate.carp_ifname = $2; log_msg(2, "config: interface %s", cfgstate.carp_ifname); } | GROUP STRING { if (cfgstate.carp_ifgroup) free(cfgstate.carp_ifgroup); cfgstate.carp_ifgroup = $2; log_msg(2, "config: group %s", cfgstate.carp_ifgroup); } | FLUSHMODE flushmode { const char *fm[] = { "STARTUP", "NEVER", "SYNC" }; cfgstate.flags |= $2; log_msg(2, "config: flush mode set to %s", fm[$2]); } | PEER STRING { struct syncpeer *peer; int duplicate = 0; for (peer = LIST_FIRST(&cfgstate.peerlist); peer; peer = LIST_NEXT(peer, link)) if (strcmp($2, peer->name) == 0) { duplicate++; break; } if (duplicate) free($2); else { peer = calloc(1, sizeof *peer); if (!peer) { log_err("config: calloc(1, %lu) " "failed", sizeof *peer); free($2); YYERROR; } peer->name = $2; } LIST_INSERT_HEAD(&cfgstate.peerlist, peer, link); cfgstate.peercnt++; log_msg(2, "config: add peer %s", peer->name); } | LISTEN ON STRING af port { char pstr[20]; if (cfgstate.listen_on) free(cfgstate.listen_on); cfgstate.listen_on = $3; cfgstate.listen_family = $4; cfgstate.listen_port = $5; if ($5 < 1 || $5 > IPPORT_HILASTAUTO) { cfgstate.listen_port = SASYNCD_DEFAULT_PORT; log_msg(0, "config: bad port, listen-port " "reset to %u", SASYNCD_DEFAULT_PORT); } if ($5 != SASYNCD_DEFAULT_PORT) snprintf(pstr, sizeof pstr, "port %d",$5); log_msg(2, "config: listen on %s %s%s", cfgstate.listen_on, $4 == AF_INET6 ? "(IPv6) " : ($4 == AF_INET ? "(IPv4) " : ""), $5 != SASYNCD_DEFAULT_PORT ? pstr : ""); } | MODE modes | SHAREDKEY key { int bits; bits = cfgstate.sharedkey_len; if (bits != 128 && bits != 192 && bits != 256) { log_err("config: bad shared key length %d, " "should be 128, 192 or 256 bits\n", bits); YYERROR; } log_msg(2, "config: shared key set"); } | CONTROL ctlmode { cfgstate.flags &= ~CTL_MASK; cfgstate.flags |= $2; } ; %% /* Program */ struct keyword { char *name; int value; }; static int match_cmp(const void *a, const void *b) { return strcmp(a, ((const struct keyword *)b)->name); } static int match(char *token) { /* Sorted */ static const struct keyword keywords[] = { { "control", CONTROL }, { "flushmode", FLUSHMODE }, { "group", GROUP }, { "inet", INET }, { "inet6", INET6 }, { "interface", INTERFACE }, { "listen", LISTEN }, { "master", Y_MASTER }, { "mode", MODE }, { "never", NEVER }, { "on", ON }, { "peer", PEER }, { "port", PORT }, { "sharedkey", SHAREDKEY }, { "skipslave", SKIPSLAVE }, { "slave", Y_SLAVE }, { "startup", STARTUP }, { "sync", SYNC }, }; const struct keyword *k; k = bsearch(token, keywords, sizeof keywords / sizeof keywords[0], sizeof keywords[0], match_cmp); return k ? k->value : STRING; } int yylex(void) { char *p; int v, i, len; /* Locate next token */ if (!confptr) confptr = confbuf; else { for (p = confptr; p < confbuf + conflen && *p; p++) ; if (p == confbuf + conflen) return 0; p++; if (!*p) return 0; confptr = p; } /* Hex token? */ p = confptr; if (!strncmp(p, "0x", 2)) { for (p = confptr + 2; *p; p++) if (!isxdigit(*p)) goto is_string; p = confptr + 2; len = strlen(p) / 2; if ((yylval.hex.data = calloc(len, sizeof(unsigned char))) == NULL) { log_err("yylex: calloc()"); exit(1); } for (i = 0; i < len; i++) yylval.hex.data[i] = x2i(p + 2 * i); yylval.hex.len = len; return HEX; } /* Numerical token? */ if (isdigit(*confptr)) { for (p = confptr; *p; p++) if (*p == '.' || *p == ':') /* IP address, or bad input */ goto is_string; v = (int)strtol(confptr, (char **)NULL, 10); yylval.val = v; return VALUE; } is_string: v = match(confptr); if (v == STRING) { yylval.string = strdup(confptr); if (!yylval.string) { log_err("yylex: strdup()"); exit(1); } } return v; } int conf_parse_file(char *cfgfile) { struct stat st; int fd, r; char *buf, *s, *d; struct passwd *pw; if (stat(cfgfile, &st) != 0) goto bad; pw = getpwnam(SASYNCD_USER); if (pw == NULL) { log_err("getpwnam(%s) failed", SASYNCD_USER); return 1; } /* Valid file? */ if ((st.st_uid && st.st_uid != pw->pw_uid) || ((st.st_mode & S_IFMT) != S_IFREG) || ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0)) { log_msg(0, "configuration file has bad owner, type or mode"); goto bad; } fd = open(cfgfile, O_RDONLY, 0); if (fd == -1) goto bad; conflen = st.st_size; buf = malloc(conflen + 1); if (!buf) { log_err("malloc(%d) failed", conflen + 1); close(fd); return 1; } if (read(fd, buf, conflen) != conflen) { log_err("read() failed"); free(buf); close(fd); return 1; } close(fd); /* Prepare the buffer somewhat in the way of strsep() */ buf[conflen] = (char)0; for (s = buf, d = s; s < buf + conflen && *s; s++) { if (isspace(*s) && isspace(*(s+1))) continue; if (*s == '#') { while (*s != '\n' && s < buf + conflen) s++; while (*s == '\n' && s < buf + conflen) s++; s--; continue; } if (d == buf && isspace(*s)) continue; *d++ = *s; } *d = (char)0; for (s = buf; s <= d; s++) if (isspace(*s)) *s = (char)0; confbuf = buf; confptr = NULL; r = yyparse(); free(buf); if (!cfgstate.carp_ifgroup) cfgstate.carp_ifgroup = strdup("carp"); return r; bad: log_msg(0, "failed to open \"%s\"", cfgfile); return 1; } unsigned char x2i(unsigned char *s) { char ss[3]; ss[0] = s[0]; ss[1] = s[1]; ss[2] = 0; return ((unsigned char)strtoul(ss, NULL, 16)); } void yyerror(const char *s) { fprintf(stderr, "config: %s\n", s); }