1*50a69bb5SSascha Wildner /* $OpenBSD: addrmatch.c,v 1.17 2021/04/03 06:18:40 djm Exp $ */
218de8d7fSPeter Avalos
318de8d7fSPeter Avalos /*
418de8d7fSPeter Avalos * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
518de8d7fSPeter Avalos *
618de8d7fSPeter Avalos * Permission to use, copy, modify, and distribute this software for any
718de8d7fSPeter Avalos * purpose with or without fee is hereby granted, provided that the above
818de8d7fSPeter Avalos * copyright notice and this permission notice appear in all copies.
918de8d7fSPeter Avalos *
1018de8d7fSPeter Avalos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1118de8d7fSPeter Avalos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1218de8d7fSPeter Avalos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1318de8d7fSPeter Avalos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1418de8d7fSPeter Avalos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1518de8d7fSPeter Avalos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1618de8d7fSPeter Avalos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1718de8d7fSPeter Avalos */
1818de8d7fSPeter Avalos
1918de8d7fSPeter Avalos #include "includes.h"
2018de8d7fSPeter Avalos
2118de8d7fSPeter Avalos #include <sys/types.h>
2218de8d7fSPeter Avalos #include <sys/socket.h>
2318de8d7fSPeter Avalos #include <netinet/in.h>
2418de8d7fSPeter Avalos #include <arpa/inet.h>
2518de8d7fSPeter Avalos
2618de8d7fSPeter Avalos #include <netdb.h>
2718de8d7fSPeter Avalos #include <string.h>
2818de8d7fSPeter Avalos #include <stdlib.h>
2918de8d7fSPeter Avalos #include <stdio.h>
3018de8d7fSPeter Avalos #include <stdarg.h>
3118de8d7fSPeter Avalos
32*50a69bb5SSascha Wildner #include "addr.h"
3318de8d7fSPeter Avalos #include "match.h"
3418de8d7fSPeter Avalos #include "log.h"
3518de8d7fSPeter Avalos
3618de8d7fSPeter Avalos /*
3718de8d7fSPeter Avalos * Match "addr" against list pattern list "_list", which may contain a
3818de8d7fSPeter Avalos * mix of CIDR addresses and old-school wildcards.
3918de8d7fSPeter Avalos *
4018de8d7fSPeter Avalos * If addr is NULL, then no matching is performed, but _list is parsed
4118de8d7fSPeter Avalos * and checked for well-formedness.
4218de8d7fSPeter Avalos *
4318de8d7fSPeter Avalos * Returns 1 on match found (never returned when addr == NULL).
4418de8d7fSPeter Avalos * Returns 0 on if no match found, or no errors found when addr == NULL.
4518de8d7fSPeter Avalos * Returns -1 on negated match found (never returned when addr == NULL).
4618de8d7fSPeter Avalos * Returns -2 on invalid list entry.
4718de8d7fSPeter Avalos */
4818de8d7fSPeter Avalos int
addr_match_list(const char * addr,const char * _list)4918de8d7fSPeter Avalos addr_match_list(const char *addr, const char *_list)
5018de8d7fSPeter Avalos {
5118de8d7fSPeter Avalos char *list, *cp, *o;
5218de8d7fSPeter Avalos struct xaddr try_addr, match_addr;
5318de8d7fSPeter Avalos u_int masklen, neg;
5418de8d7fSPeter Avalos int ret = 0, r;
5518de8d7fSPeter Avalos
5618de8d7fSPeter Avalos if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
57*50a69bb5SSascha Wildner debug2_f("couldn't parse address %.100s", addr);
5818de8d7fSPeter Avalos return 0;
5918de8d7fSPeter Avalos }
6018de8d7fSPeter Avalos if ((o = list = strdup(_list)) == NULL)
6118de8d7fSPeter Avalos return -1;
6218de8d7fSPeter Avalos while ((cp = strsep(&list, ",")) != NULL) {
6318de8d7fSPeter Avalos neg = *cp == '!';
6418de8d7fSPeter Avalos if (neg)
6518de8d7fSPeter Avalos cp++;
6618de8d7fSPeter Avalos if (*cp == '\0') {
6718de8d7fSPeter Avalos ret = -2;
6818de8d7fSPeter Avalos break;
6918de8d7fSPeter Avalos }
7018de8d7fSPeter Avalos /* Prefer CIDR address matching */
7118de8d7fSPeter Avalos r = addr_pton_cidr(cp, &match_addr, &masklen);
7218de8d7fSPeter Avalos if (r == -2) {
73*50a69bb5SSascha Wildner debug2_f("inconsistent mask length for "
74*50a69bb5SSascha Wildner "match network \"%.100s\"", cp);
7518de8d7fSPeter Avalos ret = -2;
7618de8d7fSPeter Avalos break;
7718de8d7fSPeter Avalos } else if (r == 0) {
7818de8d7fSPeter Avalos if (addr != NULL && addr_netmatch(&try_addr,
7918de8d7fSPeter Avalos &match_addr, masklen) == 0) {
8018de8d7fSPeter Avalos foundit:
8118de8d7fSPeter Avalos if (neg) {
8218de8d7fSPeter Avalos ret = -1;
8318de8d7fSPeter Avalos break;
8418de8d7fSPeter Avalos }
8518de8d7fSPeter Avalos ret = 1;
8618de8d7fSPeter Avalos }
8718de8d7fSPeter Avalos continue;
8818de8d7fSPeter Avalos } else {
8918de8d7fSPeter Avalos /* If CIDR parse failed, try wildcard string match */
9018de8d7fSPeter Avalos if (addr != NULL && match_pattern(addr, cp) == 1)
9118de8d7fSPeter Avalos goto foundit;
9218de8d7fSPeter Avalos }
9318de8d7fSPeter Avalos }
9436e94dc5SPeter Avalos free(o);
9518de8d7fSPeter Avalos
9618de8d7fSPeter Avalos return ret;
9718de8d7fSPeter Avalos }
98856ea928SPeter Avalos
99856ea928SPeter Avalos /*
100856ea928SPeter Avalos * Match "addr" against list CIDR list "_list". Lexical wildcards and
101856ea928SPeter Avalos * negation are not supported. If "addr" == NULL, will verify structure
102856ea928SPeter Avalos * of "_list".
103856ea928SPeter Avalos *
104856ea928SPeter Avalos * Returns 1 on match found (never returned when addr == NULL).
105856ea928SPeter Avalos * Returns 0 on if no match found, or no errors found when addr == NULL.
106856ea928SPeter Avalos * Returns -1 on error
107856ea928SPeter Avalos */
108856ea928SPeter Avalos int
addr_match_cidr_list(const char * addr,const char * _list)109856ea928SPeter Avalos addr_match_cidr_list(const char *addr, const char *_list)
110856ea928SPeter Avalos {
111856ea928SPeter Avalos char *list, *cp, *o;
112856ea928SPeter Avalos struct xaddr try_addr, match_addr;
113856ea928SPeter Avalos u_int masklen;
114856ea928SPeter Avalos int ret = 0, r;
115856ea928SPeter Avalos
116856ea928SPeter Avalos if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
117*50a69bb5SSascha Wildner debug2_f("couldn't parse address %.100s", addr);
118856ea928SPeter Avalos return 0;
119856ea928SPeter Avalos }
120856ea928SPeter Avalos if ((o = list = strdup(_list)) == NULL)
121856ea928SPeter Avalos return -1;
122856ea928SPeter Avalos while ((cp = strsep(&list, ",")) != NULL) {
123856ea928SPeter Avalos if (*cp == '\0') {
124*50a69bb5SSascha Wildner error_f("empty entry in list \"%.100s\"", o);
125856ea928SPeter Avalos ret = -1;
126856ea928SPeter Avalos break;
127856ea928SPeter Avalos }
128856ea928SPeter Avalos
129856ea928SPeter Avalos /*
130856ea928SPeter Avalos * NB. This function is called in pre-auth with untrusted data,
131856ea928SPeter Avalos * so be extra paranoid about junk reaching getaddrino (via
132856ea928SPeter Avalos * addr_pton_cidr).
133856ea928SPeter Avalos */
134856ea928SPeter Avalos
135856ea928SPeter Avalos /* Stop junk from reaching getaddrinfo. +3 is for masklen */
136856ea928SPeter Avalos if (strlen(cp) > INET6_ADDRSTRLEN + 3) {
137*50a69bb5SSascha Wildner error_f("list entry \"%.100s\" too long", cp);
138856ea928SPeter Avalos ret = -1;
139856ea928SPeter Avalos break;
140856ea928SPeter Avalos }
141856ea928SPeter Avalos #define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/"
142856ea928SPeter Avalos if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) {
143*50a69bb5SSascha Wildner error_f("list entry \"%.100s\" contains invalid "
144*50a69bb5SSascha Wildner "characters", cp);
145856ea928SPeter Avalos ret = -1;
146856ea928SPeter Avalos }
147856ea928SPeter Avalos
148856ea928SPeter Avalos /* Prefer CIDR address matching */
149856ea928SPeter Avalos r = addr_pton_cidr(cp, &match_addr, &masklen);
150856ea928SPeter Avalos if (r == -1) {
151856ea928SPeter Avalos error("Invalid network entry \"%.100s\"", cp);
152856ea928SPeter Avalos ret = -1;
153856ea928SPeter Avalos break;
154856ea928SPeter Avalos } else if (r == -2) {
155856ea928SPeter Avalos error("Inconsistent mask length for "
156856ea928SPeter Avalos "network \"%.100s\"", cp);
157856ea928SPeter Avalos ret = -1;
158856ea928SPeter Avalos break;
159856ea928SPeter Avalos } else if (r == 0 && addr != NULL) {
160856ea928SPeter Avalos if (addr_netmatch(&try_addr, &match_addr,
161856ea928SPeter Avalos masklen) == 0)
162856ea928SPeter Avalos ret = 1;
163856ea928SPeter Avalos continue;
164856ea928SPeter Avalos }
165856ea928SPeter Avalos }
16636e94dc5SPeter Avalos free(o);
167856ea928SPeter Avalos
168856ea928SPeter Avalos return ret;
169856ea928SPeter Avalos }
170