1 | /* $NetBSD: addrmatch.c,v 1.13 2019/01/27 02:08:33 pgoyette Exp $ */ |
2 | /* $OpenBSD: addrmatch.c,v 1.14 2018/07/31 03:07:24 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.13 2019/01/27 02:08:33 pgoyette 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 "match.h" |
34 | #include "log.h" |
35 | |
36 | struct xaddr { |
37 | sa_family_t af; |
38 | union { |
39 | struct in_addr v4; |
40 | struct in6_addr v6; |
41 | u_int8_t addr8[16]; |
42 | u_int32_t addr32[4]; |
43 | } xa; /* 128-bit address */ |
44 | u_int32_t scope_id; /* iface scope id for v6 */ |
45 | #define v4 xa.v4 |
46 | #define v6 xa.v6 |
47 | #define addr8 xa.addr8 |
48 | #define addr32 xa.addr32 |
49 | }; |
50 | |
51 | static int |
52 | addr_unicast_masklen(int af) |
53 | { |
54 | switch (af) { |
55 | case AF_INET: |
56 | return 32; |
57 | case AF_INET6: |
58 | return 128; |
59 | default: |
60 | return -1; |
61 | } |
62 | } |
63 | |
64 | static inline int |
65 | masklen_valid(int af, u_int masklen) |
66 | { |
67 | switch (af) { |
68 | case AF_INET: |
69 | return masklen <= 32 ? 0 : -1; |
70 | case AF_INET6: |
71 | return masklen <= 128 ? 0 : -1; |
72 | default: |
73 | return -1; |
74 | } |
75 | } |
76 | |
77 | /* |
78 | * Convert struct sockaddr to struct xaddr |
79 | * Returns 0 on success, -1 on failure. |
80 | */ |
81 | static int |
82 | addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa) |
83 | { |
84 | struct sockaddr_in *in4 = (struct sockaddr_in *)(void *)sa; |
85 | struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)(void *)sa; |
86 | |
87 | memset(xa, '\0', sizeof(*xa)); |
88 | |
89 | switch (sa->sa_family) { |
90 | case AF_INET: |
91 | if (slen < (socklen_t)sizeof(*in4)) |
92 | return -1; |
93 | xa->af = AF_INET; |
94 | memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4)); |
95 | break; |
96 | case AF_INET6: |
97 | if (slen < (socklen_t)sizeof(*in6)) |
98 | return -1; |
99 | xa->af = AF_INET6; |
100 | memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6)); |
101 | xa->scope_id = in6->sin6_scope_id; |
102 | break; |
103 | default: |
104 | return -1; |
105 | } |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | /* |
111 | * Calculate a netmask of length 'l' for address family 'af' and |
112 | * store it in 'n'. |
113 | * Returns 0 on success, -1 on failure. |
114 | */ |
115 | static int |
116 | addr_netmask(int af, u_int l, struct xaddr *n) |
117 | { |
118 | int i; |
119 | |
120 | if (masklen_valid(af, l) != 0 || n == NULL) |
121 | return -1; |
122 | |
123 | memset(n, '\0', sizeof(*n)); |
124 | switch (af) { |
125 | case AF_INET: |
126 | n->af = AF_INET; |
127 | if (l == 0) |
128 | return 0; |
129 | n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff); |
130 | return 0; |
131 | case AF_INET6: |
132 | n->af = AF_INET6; |
133 | for (i = 0; i < 4 && l >= 32; i++, l -= 32) |
134 | n->addr32[i] = 0xffffffffU; |
135 | if (i < 4 && l != 0) |
136 | n->addr32[i] = htonl((0xffffffff << (32 - l)) & |
137 | 0xffffffff); |
138 | return 0; |
139 | default: |
140 | return -1; |
141 | } |
142 | } |
143 | |
144 | /* |
145 | * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'. |
146 | * Returns 0 on success, -1 on failure. |
147 | */ |
148 | static int |
149 | addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b) |
150 | { |
151 | int i; |
152 | |
153 | if (dst == NULL || a == NULL || b == NULL || a->af != b->af) |
154 | return -1; |
155 | |
156 | memcpy(dst, a, sizeof(*dst)); |
157 | switch (a->af) { |
158 | case AF_INET: |
159 | dst->v4.s_addr &= b->v4.s_addr; |
160 | return 0; |
161 | case AF_INET6: |
162 | dst->scope_id = a->scope_id; |
163 | for (i = 0; i < 4; i++) |
164 | dst->addr32[i] &= b->addr32[i]; |
165 | return 0; |
166 | default: |
167 | return -1; |
168 | } |
169 | } |
170 | |
171 | /* |
172 | * Compare addresses 'a' and 'b' |
173 | * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b) |
174 | */ |
175 | static int |
176 | addr_cmp(const struct xaddr *a, const struct xaddr *b) |
177 | { |
178 | int i; |
179 | |
180 | if (a->af != b->af) |
181 | return a->af == AF_INET6 ? 1 : -1; |
182 | |
183 | switch (a->af) { |
184 | case AF_INET: |
185 | if (a->v4.s_addr == b->v4.s_addr) |
186 | return 0; |
187 | return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1; |
188 | case AF_INET6: |
189 | for (i = 0; i < 16; i++) |
190 | if (a->addr8[i] - b->addr8[i] != 0) |
191 | return a->addr8[i] > b->addr8[i] ? 1 : -1; |
192 | if (a->scope_id == b->scope_id) |
193 | return 0; |
194 | return a->scope_id > b->scope_id ? 1 : -1; |
195 | default: |
196 | return -1; |
197 | } |
198 | } |
199 | |
200 | /* |
201 | * Parse string address 'p' into 'n' |
202 | * Returns 0 on success, -1 on failure. |
203 | */ |
204 | static int |
205 | addr_pton(const char *p, struct xaddr *n) |
206 | { |
207 | struct addrinfo hints, *ai = NULL; |
208 | int ret = -1; |
209 | |
210 | memset(&hints, '\0', sizeof(hints)); |
211 | hints.ai_flags = AI_NUMERICHOST; |
212 | |
213 | if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0) |
214 | goto out; |
215 | if (ai == NULL || ai->ai_addr == NULL) |
216 | goto out; |
217 | if (n != NULL && addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) |
218 | goto out; |
219 | /* success */ |
220 | ret = 0; |
221 | out: |
222 | if (ai != NULL) |
223 | freeaddrinfo(ai); |
224 | return ret; |
225 | } |
226 | |
227 | /* |
228 | * Perform bitwise negation of address |
229 | * Returns 0 on success, -1 on failure. |
230 | */ |
231 | static int |
232 | addr_invert(struct xaddr *n) |
233 | { |
234 | int i; |
235 | |
236 | if (n == NULL) |
237 | return (-1); |
238 | |
239 | switch (n->af) { |
240 | case AF_INET: |
241 | n->v4.s_addr = ~n->v4.s_addr; |
242 | return (0); |
243 | case AF_INET6: |
244 | for (i = 0; i < 4; i++) |
245 | n->addr32[i] = ~n->addr32[i]; |
246 | return (0); |
247 | default: |
248 | return (-1); |
249 | } |
250 | } |
251 | |
252 | /* |
253 | * Calculate a netmask of length 'l' for address family 'af' and |
254 | * store it in 'n'. |
255 | * Returns 0 on success, -1 on failure. |
256 | */ |
257 | static int |
258 | addr_hostmask(int af, u_int l, struct xaddr *n) |
259 | { |
260 | if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1) |
261 | return (-1); |
262 | return (0); |
263 | } |
264 | |
265 | /* |
266 | * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::) |
267 | * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure. |
268 | */ |
269 | static int |
270 | addr_is_all0s(const struct xaddr *a) |
271 | { |
272 | int i; |
273 | |
274 | switch (a->af) { |
275 | case AF_INET: |
276 | return (a->v4.s_addr == 0 ? 0 : -1); |
277 | case AF_INET6:; |
278 | for (i = 0; i < 4; i++) |
279 | if (a->addr32[i] != 0) |
280 | return (-1); |
281 | return (0); |
282 | default: |
283 | return (-1); |
284 | } |
285 | } |
286 | |
287 | /* |
288 | * Test whether host portion of address 'a', as determined by 'masklen' |
289 | * is all zeros. |
290 | * Returns 0 on if host portion of address is all-zeros, |
291 | * -1 if not all zeros or on failure. |
292 | */ |
293 | static int |
294 | addr_host_is_all0s(const struct xaddr *a, u_int masklen) |
295 | { |
296 | struct xaddr tmp_addr, tmp_mask, tmp_result; |
297 | |
298 | memcpy(&tmp_addr, a, sizeof(tmp_addr)); |
299 | if (addr_hostmask(a->af, masklen, &tmp_mask) == -1) |
300 | return (-1); |
301 | if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1) |
302 | return (-1); |
303 | return (addr_is_all0s(&tmp_result)); |
304 | } |
305 | |
306 | /* |
307 | * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z). |
308 | * Return -1 on parse error, -2 on inconsistency or 0 on success. |
309 | */ |
310 | static int |
311 | addr_pton_cidr(const char *p, struct xaddr *n, u_int *l) |
312 | { |
313 | struct xaddr tmp; |
314 | unsigned int masklen = 999; |
315 | char addrbuf[64], *mp, *cp; |
316 | |
317 | /* Don't modify argument */ |
318 | if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) >= sizeof(addrbuf)) |
319 | return -1; |
320 | |
321 | if ((mp = strchr(addrbuf, '/')) != NULL) { |
322 | *mp = '\0'; |
323 | mp++; |
324 | masklen = strtoul(mp, &cp, 10); |
325 | if (*mp == '\0' || *cp != '\0' || masklen > 128) |
326 | return -1; |
327 | } |
328 | |
329 | if (addr_pton(addrbuf, &tmp) == -1) |
330 | return -1; |
331 | |
332 | if (mp == NULL) |
333 | masklen = addr_unicast_masklen(tmp.af); |
334 | if (masklen_valid(tmp.af, masklen) == -1) |
335 | return -2; |
336 | if (addr_host_is_all0s(&tmp, masklen) != 0) |
337 | return -2; |
338 | |
339 | if (n != NULL) |
340 | memcpy(n, &tmp, sizeof(*n)); |
341 | if (l != NULL) |
342 | *l = masklen; |
343 | |
344 | return 0; |
345 | } |
346 | |
347 | static int |
348 | addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen) |
349 | { |
350 | struct xaddr tmp_mask, tmp_result; |
351 | |
352 | if (host->af != net->af) |
353 | return -1; |
354 | |
355 | if (addr_netmask(host->af, masklen, &tmp_mask) == -1) |
356 | return -1; |
357 | if (addr_and(&tmp_result, host, &tmp_mask) == -1) |
358 | return -1; |
359 | return addr_cmp(&tmp_result, net); |
360 | } |
361 | |
362 | /* |
363 | * Match "addr" against list pattern list "_list", which may contain a |
364 | * mix of CIDR addresses and old-school wildcards. |
365 | * |
366 | * If addr is NULL, then no matching is performed, but _list is parsed |
367 | * and checked for well-formedness. |
368 | * |
369 | * Returns 1 on match found (never returned when addr == NULL). |
370 | * Returns 0 on if no match found, or no errors found when addr == NULL. |
371 | * Returns -1 on negated match found (never returned when addr == NULL). |
372 | * Returns -2 on invalid list entry. |
373 | */ |
374 | int |
375 | addr_match_list(const char *addr, const char *_list) |
376 | { |
377 | char *list, *cp, *o; |
378 | struct xaddr try_addr, match_addr; |
379 | u_int masklen, neg; |
380 | int ret = 0, r; |
381 | |
382 | if (addr != NULL && addr_pton(addr, &try_addr) != 0) { |
383 | debug2("%s: couldn't parse address %.100s" , __func__, addr); |
384 | return 0; |
385 | } |
386 | if ((o = list = strdup(_list)) == NULL) |
387 | return -1; |
388 | while ((cp = strsep(&list, "," )) != NULL) { |
389 | neg = *cp == '!'; |
390 | if (neg) |
391 | cp++; |
392 | if (*cp == '\0') { |
393 | ret = -2; |
394 | break; |
395 | } |
396 | /* Prefer CIDR address matching */ |
397 | r = addr_pton_cidr(cp, &match_addr, &masklen); |
398 | if (r == -2) { |
399 | debug2("%s: inconsistent mask length for " |
400 | "match network \"%.100s\"" , __func__, cp); |
401 | ret = -2; |
402 | break; |
403 | } else if (r == 0) { |
404 | if (addr != NULL && addr_netmatch(&try_addr, |
405 | &match_addr, masklen) == 0) { |
406 | foundit: |
407 | if (neg) { |
408 | ret = -1; |
409 | break; |
410 | } |
411 | ret = 1; |
412 | } |
413 | continue; |
414 | } else { |
415 | /* If CIDR parse failed, try wildcard string match */ |
416 | if (addr != NULL && match_pattern(addr, cp) == 1) |
417 | goto foundit; |
418 | } |
419 | } |
420 | free(o); |
421 | |
422 | return ret; |
423 | } |
424 | |
425 | /* |
426 | * Match "addr" against list CIDR list "_list". Lexical wildcards and |
427 | * negation are not supported. If "addr" == NULL, will verify structure |
428 | * of "_list". |
429 | * |
430 | * Returns 1 on match found (never returned when addr == NULL). |
431 | * Returns 0 on if no match found, or no errors found when addr == NULL. |
432 | * Returns -1 on error |
433 | */ |
434 | int |
435 | addr_match_cidr_list(const char *addr, const char *_list) |
436 | { |
437 | char *list, *cp, *o; |
438 | struct xaddr try_addr, match_addr; |
439 | u_int masklen; |
440 | int ret = 0, r; |
441 | |
442 | if (addr != NULL && addr_pton(addr, &try_addr) != 0) { |
443 | debug2("%s: couldn't parse address %.100s" , __func__, addr); |
444 | return 0; |
445 | } |
446 | if ((o = list = strdup(_list)) == NULL) |
447 | return -1; |
448 | while ((cp = strsep(&list, "," )) != NULL) { |
449 | if (*cp == '\0') { |
450 | error("%s: empty entry in list \"%.100s\"" , |
451 | __func__, o); |
452 | ret = -1; |
453 | break; |
454 | } |
455 | |
456 | /* |
457 | * NB. This function is called in pre-auth with untrusted data, |
458 | * so be extra paranoid about junk reaching getaddrino (via |
459 | * addr_pton_cidr). |
460 | */ |
461 | |
462 | /* Stop junk from reaching getaddrinfo. +3 is for masklen */ |
463 | if (strlen(cp) > INET6_ADDRSTRLEN + 3) { |
464 | error("%s: list entry \"%.100s\" too long" , |
465 | __func__, cp); |
466 | ret = -1; |
467 | break; |
468 | } |
469 | #define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" |
470 | if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) { |
471 | error("%s: list entry \"%.100s\" contains invalid " |
472 | "characters" , __func__, cp); |
473 | ret = -1; |
474 | } |
475 | |
476 | /* Prefer CIDR address matching */ |
477 | r = addr_pton_cidr(cp, &match_addr, &masklen); |
478 | if (r == -1) { |
479 | error("Invalid network entry \"%.100s\"" , cp); |
480 | ret = -1; |
481 | break; |
482 | } else if (r == -2) { |
483 | error("Inconsistent mask length for " |
484 | "network \"%.100s\"" , cp); |
485 | ret = -1; |
486 | break; |
487 | } else if (r == 0 && addr != NULL) { |
488 | if (addr_netmatch(&try_addr, &match_addr, |
489 | masklen) == 0) |
490 | ret = 1; |
491 | continue; |
492 | } |
493 | } |
494 | free(o); |
495 | |
496 | return ret; |
497 | } |
498 | |