1*08f6ba19Snaddy /* $OpenBSD: parse.y,v 1.8 2021/10/15 15:01:28 naddy Exp $ */
23b188dabSeric
33b188dabSeric /*
43b188dabSeric * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
53b188dabSeric * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
63b188dabSeric * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
73b188dabSeric * Copyright (c) 2001 Markus Friedl. All rights reserved.
83b188dabSeric * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
93b188dabSeric * Copyright (c) 2001 Theo de Raadt. All rights reserved.
103b188dabSeric *
113b188dabSeric * Permission to use, copy, modify, and distribute this software for any
123b188dabSeric * purpose with or without fee is hereby granted, provided that the above
133b188dabSeric * copyright notice and this permission notice appear in all copies.
143b188dabSeric *
153b188dabSeric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
163b188dabSeric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
173b188dabSeric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
183b188dabSeric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
193b188dabSeric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
203b188dabSeric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
213b188dabSeric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
223b188dabSeric */
233b188dabSeric
243b188dabSeric %{
253b188dabSeric #include <sys/types.h>
263b188dabSeric #include <sys/socket.h>
273b188dabSeric #include <sys/stat.h>
283b188dabSeric #include <sys/ioctl.h>
293b188dabSeric #include <sys/un.h>
303b188dabSeric
313b188dabSeric #include <net/if.h>
323b188dabSeric #include <netinet/in.h>
333b188dabSeric #include <arpa/inet.h>
343b188dabSeric
353b188dabSeric #include <ctype.h>
363b188dabSeric #include <err.h>
373b188dabSeric #include <errno.h>
383b188dabSeric #include <ifaddrs.h>
393b188dabSeric #include <inttypes.h>
403b188dabSeric #include <limits.h>
413b188dabSeric #include <stdio.h>
423b188dabSeric #include <stdlib.h>
433b188dabSeric #include <unistd.h>
443b188dabSeric
453b188dabSeric #include "lpd.h"
463b188dabSeric
473b188dabSeric #include "log.h"
483b188dabSeric
493b188dabSeric TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
503b188dabSeric static struct file {
513b188dabSeric TAILQ_ENTRY(file) entry;
523b188dabSeric FILE *stream;
533b188dabSeric char *name;
543b188dabSeric int lineno;
553b188dabSeric int errors;
563b188dabSeric } *file, *topfile;
573b188dabSeric struct file *pushfile(const char *, int);
583b188dabSeric int popfile(void);
593b188dabSeric int check_file_secrecy(int, const char *);
603b188dabSeric int yyparse(void);
613b188dabSeric int yylex(void);
623b188dabSeric int kw_cmp(const void *, const void *);
633b188dabSeric int lookup(char *);
643b188dabSeric int lgetc(int);
653b188dabSeric int lungetc(int);
663b188dabSeric int findeol(void);
673b188dabSeric int yyerror(const char *, ...)
683b188dabSeric __attribute__((__format__ (printf, 1, 2)))
693b188dabSeric __attribute__((__nonnull__ (1)));
703b188dabSeric
713b188dabSeric TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
723b188dabSeric struct sym {
733b188dabSeric TAILQ_ENTRY(sym) entry;
743b188dabSeric int used;
753b188dabSeric int persist;
763b188dabSeric char *nam;
773b188dabSeric char *val;
783b188dabSeric };
793b188dabSeric int symset(const char *, const char *, int);
803b188dabSeric char *symget(const char *);
813b188dabSeric
823b188dabSeric static int errors = 0;
833b188dabSeric
843b188dabSeric struct lpd_conf *conf = NULL;
853b188dabSeric
863b188dabSeric enum listen_options {
873b188dabSeric LO_FAMILY = 0x000001,
883b188dabSeric LO_PORT = 0x000002,
893b188dabSeric };
903b188dabSeric
913b188dabSeric static struct listen_opts {
923b188dabSeric char *ifx;
933b188dabSeric int family;
943b188dabSeric int proto;
953b188dabSeric in_port_t port;
963b188dabSeric uint32_t options;
973b188dabSeric } listen_opts;
983b188dabSeric
993b188dabSeric static void config_free(struct lpd_conf *);
1003b188dabSeric static void create_listeners(struct listen_opts *);
1013b188dabSeric static void config_listener(struct listener *, struct listen_opts *);
1023b188dabSeric static int local(struct listen_opts *);
1033b188dabSeric static int host_v4(struct listen_opts *);
1043b188dabSeric static int host_v6(struct listen_opts *);
1053b188dabSeric static int host_dns(struct listen_opts *);
1063b188dabSeric static int interface(struct listen_opts *);
1073b188dabSeric static int is_if_in_group(const char *, const char *);
1083b188dabSeric
1093b188dabSeric typedef struct {
1103b188dabSeric union {
1113b188dabSeric int64_t number;
1123b188dabSeric char *string;
1133b188dabSeric struct host *host;
1143b188dabSeric } v;
1153b188dabSeric int lineno;
1163b188dabSeric } YYSTYPE;
1173b188dabSeric
1183b188dabSeric %}
1193b188dabSeric
1203b188dabSeric %token ERROR ARROW INCLUDE
1213b188dabSeric %token LISTEN ON PORT INET4 INET6 LOCAL SOCKET
1223b188dabSeric %token <v.string> STRING
1233b188dabSeric %token <v.number> NUMBER
1243b188dabSeric %type <v.number> family_inet portno
1253b188dabSeric
1263b188dabSeric %%
1273b188dabSeric
1283b188dabSeric grammar : /* empty */
1293b188dabSeric | grammar '\n'
1303b188dabSeric | grammar include '\n'
1313b188dabSeric | grammar varset '\n'
1323b188dabSeric | grammar main '\n'
1333b188dabSeric | grammar error '\n' { file->errors++; }
1343b188dabSeric ;
1353b188dabSeric
1363b188dabSeric include : INCLUDE STRING {
1373b188dabSeric struct file *nfile;
1383b188dabSeric
1393b188dabSeric if ((nfile = pushfile($2, 0)) == NULL) {
1403b188dabSeric yyerror("failed to include file %s", $2);
1413b188dabSeric free($2);
1423b188dabSeric YYERROR;
1433b188dabSeric }
1443b188dabSeric free($2);
1453b188dabSeric
1463b188dabSeric file = nfile;
1473b188dabSeric lungetc('\n');
1483b188dabSeric }
1493b188dabSeric ;
1503b188dabSeric
1513b188dabSeric varset : STRING '=' STRING {
1523b188dabSeric char *s = $1;
1533b188dabSeric while (*s++) {
1543b188dabSeric if (isspace((unsigned char)*s)) {
1553b188dabSeric yyerror("macro name cannot contain "
1563b188dabSeric "whitespace");
1573b188dabSeric free($1);
1583b188dabSeric free($3);
1593b188dabSeric YYERROR;
1603b188dabSeric }
1613b188dabSeric }
1623b188dabSeric if (symset($1, $3, 0) == -1)
1633b188dabSeric fatal("cannot store variable");
1643b188dabSeric free($1);
1653b188dabSeric free($3);
1663b188dabSeric }
1673b188dabSeric ;
1683b188dabSeric
1693b188dabSeric portno : STRING {
1703b188dabSeric struct servent *servent;
1713b188dabSeric servent = getservbyname($1, "tcp");
1723b188dabSeric if (servent == NULL) {
1733b188dabSeric yyerror("invalid port: %s", $1);
1743b188dabSeric free($1);
1753b188dabSeric YYERROR;
1763b188dabSeric }
1773b188dabSeric free($1);
1783b188dabSeric $$ = ntohs(servent->s_port);
1793b188dabSeric }
1803b188dabSeric | NUMBER {
1813b188dabSeric if ($1 <= 0 || $1 > (int)USHRT_MAX) {
1823b188dabSeric yyerror("invalid port: %" PRId64, $1);
1833b188dabSeric YYERROR;
1843b188dabSeric }
1853b188dabSeric $$ = $1;
1863b188dabSeric }
1873b188dabSeric ;
1883b188dabSeric
1893b188dabSeric family_inet : INET4 { $$ = AF_INET; }
1903b188dabSeric | INET6 { $$ = AF_INET6; }
1913b188dabSeric ;
1923b188dabSeric
1933b188dabSeric opt_listen : family_inet {
1943b188dabSeric if (listen_opts.options & LO_FAMILY) {
1953b188dabSeric yyerror("address family already specified");
1963b188dabSeric YYERROR;
1973b188dabSeric }
1983b188dabSeric listen_opts.options |= LO_FAMILY;
1993b188dabSeric listen_opts.family = $1;
2003b188dabSeric }
2013b188dabSeric | PORT portno {
2023b188dabSeric if (listen_opts.options & LO_PORT) {
2033b188dabSeric yyerror("port already specified");
2043b188dabSeric YYERROR;
2053b188dabSeric }
2063b188dabSeric listen_opts.options |= LO_PORT;
2073b188dabSeric listen_opts.port = htons($2);
2083b188dabSeric }
2093b188dabSeric ;
2103b188dabSeric
2113b188dabSeric listener : opt_listen listener
2123b188dabSeric | /* empty */ {
2133b188dabSeric create_listeners(&listen_opts);
2143b188dabSeric }
2153b188dabSeric ;
2163b188dabSeric
2173b188dabSeric main : LISTEN ON STRING {
2183b188dabSeric memset(&listen_opts, 0, sizeof listen_opts);
2193b188dabSeric listen_opts.ifx = $3;
2203b188dabSeric listen_opts.family = AF_UNSPEC;
2213b188dabSeric listen_opts.proto = PROTO_LPR;
2223b188dabSeric listen_opts.port = htons(PORT_LPR);
2233b188dabSeric } listener
2243b188dabSeric ;
2253b188dabSeric %%
2263b188dabSeric
2273b188dabSeric struct keywords {
2283b188dabSeric const char *k_name;
2293b188dabSeric int k_val;
2303b188dabSeric };
2313b188dabSeric
2323b188dabSeric int
yyerror(const char * fmt,...)2333b188dabSeric yyerror(const char *fmt, ...)
2343b188dabSeric {
2353b188dabSeric va_list ap;
2363b188dabSeric char *msg;
2373b188dabSeric
2383b188dabSeric file->errors++;
2393b188dabSeric va_start(ap, fmt);
2403b188dabSeric if (vasprintf(&msg, fmt, ap) == -1)
2413b188dabSeric fatalx("yyerror vasprintf");
2423b188dabSeric va_end(ap);
2433b188dabSeric log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
2443b188dabSeric free(msg);
2453b188dabSeric return (0);
2463b188dabSeric }
2473b188dabSeric
2483b188dabSeric int
kw_cmp(const void * k,const void * e)2493b188dabSeric kw_cmp(const void *k, const void *e)
2503b188dabSeric {
2513b188dabSeric return (strcmp(k, ((const struct keywords *)e)->k_name));
2523b188dabSeric }
2533b188dabSeric
2543b188dabSeric int
lookup(char * s)2553b188dabSeric lookup(char *s)
2563b188dabSeric {
2573b188dabSeric /* this has to be sorted always */
2583b188dabSeric static const struct keywords keywords[] = {
2593b188dabSeric { "include", INCLUDE },
2603b188dabSeric { "inet4", INET4 },
2613b188dabSeric { "inet6", INET6 },
2623b188dabSeric { "listen", LISTEN },
2633b188dabSeric { "on", ON },
2643b188dabSeric { "port", PORT },
2653b188dabSeric { "socket", SOCKET },
2663b188dabSeric };
2673b188dabSeric const struct keywords *p;
2683b188dabSeric
2693b188dabSeric p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
2703b188dabSeric sizeof(keywords[0]), kw_cmp);
2713b188dabSeric
2723b188dabSeric if (p)
2733b188dabSeric return (p->k_val);
2743b188dabSeric else
2753b188dabSeric return (STRING);
2763b188dabSeric }
2773b188dabSeric
2783b188dabSeric #define MAXPUSHBACK 128
2793b188dabSeric
280*08f6ba19Snaddy char *parsebuf;
2813b188dabSeric int parseindex;
282*08f6ba19Snaddy char pushback_buffer[MAXPUSHBACK];
2833b188dabSeric int pushback_index = 0;
2843b188dabSeric
2853b188dabSeric int
lgetc(int quotec)2863b188dabSeric lgetc(int quotec)
2873b188dabSeric {
2883b188dabSeric int c, next;
2893b188dabSeric
2903b188dabSeric if (parsebuf) {
2913b188dabSeric /* Read character from the parsebuffer instead of input. */
2923b188dabSeric if (parseindex >= 0) {
293*08f6ba19Snaddy c = (unsigned char)parsebuf[parseindex++];
2943b188dabSeric if (c != '\0')
2953b188dabSeric return (c);
2963b188dabSeric parsebuf = NULL;
2973b188dabSeric } else
2983b188dabSeric parseindex++;
2993b188dabSeric }
3003b188dabSeric
3013b188dabSeric if (pushback_index)
302*08f6ba19Snaddy return ((unsigned char)pushback_buffer[--pushback_index]);
3033b188dabSeric
3043b188dabSeric if (quotec) {
3053b188dabSeric if ((c = getc(file->stream)) == EOF) {
3063b188dabSeric yyerror("reached end of file while parsing "
3073b188dabSeric "quoted string");
3083b188dabSeric if (file == topfile || popfile() == EOF)
3093b188dabSeric return (EOF);
3103b188dabSeric return (quotec);
3113b188dabSeric }
3123b188dabSeric return (c);
3133b188dabSeric }
3143b188dabSeric
3153b188dabSeric while ((c = getc(file->stream)) == '\\') {
3163b188dabSeric next = getc(file->stream);
3173b188dabSeric if (next != '\n') {
3183b188dabSeric c = next;
3193b188dabSeric break;
3203b188dabSeric }
3213b188dabSeric yylval.lineno = file->lineno;
3223b188dabSeric file->lineno++;
3233b188dabSeric }
3243b188dabSeric
3253b188dabSeric while (c == EOF) {
3263b188dabSeric if (file == topfile || popfile() == EOF)
3273b188dabSeric return (EOF);
3283b188dabSeric c = getc(file->stream);
3293b188dabSeric }
3303b188dabSeric return (c);
3313b188dabSeric }
3323b188dabSeric
3333b188dabSeric int
lungetc(int c)3343b188dabSeric lungetc(int c)
3353b188dabSeric {
3363b188dabSeric if (c == EOF)
3373b188dabSeric return (EOF);
3383b188dabSeric if (parsebuf) {
3393b188dabSeric parseindex--;
3403b188dabSeric if (parseindex >= 0)
3413b188dabSeric return (c);
3423b188dabSeric }
343*08f6ba19Snaddy if (pushback_index + 1 >= MAXPUSHBACK)
3443b188dabSeric return (EOF);
345*08f6ba19Snaddy pushback_buffer[pushback_index++] = c;
346*08f6ba19Snaddy return (c);
3473b188dabSeric }
3483b188dabSeric
3493b188dabSeric int
findeol(void)3503b188dabSeric findeol(void)
3513b188dabSeric {
3523b188dabSeric int c;
3533b188dabSeric
3543b188dabSeric parsebuf = NULL;
3553b188dabSeric pushback_index = 0;
3563b188dabSeric
3573b188dabSeric /* skip to either EOF or the first real EOL */
3583b188dabSeric while (1) {
3593b188dabSeric c = lgetc(0);
3603b188dabSeric if (c == '\n') {
3613b188dabSeric file->lineno++;
3623b188dabSeric break;
3633b188dabSeric }
3643b188dabSeric if (c == EOF)
3653b188dabSeric break;
3663b188dabSeric }
3673b188dabSeric return (ERROR);
3683b188dabSeric }
3693b188dabSeric
3703b188dabSeric int
yylex(void)3713b188dabSeric yylex(void)
3723b188dabSeric {
373*08f6ba19Snaddy char buf[8096];
374*08f6ba19Snaddy char *p, *val;
3753b188dabSeric int quotec, next, c;
3763b188dabSeric int token;
3773b188dabSeric
3783b188dabSeric top:
3793b188dabSeric p = buf;
3803b188dabSeric while ((c = lgetc(0)) == ' ' || c == '\t')
3813b188dabSeric ; /* nothing */
3823b188dabSeric
3833b188dabSeric yylval.lineno = file->lineno;
3843b188dabSeric if (c == '#')
3853b188dabSeric while ((c = lgetc(0)) != '\n' && c != EOF)
3863b188dabSeric ; /* nothing */
3873b188dabSeric if (c == '$' && parsebuf == NULL) {
3883b188dabSeric while (1) {
3893b188dabSeric if ((c = lgetc(0)) == EOF)
3903b188dabSeric return (0);
3913b188dabSeric
3923b188dabSeric if (p + 1 >= buf + sizeof(buf) - 1) {
3933b188dabSeric yyerror("string too long");
3943b188dabSeric return (findeol());
3953b188dabSeric }
3963b188dabSeric if (isalnum(c) || c == '_') {
3973b188dabSeric *p++ = c;
3983b188dabSeric continue;
3993b188dabSeric }
4003b188dabSeric *p = '\0';
4013b188dabSeric lungetc(c);
4023b188dabSeric break;
4033b188dabSeric }
4043b188dabSeric val = symget(buf);
4053b188dabSeric if (val == NULL) {
4063b188dabSeric yyerror("macro '%s' not defined", buf);
4073b188dabSeric return (findeol());
4083b188dabSeric }
4093b188dabSeric parsebuf = val;
4103b188dabSeric parseindex = 0;
4113b188dabSeric goto top;
4123b188dabSeric }
4133b188dabSeric
4143b188dabSeric switch (c) {
4153b188dabSeric case '\'':
4163b188dabSeric case '"':
4173b188dabSeric quotec = c;
4183b188dabSeric while (1) {
4193b188dabSeric if ((c = lgetc(quotec)) == EOF)
4203b188dabSeric return (0);
4213b188dabSeric if (c == '\n') {
4223b188dabSeric file->lineno++;
4233b188dabSeric continue;
4243b188dabSeric } else if (c == '\\') {
4253b188dabSeric if ((next = lgetc(quotec)) == EOF)
4263b188dabSeric return (0);
427a1533359Ssashan if (next == quotec || next == ' ' ||
428a1533359Ssashan next == '\t')
4293b188dabSeric c = next;
4303b188dabSeric else if (next == '\n') {
4313b188dabSeric file->lineno++;
4323b188dabSeric continue;
4333b188dabSeric } else
4343b188dabSeric lungetc(next);
4353b188dabSeric } else if (c == quotec) {
4363b188dabSeric *p = '\0';
4373b188dabSeric break;
4383b188dabSeric } else if (c == '\0') {
4393b188dabSeric yyerror("syntax error");
4403b188dabSeric return (findeol());
4413b188dabSeric }
4423b188dabSeric if (p + 1 >= buf + sizeof(buf) - 1) {
4433b188dabSeric yyerror("string too long");
4443b188dabSeric return (findeol());
4453b188dabSeric }
4463b188dabSeric *p++ = c;
4473b188dabSeric }
4483b188dabSeric yylval.v.string = strdup(buf);
4493b188dabSeric if (yylval.v.string == NULL)
450a062aa9dSkrw err(1, "%s", __func__);
4513b188dabSeric return (STRING);
4523b188dabSeric }
4533b188dabSeric
4543b188dabSeric #define allowed_to_end_number(x) \
4553b188dabSeric (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
4563b188dabSeric
4573b188dabSeric if (c == '-' || isdigit(c)) {
4583b188dabSeric do {
4593b188dabSeric *p++ = c;
460915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) {
4613b188dabSeric yyerror("string too long");
4623b188dabSeric return (findeol());
4633b188dabSeric }
4643b188dabSeric } while ((c = lgetc(0)) != EOF && isdigit(c));
4653b188dabSeric lungetc(c);
4663b188dabSeric if (p == buf + 1 && buf[0] == '-')
4673b188dabSeric goto nodigits;
4683b188dabSeric if (c == EOF || allowed_to_end_number(c)) {
4693b188dabSeric const char *errstr = NULL;
4703b188dabSeric
4713b188dabSeric *p = '\0';
4723b188dabSeric yylval.v.number = strtonum(buf, LLONG_MIN,
4733b188dabSeric LLONG_MAX, &errstr);
4743b188dabSeric if (errstr) {
4753b188dabSeric yyerror("\"%s\" invalid number: %s",
4763b188dabSeric buf, errstr);
4773b188dabSeric return (findeol());
4783b188dabSeric }
4793b188dabSeric return (NUMBER);
4803b188dabSeric } else {
4813b188dabSeric nodigits:
4823b188dabSeric while (p > buf + 1)
483*08f6ba19Snaddy lungetc((unsigned char)*--p);
484*08f6ba19Snaddy c = (unsigned char)*--p;
4853b188dabSeric if (c == '-')
4863b188dabSeric return (c);
4873b188dabSeric }
4883b188dabSeric }
4893b188dabSeric
4903b188dabSeric if (c == '=') {
4913b188dabSeric if ((c = lgetc(0)) != EOF && c == '>')
4923b188dabSeric return (ARROW);
4933b188dabSeric lungetc(c);
4943b188dabSeric c = '=';
4953b188dabSeric }
4963b188dabSeric
4973b188dabSeric #define allowed_in_string(x) \
4983b188dabSeric (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
4993b188dabSeric x != '{' && x != '}' && x != '<' && x != '>' && \
5003b188dabSeric x != '!' && x != '=' && x != '#' && \
5013b188dabSeric x != ','))
5023b188dabSeric
5033b188dabSeric if (isalnum(c) || c == ':' || c == '_') {
5043b188dabSeric do {
5053b188dabSeric *p++ = c;
506915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) {
5073b188dabSeric yyerror("string too long");
5083b188dabSeric return (findeol());
5093b188dabSeric }
5103b188dabSeric } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
5113b188dabSeric lungetc(c);
5123b188dabSeric *p = '\0';
5133b188dabSeric if ((token = lookup(buf)) == STRING)
5143b188dabSeric if ((yylval.v.string = strdup(buf)) == NULL)
515a062aa9dSkrw err(1, "%s", __func__);
5163b188dabSeric return (token);
5173b188dabSeric }
5183b188dabSeric if (c == '\n') {
5193b188dabSeric yylval.lineno = file->lineno;
5203b188dabSeric file->lineno++;
5213b188dabSeric }
5223b188dabSeric if (c == EOF)
5233b188dabSeric return (0);
5243b188dabSeric return (c);
5253b188dabSeric }
5263b188dabSeric
5273b188dabSeric int
check_file_secrecy(int fd,const char * fname)5283b188dabSeric check_file_secrecy(int fd, const char *fname)
5293b188dabSeric {
5303b188dabSeric struct stat st;
5313b188dabSeric
5323b188dabSeric if (fstat(fd, &st)) {
5333b188dabSeric log_warn("warn: cannot stat %s", fname);
5343b188dabSeric return (-1);
5353b188dabSeric }
5363b188dabSeric if (st.st_uid != 0 && st.st_uid != getuid()) {
5373b188dabSeric log_warnx("warn: %s: owner not root or current user", fname);
5383b188dabSeric return (-1);
5393b188dabSeric }
5403b188dabSeric if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
5413b188dabSeric log_warnx("warn: %s: group/world readable/writeable", fname);
5423b188dabSeric return (-1);
5433b188dabSeric }
5443b188dabSeric return (0);
5453b188dabSeric }
5463b188dabSeric
5473b188dabSeric struct file *
pushfile(const char * name,int secret)5483b188dabSeric pushfile(const char *name, int secret)
5493b188dabSeric {
5503b188dabSeric struct file *nfile;
5513b188dabSeric
5523b188dabSeric if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
5536a3d55f9Skrw log_warn("%s", __func__);
5543b188dabSeric return (NULL);
5553b188dabSeric }
5563b188dabSeric if ((nfile->name = strdup(name)) == NULL) {
5576a3d55f9Skrw log_warn("%s", __func__);
5583b188dabSeric free(nfile);
5593b188dabSeric return (NULL);
5603b188dabSeric }
5613b188dabSeric if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
5626a3d55f9Skrw log_warn("%s: %s", __func__, nfile->name);
5633b188dabSeric free(nfile->name);
5643b188dabSeric free(nfile);
5653b188dabSeric return (NULL);
5663b188dabSeric } else if (secret &&
5673b188dabSeric check_file_secrecy(fileno(nfile->stream), nfile->name)) {
5683b188dabSeric fclose(nfile->stream);
5693b188dabSeric free(nfile->name);
5703b188dabSeric free(nfile);
5713b188dabSeric return (NULL);
5723b188dabSeric }
5733b188dabSeric nfile->lineno = 1;
5743b188dabSeric TAILQ_INSERT_TAIL(&files, nfile, entry);
5753b188dabSeric return (nfile);
5763b188dabSeric }
5773b188dabSeric
5783b188dabSeric int
popfile(void)5793b188dabSeric popfile(void)
5803b188dabSeric {
5813b188dabSeric struct file *prev;
5823b188dabSeric
5833b188dabSeric if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
5843b188dabSeric prev->errors += file->errors;
5853b188dabSeric
5863b188dabSeric TAILQ_REMOVE(&files, file, entry);
5873b188dabSeric fclose(file->stream);
5883b188dabSeric free(file->name);
5893b188dabSeric free(file);
5903b188dabSeric file = prev;
5913b188dabSeric return (file ? 0 : EOF);
5923b188dabSeric }
5933b188dabSeric
5943b188dabSeric struct lpd_conf *
parse_config(const char * filename,int verbose)5953b188dabSeric parse_config(const char *filename, int verbose)
5963b188dabSeric {
5973b188dabSeric struct sym *sym, *next;
5983b188dabSeric
5993b188dabSeric conf = calloc(1, sizeof(*conf));
6003b188dabSeric if (conf == NULL)
6013b188dabSeric return NULL;
6023b188dabSeric TAILQ_INIT(&conf->listeners);
6033b188dabSeric
6043b188dabSeric errors = 0;
6053b188dabSeric
6063b188dabSeric if ((file = pushfile(filename, 0)) == NULL) {
6073b188dabSeric config_free(conf);
6083b188dabSeric return NULL;
6093b188dabSeric }
6103b188dabSeric topfile = file;
6113b188dabSeric
6123b188dabSeric /*
6133b188dabSeric * parse configuration
6143b188dabSeric */
6153b188dabSeric setservent(1);
6163b188dabSeric yyparse();
6173b188dabSeric errors = file->errors;
6183b188dabSeric popfile();
6193b188dabSeric endservent();
6203b188dabSeric
6213b188dabSeric /* Free macros and check which have not been used. */
6223b188dabSeric TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
6233b188dabSeric if ((verbose) && !sym->used)
6243b188dabSeric log_warnx("warning: macro '%s' not used\n", sym->nam);
6253b188dabSeric if (!sym->persist) {
6263b188dabSeric free(sym->nam);
6273b188dabSeric free(sym->val);
6283b188dabSeric TAILQ_REMOVE(&symhead, sym, entry);
6293b188dabSeric free(sym);
6303b188dabSeric }
6313b188dabSeric }
6323b188dabSeric
6333b188dabSeric if (errors) {
6343b188dabSeric config_free(conf);
6353b188dabSeric return NULL;
6363b188dabSeric }
6373b188dabSeric
6383b188dabSeric return conf;
6393b188dabSeric }
6403b188dabSeric
6413b188dabSeric int
symset(const char * nam,const char * val,int persist)6423b188dabSeric symset(const char *nam, const char *val, int persist)
6433b188dabSeric {
6443b188dabSeric struct sym *sym;
6453b188dabSeric
6463b188dabSeric TAILQ_FOREACH(sym, &symhead, entry) {
6473b188dabSeric if (strcmp(nam, sym->nam) == 0)
6483b188dabSeric break;
6493b188dabSeric }
6503b188dabSeric
6513b188dabSeric if (sym != NULL) {
6523b188dabSeric if (sym->persist == 1)
6533b188dabSeric return (0);
6543b188dabSeric else {
6553b188dabSeric free(sym->nam);
6563b188dabSeric free(sym->val);
6573b188dabSeric TAILQ_REMOVE(&symhead, sym, entry);
6583b188dabSeric free(sym);
6593b188dabSeric }
6603b188dabSeric }
6613b188dabSeric if ((sym = calloc(1, sizeof(*sym))) == NULL)
6623b188dabSeric return (-1);
6633b188dabSeric
6643b188dabSeric sym->nam = strdup(nam);
6653b188dabSeric if (sym->nam == NULL) {
6663b188dabSeric free(sym);
6673b188dabSeric return (-1);
6683b188dabSeric }
6693b188dabSeric sym->val = strdup(val);
6703b188dabSeric if (sym->val == NULL) {
6713b188dabSeric free(sym->nam);
6723b188dabSeric free(sym);
6733b188dabSeric return (-1);
6743b188dabSeric }
6753b188dabSeric sym->used = 0;
6763b188dabSeric sym->persist = persist;
6773b188dabSeric TAILQ_INSERT_TAIL(&symhead, sym, entry);
6783b188dabSeric return (0);
6793b188dabSeric }
6803b188dabSeric
6813b188dabSeric int
cmdline_symset(char * s)6823b188dabSeric cmdline_symset(char *s)
6833b188dabSeric {
6843b188dabSeric char *sym, *val;
6853b188dabSeric int ret;
6863b188dabSeric
6873b188dabSeric if ((val = strrchr(s, '=')) == NULL)
6883b188dabSeric return (-1);
689ed1b9eb8Smiko sym = strndup(s, val - s);
690ed1b9eb8Smiko if (sym == NULL)
691ed1b9eb8Smiko errx(1, "%s: strndup", __func__);
6923b188dabSeric ret = symset(sym, val + 1, 1);
6933b188dabSeric free(sym);
6943b188dabSeric
6953b188dabSeric return (ret);
6963b188dabSeric }
6973b188dabSeric
6983b188dabSeric char *
symget(const char * nam)6993b188dabSeric symget(const char *nam)
7003b188dabSeric {
7013b188dabSeric struct sym *sym;
7023b188dabSeric
7033b188dabSeric TAILQ_FOREACH(sym, &symhead, entry) {
7043b188dabSeric if (strcmp(nam, sym->nam) == 0) {
7053b188dabSeric sym->used = 1;
7063b188dabSeric return (sym->val);
7073b188dabSeric }
7083b188dabSeric }
7093b188dabSeric return (NULL);
7103b188dabSeric }
7113b188dabSeric
7123b188dabSeric static void
config_free(struct lpd_conf * c)7133b188dabSeric config_free(struct lpd_conf *c)
7143b188dabSeric {
7153b188dabSeric struct listener *l;
7163b188dabSeric
7173b188dabSeric while ((l = TAILQ_FIRST(&c->listeners))) {
7183b188dabSeric TAILQ_REMOVE(&c->listeners, l, entry);
7193b188dabSeric free(l);
7203b188dabSeric }
7213b188dabSeric free(c);
7223b188dabSeric }
7233b188dabSeric
7243b188dabSeric static void
create_listeners(struct listen_opts * lo)7253b188dabSeric create_listeners(struct listen_opts *lo)
7263b188dabSeric {
7273b188dabSeric if (local(lo))
7283b188dabSeric return;
7293b188dabSeric if (interface(lo))
7303b188dabSeric return;
7313b188dabSeric if (host_v4(lo))
7323b188dabSeric return;
7333b188dabSeric if (host_v6(lo))
7343b188dabSeric return;
7353b188dabSeric if (host_dns(lo))
7363b188dabSeric return;
7373b188dabSeric
7383b188dabSeric errx(1, "invalid virtual ip or interface: %s", lo->ifx);
7393b188dabSeric }
7403b188dabSeric
7413b188dabSeric static void
config_listener(struct listener * l,struct listen_opts * lo)7423b188dabSeric config_listener(struct listener *l, struct listen_opts *lo)
7433b188dabSeric {
7443b188dabSeric l->sock = -1;
7453b188dabSeric l->proto = lo->proto;
7463b188dabSeric
7473b188dabSeric TAILQ_INSERT_TAIL(&conf->listeners, l, entry);
7483b188dabSeric }
7493b188dabSeric
7503b188dabSeric static int
local(struct listen_opts * lo)7513b188dabSeric local(struct listen_opts *lo)
7523b188dabSeric {
7533b188dabSeric struct sockaddr_un *sun;
7543b188dabSeric struct listener *h;
7553b188dabSeric
7563b188dabSeric if (lo->family != AF_UNSPEC && lo->family != AF_LOCAL)
7573b188dabSeric return 0;
7583b188dabSeric
7593b188dabSeric if (lo->ifx[0] != '/')
7603b188dabSeric return 0;
7613b188dabSeric
7623b188dabSeric h = calloc(1, sizeof(*h));
7633b188dabSeric sun = (struct sockaddr_un *)&h->ss;
7643b188dabSeric sun->sun_len = sizeof(*sun);
7653b188dabSeric sun->sun_family = AF_LOCAL;
7663b188dabSeric if (strlcpy(sun->sun_path, lo->ifx, sizeof(sun->sun_path))
7673b188dabSeric >= sizeof(sun->sun_path))
7683b188dabSeric fatalx("path too long");
7693b188dabSeric
7703b188dabSeric config_listener(h, lo);
7713b188dabSeric
7723b188dabSeric return (1);
7733b188dabSeric }
7743b188dabSeric
7753b188dabSeric static int
host_v4(struct listen_opts * lo)7763b188dabSeric host_v4(struct listen_opts *lo)
7773b188dabSeric {
7783b188dabSeric struct in_addr ina;
7793b188dabSeric struct sockaddr_in *sain;
7803b188dabSeric struct listener *h;
7813b188dabSeric
7823b188dabSeric if (lo->family != AF_UNSPEC && lo->family != AF_INET)
7833b188dabSeric return (0);
7843b188dabSeric
7853b188dabSeric memset(&ina, 0, sizeof(ina));
7863b188dabSeric if (inet_pton(AF_INET, lo->ifx, &ina) != 1)
7873b188dabSeric return (0);
7883b188dabSeric
7893b188dabSeric h = calloc(1, sizeof(*h));
7903b188dabSeric sain = (struct sockaddr_in *)&h->ss;
7913b188dabSeric sain->sin_len = sizeof(struct sockaddr_in);
7923b188dabSeric sain->sin_family = AF_INET;
7933b188dabSeric sain->sin_addr.s_addr = ina.s_addr;
7943b188dabSeric sain->sin_port = lo->port;
7953b188dabSeric
7963b188dabSeric config_listener(h, lo);
7973b188dabSeric
7983b188dabSeric return (1);
7993b188dabSeric }
8003b188dabSeric
8013b188dabSeric static int
host_v6(struct listen_opts * lo)8023b188dabSeric host_v6(struct listen_opts *lo)
8033b188dabSeric {
8043b188dabSeric struct in6_addr ina6;
8053b188dabSeric struct sockaddr_in6 *sin6;
8063b188dabSeric struct listener *h;
8073b188dabSeric
8083b188dabSeric if (lo->family != AF_UNSPEC && lo->family != AF_INET6)
8093b188dabSeric return (0);
8103b188dabSeric
8113b188dabSeric memset(&ina6, 0, sizeof(ina6));
8123b188dabSeric if (inet_pton(AF_INET6, lo->ifx, &ina6) != 1)
8133b188dabSeric return (0);
8143b188dabSeric
8153b188dabSeric h = calloc(1, sizeof(*h));
8163b188dabSeric sin6 = (struct sockaddr_in6 *)&h->ss;
8173b188dabSeric sin6->sin6_len = sizeof(struct sockaddr_in6);
8183b188dabSeric sin6->sin6_family = AF_INET6;
8193b188dabSeric sin6->sin6_port = lo->port;
8203b188dabSeric memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
8213b188dabSeric
8223b188dabSeric config_listener(h, lo);
8233b188dabSeric
8243b188dabSeric return (1);
8253b188dabSeric }
8263b188dabSeric
8273b188dabSeric static int
host_dns(struct listen_opts * lo)8283b188dabSeric host_dns(struct listen_opts *lo)
8293b188dabSeric {
8303b188dabSeric struct addrinfo hints, *res0, *res;
8313b188dabSeric int error, cnt = 0;
8323b188dabSeric struct sockaddr_in *sain;
8333b188dabSeric struct sockaddr_in6 *sin6;
8343b188dabSeric struct listener *h;
8353b188dabSeric
8363b188dabSeric memset(&hints, 0, sizeof(hints));
8373b188dabSeric hints.ai_family = lo->family;
8383b188dabSeric hints.ai_socktype = SOCK_STREAM;
8393b188dabSeric hints.ai_flags = AI_ADDRCONFIG;
8403b188dabSeric error = getaddrinfo(lo->ifx, NULL, &hints, &res0);
8413b188dabSeric if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
8423b188dabSeric return (0);
8433b188dabSeric if (error) {
8443b188dabSeric log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx,
8453b188dabSeric gai_strerror(error));
8463b188dabSeric return (-1);
8473b188dabSeric }
8483b188dabSeric
8493b188dabSeric for (res = res0; res; res = res->ai_next) {
8503b188dabSeric if (res->ai_family != AF_INET &&
8513b188dabSeric res->ai_family != AF_INET6)
8523b188dabSeric continue;
8533b188dabSeric h = calloc(1, sizeof(*h));
8543b188dabSeric h->ss.ss_family = res->ai_family;
8553b188dabSeric if (res->ai_family == AF_INET) {
8563b188dabSeric sain = (struct sockaddr_in *)&h->ss;
8573b188dabSeric sain->sin_len = sizeof(struct sockaddr_in);
8583b188dabSeric sain->sin_addr.s_addr = ((struct sockaddr_in *)
8593b188dabSeric res->ai_addr)->sin_addr.s_addr;
8603b188dabSeric sain->sin_port = lo->port;
8613b188dabSeric } else {
8623b188dabSeric sin6 = (struct sockaddr_in6 *)&h->ss;
8633b188dabSeric sin6->sin6_len = sizeof(struct sockaddr_in6);
8643b188dabSeric memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
8653b188dabSeric res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
8663b188dabSeric sin6->sin6_port = lo->port;
8673b188dabSeric }
8683b188dabSeric
8693b188dabSeric config_listener(h, lo);
8703b188dabSeric
8713b188dabSeric cnt++;
8723b188dabSeric }
8733b188dabSeric
8743b188dabSeric freeaddrinfo(res0);
8753b188dabSeric return (cnt);
8763b188dabSeric }
8773b188dabSeric
8783b188dabSeric static int
interface(struct listen_opts * lo)8793b188dabSeric interface(struct listen_opts *lo)
8803b188dabSeric {
8813b188dabSeric struct ifaddrs *ifap, *p;
8823b188dabSeric struct sockaddr_in *sain;
8833b188dabSeric struct sockaddr_in6 *sin6;
8843b188dabSeric struct listener *h;
8853b188dabSeric int ret = 0;
8863b188dabSeric
8873b188dabSeric if (getifaddrs(&ifap) == -1)
8883b188dabSeric fatal("getifaddrs");
8893b188dabSeric
8903b188dabSeric for (p = ifap; p != NULL; p = p->ifa_next) {
8913b188dabSeric if (p->ifa_addr == NULL)
8923b188dabSeric continue;
8933b188dabSeric if (strcmp(p->ifa_name, lo->ifx) != 0 &&
8943b188dabSeric !is_if_in_group(p->ifa_name, lo->ifx))
8953b188dabSeric continue;
8963b188dabSeric if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family)
8973b188dabSeric continue;
8983b188dabSeric
8993b188dabSeric h = calloc(1, sizeof(*h));
9003b188dabSeric
9013b188dabSeric switch (p->ifa_addr->sa_family) {
9023b188dabSeric case AF_INET:
9033b188dabSeric sain = (struct sockaddr_in *)&h->ss;
9043b188dabSeric *sain = *(struct sockaddr_in *)p->ifa_addr;
9053b188dabSeric sain->sin_len = sizeof(struct sockaddr_in);
9063b188dabSeric sain->sin_port = lo->port;
9073b188dabSeric break;
9083b188dabSeric
9093b188dabSeric case AF_INET6:
9103b188dabSeric sin6 = (struct sockaddr_in6 *)&h->ss;
9113b188dabSeric *sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
9123b188dabSeric sin6->sin6_len = sizeof(struct sockaddr_in6);
9133b188dabSeric sin6->sin6_port = lo->port;
9143b188dabSeric break;
9153b188dabSeric
9163b188dabSeric default:
9173b188dabSeric free(h);
9183b188dabSeric continue;
9193b188dabSeric }
9203b188dabSeric
9213b188dabSeric config_listener(h, lo);
9223b188dabSeric ret = 1;
9233b188dabSeric }
9243b188dabSeric
9253b188dabSeric freeifaddrs(ifap);
9263b188dabSeric
9273b188dabSeric return ret;
9283b188dabSeric }
9293b188dabSeric
9303b188dabSeric static int
is_if_in_group(const char * ifname,const char * groupname)9313b188dabSeric is_if_in_group(const char *ifname, const char *groupname)
9323b188dabSeric {
9333b188dabSeric unsigned int len;
9343b188dabSeric struct ifgroupreq ifgr;
9353b188dabSeric struct ifg_req *ifg;
9363b188dabSeric int s;
9373b188dabSeric int ret = 0;
9383b188dabSeric
939df69c215Sderaadt if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
9403b188dabSeric err(1, "socket");
9413b188dabSeric
9423b188dabSeric memset(&ifgr, 0, sizeof(ifgr));
9433b188dabSeric if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ)
9443b188dabSeric errx(1, "interface name too large");
9453b188dabSeric
9463b188dabSeric if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) {
9473b188dabSeric if (errno == EINVAL || errno == ENOTTY)
9483b188dabSeric goto end;
9493b188dabSeric err(1, "SIOCGIFGROUP");
9503b188dabSeric }
9513b188dabSeric
9523b188dabSeric len = ifgr.ifgr_len;
9533b188dabSeric ifgr.ifgr_groups = calloc(len/sizeof(struct ifg_req),
9543b188dabSeric sizeof(struct ifg_req));
9553b188dabSeric if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1)
9563b188dabSeric err(1, "SIOCGIFGROUP");
9573b188dabSeric
9583b188dabSeric ifg = ifgr.ifgr_groups;
9593b188dabSeric for (; ifg && len >= sizeof(struct ifg_req); ifg++) {
9603b188dabSeric len -= sizeof(struct ifg_req);
9613b188dabSeric if (strcmp(ifg->ifgrq_group, groupname) == 0) {
9623b188dabSeric ret = 1;
9633b188dabSeric break;
9643b188dabSeric }
9653b188dabSeric }
9663b188dabSeric free(ifgr.ifgr_groups);
9673b188dabSeric
9683b188dabSeric end:
9693b188dabSeric close(s);
9703b188dabSeric return ret;
9713b188dabSeric }
972