addrmatch.c revision 1.2
1/*	$OpenBSD: addrmatch.c,v 1.2 2008/06/10 05:22:45 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 "match.h"
31#include "log.h"
32
33struct xaddr {
34	sa_family_t	af;
35	union {
36		struct in_addr		v4;
37		struct in6_addr		v6;
38		u_int8_t		addr8[16];
39		u_int32_t		addr32[4];
40	} xa;		    /* 128-bit address */
41	u_int32_t	scope_id;	/* iface scope id for v6 */
42#define v4	xa.v4
43#define v6	xa.v6
44#define addr8	xa.addr8
45#define addr32	xa.addr32
46};
47
48static int
49addr_unicast_masklen(int af)
50{
51	switch (af) {
52	case AF_INET:
53		return 32;
54	case AF_INET6:
55		return 128;
56	default:
57		return -1;
58	}
59}
60
61static inline int
62masklen_valid(int af, u_int masklen)
63{
64	switch (af) {
65	case AF_INET:
66		return masklen <= 32 ? 0 : -1;
67	case AF_INET6:
68		return masklen <= 128 ? 0 : -1;
69	default:
70		return -1;
71	}
72}
73
74/*
75 * Convert struct sockaddr to struct xaddr
76 * Returns 0 on success, -1 on failure.
77 */
78static int
79addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa)
80{
81	struct sockaddr_in *in4 = (struct sockaddr_in *)sa;
82	struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
83
84	memset(xa, '\0', sizeof(*xa));
85
86	switch (sa->sa_family) {
87	case AF_INET:
88		if (slen < sizeof(*in4))
89			return -1;
90		xa->af = AF_INET;
91		memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4));
92		break;
93	case AF_INET6:
94		if (slen < sizeof(*in6))
95			return -1;
96		xa->af = AF_INET6;
97		memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6));
98		xa->scope_id = in6->sin6_scope_id;
99		break;
100	default:
101		return -1;
102	}
103
104	return 0;
105}
106
107/*
108 * Calculate a netmask of length 'l' for address family 'af' and
109 * store it in 'n'.
110 * Returns 0 on success, -1 on failure.
111 */
112static int
113addr_netmask(int af, u_int l, struct xaddr *n)
114{
115	int i;
116
117	if (masklen_valid(af, l) != 0 || n == NULL)
118		return -1;
119
120	memset(n, '\0', sizeof(*n));
121	switch (af) {
122	case AF_INET:
123		n->af = AF_INET;
124		n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff);
125		return 0;
126	case AF_INET6:
127		n->af = AF_INET6;
128		for (i = 0; i < 4 && l >= 32; i++, l -= 32)
129			n->addr32[i] = 0xffffffffU;
130		if (i < 4 && l != 0)
131			n->addr32[i] = htonl((0xffffffff << (32 - l)) &
132			    0xffffffff);
133		return 0;
134	default:
135		return -1;
136	}
137}
138
139/*
140 * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'.
141 * Returns 0 on success, -1 on failure.
142 */
143static int
144addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b)
145{
146	int i;
147
148	if (dst == NULL || a == NULL || b == NULL || a->af != b->af)
149		return -1;
150
151	memcpy(dst, a, sizeof(*dst));
152	switch (a->af) {
153	case AF_INET:
154		dst->v4.s_addr &= b->v4.s_addr;
155		return 0;
156	case AF_INET6:
157		dst->scope_id = a->scope_id;
158		for (i = 0; i < 4; i++)
159			dst->addr32[i] &= b->addr32[i];
160		return 0;
161	default:
162		return -1;
163	}
164}
165
166/*
167 * Compare addresses 'a' and 'b'
168 * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b)
169 */
170static int
171addr_cmp(const struct xaddr *a, const struct xaddr *b)
172{
173	int i;
174
175	if (a->af != b->af)
176		return a->af == AF_INET6 ? 1 : -1;
177
178	switch (a->af) {
179	case AF_INET:
180		if (a->v4.s_addr == b->v4.s_addr)
181			return 0;
182		return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1;
183	case AF_INET6:
184		for (i = 0; i < 16; i++)
185			if (a->addr8[i] - b->addr8[i] != 0)
186				return a->addr8[i] > b->addr8[i] ? 1 : -1;
187		if (a->scope_id == b->scope_id)
188			return 0;
189		return a->scope_id > b->scope_id ? 1 : -1;
190	default:
191		return -1;
192	}
193}
194
195/*
196 * Parse string address 'p' into 'n'
197 * Returns 0 on success, -1 on failure.
198 */
199static int
200addr_pton(const char *p, struct xaddr *n)
201{
202	struct addrinfo hints, *ai;
203
204	memset(&hints, '\0', sizeof(hints));
205	hints.ai_flags = AI_NUMERICHOST;
206
207	if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0)
208		return -1;
209
210	if (ai == NULL || ai->ai_addr == NULL)
211		return -1;
212
213	if (n != NULL &&
214	    addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) {
215		freeaddrinfo(ai);
216		return -1;
217	}
218
219	freeaddrinfo(ai);
220	return 0;
221}
222
223/*
224 * Perform bitwise negation of address
225 * Returns 0 on success, -1 on failure.
226 */
227static int
228addr_invert(struct xaddr *n)
229{
230	int i;
231
232	if (n == NULL)
233		return (-1);
234
235	switch (n->af) {
236	case AF_INET:
237		n->v4.s_addr = ~n->v4.s_addr;
238		return (0);
239	case AF_INET6:
240		for (i = 0; i < 4; i++)
241			n->addr32[i] = ~n->addr32[i];
242		return (0);
243	default:
244		return (-1);
245	}
246}
247
248/*
249 * Calculate a netmask of length 'l' for address family 'af' and
250 * store it in 'n'.
251 * Returns 0 on success, -1 on failure.
252 */
253static int
254addr_hostmask(int af, u_int l, struct xaddr *n)
255{
256	if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1)
257		return (-1);
258	return (0);
259}
260
261/*
262 * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::)
263 * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure.
264 */
265static int
266addr_is_all0s(const struct xaddr *a)
267{
268	int i;
269
270	switch (a->af) {
271	case AF_INET:
272		return (a->v4.s_addr == 0 ? 0 : -1);
273	case AF_INET6:;
274		for (i = 0; i < 4; i++)
275			if (a->addr32[i] != 0)
276				return (-1);
277		return (0);
278	default:
279		return (-1);
280	}
281}
282
283/*
284 * Test whether host portion of address 'a', as determined by 'masklen'
285 * is all zeros.
286 * Returns 0 on if host portion of address is all-zeros,
287 * -1 if not all zeros or on failure.
288 */
289static int
290addr_host_is_all0s(const struct xaddr *a, u_int masklen)
291{
292	struct xaddr tmp_addr, tmp_mask, tmp_result;
293
294	memcpy(&tmp_addr, a, sizeof(tmp_addr));
295	if (addr_hostmask(a->af, masklen, &tmp_mask) == -1)
296		return (-1);
297	if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1)
298		return (-1);
299	return (addr_is_all0s(&tmp_result));
300}
301
302/*
303 * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z).
304 * Return -1 on parse error, -2 on inconsistency or 0 on success.
305 */
306static int
307addr_pton_cidr(const char *p, struct xaddr *n, u_int *l)
308{
309	struct xaddr tmp;
310	long unsigned int masklen = 999;
311	char addrbuf[64], *mp, *cp;
312
313	/* Don't modify argument */
314	if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf))
315		return -1;
316
317	if ((mp = strchr(addrbuf, '/')) != NULL) {
318		*mp = '\0';
319		mp++;
320		masklen = strtoul(mp, &cp, 10);
321		if (*mp == '\0' || *cp != '\0' || masklen > 128)
322			return -1;
323	}
324
325	if (addr_pton(addrbuf, &tmp) == -1)
326		return -1;
327
328	if (mp == NULL)
329		masklen = addr_unicast_masklen(tmp.af);
330	if (masklen_valid(tmp.af, masklen) == -1)
331		return -2;
332	if (addr_host_is_all0s(&tmp, masklen) != 0)
333		return -2;
334
335	if (n != NULL)
336		memcpy(n, &tmp, sizeof(*n));
337	if (l != NULL)
338		*l = masklen;
339
340	return 0;
341}
342
343static int
344addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen)
345{
346	struct xaddr tmp_mask, tmp_result;
347
348	if (host->af != net->af)
349		return -1;
350
351	if (addr_netmask(host->af, masklen, &tmp_mask) == -1)
352		return -1;
353	if (addr_and(&tmp_result, host, &tmp_mask) == -1)
354		return -1;
355	return addr_cmp(&tmp_result, net);
356}
357
358/*
359 * Match "addr" against list pattern list "_list", which may contain a
360 * mix of CIDR addresses and old-school wildcards.
361 *
362 * If addr is NULL, then no matching is performed, but _list is parsed
363 * and checked for well-formedness.
364 *
365 * Returns 1 on match found (never returned when addr == NULL).
366 * Returns 0 on if no match found, or no errors found when addr == NULL.
367 * Returns -1 on invalid list entry.
368 */
369int
370addr_match_list(const char *addr, const char *_list)
371{
372	char *list, *cp, *o;
373	struct xaddr try_addr, match_addr;
374	u_int masklen, neg;
375	int ret = 0, r;
376
377	if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
378		debug2("%s: couldn't parse address %.100s", __func__, addr);
379		return 0;
380	}
381	if ((o = list = strdup(_list)) == NULL)
382		return -1;
383	while ((cp = strsep(&list, ",")) != NULL) {
384		neg = *cp == '!';
385		if (neg)
386			cp++;
387		if (*cp == '\0') {
388			ret = -1;
389			break;
390		}
391		/* Prefer CIDR address matching */
392		r = addr_pton_cidr(cp, &match_addr, &masklen);
393		if (r == -2) {
394			error("Inconsistent mask length for "
395			    "network \"%.100s\"", cp);
396			ret = -1;
397			break;
398		} else if (r == 0) {
399			if (addr != NULL && addr_netmatch(&try_addr,
400                           &match_addr, masklen) == 0) {
401 foundit:
402				if (neg) {
403					ret = 0;
404					break;
405				}
406				ret = 1;
407			}
408			continue;
409		} else {
410			/* If CIDR parse failed, try wildcard string match */
411			if (addr != NULL && match_pattern(addr, cp) == 1)
412				goto foundit;
413		}
414	}
415	free(o);
416
417	return ret;
418}
419