1 /* $OpenBSD: addrmatch.c,v 1.17 2021/04/03 06:18:40 djm Exp $ */ 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 "addr.h" 31 #include "match.h" 32 #include "log.h" 33 34 /* 35 * Match "addr" against list pattern list "_list", which may contain a 36 * mix of CIDR addresses and old-school wildcards. 37 * 38 * If addr is NULL, then no matching is performed, but _list is parsed 39 * and checked for well-formedness. 40 * 41 * Returns 1 on match found (never returned when addr == NULL). 42 * Returns 0 on if no match found, or no errors found when addr == NULL. 43 * Returns -1 on negated match found (never returned when addr == NULL). 44 * Returns -2 on invalid list entry. 45 */ 46 int 47 addr_match_list(const char *addr, const char *_list) 48 { 49 char *list, *cp, *o; 50 struct xaddr try_addr, match_addr; 51 u_int masklen, neg; 52 int ret = 0, r; 53 54 if (addr != NULL && addr_pton(addr, &try_addr) != 0) { 55 debug2_f("couldn't parse address %.100s", addr); 56 return 0; 57 } 58 if ((o = list = strdup(_list)) == NULL) 59 return -1; 60 while ((cp = strsep(&list, ",")) != NULL) { 61 neg = *cp == '!'; 62 if (neg) 63 cp++; 64 if (*cp == '\0') { 65 ret = -2; 66 break; 67 } 68 /* Prefer CIDR address matching */ 69 r = addr_pton_cidr(cp, &match_addr, &masklen); 70 if (r == -2) { 71 debug2_f("inconsistent mask length for " 72 "match network \"%.100s\"", cp); 73 ret = -2; 74 break; 75 } else if (r == 0) { 76 if (addr != NULL && addr_netmatch(&try_addr, 77 &match_addr, masklen) == 0) { 78 foundit: 79 if (neg) { 80 ret = -1; 81 break; 82 } 83 ret = 1; 84 } 85 continue; 86 } else { 87 /* If CIDR parse failed, try wildcard string match */ 88 if (addr != NULL && match_pattern(addr, cp) == 1) 89 goto foundit; 90 } 91 } 92 free(o); 93 94 return ret; 95 } 96 97 /* 98 * Match "addr" against list CIDR list "_list". Lexical wildcards and 99 * negation are not supported. If "addr" == NULL, will verify structure 100 * of "_list". 101 * 102 * Returns 1 on match found (never returned when addr == NULL). 103 * Returns 0 on if no match found, or no errors found when addr == NULL. 104 * Returns -1 on error 105 */ 106 int 107 addr_match_cidr_list(const char *addr, const char *_list) 108 { 109 char *list, *cp, *o; 110 struct xaddr try_addr, match_addr; 111 u_int masklen; 112 int ret = 0, r; 113 114 if (addr != NULL && addr_pton(addr, &try_addr) != 0) { 115 debug2_f("couldn't parse address %.100s", addr); 116 return 0; 117 } 118 if ((o = list = strdup(_list)) == NULL) 119 return -1; 120 while ((cp = strsep(&list, ",")) != NULL) { 121 if (*cp == '\0') { 122 error_f("empty entry in list \"%.100s\"", o); 123 ret = -1; 124 break; 125 } 126 127 /* 128 * NB. This function is called in pre-auth with untrusted data, 129 * so be extra paranoid about junk reaching getaddrino (via 130 * addr_pton_cidr). 131 */ 132 133 /* Stop junk from reaching getaddrinfo. +3 is for masklen */ 134 if (strlen(cp) > INET6_ADDRSTRLEN + 3) { 135 error_f("list entry \"%.100s\" too long", cp); 136 ret = -1; 137 break; 138 } 139 #define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" 140 if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) { 141 error_f("list entry \"%.100s\" contains invalid " 142 "characters", cp); 143 ret = -1; 144 } 145 146 /* Prefer CIDR address matching */ 147 r = addr_pton_cidr(cp, &match_addr, &masklen); 148 if (r == -1) { 149 error("Invalid network entry \"%.100s\"", cp); 150 ret = -1; 151 break; 152 } else if (r == -2) { 153 error("Inconsistent mask length for " 154 "network \"%.100s\"", cp); 155 ret = -1; 156 break; 157 } else if (r == 0 && addr != NULL) { 158 if (addr_netmatch(&try_addr, &match_addr, 159 masklen) == 0) 160 ret = 1; 161 continue; 162 } 163 } 164 free(o); 165 166 return ret; 167 } 168