diff --git a/contrib/blacklist/bin/conf.c b/contrib/blacklist/bin/conf.c index 94baa75c09b0..f872b3de787a 100644 --- a/contrib/blacklist/bin/conf.c +++ b/contrib/blacklist/bin/conf.c @@ -1,1143 +1,1143 @@ /* $NetBSD: conf.c,v 1.24 2016/04/04 15:52:56 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 #include __RCSID("$NetBSD: conf.c,v 1.24 2016/04/04 15:52:56 christos Exp $"); #include #ifdef HAVE_LIBUTIL_H #include #endif #ifdef HAVE_UTIL_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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 getnfail(const char *f, size_t l, bool local, struct conf *c, const char *p) { return getnum(f, l, local, &c->c_nfail, "nfail", p); } static int 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 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 getnum(f, l, local, r, "service", p); } static int 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 getnum(f, l, local, mask, "mask", d); } static int 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 (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; } } 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 (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, pstr); 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 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 getnum(f, l, local, &c->c_proto, "protocol", p); } static int 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 getnum(f, l, local, &c->c_family, "family", p); } static int 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 getnum(f, l, local, &c->c_uid, "user", p); } static int getname(const char *f, size_t l, bool local, struct conf *c, const char *p) { if (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; while (*p && isspace((unsigned char)*p)) p++; memset(c, 0, sizeof(*c)); e = getvalue(f, l, local, c, &p, gethostport); if (e) return -1; e = getvalue(f, l, local, c, &p, getproto); if (e) return -1; e = getvalue(f, l, local, c, &p, getfamily); if (e) return -1; e = getvalue(f, l, local, c, &p, getuid); if (e) return -1; e = getvalue(f, l, local, c, &p, getname); if (e) return -1; e = getvalue(f, l, local, c, &p, getnfail); if (e) return -1; e = getvalue(f, l, local, c, &p, 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; len >>= 2; 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; 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]; len <<= 2; 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, 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_replace(struct confset *dc, struct confset *sc) { struct confset tc; tc = *dc; *dc = *sc; confset_init(sc); confset_free(&tc); } 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_eq(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; } const struct conf * conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss, struct conf *cr) { int proto; 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; } slen = sizeof(proto); if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) { (*lfun)(LOG_ERR, "getsockopt failed (%m)"); return NULL; } if (debug) { sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&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 NULL; } switch (lss.ss_family) { case AF_INET: cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port); break; case AF_INET6: cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port); break; 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; } void conf_parse(const char *f) { FILE *fp; char *line; size_t lineno, len; struct confset lc, rc, *cs; if ((fp = fopen(f, "r")) == NULL) { (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, f); return; } lineno = 1; 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); fclose(fp); free(line); return; } } if (conf_parseline(f, lineno, line, confset_get(cs), cs == &lc) == -1) continue; confset_add(cs); } fclose(fp); confset_sort(&lc); confset_sort(&rc); confset_replace(&rconf, &rc); confset_replace(&lconf, &lc); if (debug) { confset_list(&lconf, "local", "target"); confset_list(&rconf, "remote", "source"); } } diff --git a/contrib/blacklist/lib/bl.c b/contrib/blacklist/lib/bl.c index a3aa4ef1f9f6..409317bc3fc0 100644 --- a/contrib/blacklist/lib/bl.c +++ b/contrib/blacklist/lib/bl.c @@ -1,533 +1,532 @@ /* $NetBSD: bl.c,v 1.28 2016/07/29 17:13:09 christos Exp $ */ /*- * Copyright (c) 2014 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 #include __RCSID("$NetBSD: bl.c,v 1.28 2016/07/29 17:13:09 christos Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _REENTRANT #include #endif #include "bl.h" typedef struct { uint32_t bl_len; uint32_t bl_version; uint32_t bl_type; uint32_t bl_salen; struct sockaddr_storage bl_ss; char bl_data[]; } bl_message_t; struct blacklist { #ifdef _REENTRANT pthread_mutex_t b_mutex; # define BL_INIT(b) pthread_mutex_init(&b->b_mutex, NULL) # define BL_LOCK(b) pthread_mutex_lock(&b->b_mutex) # define BL_UNLOCK(b) pthread_mutex_unlock(&b->b_mutex) #else # define BL_INIT(b) do {} while(/*CONSTCOND*/0) # define BL_LOCK(b) BL_INIT(b) # define BL_UNLOCK(b) BL_INIT(b) #endif int b_fd; int b_connected; struct sockaddr_un b_sun; void (*b_fun)(int, const char *, va_list); bl_info_t b_info; }; #define BL_VERSION 1 bool bl_isconnected(bl_t b) { return b->b_connected == 0; } int bl_getfd(bl_t b) { return b->b_fd; } static void bl_reset(bl_t b, bool locked) { int serrno = errno; if (!locked) BL_LOCK(b); close(b->b_fd); errno = serrno; b->b_fd = -1; b->b_connected = -1; if (!locked) BL_UNLOCK(b); } static void bl_log(void (*fun)(int, const char *, va_list), int level, const char *fmt, ...) { va_list ap; int serrno = errno; va_start(ap, fmt); (*fun)(level, fmt, ap); va_end(ap); errno = serrno; } static int bl_init(bl_t b, bool srv) { static int one = 1; /* AF_UNIX address of local logger */ mode_t om; int rv, serrno; struct sockaddr_un *sun = &b->b_sun; #ifndef SOCK_NONBLOCK #define SOCK_NONBLOCK 0 #endif #ifndef SOCK_CLOEXEC #define SOCK_CLOEXEC 0 #endif #ifndef SOCK_NOSIGPIPE #define SOCK_NOSIGPIPE 0 #endif BL_LOCK(b); if (b->b_fd == -1) { b->b_fd = socket(PF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0); if (b->b_fd == -1) { bl_log(b->b_fun, LOG_ERR, "%s: socket failed (%s)", __func__, strerror(errno)); BL_UNLOCK(b); return -1; } #if SOCK_CLOEXEC == 0 fcntl(b->b_fd, F_SETFD, FD_CLOEXEC); #endif #if SOCK_NONBLOCK == 0 fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK); #endif #if SOCK_NOSIGPIPE == 0 #ifdef SO_NOSIGPIPE int o = 1; setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o)); #else signal(SIGPIPE, SIG_IGN); #endif #endif } if (bl_isconnected(b)) { BL_UNLOCK(b); return 0; } /* * We try to connect anyway even when we are a server to verify * that no other server is listening to the socket. If we succeed * to connect and we are a server, someone else owns it. */ rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun)); if (rv == 0) { if (srv) { bl_log(b->b_fun, LOG_ERR, "%s: another daemon is handling `%s'", __func__, sun->sun_path); goto out; } } else { if (!srv) { /* * If the daemon is not running, we just try a * connect, so leave the socket alone until it does * and only log once. */ if (b->b_connected != 1) { bl_log(b->b_fun, LOG_DEBUG, "%s: connect failed for `%s' (%s)", __func__, sun->sun_path, strerror(errno)); b->b_connected = 1; } BL_UNLOCK(b); return -1; } bl_log(b->b_fun, LOG_DEBUG, "Connected to blacklist server", __func__); } if (srv) { (void)unlink(sun->sun_path); om = umask(0); rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun)); serrno = errno; (void)umask(om); errno = serrno; if (rv == -1) { bl_log(b->b_fun, LOG_ERR, "%s: bind failed for `%s' (%s)", __func__, sun->sun_path, strerror(errno)); goto out; } } b->b_connected = 0; #define GOT_FD 1 #if defined(LOCAL_CREDS) #define CRED_LEVEL 0 #define CRED_NAME LOCAL_CREDS #define CRED_SC_UID sc_euid #define CRED_SC_GID sc_egid #define CRED_MESSAGE SCM_CREDS #define CRED_SIZE SOCKCREDSIZE(NGROUPS_MAX) #define CRED_TYPE struct sockcred #define GOT_CRED 2 #elif defined(SO_PASSCRED) #define CRED_LEVEL SOL_SOCKET #define CRED_NAME SO_PASSCRED #define CRED_SC_UID uid #define CRED_SC_GID gid #define CRED_MESSAGE SCM_CREDENTIALS #define CRED_SIZE sizeof(struct ucred) #define CRED_TYPE struct ucred #define GOT_CRED 2 #else #define GOT_CRED 0 /* * getpeereid() and LOCAL_PEERCRED don't help here * because we are not a stream socket! */ #define CRED_SIZE 0 #define CRED_TYPE void * __unused #endif #ifdef CRED_LEVEL if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME, &one, (socklen_t)sizeof(one)) == -1) { bl_log(b->b_fun, LOG_ERR, "%s: setsockopt %s " "failed (%s)", __func__, __STRING(CRED_NAME), strerror(errno)); goto out; } #endif BL_UNLOCK(b); return 0; out: bl_reset(b, true); BL_UNLOCK(b); return -1; } bl_t bl_create(bool srv, const char *path, void (*fun)(int, const char *, va_list)) { bl_t b = calloc(1, sizeof(*b)); if (b == NULL) goto out; b->b_fun = fun == NULL ? vsyslog : fun; b->b_fd = -1; b->b_connected = -1; BL_INIT(b); memset(&b->b_sun, 0, sizeof(b->b_sun)); b->b_sun.sun_family = AF_LOCAL; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN b->b_sun.sun_len = sizeof(b->b_sun); #endif strlcpy(b->b_sun.sun_path, path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path)); bl_init(b, srv); return b; out: free(b); bl_log(fun, LOG_ERR, "%s: malloc failed (%s)", __func__, strerror(errno)); return NULL; } void bl_destroy(bl_t b) { bl_reset(b, false); free(b); } static int bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa, socklen_t slen, const char *ctx) { uint8_t family; memset(ss, 0, sizeof(*ss)); switch (slen) { case 0: return 0; case sizeof(struct sockaddr_in): family = AF_INET; break; case sizeof(struct sockaddr_in6): family = AF_INET6; break; default: bl_log(b->b_fun, LOG_ERR, "%s: invalid socket len %u (%s)", __func__, (unsigned)slen, ctx); errno = EINVAL; return -1; } memcpy(ss, sa, slen); if (ss->ss_family != family) { bl_log(b->b_fun, LOG_INFO, "%s: correcting socket family %d to %d (%s)", __func__, ss->ss_family, family, ctx); ss->ss_family = family; } #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN if (ss->ss_len != slen) { bl_log(b->b_fun, LOG_INFO, "%s: correcting socket len %u to %u (%s)", __func__, ss->ss_len, (unsigned)slen, ctx); ss->ss_len = (uint8_t)slen; } #endif return 0; } int bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa, socklen_t slen, const char *ctx) { struct msghdr msg; struct iovec iov; union { char ctrl[CMSG_SPACE(sizeof(int))]; uint32_t fd; } ua; struct cmsghdr *cmsg; union { bl_message_t bl; char buf[512]; } ub; size_t ctxlen, tried; #define NTRIES 5 ctxlen = strlen(ctx); if (ctxlen > 128) ctxlen = 128; iov.iov_base = ub.buf; iov.iov_len = sizeof(bl_message_t) + ctxlen; ub.bl.bl_len = (uint32_t)iov.iov_len; ub.bl.bl_version = BL_VERSION; ub.bl.bl_type = (uint32_t)e; if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1) return -1; ub.bl.bl_salen = slen; memcpy(ub.bl.bl_data, ctx, ctxlen); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = ua.ctrl; msg.msg_controllen = sizeof(ua.ctrl); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd)); tried = 0; again: if (bl_init(b, false) == -1) return -1; if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) { bl_reset(b, false); goto again; } return tried >= NTRIES ? -1 : 0; } bl_info_t * bl_recv(bl_t b) { struct msghdr msg; struct iovec iov; union { char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)]; uint32_t fd; CRED_TYPE sc; } ua; struct cmsghdr *cmsg; CRED_TYPE *sc; union { bl_message_t bl; char buf[512]; } ub; int got; ssize_t rlen; size_t rem; bl_info_t *bi = &b->b_info; got = 0; memset(bi, 0, sizeof(*bi)); iov.iov_base = ub.buf; iov.iov_len = sizeof(ub); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = ua.ctrl; msg.msg_controllen = sizeof(ua.ctrl) + 100; rlen = recvmsg(b->b_fd, &msg, 0); if (rlen == -1) { bl_log(b->b_fun, LOG_ERR, "%s: recvmsg failed (%s)", __func__, strerror(errno)); return NULL; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET) { bl_log(b->b_fun, LOG_ERR, "%s: unexpected cmsg_level %d", __func__, cmsg->cmsg_level); continue; } switch (cmsg->cmsg_type) { case SCM_RIGHTS: if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { bl_log(b->b_fun, LOG_ERR, "%s: unexpected cmsg_len %d != %zu", __func__, cmsg->cmsg_len, CMSG_LEN(2 * sizeof(int))); continue; } memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd)); got |= GOT_FD; break; #ifdef CRED_MESSAGE case CRED_MESSAGE: sc = (void *)CMSG_DATA(cmsg); bi->bi_uid = sc->CRED_SC_UID; bi->bi_gid = sc->CRED_SC_GID; got |= GOT_CRED; break; #endif default: bl_log(b->b_fun, LOG_ERR, "%s: unexpected cmsg_type %d", __func__, cmsg->cmsg_type); continue; } } if (got != (GOT_CRED|GOT_FD)) { bl_log(b->b_fun, LOG_ERR, "message missing %s %s", #if GOT_CRED != 0 (got & GOT_CRED) == 0 ? "cred" : #endif "", (got & GOT_FD) == 0 ? "fd" : ""); - return NULL; } rem = (size_t)rlen; if (rem < sizeof(ub.bl)) { bl_log(b->b_fun, LOG_ERR, "message too short %zd", rlen); return NULL; } rem -= sizeof(ub.bl); if (ub.bl.bl_version != BL_VERSION) { bl_log(b->b_fun, LOG_ERR, "bad version %d", ub.bl.bl_version); return NULL; } bi->bi_type = ub.bl.bl_type; bi->bi_slen = ub.bl.bl_salen; bi->bi_ss = ub.bl.bl_ss; #ifndef CRED_MESSAGE bi->bi_uid = -1; bi->bi_gid = -1; #endif rem = MIN(sizeof(bi->bi_msg), rem); if (rem == 0) bi->bi_msg[0] = '\0'; else strlcpy(bi->bi_msg, ub.bl.bl_data, rem); return bi; }