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