From a241c3837c3991afdde9c9ecfb79cb6e9df3770f Mon Sep 17 00:00:00 2001 From: henning <> Date: Tue, 1 Jun 2004 21:58:09 +0000 Subject: [PATCH] first cut at config file parser for now, one can set the addresses to listen on --- src/usr.sbin/ntpd/Makefile | 4 +- src/usr.sbin/ntpd/config.c | 147 ++++++++++ src/usr.sbin/ntpd/ntp.c | 18 +- src/usr.sbin/ntpd/ntpd.c | 5 +- src/usr.sbin/ntpd/ntpd.h | 14 +- src/usr.sbin/ntpd/parse.y | 540 +++++++++++++++++++++++++++++++++++++ 6 files changed, 723 insertions(+), 5 deletions(-) create mode 100644 src/usr.sbin/ntpd/config.c create mode 100644 src/usr.sbin/ntpd/parse.y diff --git a/src/usr.sbin/ntpd/Makefile b/src/usr.sbin/ntpd/Makefile index f122a489..56866b6e 100644 --- a/src/usr.sbin/ntpd/Makefile +++ b/src/usr.sbin/ntpd/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.2 2004/05/31 13:59:55 henning Exp $ +# $OpenBSD: Makefile,v 1.3 2004/06/01 21:58:08 henning Exp $ .PATH: ${.CURDIR}/.. PROG= ntpd -SRCS= ntpd.c buffer.c log.c imsg.c ntp.c +SRCS= ntpd.c buffer.c log.c imsg.c ntp.c parse.y config.c CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations diff --git a/src/usr.sbin/ntpd/config.c b/src/usr.sbin/ntpd/config.c new file mode 100644 index 00000000..670f0e0b --- /dev/null +++ b/src/usr.sbin/ntpd/config.c @@ -0,0 +1,147 @@ +/* $OpenBSD: config.c,v 1.1 2004/06/01 21:58:08 henning Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ntpd.h" + +int host_v4(const char *, struct sockaddr *, u_int8_t *); +int host_v6(const char *, struct sockaddr *); + +int +check_file_secrecy(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) { + log_warn("cannot stat %s", fname); + return (-1); + } + + if (st.st_uid != 0 && st.st_uid != getuid()) { + log_warnx("%s: owner not root or current user", fname); + return (-1); + } + + if (st.st_mode & (S_IRWXG | S_IRWXO)) { + log_warnx("%s: group/world readable/writeable", fname); + return (-1); + } + + return (0); +} + +int +host(const char *s, struct sockaddr *sa, u_int8_t *len) +{ + int done = 0; + int mask; + char *p, *q, *ps; + + if ((p = strrchr(s, '/')) != NULL) { + errno = 0; + mask = strtol(p+1, &q, 0); + if (errno == ERANGE || !q || *q || mask > 128 || q == (p+1)) { + log_warnx("invalid netmask"); + return (0); + } + if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL) + fatal("host: malloc"); + strlcpy(ps, s, strlen(s) - strlen(p) + 1); + } else { + if ((ps = strdup(s)) == NULL) + fatal("host: strdup"); + mask = 128; + } + + /* IPv4 address? */ + if (!done) + done = host_v4(s, sa, len); + + /* IPv6 address? */ + if (!done) { + done = host_v6(ps, sa); + *len = mask; + } + + free(ps); + + return (done); +} + +int +host_v4(const char *s, struct sockaddr *sa, u_int8_t *len) +{ + struct in_addr ina; + struct sockaddr_in *sa_in; + int bits = 32; + + bzero(&ina, sizeof(struct in_addr)); + if (strrchr(s, '/') != NULL) { + if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) + return (0); + } else { + if (inet_pton(AF_INET, s, &ina) != 1) + return (0); + } + + sa_in = (struct sockaddr_in *)sa; + sa_in->sin_len = sizeof(struct sockaddr_in); + sa_in->sin_family = AF_INET; + sa_in->sin_addr.s_addr = ina.s_addr; + *len = bits; + + return (1); +} + +int +host_v6(const char *s, struct sockaddr *sa) +{ + struct addrinfo hints, *res; + struct sockaddr_in6 *sa_in6; + + sa_in6 = (struct sockaddr_in6 *)sa; + sa_in6->sin6_len = sizeof(struct sockaddr_in6); + bzero(&hints, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(s, "0", &hints, &res) == 0) { + sa_in6->sin6_family = AF_INET6; + memcpy(&sa_in6->sin6_addr, + &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, + sizeof(sa_in6->sin6_addr)); + sa_in6->sin6_scope_id = + ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; + + freeaddrinfo(res); + return (1); + } + + return (0); +} diff --git a/src/usr.sbin/ntpd/ntp.c b/src/usr.sbin/ntpd/ntp.c index a12bb706..78a7d2b9 100644 --- a/src/usr.sbin/ntpd/ntp.c +++ b/src/usr.sbin/ntpd/ntp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ntp.c,v 1.4 2004/06/01 16:27:09 henning Exp $ */ +/* $OpenBSD: ntp.c,v 1.5 2004/06/01 21:58:08 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -178,6 +178,22 @@ setup_listeners(struct servent *se, struct ntpd_conf *conf) } TAILQ_FOREACH(la, &conf->listen_addrs, entry) { + switch (la->sa.ss_family) { + case AF_INET: + if (((struct sockaddr_in *)&la->sa)->sin_port == 0) + ((struct sockaddr_in *)&la->sa)->sin_port = + se->s_port; + break; + case AF_INET6: + if (((struct sockaddr_in6 *)&la->sa)->sin6_port == 0) + ((struct sockaddr_in6 *)&la->sa)->sin6_port = + se->s_port; + break; + default: + fatalx("king bula sez: af borked"); + + } + if ((la->fd = socket(la->sa.ss_family, SOCK_DGRAM, 0)) == -1) fatal("socket"); diff --git a/src/usr.sbin/ntpd/ntpd.c b/src/usr.sbin/ntpd/ntpd.c index 634dc844..a60cb153 100644 --- a/src/usr.sbin/ntpd/ntpd.c +++ b/src/usr.sbin/ntpd/ntpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ntpd.c,v 1.3 2004/06/01 16:27:09 henning Exp $ */ +/* $OpenBSD: ntpd.c,v 1.4 2004/06/01 21:58:08 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -108,6 +108,9 @@ main(int argc, char *argv[]) } } + if (parse_config(conffile, &conf)) + exit(1); + if (geteuid()) errx(1, "need root privileges"); diff --git a/src/usr.sbin/ntpd/ntpd.h b/src/usr.sbin/ntpd/ntpd.h index 80c48b4a..bd5cd05a 100644 --- a/src/usr.sbin/ntpd/ntpd.h +++ b/src/usr.sbin/ntpd/ntpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ntpd.h,v 1.2 2004/06/01 16:27:09 henning Exp $ */ +/* $OpenBSD: ntpd.h,v 1.3 2004/06/01 21:58:09 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -28,6 +28,9 @@ #define READ_BUF_SIZE 65535 +#define NTPD_OPT_VERBOSE 0x0001 +#define NTPD_OPT_VERBOSE2 0x0002 + struct listen_addr { TAILQ_ENTRY(listen_addr) entry; struct sockaddr_storage sa; @@ -36,6 +39,7 @@ struct listen_addr { struct ntpd_conf { TAILQ_HEAD(listen_addrs, listen_addr) listen_addrs; + u_int8_t opts; }; struct buf { @@ -120,3 +124,11 @@ void imsg_free(struct imsg *); /* ntp.c */ pid_t ntp_main(int[2], struct ntpd_conf *); + +/* parse.y */ +int parse_config(char *, struct ntpd_conf *); +int cmdline_symset(char *); + +/* config.c */ +int check_file_secrecy(int, const char *); +int host(const char *, struct sockaddr *, u_int8_t *); diff --git a/src/usr.sbin/ntpd/parse.y b/src/usr.sbin/ntpd/parse.y new file mode 100644 index 00000000..18e3ab37 --- /dev/null +++ b/src/usr.sbin/ntpd/parse.y @@ -0,0 +1,540 @@ +/* $OpenBSD: parse.y,v 1.1 2004/06/01 21:58:09 henning Exp $ */ + +/* + * Copyright (c) 2002, 2003, 2004 Henning Brauer + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ntpd.h" + +static struct ntpd_conf *conf; +static FILE *fin = NULL; +static int lineno = 1; +static int errors = 0; +static int pdebug = 1; +char *infile; + +int yyerror(const char *, ...); +int yyparse(void); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(FILE *); +int lungetc(int); +int findeol(void); +int yylex(void); + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entries; + int used; + int persist; + char *nam; + char *val; +}; + +int symset(const char *, const char *, int); +char *symget(const char *); +int atoul(char *, u_long *); + +typedef struct { + union { + u_int32_t number; + char *string; + struct sockaddr_storage ss; + } v; + int lineno; +} YYSTYPE; + +%} + +%token LISTEN ON +%token ERROR +%token STRING +%type number +%type string +%type address +%% + +grammar : /* empty */ + | grammar '\n' + | grammar conf_main '\n' + | grammar varset '\n' + | grammar error '\n' { errors++; } + ; + +number : STRING { + u_long ulval; + + if (atoul($1, &ulval) == -1) { + yyerror("\"%s\" is not a number", $1); + free($1); + YYERROR; + } else + $$ = ulval; + free($1); + } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) + fatal("string: asprintf"); + free($1); + free($2); + } + | STRING + ; + +varset : STRING '=' string { + if (conf->opts & NTPD_OPT_VERBOSE) + printf("%s = \"%s\"\n", $1, $3); + if (symset($1, $3, 0) == -1) + fatal("cannot store variable"); + free($1); + free($3); + } + ; + +conf_main : LISTEN ON address { + struct listen_addr *la; + + if ((la = calloc(1, sizeof(struct listen_addr))) == + NULL) + fatal("parse conf_main listen on calloc"); + + memcpy(&la->sa, &$3, sizeof(struct sockaddr_storage)); + TAILQ_INSERT_TAIL(&conf->listen_addrs, la, entry); + } + ; + +address : STRING { + u_int8_t len; + + bzero(&$$, sizeof($$)); + if (!host($1, (struct sockaddr *)&$$, &len)) { + yyerror("could not parse address spec \"%s\"", + $1); + free($1); + YYERROR; + } + free($1); + + if (($$.ss_family == AF_INET && len != 32) || + ($$.ss_family == AF_INET6 && len != 128)) { + /* unreachable */ + yyerror("got prefixlen %u, expected %u", + len, $$.ss_family == AF_INET ? 32 : 128); + YYERROR; + } + } + ; + +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + char *nfmt; + + errors = 1; + va_start(ap, fmt); + if (asprintf(&nfmt, "%s:%d: %s", infile, yylval.lineno, fmt) == -1) + fatalx("yyerror asprintf"); + vlog(LOG_CRIT, nfmt, ap); + va_end(ap); + free(nfmt); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "listen", LISTEN}, + { "on", ON} + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) { + if (pdebug > 1) + fprintf(stderr, "%s: %d\n", s, p->k_val); + return (p->k_val); + } else { + if (pdebug > 1) + fprintf(stderr, "string: %s\n", s); + return (STRING); + } +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(FILE *f) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + while ((c = getc(f)) == '\\') { + next = getc(f); + if (next != '\n') { + if (isspace(next)) + yyerror("whitespace after \\"); + ungetc(next, f); + break; + } + yylval.lineno = lineno; + lineno++; + } + if (c == '\t' || c == ' ') { + /* Compress blanks to a single space. */ + do { + c = getc(f); + } while (c == '\t' || c == ' '); + ungetc(c, f); + c = ' '; + } + + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + pushback_index = 0; + + /* skip to either EOF or the first real EOL */ + while (1) { + c = lgetc(fin); + if (c == '\n') { + lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p, *val; + int endc, c; + int token; + +top: + p = buf; + while ((c = lgetc(fin)) == ' ') + ; /* nothing */ + + yylval.lineno = lineno; + if (c == '#') + while ((c = lgetc(fin)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && parsebuf == NULL) { + while (1) { + if ((c = lgetc(fin)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = (char)c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro \"%s\" not defined", buf); + return (findeol()); + } + parsebuf = val; + parseindex = 0; + goto top; + } + + switch (c) { + case '\'': + case '"': + endc = c; + while (1) { + if ((c = lgetc(fin)) == EOF) + return (0); + if (c == endc) { + *p = '\0'; + break; + } + if (c == '\n') { + lineno++; + continue; + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + fatal("yylex: strdup"); + return (STRING); + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && x != '<' && x != '>' && \ + x != '!' && x != '=' && x != '/' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_' || c == '*') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + fatal("yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = lineno; + lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +int +parse_config(char *filename, struct ntpd_conf *xconf) +{ + struct sym *sym, *next; + struct listen_addr *la; + + if ((conf = calloc(1, sizeof(struct ntpd_conf))) == NULL) + fatal(NULL); + lineno = 1; + errors = 0; + TAILQ_INIT(&conf->listen_addrs); + + if ((fin = fopen(filename, "r")) == NULL) { + log_warn("%s", filename); + free(conf); + return (-1); + } + infile = filename; + + yyparse(); + + fclose(fin); + + /* Free macros and check which have not been used. */ + for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { + next = TAILQ_NEXT(sym, entries); + if ((conf->opts & NTPD_OPT_VERBOSE2) && !sym->used) + fprintf(stderr, "warning: macro \"%s\" not " + "used\n", sym->nam); + if (!sym->persist) { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entries); + free(sym); + } + } + + while ((la = TAILQ_FIRST(&xconf->listen_addrs)) != NULL) { + TAILQ_REMOVE(&xconf->listen_addrs, la, entry); + free(la); + } + + while ((la = TAILQ_FIRST(&conf->listen_addrs)) != NULL) { + TAILQ_REMOVE(&conf->listen_addrs, la, entry); + TAILQ_INSERT_TAIL(&xconf->listen_addrs, la, entry); + } + + free(conf); + + return (errors ? -1 : 0); +} + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); + sym = TAILQ_NEXT(sym, entries)) + ; /* nothing */ + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entries); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entries); + return (0); +} + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + size_t len; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + + len = strlen(s) - strlen(val) + 1; + if ((sym = malloc(len)) == NULL) + fatal("cmdline_symset: malloc"); + + strlcpy(sym, s, len); + + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entries) + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + return (NULL); +} + +int +atoul(char *s, u_long *ulvalp) +{ + u_long ulval; + char *ep; + + errno = 0; + ulval = strtoul(s, &ep, 0); + if (s[0] == '\0' || *ep != '\0') + return (-1); + if (errno == ERANGE && ulval == ULONG_MAX) + return (-1); + *ulvalp = ulval; + return (0); +}