xref: /dflybsd-src/crypto/openssh/addrmatch.c (revision 50a69bb51183a7916e776f2c9f5fa64c999f1a2f)
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