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