1 /* $NetBSD: addrmatch.c,v 1.15 2021/04/19 14:40:15 christos Exp $ */
2 /* $OpenBSD: addrmatch.c,v 1.17 2021/04/03 06:18:40 djm Exp $ */
3
4 /*
5 * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include "includes.h"
21 __RCSID("$NetBSD: addrmatch.c,v 1.15 2021/04/19 14:40:15 christos Exp $");
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26
27 #include <netdb.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <stdarg.h>
32
33 #include "addr.h"
34 #include "match.h"
35 #include "log.h"
36
37 /*
38 * Match "addr" against list pattern list "_list", which may contain a
39 * mix of CIDR addresses and old-school wildcards.
40 *
41 * If addr is NULL, then no matching is performed, but _list is parsed
42 * and checked for well-formedness.
43 *
44 * Returns 1 on match found (never returned when addr == NULL).
45 * Returns 0 on if no match found, or no errors found when addr == NULL.
46 * Returns -1 on negated match found (never returned when addr == NULL).
47 * Returns -2 on invalid list entry.
48 */
49 int
addr_match_list(const char * addr,const char * _list)50 addr_match_list(const char *addr, const char *_list)
51 {
52 char *list, *cp, *o;
53 struct xaddr try_addr, match_addr;
54 u_int masklen, neg;
55 int ret = 0, r;
56
57 if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
58 debug2_f("couldn't parse address %.100s", addr);
59 return 0;
60 }
61 if ((o = list = strdup(_list)) == NULL)
62 return -1;
63 while ((cp = strsep(&list, ",")) != NULL) {
64 neg = *cp == '!';
65 if (neg)
66 cp++;
67 if (*cp == '\0') {
68 ret = -2;
69 break;
70 }
71 /* Prefer CIDR address matching */
72 r = addr_pton_cidr(cp, &match_addr, &masklen);
73 if (r == -2) {
74 debug2_f("inconsistent mask length for "
75 "match network \"%.100s\"", cp);
76 ret = -2;
77 break;
78 } else if (r == 0) {
79 if (addr != NULL && addr_netmatch(&try_addr,
80 &match_addr, masklen) == 0) {
81 foundit:
82 if (neg) {
83 ret = -1;
84 break;
85 }
86 ret = 1;
87 }
88 continue;
89 } else {
90 /* If CIDR parse failed, try wildcard string match */
91 if (addr != NULL && match_pattern(addr, cp) == 1)
92 goto foundit;
93 }
94 }
95 free(o);
96
97 return ret;
98 }
99
100 /*
101 * Match "addr" against list CIDR list "_list". Lexical wildcards and
102 * negation are not supported. If "addr" == NULL, will verify structure
103 * of "_list".
104 *
105 * Returns 1 on match found (never returned when addr == NULL).
106 * Returns 0 on if no match found, or no errors found when addr == NULL.
107 * Returns -1 on error
108 */
109 int
addr_match_cidr_list(const char * addr,const char * _list)110 addr_match_cidr_list(const char *addr, const char *_list)
111 {
112 char *list, *cp, *o;
113 struct xaddr try_addr, match_addr;
114 u_int masklen;
115 int ret = 0, r;
116
117 if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
118 debug2_f("couldn't parse address %.100s", addr);
119 return 0;
120 }
121 if ((o = list = strdup(_list)) == NULL)
122 return -1;
123 while ((cp = strsep(&list, ",")) != NULL) {
124 if (*cp == '\0') {
125 error_f("empty entry in list \"%.100s\"", o);
126 ret = -1;
127 break;
128 }
129
130 /*
131 * NB. This function is called in pre-auth with untrusted data,
132 * so be extra paranoid about junk reaching getaddrino (via
133 * addr_pton_cidr).
134 */
135
136 /* Stop junk from reaching getaddrinfo. +3 is for masklen */
137 if (strlen(cp) > INET6_ADDRSTRLEN + 3) {
138 error_f("list entry \"%.100s\" too long", cp);
139 ret = -1;
140 break;
141 }
142 #define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/"
143 if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) {
144 error_f("list entry \"%.100s\" contains invalid "
145 "characters", cp);
146 ret = -1;
147 }
148
149 /* Prefer CIDR address matching */
150 r = addr_pton_cidr(cp, &match_addr, &masklen);
151 if (r == -1) {
152 error("Invalid network entry \"%.100s\"", cp);
153 ret = -1;
154 break;
155 } else if (r == -2) {
156 error("Inconsistent mask length for "
157 "network \"%.100s\"", cp);
158 ret = -1;
159 break;
160 } else if (r == 0 && addr != NULL) {
161 if (addr_netmatch(&try_addr, &match_addr,
162 masklen) == 0)
163 ret = 1;
164 continue;
165 }
166 }
167 free(o);
168
169 return ret;
170 }
171