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