/* $OpenBSD: addrmatch.c,v 1.17 2021/04/03 06:18:40 djm Exp $ */ /* * Copyright (c) 2004-2008 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "addr.h" #include "match.h" #include "log.h" /* * Match "addr" against list pattern list "_list", which may contain a * mix of CIDR addresses and old-school wildcards. * * If addr is NULL, then no matching is performed, but _list is parsed * and checked for well-formedness. * * Returns 1 on match found (never returned when addr == NULL). * Returns 0 on if no match found, or no errors found when addr == NULL. * Returns -1 on negated match found (never returned when addr == NULL). * Returns -2 on invalid list entry. */ int addr_match_list(const char *addr, const char *_list) { char *list, *cp, *o; struct xaddr try_addr, match_addr; u_int masklen, neg; int ret = 0, r; if (addr != NULL && addr_pton(addr, &try_addr) != 0) { debug2_f("couldn't parse address %.100s", addr); return 0; } if ((o = list = strdup(_list)) == NULL) return -1; while ((cp = strsep(&list, ",")) != NULL) { neg = *cp == '!'; if (neg) cp++; if (*cp == '\0') { ret = -2; break; } /* Prefer CIDR address matching */ r = addr_pton_cidr(cp, &match_addr, &masklen); if (r == -2) { debug2_f("inconsistent mask length for " "match network \"%.100s\"", cp); ret = -2; break; } else if (r == 0) { if (addr != NULL && addr_netmatch(&try_addr, &match_addr, masklen) == 0) { foundit: if (neg) { ret = -1; break; } ret = 1; } continue; } else { /* If CIDR parse failed, try wildcard string match */ if (addr != NULL && match_pattern(addr, cp) == 1) goto foundit; } } free(o); return ret; } /* * Match "addr" against list CIDR list "_list". Lexical wildcards and * negation are not supported. If "addr" == NULL, will verify structure * of "_list". * * Returns 1 on match found (never returned when addr == NULL). * Returns 0 on if no match found, or no errors found when addr == NULL. * Returns -1 on error */ int addr_match_cidr_list(const char *addr, const char *_list) { char *list, *cp, *o; struct xaddr try_addr, match_addr; u_int masklen; int ret = 0, r; if (addr != NULL && addr_pton(addr, &try_addr) != 0) { debug2_f("couldn't parse address %.100s", addr); return 0; } if ((o = list = strdup(_list)) == NULL) return -1; while ((cp = strsep(&list, ",")) != NULL) { if (*cp == '\0') { error_f("empty entry in list \"%.100s\"", o); ret = -1; break; } /* * NB. This function is called in pre-auth with untrusted data, * so be extra paranoid about junk reaching getaddrino (via * addr_pton_cidr). */ /* Stop junk from reaching getaddrinfo. +3 is for masklen */ if (strlen(cp) > INET6_ADDRSTRLEN + 3) { error_f("list entry \"%.100s\" too long", cp); ret = -1; break; } #define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) { error_f("list entry \"%.100s\" contains invalid " "characters", cp); ret = -1; } /* Prefer CIDR address matching */ r = addr_pton_cidr(cp, &match_addr, &masklen); if (r == -1) { error("Invalid network entry \"%.100s\"", cp); ret = -1; break; } else if (r == -2) { error("Inconsistent mask length for " "network \"%.100s\"", cp); ret = -1; break; } else if (r == 0 && addr != NULL) { if (addr_netmatch(&try_addr, &match_addr, masklen) == 0) ret = 1; continue; } } free(o); return ret; }