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