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