Annotation of src/usr.bin/ssh/addrmatch.c, Revision 1.1
1.1 ! djm 1: /* $OpenBSD$ */
! 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:
! 377: if (addr != NULL && addr_pton(addr, &try_addr) != 0)
! 378: fatal("%s: addr_pton(%s)", __func__, addr);
! 379: if ((o = list = strdup(_list)) == NULL)
! 380: return -1;
! 381: while ((cp = strsep(&list, ",")) != NULL) {
! 382: neg = *cp == '!';
! 383: if (neg)
! 384: cp++;
! 385: if (*cp == '\0') {
! 386: ret = -1;
! 387: break;
! 388: }
! 389: /* Prefer CIDR address matching */
! 390: r = addr_pton_cidr(cp, &match_addr, &masklen);
! 391: if (r == -2) {
! 392: error("Inconsistent mask length for "
! 393: "network \"%.100s\"", cp);
! 394: ret = -1;
! 395: break;
! 396: } else if (r == 0) {
! 397: if (addr != NULL && addr_netmatch(&try_addr,
! 398: &match_addr, masklen) == 0) {
! 399: foundit:
! 400: if (neg) {
! 401: ret = 0;
! 402: break;
! 403: }
! 404: ret = 1;
! 405: }
! 406: continue;
! 407: } else {
! 408: /* If CIDR parse failed, try wildcard string match */
! 409: if (addr != NULL && match_pattern(addr, cp) == 1)
! 410: goto foundit;
! 411: }
! 412: }
! 413: free(o);
! 414:
! 415: return ret;
! 416: }