1/* $NetBSD: addrmatch.c,v 1.15 2021/04/19 14:40:15 christos Exp $ */ 2/* $OpenBSD: addrmatch.c,v 1.17 2021/04/03 06:18:40 djm Exp $ */ 3 4/* 5 * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include "includes.h" 21__RCSID("$NetBSD: addrmatch.c,v 1.15 2021/04/19 14:40:15 christos Exp $"); 22#include <sys/types.h> 23#include <sys/socket.h> 24#include <netinet/in.h> 25#include <arpa/inet.h> 26 27#include <netdb.h> 28#include <string.h> 29#include <stdlib.h> 30#include <stdio.h> 31#include <stdarg.h> 32 33#include "addr.h" 34#include "match.h" 35#include "log.h" 36 37/* 38 * Match "addr" against list pattern list "_list", which may contain a 39 * mix of CIDR addresses and old-school wildcards. 40 * 41 * If addr is NULL, then no matching is performed, but _list is parsed 42 * and checked for well-formedness. 43 * 44 * Returns 1 on match found (never returned when addr == NULL). 45 * Returns 0 on if no match found, or no errors found when addr == NULL. 46 * Returns -1 on negated match found (never returned when addr == NULL). 47 * Returns -2 on invalid list entry. 48 */ 49int 50addr_match_list(const char *addr, const char *_list) 51{ 52 char *list, *cp, *o; 53 struct xaddr try_addr, match_addr; 54 u_int masklen, neg; 55 int ret = 0, r; 56 57 if (addr != NULL && addr_pton(addr, &try_addr) != 0) { 58 debug2_f("couldn't parse address %.100s", addr); 59 return 0; 60 } 61 if ((o = list = strdup(_list)) == NULL) 62 return -1; 63 while ((cp = strsep(&list, ",")) != NULL) { 64 neg = *cp == '!'; 65 if (neg) 66 cp++; 67 if (*cp == '\0') { 68 ret = -2; 69 break; 70 } 71 /* Prefer CIDR address matching */ 72 r = addr_pton_cidr(cp, &match_addr, &masklen); 73 if (r == -2) { 74 debug2_f("inconsistent mask length for " 75 "match network \"%.100s\"", cp); 76 ret = -2; 77 break; 78 } else if (r == 0) { 79 if (addr != NULL && addr_netmatch(&try_addr, 80 &match_addr, masklen) == 0) { 81 foundit: 82 if (neg) { 83 ret = -1; 84 break; 85 } 86 ret = 1; 87 } 88 continue; 89 } else { 90 /* If CIDR parse failed, try wildcard string match */ 91 if (addr != NULL && match_pattern(addr, cp) == 1) 92 goto foundit; 93 } 94 } 95 free(o); 96 97 return ret; 98} 99 100/* 101 * Match "addr" against list CIDR list "_list". Lexical wildcards and 102 * negation are not supported. If "addr" == NULL, will verify structure 103 * of "_list". 104 * 105 * Returns 1 on match found (never returned when addr == NULL). 106 * Returns 0 on if no match found, or no errors found when addr == NULL. 107 * Returns -1 on error 108 */ 109int 110addr_match_cidr_list(const char *addr, const char *_list) 111{ 112 char *list, *cp, *o; 113 struct xaddr try_addr, match_addr; 114 u_int masklen; 115 int ret = 0, r; 116 117 if (addr != NULL && addr_pton(addr, &try_addr) != 0) { 118 debug2_f("couldn't parse address %.100s", addr); 119 return 0; 120 } 121 if ((o = list = strdup(_list)) == NULL) 122 return -1; 123 while ((cp = strsep(&list, ",")) != NULL) { 124 if (*cp == '\0') { 125 error_f("empty entry in list \"%.100s\"", o); 126 ret = -1; 127 break; 128 } 129 130 /* 131 * NB. This function is called in pre-auth with untrusted data, 132 * so be extra paranoid about junk reaching getaddrino (via 133 * addr_pton_cidr). 134 */ 135 136 /* Stop junk from reaching getaddrinfo. +3 is for masklen */ 137 if (strlen(cp) > INET6_ADDRSTRLEN + 3) { 138 error_f("list entry \"%.100s\" too long", cp); 139 ret = -1; 140 break; 141 } 142#define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" 143 if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) { 144 error_f("list entry \"%.100s\" contains invalid " 145 "characters", cp); 146 ret = -1; 147 } 148 149 /* Prefer CIDR address matching */ 150 r = addr_pton_cidr(cp, &match_addr, &masklen); 151 if (r == -1) { 152 error("Invalid network entry \"%.100s\"", cp); 153 ret = -1; 154 break; 155 } else if (r == -2) { 156 error("Inconsistent mask length for " 157 "network \"%.100s\"", cp); 158 ret = -1; 159 break; 160 } else if (r == 0 && addr != NULL) { 161 if (addr_netmatch(&try_addr, &match_addr, 162 masklen) == 0) 163 ret = 1; 164 continue; 165 } 166 } 167 free(o); 168 169 return ret; 170} 171