diff options
Diffstat (limited to 'bin/conf.c')
| -rw-r--r-- | bin/conf.c | 1360 |
1 files changed, 0 insertions, 1360 deletions
diff --git a/bin/conf.c b/bin/conf.c deleted file mode 100644 index 3c6fadb5fa45..000000000000 --- a/bin/conf.c +++ /dev/null @@ -1,1360 +0,0 @@ -/* $NetBSD: conf.c,v 1.10 2025/02/11 17:48:30 christos Exp $ */ - -/*- - * Copyright (c) 2015 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Christos Zoulas. - * - * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``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 FOUNDATION OR CONTRIBUTORS - * 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. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_SYS_CDEFS_H -#include <sys/cdefs.h> -#endif -__RCSID("$NetBSD: conf.c,v 1.10 2025/02/11 17:48:30 christos Exp $"); - -#include <stdio.h> -#ifdef HAVE_LIBUTIL_H -#include <libutil.h> -#endif -#ifdef HAVE_UTIL_H -#include <util.h> -#endif -#include <string.h> -#include <ctype.h> -#include <inttypes.h> -#include <netdb.h> -#include <unistd.h> -#include <pwd.h> -#include <syslog.h> -#include <errno.h> -#include <stdlib.h> -#include <limits.h> -#include <ifaddrs.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include <net/if.h> -#include <net/route.h> -#include <sys/socket.h> -#include <dirent.h> - -#include "bl.h" -#include "internal.h" -#include "support.h" -#include "conf.h" - - -struct sockaddr_if { - uint8_t sif_len; - sa_family_t sif_family; - in_port_t sif_port; - char sif_name[16]; -}; - -#define SIF_NAME(a) \ - ((const struct sockaddr_if *)(const void *)(a))->sif_name - -static int conf_is_interface(const char *); - -#define FSTAR -1 -#define FEQUAL -2 - -static void -advance(char **p) -{ - char *ep = *p; - while (*ep && !isspace((unsigned char)*ep)) - ep++; - while (*ep && isspace((unsigned char)*ep)) - *ep++ = '\0'; - *p = ep; -} - -static int -conf_getnum(const char *f, size_t l, bool local, void *rp, const char *name, - const char *p) -{ - int e; - intmax_t im; - int *r = rp; - - if (strcmp(p, "*") == 0) { - *r = FSTAR; - return 0; - } - if (strcmp(p, "=") == 0) { - if (local) - goto out; - *r = FEQUAL; - return 0; - } - - im = strtoi(p, NULL, 0, 0, INT_MAX, &e); - if (e == 0) { - *r = (int)im; - return 0; - } - - if (f == NULL) - return -1; - (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l, - name, p); - return -1; -out: - (*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config", - __func__, f, l, name); - return -1; - -} - -static int -conf_getnfail(const char *f, size_t l, bool local, struct conf *c, - const char *p) -{ - return conf_getnum(f, l, local, &c->c_nfail, "nfail", p); -} - -static int -conf_getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p) -{ - int e; - char *ep; - intmax_t tot, im; - - tot = 0; - if (strcmp(p, "*") == 0) { - c->c_duration = FSTAR; - return 0; - } - if (strcmp(p, "=") == 0) { - if (local) - goto out; - c->c_duration = FEQUAL; - return 0; - } -again: - im = strtoi(p, &ep, 0, 0, INT_MAX, &e); - - if (e == ENOTSUP) { - switch (*ep) { - case 'd': - im *= 24; - /*FALLTHROUGH*/ - case 'h': - im *= 60; - /*FALLTHROUGH*/ - case 'm': - im *= 60; - /*FALLTHROUGH*/ - case 's': - e = 0; - tot += im; - if (ep[1] != '\0') { - p = ep + 2; - goto again; - } - break; - } - } else - tot = im; - - if (e == 0) { - c->c_duration = (int)tot; - return 0; - } - - if (f == NULL) - return -1; - (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p); - return -1; -out: - (*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local" - " config", __func__, f, l); - return -1; - -} - -static int -conf_getport(const char *f, size_t l, bool local, void *r, const char *p) -{ - struct servent *sv; - - // XXX: Pass in the proto instead - if ((sv = getservbyname(p, "tcp")) != NULL) { - *(int *)r = ntohs(sv->s_port); - return 0; - } - if ((sv = getservbyname(p, "udp")) != NULL) { - *(int *)r = ntohs(sv->s_port); - return 0; - } - - return conf_getnum(f, l, local, r, "service", p); -} - -static int -conf_getmask(const char *f, size_t l, bool local, const char **p, int *mask) -{ - char *d; - const char *s = *p; - - if ((d = strchr(s, ':')) != NULL) { - *d++ = '\0'; - *p = d; - } - if ((d = strchr(s, '/')) == NULL) { - *mask = FSTAR; - return 0; - } - - *d++ = '\0'; - return conf_getnum(f, l, local, mask, "mask", d); -} - -static int -conf_gethostport(const char *f, size_t l, bool local, struct conf *c, - const char *p) -{ - char *d; // XXX: Ok to write to string. - in_port_t *port = NULL; - const char *pstr; - - if (strcmp(p, "*") == 0) { - c->c_port = FSTAR; - c->c_lmask = FSTAR; - return 0; - } - - if ((d = strchr(p, ']')) != NULL) { - *d++ = '\0'; - pstr = d; - p++; - } else - pstr = p; - - if (conf_getmask(f, l, local, &pstr, &c->c_lmask) == -1) - goto out; - - if (d) { - struct sockaddr_in6 *sin6 = (void *)&c->c_ss; - if (debug) - (*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p); - if (strcmp(p, "*") != 0) { - if (inet_pton(AF_INET6, p, &sin6->sin6_addr) != 1) - goto out; - sin6->sin6_family = AF_INET6; -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - sin6->sin6_len = sizeof(*sin6); -#endif - port = &sin6->sin6_port; - } - if (!*pstr) - pstr = "*"; - } else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) { - if (pstr == p) - pstr = "*"; - struct sockaddr_in *sin = (void *)&c->c_ss; - struct sockaddr_if *sif = (void *)&c->c_ss; - if (debug) - (*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p); - if (strcmp(p, "*") != 0) { - if (conf_is_interface(p)) { - if (!local) - goto out2; - if (debug) - (*lfun)(LOG_DEBUG, "%s: interface %s", - __func__, p); - if (c->c_lmask != FSTAR) - goto out1; - sif->sif_family = AF_MAX; - strlcpy(sif->sif_name, p, - sizeof(sif->sif_name)); -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - sif->sif_len = sizeof(*sif); -#endif - port = &sif->sif_port; - } else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1) - { - sin->sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - sin->sin_len = sizeof(*sin); -#endif - port = &sin->sin_port; - } else - goto out; - } - } - - if (conf_getport(f, l, local, &c->c_port, pstr) == -1) - return -1; - - if (port && c->c_port != FSTAR && c->c_port != FEQUAL) - *port = htons((in_port_t)c->c_port); - return 0; -out: - (*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, p); - return -1; -out1: - (*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with " - "interface [%s]", __func__, f, l, c->c_lmask, p); - return -1; -out2: - (*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense " - "with remote config [%s]", __func__, f, l, p); - return -1; -} - -static int -conf_getproto(const char *f, size_t l, bool local __unused, struct conf *c, - const char *p) -{ - if (strcmp(p, "stream") == 0) { - c->c_proto = IPPROTO_TCP; - return 0; - } - if (strcmp(p, "dgram") == 0) { - c->c_proto = IPPROTO_UDP; - return 0; - } - return conf_getnum(f, l, local, &c->c_proto, "protocol", p); -} - -static int -conf_getfamily(const char *f, size_t l, bool local __unused, struct conf *c, - const char *p) -{ - if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) { - c->c_family = p[3] == '6' ? AF_INET6 : AF_INET; - return 0; - } - return conf_getnum(f, l, local, &c->c_family, "family", p); -} - -static int -conf_getuid(const char *f, size_t l, bool local __unused, struct conf *c, - const char *p) -{ - struct passwd *pw; - - if ((pw = getpwnam(p)) != NULL) { - c->c_uid = (int)pw->pw_uid; - return 0; - } - - return conf_getnum(f, l, local, &c->c_uid, "user", p); -} - - -static int -conf_getname(const char *f, size_t l, bool local, struct conf *c, - const char *p) -{ - if (conf_getmask(f, l, local, &p, &c->c_rmask) == -1) - return -1; - - if (strcmp(p, "*") == 0) { - strlcpy(c->c_name, rulename, CONFNAMESZ); - return 0; - } - - if (strcmp(p, "=") == 0) { - if (local) - goto out; - c->c_name[0] = '\0'; - return 0; - } - - snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p); - return 0; -out: - (*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local" - " config", __func__, f, l); - return -1; -} - -static int -getvalue(const char *f, size_t l, bool local, void *r, char **p, - int (*fun)(const char *, size_t, bool, struct conf *, const char *)) -{ - char *ep = *p; - - advance(p); - return (*fun)(f, l, local, r, ep); -} - - -static int -conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local) -{ - int e; - - c->c_lineno = l; - - while (*p && isspace((unsigned char)*p)) - p++; - - memset(c, 0, sizeof(*c)); - e = getvalue(f, l, local, c, &p, conf_gethostport); - if (e) return -1; - e = getvalue(f, l, local, c, &p, conf_getproto); - if (e) return -1; - e = getvalue(f, l, local, c, &p, conf_getfamily); - if (e) return -1; - e = getvalue(f, l, local, c, &p, conf_getuid); - if (e) return -1; - e = getvalue(f, l, local, c, &p, conf_getname); - if (e) return -1; - e = getvalue(f, l, local, c, &p, conf_getnfail); - if (e) return -1; - e = getvalue(f, l, local, c, &p, conf_getsecs); - if (e) return -1; - - return 0; -} - -static int -conf_sort(const void *v1, const void *v2) -{ - const struct conf *c1 = v1; - const struct conf *c2 = v2; - -#define CMP(a, b, f) \ - if ((a)->f > (b)->f) return -1; \ - else if ((a)->f < (b)->f) return 1 - - CMP(c1, c2, c_ss.ss_family); - CMP(c1, c2, c_lmask); - CMP(c1, c2, c_port); - CMP(c1, c2, c_proto); - CMP(c1, c2, c_family); - CMP(c1, c2, c_rmask); - CMP(c1, c2, c_uid); -#undef CMP - return 0; -} - -static int -conf_is_interface(const char *name) -{ - const struct ifaddrs *ifa; - - for (ifa = ifas; ifa; ifa = ifa->ifa_next) - if (strcmp(ifa->ifa_name, name) == 0) - return 1; - return 0; -} - -#define MASK(m) ((uint32_t)~((1 << (32 - (m))) - 1)) - -static int -conf_amask_eq(const void *v1, const void *v2, size_t len, int mask) -{ - const uint32_t *a1 = v1; - const uint32_t *a2 = v2; - uint32_t m; - int omask = mask; - - switch (mask) { - case FSTAR: - if (memcmp(v1, v2, len) == 0) - return 1; - goto out; - case FEQUAL: - (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__, - mask); - abort(); - default: - break; - } - - for (size_t i = 0; i < (len >> 2); i++) { - if (mask > 32) { - m = htonl((uint32_t)~0); - mask -= 32; - } else if (mask) { - m = htonl(MASK(mask)); - mask = 0; - } else - return 1; - if ((a1[i] & m) != (a2[i] & m)) - goto out; - } - return 1; -out: - if (debug > 1) { - char b1[256], b2[256]; - blhexdump(b1, sizeof(b1), "a1", v1, len); - blhexdump(b2, sizeof(b2), "a2", v2, len); - (*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__, - b1, b2, omask); - } - return 0; -} - -/* - * Apply the mask to the given address - */ -static void -conf_apply_mask(void *v, size_t len, int mask) -{ - uint32_t *a = v; - uint32_t m; - - switch (mask) { - case FSTAR: - return; - case FEQUAL: - (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__, - mask); - abort(); - default: - break; - } - len >>= 2; - - for (size_t i = 0; i < len; i++) { - if (mask > 32) { - m = htonl((uint32_t)~0); - mask -= 32; - } else if (mask) { - m = htonl(MASK(mask)); - mask = 0; - } else - m = 0; - a[i] &= m; - } -} - -/* - * apply the mask and the port to the address given - */ -static void -conf_addr_set(struct conf *c, const struct sockaddr_storage *ss) -{ - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - in_port_t *port; - void *addr; - size_t alen; - - c->c_lmask = c->c_rmask; - c->c_ss = *ss; - - if (c->c_ss.ss_family != c->c_family) { - (*lfun)(LOG_CRIT, "%s: Internal error: mismatched family " - "%u != %u", __func__, c->c_ss.ss_family, c->c_family); - abort(); - } - - switch (c->c_ss.ss_family) { - case AF_INET: - sin = (void *)&c->c_ss; - port = &sin->sin_port; - addr = &sin->sin_addr; - alen = sizeof(sin->sin_addr); - break; - case AF_INET6: - sin6 = (void *)&c->c_ss; - port = &sin6->sin6_port; - addr = &sin6->sin6_addr; - alen = sizeof(sin6->sin6_addr); - break; - default: - (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u", - __func__, c->c_ss.ss_family); - abort(); - } - - *port = htons((in_port_t)c->c_port); - conf_apply_mask(addr, alen, c->c_lmask); - if (c->c_lmask == FSTAR) - c->c_lmask = (int)(alen * 8); - if (debug) { - char buf[128]; - sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss); - (*lfun)(LOG_DEBUG, "Applied address %s", buf); - } -} - -/* - * Compared two addresses for equality applying the mask - */ -static int -conf_inet_eq(const void *v1, const void *v2, int mask) -{ - const struct sockaddr *sa1 = v1; - const struct sockaddr *sa2 = v2; - size_t size; - - if (sa1->sa_family != sa2->sa_family) - return 0; - - switch (sa1->sa_family) { - case AF_INET: { - const struct sockaddr_in *s1 = v1; - const struct sockaddr_in *s2 = v2; - size = sizeof(s1->sin_addr); - v1 = &s1->sin_addr; - v2 = &s2->sin_addr; - break; - } - - case AF_INET6: { - const struct sockaddr_in6 *s1 = v1; - const struct sockaddr_in6 *s2 = v2; - size = sizeof(s1->sin6_addr); - v1 = &s1->sin6_addr; - v2 = &s2->sin6_addr; - break; - } - - default: - (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u", - __func__, sa1->sa_family); - abort(); - } - - return conf_amask_eq(v1, v2, size, mask); -} - -static int -conf_addr_in_interface(const struct sockaddr_storage *s1, - const struct sockaddr_storage *s2, int mask) -{ - const char *name = SIF_NAME(s2); - const struct ifaddrs *ifa; - - for (ifa = ifas; ifa; ifa = ifa->ifa_next) { - if ((ifa->ifa_flags & IFF_UP) == 0) - continue; - - if (strcmp(ifa->ifa_name, name) != 0) - continue; - - if (s1->ss_family != ifa->ifa_addr->sa_family) - continue; - - bool eq; - switch (s1->ss_family) { - case AF_INET: - case AF_INET6: - eq = conf_inet_eq(ifa->ifa_addr, s1, mask); - break; - default: - (*lfun)(LOG_ERR, "Bad family %u", s1->ss_family); - continue; - } - if (eq) - return 1; - } - return 0; -} - -static int -conf_addr_eq(const struct sockaddr_storage *s1, - const struct sockaddr_storage *s2, int mask) -{ - switch (s2->ss_family) { - case 0: - return 1; - case AF_MAX: - return conf_addr_in_interface(s1, s2, mask); - case AF_INET: - case AF_INET6: - return conf_inet_eq(s1, s2, mask); - default: - (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u", - __func__, s1->ss_family); - abort(); - } -} - -static int -conf_eq(const struct conf *c1, const struct conf *c2) -{ - if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, FSTAR)) - return 0; - -#define CMP(a, b, f) \ - if ((a)->f != (b)->f) \ - return 0; - - CMP(c1, c2, c_port); - CMP(c1, c2, c_proto); - CMP(c1, c2, c_family); - CMP(c1, c2, c_uid); -#undef CMP - - return 1; -} - -static int -conf_match(const struct conf *c1, const struct conf *c2) -{ - - if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask)) - return 0; - -#define CMP(a, b, f) \ - if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \ - if (debug > 1) \ - (*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \ - __STRING(f), (a)->f, (b)->f); \ - return 0; \ - } - CMP(c1, c2, c_port); - CMP(c1, c2, c_proto); - CMP(c1, c2, c_family); - CMP(c1, c2, c_uid); -#undef CMP - return 1; -} - -static const char * -conf_num(char *b, size_t l, int n) -{ - switch (n) { - case FSTAR: - return "*"; - case FEQUAL: - return "="; - default: - snprintf(b, l, "%d", n); - return b; - } -} - -static const char * -fmtname(const char *n) { - size_t l = strlen(rulename); - if (l == 0) - return "*"; - if (strncmp(n, rulename, l) == 0) { - if (n[l] != '\0') - return n + l; - else - return "*"; - } else if (!*n) - return "="; - else - return n; -} - -static void -fmtport(char *b, size_t l, int port) -{ - char buf[128]; - - if (port == FSTAR) - return; - - if (b[0] == '\0' || strcmp(b, "*") == 0) - snprintf(b, l, "%d", port); - else { - snprintf(buf, sizeof(buf), ":%d", port); - strlcat(b, buf, l); - } -} - -static const char * -fmtmask(char *b, size_t l, int fam, int mask) -{ - char buf[128]; - - switch (mask) { - case FSTAR: - return ""; - case FEQUAL: - if (strcmp(b, "=") == 0) - return ""; - else { - strlcat(b, "/=", l); - return b; - } - default: - break; - } - - switch (fam) { - case AF_INET: - if (mask == 32) - return ""; - break; - case AF_INET6: - if (mask == 128) - return ""; - break; - default: - break; - } - - snprintf(buf, sizeof(buf), "/%d", mask); - strlcat(b, buf, l); - return b; -} - -static const char * -conf_namemask(char *b, size_t l, const struct conf *c) -{ - strlcpy(b, fmtname(c->c_name), l); - fmtmask(b, l, c->c_family, c->c_rmask); - return b; -} - -const char * -conf_print(char *buf, size_t len, const char *pref, const char *delim, - const struct conf *c) -{ - char ha[128], hb[32], b[5][64]; - int sp; - -#define N(n, v) conf_num(b[n], sizeof(b[n]), (v)) - - switch (c->c_ss.ss_family) { - case 0: - snprintf(ha, sizeof(ha), "*"); - break; - case AF_MAX: - snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss)); - break; - default: - sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss); - break; - } - - fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask); - fmtport(ha, sizeof(ha), c->c_port); - - sp = *delim == '\t' ? 20 : -1; - hb[0] = '\0'; - if (*delim) - snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s" - "%s%s" "%s%s%s", - pref, sp, sp, ha, delim, N(0, c->c_proto), delim, - N(1, c->c_family), delim, N(2, c->c_uid), delim, - conf_namemask(hb, sizeof(hb), c), delim, - N(3, c->c_nfail), delim, N(4, c->c_duration)); - else - snprintf(buf, len, "%starget:%s, proto:%s, family:%s, " - "uid:%s, name:%s, nfail:%s, duration:%s", pref, - ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid), - conf_namemask(hb, sizeof(hb), c), - N(3, c->c_nfail), N(4, c->c_duration)); - return buf; -} - -/* - * Apply the local config match to the result - */ -static void -conf_apply(struct conf *c, const struct conf *sc) -{ - char buf[BUFSIZ]; - - if (debug) { - (*lfun)(LOG_DEBUG, "%s: %s", __func__, - conf_print(buf, sizeof(buf), "merge:\t", "", sc)); - (*lfun)(LOG_DEBUG, "%s: %s", __func__, - conf_print(buf, sizeof(buf), "to:\t", "", c)); - } - memcpy(c->c_name, sc->c_name, CONFNAMESZ); - c->c_uid = sc->c_uid; - c->c_rmask = sc->c_rmask; - c->c_nfail = sc->c_nfail; - c->c_duration = sc->c_duration; - - if (debug) - (*lfun)(LOG_DEBUG, "%s: %s", __func__, - conf_print(buf, sizeof(buf), "result:\t", "", c)); -} - -/* - * Merge a remote configuration to the result - */ -static void -conf_merge(struct conf *c, const struct conf *sc) -{ - char buf[BUFSIZ]; - - if (debug) { - (*lfun)(LOG_DEBUG, "%s: %s", __func__, - conf_print(buf, sizeof(buf), "merge:\t", "", sc)); - (*lfun)(LOG_DEBUG, "%s: %s", __func__, - conf_print(buf, sizeof(buf), "to:\t", "", c)); - } - - if (sc->c_name[0]) - memcpy(c->c_name, sc->c_name, CONFNAMESZ); - if (sc->c_uid != FEQUAL) - c->c_uid = sc->c_uid; - if (sc->c_rmask != FEQUAL) - c->c_lmask = c->c_rmask = sc->c_rmask; - if (sc->c_nfail != FEQUAL) - c->c_nfail = sc->c_nfail; - if (sc->c_duration != FEQUAL) - c->c_duration = sc->c_duration; - if (debug) - (*lfun)(LOG_DEBUG, "%s: %s", __func__, - conf_print(buf, sizeof(buf), "result:\t", "", c)); -} - -static void -confset_init(struct confset *cs) -{ - cs->cs_c = NULL; - cs->cs_n = 0; - cs->cs_m = 0; -} - -static int -confset_grow(struct confset *cs) -{ - void *tc; - - cs->cs_m += 10; - tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c)); - if (tc == NULL) { - (*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__); - return -1; - } - cs->cs_c = tc; - return 0; -} - -static struct conf * -confset_get(struct confset *cs) -{ - return &cs->cs_c[cs->cs_n]; -} - -static bool -confset_full(const struct confset *cs) -{ - return cs->cs_n == cs->cs_m; -} - -static void -confset_sort(struct confset *cs) -{ - qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort); -} - -static void -confset_add(struct confset *cs) -{ - cs->cs_n++; -} - -static void -confset_free(struct confset *cs) -{ - free(cs->cs_c); - confset_init(cs); -} - -static void -confset_merge(struct confset *dc, struct confset *sc) -{ - size_t i, j; - char buf[BUFSIZ]; - - /* Check each rule of the src confset (sc) */ - for (i = 0; i < sc->cs_n; i++) { - /* Compare to each rule in the dest confset (dc) */ - for (j = 0; j < dc->cs_n; j++) { - if (conf_eq(&dc->cs_c[j], &sc->cs_c[i])) { - break; - } - } - - if (j == dc->cs_n) { - /* This is a new rule to add to the dest confset. */ - if (confset_full(dc) && confset_grow(dc) == -1) - return; - - *confset_get(dc) = sc->cs_c[i]; - confset_add(dc); - continue; - } - - /* We had a match above. */ - /* - * Check whether the rule from the src confset is more - * restrictive than the existing one. Adjust the - * existing rule if necessary. - */ - if (sc->cs_c[i].c_nfail == dc->cs_c[j].c_nfail && - sc->cs_c[i].c_duration && dc->cs_c[j].c_duration) { - (*lfun)(LOG_DEBUG, "skipping existing rule: %s", - conf_print(buf, sizeof (buf), "", "\t", &sc->cs_c[i])); - continue; - } - - if (sc->cs_c[i].c_nfail < dc->cs_c[j].c_nfail) - dc->cs_c[j].c_nfail = sc->cs_c[i].c_nfail; - - if (sc->cs_c[i].c_duration > dc->cs_c[j].c_duration) - dc->cs_c[j].c_duration = sc->cs_c[i].c_duration; - - (*lfun)(LOG_DEBUG, "adjusted existing rule: %s", - conf_print(buf, sizeof (buf), "", "\t", &dc->cs_c[j])); - } - - confset_free(sc); -} - -static void -confset_list(const struct confset *cs, const char *msg, const char *where) -{ - char buf[BUFSIZ]; - - (*lfun)(LOG_DEBUG, "[%s]", msg); - (*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration", - where); - for (size_t i = 0; i < cs->cs_n; i++) - (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t", - &cs->cs_c[i])); -} - -/* - * Match a configuration against the given list and apply the function - * to it, returning the matched entry number. - */ -static size_t -confset_match(const struct confset *cs, struct conf *c, - void (*fun)(struct conf *, const struct conf *)) -{ - char buf[BUFSIZ]; - size_t i; - - for (i = 0; i < cs->cs_n; i++) { - if (debug) - (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), - "check:\t", "", &cs->cs_c[i])); - if (conf_match(c, &cs->cs_c[i])) { - if (debug) - (*lfun)(LOG_DEBUG, "%s", - conf_print(buf, sizeof(buf), - "found:\t", "", &cs->cs_c[i])); - (*fun)(c, &cs->cs_c[i]); - break; - } - } - return i; -} - -#ifdef AF_ROUTE -static int -conf_route_perm(int fd) { -#if defined(RTM_IFANNOUNCE) && defined(RT_ROUNDUP) - /* - * Send a routing message that is not supported to check for access - * We expect EOPNOTSUPP for having access, since we are sending a - * request the system does not understand and EACCES if we don't have - * access. - */ - static struct sockaddr_in sin = { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - .sin_len = sizeof(sin), -#endif - .sin_family = AF_INET, - }; - char buf[4096]; - struct rt_msghdr *rtm = (void *)buf; - char *cp = (char *)(rtm + 1); - size_t l; - -#define NEXTADDR(s) \ - l = RT_ROUNDUP(sizeof(*s)); memmove(cp, s, l); cp += l; - memset(buf, 0, sizeof(buf)); - rtm->rtm_type = RTM_IFANNOUNCE; - rtm->rtm_flags = 0; - rtm->rtm_addrs = RTA_DST|RTA_GATEWAY; - rtm->rtm_version = RTM_VERSION; - rtm->rtm_seq = 666; - NEXTADDR(&sin); - NEXTADDR(&sin); - rtm->rtm_msglen = (u_short)((char *)cp - (char *)rtm); - if (write(fd, rtm, rtm->rtm_msglen) != -1) { - (*lfun)(LOG_ERR, "Writing to routing socket succeeded!"); - return 0; - } - switch (errno) { - case EACCES: - return 0; - case EOPNOTSUPP: - return 1; - default: - (*lfun)(LOG_ERR, - "Unexpected error writing to routing socket (%m)"); - return 0; - } -#else - return 0; -#endif -} -#endif - -static int -conf_handle_inet(int fd, const void *lss, struct conf *cr) -{ - char buf[BUFSIZ]; - int proto; - socklen_t slen = sizeof(proto); - - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) { - (*lfun)(LOG_ERR, "getsockopt failed (%m)"); - return -1; - } - - if (debug) { - sockaddr_snprintf(buf, sizeof(buf), "%a:%p", lss); - (*lfun)(LOG_DEBUG, "listening socket: %s", buf); - } - - switch (proto) { - case SOCK_STREAM: - cr->c_proto = IPPROTO_TCP; - break; - case SOCK_DGRAM: - cr->c_proto = IPPROTO_UDP; - break; - default: - (*lfun)(LOG_ERR, "unsupported protocol %d", proto); - return -1; - } - return 0; -} - -const struct conf * -conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss, - struct conf *cr) -{ - socklen_t slen; - struct sockaddr_storage lss; - size_t i; - char buf[BUFSIZ]; - - memset(cr, 0, sizeof(*cr)); - slen = sizeof(lss); - memset(&lss, 0, slen); - if (getsockname(fd, (void *)&lss, &slen) == -1) { - (*lfun)(LOG_ERR, "getsockname failed (%m)"); - return NULL; - } - - switch (lss.ss_family) { - case AF_INET: - cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port); - if (conf_handle_inet(fd, &lss, cr) == -1) - return NULL; - break; - case AF_INET6: - cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port); - if (conf_handle_inet(fd, &lss, cr) == -1) - return NULL; - break; -#ifdef AF_ROUTE - case AF_ROUTE: - if (!conf_route_perm(fd)) { - (*lfun)(LOG_ERR, - "permission denied to routing socket (%m)"); - return NULL; - } - cr->c_proto = FSTAR; - cr->c_port = FSTAR; - memcpy(&lss, rss, sizeof(lss)); - break; -#endif - default: - (*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family); - return NULL; - } - - cr->c_ss = lss; - cr->c_lmask = FSTAR; - cr->c_uid = (int)uid; - cr->c_family = lss.ss_family; - cr->c_name[0] = '\0'; - cr->c_rmask = FSTAR; - cr->c_nfail = FSTAR; - cr->c_duration = FSTAR; - - if (debug) - (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), - "look:\t", "", cr)); - - /* match the local config */ - i = confset_match(&lconf, cr, conf_apply); - if (i == lconf.cs_n) { - if (debug) - (*lfun)(LOG_DEBUG, "not found"); - return NULL; - } - - conf_addr_set(cr, rss); - /* match the remote config */ - confset_match(&rconf, cr, conf_merge); - /* to apply the mask */ - conf_addr_set(cr, &cr->c_ss); - - return cr; -} - -static void -conf_parsefile(FILE *fp, const char *config_file) -{ - char *line; - size_t lineno, len; - struct confset lc, rc, *cs; - - lineno = 0; - - confset_init(&rc); - confset_init(&lc); - cs = &lc; - for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL; - free(line)) - { - if (!*line) - continue; - if (strcmp(line, "[local]") == 0) { - cs = &lc; - continue; - } - if (strcmp(line, "[remote]") == 0) { - cs = &rc; - continue; - } - - if (confset_full(cs)) { - if (confset_grow(cs) == -1) { - confset_free(&lc); - confset_free(&rc); - free(line); - return; - } - } - if (conf_parseline(config_file, lineno, line, confset_get(cs), - cs == &lc) == -1) - continue; - confset_add(cs); - } - - confset_merge(&rconf, &rc); - confset_merge(&lconf, &lc); -} - - -static void -conf_parsedir(DIR *dir, const char *config_path) -{ - long path_max; - struct dirent *dent; - char *path; - FILE *fp; - - if ((path_max = pathconf(config_path, _PC_PATH_MAX)) == -1) - path_max = 2048; - - if ((path = malloc((size_t)path_max)) == NULL) { - (*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)", - __func__); - return; - } - - while ((dent = readdir(dir)) != NULL) { - if (strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0) - continue; - - (void) snprintf(path, (size_t)path_max, "%s/%s", config_path, - dent->d_name); - if ((fp = fopen(path, "r")) == NULL) { - (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, - path); - continue; - } - conf_parsefile(fp, path); - fclose(fp); - } - - free(path); -} - -void -conf_parse(const char *config_path) -{ - char *path; - DIR *dir; - FILE *fp; - - if ((dir = opendir(config_path)) != NULL) { - /* - * If config_path is a directory, parse the configuration files - * in the directory. Then we're done here. - */ - conf_parsedir(dir, config_path); - closedir(dir); - goto out; - } else if ((fp = fopen(config_path, "r")) != NULL) { - /* If config_path is a file, parse it. */ - conf_parsefile(fp, config_path); - fclose(fp); - } - - /* - * Append ".d" to config_path, and if that is a directory, parse the - * configuration files in the directory. - */ - if (asprintf(&path, "%s.d", config_path) < 0) { - (*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)", - __func__); - goto out; - } - - if ((dir = opendir(path)) != NULL) { - conf_parsedir(dir, path); - closedir(dir); - } - free(path); - -out: - if (dir == NULL && fp == NULL) { - (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, - config_path); - return; - } - - confset_sort(&lconf); - confset_sort(&rconf); - - if (debug) { - confset_list(&lconf, "local", "target"); - confset_list(&rconf, "remote", "source"); - } -} |
