xref: /onnv-gate/usr/src/cmd/ssh/libssh/common/addrmatch.c (revision 11060:40daac51fc0c)
111044SHuie-Ying.Lee@Sun.COM /*	$OpenBSD: addrmatch.c,v 1.4 2008/12/10 03:55:20 stevesk Exp $ */
211044SHuie-Ying.Lee@Sun.COM 
311044SHuie-Ying.Lee@Sun.COM /*
411044SHuie-Ying.Lee@Sun.COM  * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
511044SHuie-Ying.Lee@Sun.COM  *
611044SHuie-Ying.Lee@Sun.COM  * Permission to use, copy, modify, and distribute this software for any
711044SHuie-Ying.Lee@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
811044SHuie-Ying.Lee@Sun.COM  * copyright notice and this permission notice appear in all copies.
911044SHuie-Ying.Lee@Sun.COM  *
1011044SHuie-Ying.Lee@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1111044SHuie-Ying.Lee@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1211044SHuie-Ying.Lee@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1311044SHuie-Ying.Lee@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1411044SHuie-Ying.Lee@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1511044SHuie-Ying.Lee@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1611044SHuie-Ying.Lee@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1711044SHuie-Ying.Lee@Sun.COM  */
1811044SHuie-Ying.Lee@Sun.COM 
1911044SHuie-Ying.Lee@Sun.COM #include "includes.h"
2011044SHuie-Ying.Lee@Sun.COM 
2111044SHuie-Ying.Lee@Sun.COM #include <sys/types.h>
2211044SHuie-Ying.Lee@Sun.COM #include <sys/socket.h>
2311044SHuie-Ying.Lee@Sun.COM #include <netinet/in.h>
2411044SHuie-Ying.Lee@Sun.COM #include <arpa/inet.h>
2511044SHuie-Ying.Lee@Sun.COM 
2611044SHuie-Ying.Lee@Sun.COM #include <netdb.h>
2711044SHuie-Ying.Lee@Sun.COM #include <string.h>
2811044SHuie-Ying.Lee@Sun.COM #include <stdlib.h>
2911044SHuie-Ying.Lee@Sun.COM #include <stdio.h>
3011044SHuie-Ying.Lee@Sun.COM #include <stdarg.h>
3111044SHuie-Ying.Lee@Sun.COM 
3211044SHuie-Ying.Lee@Sun.COM #include "match.h"
3311044SHuie-Ying.Lee@Sun.COM #include "log.h"
3411044SHuie-Ying.Lee@Sun.COM #include "xmalloc.h"
3511044SHuie-Ying.Lee@Sun.COM 
3611044SHuie-Ying.Lee@Sun.COM struct xaddr {
3711044SHuie-Ying.Lee@Sun.COM 	sa_family_t	af;
3811044SHuie-Ying.Lee@Sun.COM 	union {
3911044SHuie-Ying.Lee@Sun.COM 		struct in_addr		v4;
4011044SHuie-Ying.Lee@Sun.COM 		struct in6_addr		v6;
4111044SHuie-Ying.Lee@Sun.COM 		u_int8_t		addr8[16];
4211044SHuie-Ying.Lee@Sun.COM 		u_int32_t		addr32[4];
4311044SHuie-Ying.Lee@Sun.COM 	} xa;		    /* 128-bit address */
4411044SHuie-Ying.Lee@Sun.COM 	u_int32_t	scope_id;	/* iface scope id for v6 */
4511044SHuie-Ying.Lee@Sun.COM #define v4	xa.v4
4611044SHuie-Ying.Lee@Sun.COM #define v6	xa.v6
4711044SHuie-Ying.Lee@Sun.COM #define addr8	xa.addr8
4811044SHuie-Ying.Lee@Sun.COM #define addr32	xa.addr32
4911044SHuie-Ying.Lee@Sun.COM };
5011044SHuie-Ying.Lee@Sun.COM 
5111044SHuie-Ying.Lee@Sun.COM static int
addr_unicast_masklen(int af)5211044SHuie-Ying.Lee@Sun.COM addr_unicast_masklen(int af)
5311044SHuie-Ying.Lee@Sun.COM {
5411044SHuie-Ying.Lee@Sun.COM 	switch (af) {
5511044SHuie-Ying.Lee@Sun.COM 	case AF_INET:
5611044SHuie-Ying.Lee@Sun.COM 		return 32;
5711044SHuie-Ying.Lee@Sun.COM 	case AF_INET6:
5811044SHuie-Ying.Lee@Sun.COM 		return 128;
5911044SHuie-Ying.Lee@Sun.COM 	default:
6011044SHuie-Ying.Lee@Sun.COM 		return -1;
6111044SHuie-Ying.Lee@Sun.COM 	}
6211044SHuie-Ying.Lee@Sun.COM }
6311044SHuie-Ying.Lee@Sun.COM 
6411044SHuie-Ying.Lee@Sun.COM static inline int
masklen_valid(int af,u_int masklen)6511044SHuie-Ying.Lee@Sun.COM masklen_valid(int af, u_int masklen)
6611044SHuie-Ying.Lee@Sun.COM {
6711044SHuie-Ying.Lee@Sun.COM 	switch (af) {
6811044SHuie-Ying.Lee@Sun.COM 	case AF_INET:
6911044SHuie-Ying.Lee@Sun.COM 		return masklen <= 32 ? 0 : -1;
7011044SHuie-Ying.Lee@Sun.COM 	case AF_INET6:
7111044SHuie-Ying.Lee@Sun.COM 		return masklen <= 128 ? 0 : -1;
7211044SHuie-Ying.Lee@Sun.COM 	default:
7311044SHuie-Ying.Lee@Sun.COM 		return -1;
7411044SHuie-Ying.Lee@Sun.COM 	}
7511044SHuie-Ying.Lee@Sun.COM }
7611044SHuie-Ying.Lee@Sun.COM 
7711044SHuie-Ying.Lee@Sun.COM /*
7811044SHuie-Ying.Lee@Sun.COM  * Convert struct sockaddr to struct xaddr
7911044SHuie-Ying.Lee@Sun.COM  * Returns 0 on success, -1 on failure.
8011044SHuie-Ying.Lee@Sun.COM  */
8111044SHuie-Ying.Lee@Sun.COM static int
addr_sa_to_xaddr(struct sockaddr * sa,socklen_t slen,struct xaddr * xa)8211044SHuie-Ying.Lee@Sun.COM addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa)
8311044SHuie-Ying.Lee@Sun.COM {
84*11060SHuie-Ying.Lee@Sun.COM 	/* LINTED E_BAD_PTR_CAST_ALIGN */
8511044SHuie-Ying.Lee@Sun.COM 	struct sockaddr_in *in4 = (struct sockaddr_in *)sa;
86*11060SHuie-Ying.Lee@Sun.COM 	/* LINTED E_BAD_PTR_CAST_ALIGN */
8711044SHuie-Ying.Lee@Sun.COM 	struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
8811044SHuie-Ying.Lee@Sun.COM 
8911044SHuie-Ying.Lee@Sun.COM 	memset(xa, '\0', sizeof(*xa));
9011044SHuie-Ying.Lee@Sun.COM 
9111044SHuie-Ying.Lee@Sun.COM 	switch (sa->sa_family) {
9211044SHuie-Ying.Lee@Sun.COM 	case AF_INET:
9311044SHuie-Ying.Lee@Sun.COM 		if (slen < sizeof(*in4))
9411044SHuie-Ying.Lee@Sun.COM 			return -1;
9511044SHuie-Ying.Lee@Sun.COM 		xa->af = AF_INET;
9611044SHuie-Ying.Lee@Sun.COM 		memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4));
9711044SHuie-Ying.Lee@Sun.COM 		break;
9811044SHuie-Ying.Lee@Sun.COM 	case AF_INET6:
9911044SHuie-Ying.Lee@Sun.COM 		if (slen < sizeof(*in6))
10011044SHuie-Ying.Lee@Sun.COM 			return -1;
10111044SHuie-Ying.Lee@Sun.COM 		xa->af = AF_INET6;
10211044SHuie-Ying.Lee@Sun.COM 		memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6));
10311044SHuie-Ying.Lee@Sun.COM #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
10411044SHuie-Ying.Lee@Sun.COM 		xa->scope_id = in6->sin6_scope_id;
10511044SHuie-Ying.Lee@Sun.COM #endif
10611044SHuie-Ying.Lee@Sun.COM 		break;
10711044SHuie-Ying.Lee@Sun.COM 	default:
10811044SHuie-Ying.Lee@Sun.COM 		return -1;
10911044SHuie-Ying.Lee@Sun.COM 	}
11011044SHuie-Ying.Lee@Sun.COM 
11111044SHuie-Ying.Lee@Sun.COM 	return 0;
11211044SHuie-Ying.Lee@Sun.COM }
11311044SHuie-Ying.Lee@Sun.COM 
11411044SHuie-Ying.Lee@Sun.COM /*
11511044SHuie-Ying.Lee@Sun.COM  * Calculate a netmask of length 'l' for address family 'af' and
11611044SHuie-Ying.Lee@Sun.COM  * store it in 'n'.
11711044SHuie-Ying.Lee@Sun.COM  * Returns 0 on success, -1 on failure.
11811044SHuie-Ying.Lee@Sun.COM  */
11911044SHuie-Ying.Lee@Sun.COM static int
addr_netmask(int af,u_int l,struct xaddr * n)12011044SHuie-Ying.Lee@Sun.COM addr_netmask(int af, u_int l, struct xaddr *n)
12111044SHuie-Ying.Lee@Sun.COM {
12211044SHuie-Ying.Lee@Sun.COM 	int i;
12311044SHuie-Ying.Lee@Sun.COM 
12411044SHuie-Ying.Lee@Sun.COM 	if (masklen_valid(af, l) != 0 || n == NULL)
12511044SHuie-Ying.Lee@Sun.COM 		return -1;
12611044SHuie-Ying.Lee@Sun.COM 
12711044SHuie-Ying.Lee@Sun.COM 	memset(n, '\0', sizeof(*n));
12811044SHuie-Ying.Lee@Sun.COM 	switch (af) {
12911044SHuie-Ying.Lee@Sun.COM 	case AF_INET:
13011044SHuie-Ying.Lee@Sun.COM 		n->af = AF_INET;
13111044SHuie-Ying.Lee@Sun.COM 		n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff);
13211044SHuie-Ying.Lee@Sun.COM 		return 0;
13311044SHuie-Ying.Lee@Sun.COM 	case AF_INET6:
13411044SHuie-Ying.Lee@Sun.COM 		n->af = AF_INET6;
13511044SHuie-Ying.Lee@Sun.COM 		for (i = 0; i < 4 && l >= 32; i++, l -= 32)
13611044SHuie-Ying.Lee@Sun.COM 			n->addr32[i] = 0xffffffffU;
13711044SHuie-Ying.Lee@Sun.COM 		if (i < 4 && l != 0)
13811044SHuie-Ying.Lee@Sun.COM 			n->addr32[i] = htonl((0xffffffff << (32 - l)) &
13911044SHuie-Ying.Lee@Sun.COM 			    0xffffffff);
14011044SHuie-Ying.Lee@Sun.COM 		return 0;
14111044SHuie-Ying.Lee@Sun.COM 	default:
14211044SHuie-Ying.Lee@Sun.COM 		return -1;
14311044SHuie-Ying.Lee@Sun.COM 	}
14411044SHuie-Ying.Lee@Sun.COM }
14511044SHuie-Ying.Lee@Sun.COM 
14611044SHuie-Ying.Lee@Sun.COM /*
14711044SHuie-Ying.Lee@Sun.COM  * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'.
14811044SHuie-Ying.Lee@Sun.COM  * Returns 0 on success, -1 on failure.
14911044SHuie-Ying.Lee@Sun.COM  */
15011044SHuie-Ying.Lee@Sun.COM static int
addr_and(struct xaddr * dst,const struct xaddr * a,const struct xaddr * b)15111044SHuie-Ying.Lee@Sun.COM addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b)
15211044SHuie-Ying.Lee@Sun.COM {
15311044SHuie-Ying.Lee@Sun.COM 	int i;
15411044SHuie-Ying.Lee@Sun.COM 
15511044SHuie-Ying.Lee@Sun.COM 	if (dst == NULL || a == NULL || b == NULL || a->af != b->af)
15611044SHuie-Ying.Lee@Sun.COM 		return -1;
15711044SHuie-Ying.Lee@Sun.COM 
15811044SHuie-Ying.Lee@Sun.COM 	memcpy(dst, a, sizeof(*dst));
15911044SHuie-Ying.Lee@Sun.COM 	switch (a->af) {
16011044SHuie-Ying.Lee@Sun.COM 	case AF_INET:
16111044SHuie-Ying.Lee@Sun.COM 		dst->v4.s_addr &= b->v4.s_addr;
16211044SHuie-Ying.Lee@Sun.COM 		return 0;
16311044SHuie-Ying.Lee@Sun.COM 	case AF_INET6:
16411044SHuie-Ying.Lee@Sun.COM 		dst->scope_id = a->scope_id;
16511044SHuie-Ying.Lee@Sun.COM 		for (i = 0; i < 4; i++)
16611044SHuie-Ying.Lee@Sun.COM 			dst->addr32[i] &= b->addr32[i];
16711044SHuie-Ying.Lee@Sun.COM 		return 0;
16811044SHuie-Ying.Lee@Sun.COM 	default:
16911044SHuie-Ying.Lee@Sun.COM 		return -1;
17011044SHuie-Ying.Lee@Sun.COM 	}
17111044SHuie-Ying.Lee@Sun.COM }
17211044SHuie-Ying.Lee@Sun.COM 
17311044SHuie-Ying.Lee@Sun.COM /*
17411044SHuie-Ying.Lee@Sun.COM  * Compare addresses 'a' and 'b'
17511044SHuie-Ying.Lee@Sun.COM  * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b)
17611044SHuie-Ying.Lee@Sun.COM  */
17711044SHuie-Ying.Lee@Sun.COM static int
addr_cmp(const struct xaddr * a,const struct xaddr * b)17811044SHuie-Ying.Lee@Sun.COM addr_cmp(const struct xaddr *a, const struct xaddr *b)
17911044SHuie-Ying.Lee@Sun.COM {
18011044SHuie-Ying.Lee@Sun.COM 	int i;
18111044SHuie-Ying.Lee@Sun.COM 
18211044SHuie-Ying.Lee@Sun.COM 	if (a->af != b->af)
18311044SHuie-Ying.Lee@Sun.COM 		return a->af == AF_INET6 ? 1 : -1;
18411044SHuie-Ying.Lee@Sun.COM 
18511044SHuie-Ying.Lee@Sun.COM 	switch (a->af) {
18611044SHuie-Ying.Lee@Sun.COM 	case AF_INET:
18711044SHuie-Ying.Lee@Sun.COM 		if (a->v4.s_addr == b->v4.s_addr)
18811044SHuie-Ying.Lee@Sun.COM 			return 0;
18911044SHuie-Ying.Lee@Sun.COM 		return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1;
19011044SHuie-Ying.Lee@Sun.COM 	case AF_INET6:
19111044SHuie-Ying.Lee@Sun.COM 		for (i = 0; i < 16; i++)
19211044SHuie-Ying.Lee@Sun.COM 			if (a->addr8[i] - b->addr8[i] != 0)
19311044SHuie-Ying.Lee@Sun.COM 				return a->addr8[i] > b->addr8[i] ? 1 : -1;
19411044SHuie-Ying.Lee@Sun.COM 		if (a->scope_id == b->scope_id)
19511044SHuie-Ying.Lee@Sun.COM 			return 0;
19611044SHuie-Ying.Lee@Sun.COM 		return a->scope_id > b->scope_id ? 1 : -1;
19711044SHuie-Ying.Lee@Sun.COM 	default:
19811044SHuie-Ying.Lee@Sun.COM 		return -1;
19911044SHuie-Ying.Lee@Sun.COM 	}
20011044SHuie-Ying.Lee@Sun.COM }
20111044SHuie-Ying.Lee@Sun.COM 
20211044SHuie-Ying.Lee@Sun.COM /*
20311044SHuie-Ying.Lee@Sun.COM  * Parse string address 'p' into 'n'
20411044SHuie-Ying.Lee@Sun.COM  * Returns 0 on success, -1 on failure.
20511044SHuie-Ying.Lee@Sun.COM  */
20611044SHuie-Ying.Lee@Sun.COM static int
addr_pton(const char * p,struct xaddr * n)20711044SHuie-Ying.Lee@Sun.COM addr_pton(const char *p, struct xaddr *n)
20811044SHuie-Ying.Lee@Sun.COM {
20911044SHuie-Ying.Lee@Sun.COM 	struct addrinfo hints, *ai;
21011044SHuie-Ying.Lee@Sun.COM 
21111044SHuie-Ying.Lee@Sun.COM 	memset(&hints, '\0', sizeof(hints));
21211044SHuie-Ying.Lee@Sun.COM 	hints.ai_flags = AI_NUMERICHOST;
21311044SHuie-Ying.Lee@Sun.COM 
21411044SHuie-Ying.Lee@Sun.COM 	if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0)
21511044SHuie-Ying.Lee@Sun.COM 		return -1;
21611044SHuie-Ying.Lee@Sun.COM 
21711044SHuie-Ying.Lee@Sun.COM 	if (ai == NULL || ai->ai_addr == NULL)
21811044SHuie-Ying.Lee@Sun.COM 		return -1;
21911044SHuie-Ying.Lee@Sun.COM 
22011044SHuie-Ying.Lee@Sun.COM 	if (n != NULL &&
22111044SHuie-Ying.Lee@Sun.COM 	    addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) {
22211044SHuie-Ying.Lee@Sun.COM 		freeaddrinfo(ai);
22311044SHuie-Ying.Lee@Sun.COM 		return -1;
22411044SHuie-Ying.Lee@Sun.COM 	}
22511044SHuie-Ying.Lee@Sun.COM 
22611044SHuie-Ying.Lee@Sun.COM 	freeaddrinfo(ai);
22711044SHuie-Ying.Lee@Sun.COM 	return 0;
22811044SHuie-Ying.Lee@Sun.COM }
22911044SHuie-Ying.Lee@Sun.COM 
23011044SHuie-Ying.Lee@Sun.COM /*
23111044SHuie-Ying.Lee@Sun.COM  * Perform bitwise negation of address
23211044SHuie-Ying.Lee@Sun.COM  * Returns 0 on success, -1 on failure.
23311044SHuie-Ying.Lee@Sun.COM  */
23411044SHuie-Ying.Lee@Sun.COM static int
addr_invert(struct xaddr * n)23511044SHuie-Ying.Lee@Sun.COM addr_invert(struct xaddr *n)
23611044SHuie-Ying.Lee@Sun.COM {
23711044SHuie-Ying.Lee@Sun.COM 	int i;
23811044SHuie-Ying.Lee@Sun.COM 
23911044SHuie-Ying.Lee@Sun.COM 	if (n == NULL)
24011044SHuie-Ying.Lee@Sun.COM 		return (-1);
24111044SHuie-Ying.Lee@Sun.COM 
24211044SHuie-Ying.Lee@Sun.COM 	switch (n->af) {
24311044SHuie-Ying.Lee@Sun.COM 	case AF_INET:
24411044SHuie-Ying.Lee@Sun.COM 		n->v4.s_addr = ~n->v4.s_addr;
24511044SHuie-Ying.Lee@Sun.COM 		return (0);
24611044SHuie-Ying.Lee@Sun.COM 	case AF_INET6:
24711044SHuie-Ying.Lee@Sun.COM 		for (i = 0; i < 4; i++)
24811044SHuie-Ying.Lee@Sun.COM 			n->addr32[i] = ~n->addr32[i];
24911044SHuie-Ying.Lee@Sun.COM 		return (0);
25011044SHuie-Ying.Lee@Sun.COM 	default:
25111044SHuie-Ying.Lee@Sun.COM 		return (-1);
25211044SHuie-Ying.Lee@Sun.COM 	}
25311044SHuie-Ying.Lee@Sun.COM }
25411044SHuie-Ying.Lee@Sun.COM 
25511044SHuie-Ying.Lee@Sun.COM /*
25611044SHuie-Ying.Lee@Sun.COM  * Calculate a netmask of length 'l' for address family 'af' and
25711044SHuie-Ying.Lee@Sun.COM  * store it in 'n'.
25811044SHuie-Ying.Lee@Sun.COM  * Returns 0 on success, -1 on failure.
25911044SHuie-Ying.Lee@Sun.COM  */
26011044SHuie-Ying.Lee@Sun.COM static int
addr_hostmask(int af,u_int l,struct xaddr * n)26111044SHuie-Ying.Lee@Sun.COM addr_hostmask(int af, u_int l, struct xaddr *n)
26211044SHuie-Ying.Lee@Sun.COM {
26311044SHuie-Ying.Lee@Sun.COM 	if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1)
26411044SHuie-Ying.Lee@Sun.COM 		return (-1);
26511044SHuie-Ying.Lee@Sun.COM 	return (0);
26611044SHuie-Ying.Lee@Sun.COM }
26711044SHuie-Ying.Lee@Sun.COM 
26811044SHuie-Ying.Lee@Sun.COM /*
26911044SHuie-Ying.Lee@Sun.COM  * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::)
27011044SHuie-Ying.Lee@Sun.COM  * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure.
27111044SHuie-Ying.Lee@Sun.COM  */
27211044SHuie-Ying.Lee@Sun.COM static int
addr_is_all0s(const struct xaddr * a)27311044SHuie-Ying.Lee@Sun.COM addr_is_all0s(const struct xaddr *a)
27411044SHuie-Ying.Lee@Sun.COM {
27511044SHuie-Ying.Lee@Sun.COM 	int i;
27611044SHuie-Ying.Lee@Sun.COM 
27711044SHuie-Ying.Lee@Sun.COM 	switch (a->af) {
27811044SHuie-Ying.Lee@Sun.COM 	case AF_INET:
27911044SHuie-Ying.Lee@Sun.COM 		return (a->v4.s_addr == 0 ? 0 : -1);
28011044SHuie-Ying.Lee@Sun.COM 	case AF_INET6:;
28111044SHuie-Ying.Lee@Sun.COM 		for (i = 0; i < 4; i++)
28211044SHuie-Ying.Lee@Sun.COM 			if (a->addr32[i] != 0)
28311044SHuie-Ying.Lee@Sun.COM 				return (-1);
28411044SHuie-Ying.Lee@Sun.COM 		return (0);
28511044SHuie-Ying.Lee@Sun.COM 	default:
28611044SHuie-Ying.Lee@Sun.COM 		return (-1);
28711044SHuie-Ying.Lee@Sun.COM 	}
28811044SHuie-Ying.Lee@Sun.COM }
28911044SHuie-Ying.Lee@Sun.COM 
29011044SHuie-Ying.Lee@Sun.COM /*
29111044SHuie-Ying.Lee@Sun.COM  * Test whether host portion of address 'a', as determined by 'masklen'
29211044SHuie-Ying.Lee@Sun.COM  * is all zeros.
29311044SHuie-Ying.Lee@Sun.COM  * Returns 0 on if host portion of address is all-zeros,
29411044SHuie-Ying.Lee@Sun.COM  * -1 if not all zeros or on failure.
29511044SHuie-Ying.Lee@Sun.COM  */
29611044SHuie-Ying.Lee@Sun.COM static int
addr_host_is_all0s(const struct xaddr * a,u_int masklen)29711044SHuie-Ying.Lee@Sun.COM addr_host_is_all0s(const struct xaddr *a, u_int masklen)
29811044SHuie-Ying.Lee@Sun.COM {
29911044SHuie-Ying.Lee@Sun.COM 	struct xaddr tmp_addr, tmp_mask, tmp_result;
30011044SHuie-Ying.Lee@Sun.COM 
30111044SHuie-Ying.Lee@Sun.COM 	memcpy(&tmp_addr, a, sizeof(tmp_addr));
30211044SHuie-Ying.Lee@Sun.COM 	if (addr_hostmask(a->af, masklen, &tmp_mask) == -1)
30311044SHuie-Ying.Lee@Sun.COM 		return (-1);
30411044SHuie-Ying.Lee@Sun.COM 	if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1)
30511044SHuie-Ying.Lee@Sun.COM 		return (-1);
30611044SHuie-Ying.Lee@Sun.COM 	return (addr_is_all0s(&tmp_result));
30711044SHuie-Ying.Lee@Sun.COM }
30811044SHuie-Ying.Lee@Sun.COM 
30911044SHuie-Ying.Lee@Sun.COM /*
31011044SHuie-Ying.Lee@Sun.COM  * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z).
31111044SHuie-Ying.Lee@Sun.COM  * Return -1 on parse error, -2 on inconsistency or 0 on success.
31211044SHuie-Ying.Lee@Sun.COM  */
31311044SHuie-Ying.Lee@Sun.COM static int
addr_pton_cidr(const char * p,struct xaddr * n,u_int * l)31411044SHuie-Ying.Lee@Sun.COM addr_pton_cidr(const char *p, struct xaddr *n, u_int *l)
31511044SHuie-Ying.Lee@Sun.COM {
31611044SHuie-Ying.Lee@Sun.COM 	struct xaddr tmp;
31711044SHuie-Ying.Lee@Sun.COM 	long unsigned int masklen = 999;
31811044SHuie-Ying.Lee@Sun.COM 	char addrbuf[64], *mp, *cp;
31911044SHuie-Ying.Lee@Sun.COM 
32011044SHuie-Ying.Lee@Sun.COM 	/* Don't modify argument */
32111044SHuie-Ying.Lee@Sun.COM 	if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf))
32211044SHuie-Ying.Lee@Sun.COM 		return -1;
32311044SHuie-Ying.Lee@Sun.COM 
32411044SHuie-Ying.Lee@Sun.COM 	if ((mp = strchr(addrbuf, '/')) != NULL) {
32511044SHuie-Ying.Lee@Sun.COM 		*mp = '\0';
32611044SHuie-Ying.Lee@Sun.COM 		mp++;
32711044SHuie-Ying.Lee@Sun.COM 		masklen = strtoul(mp, &cp, 10);
32811044SHuie-Ying.Lee@Sun.COM 		if (*mp == '\0' || *cp != '\0' || masklen > 128)
32911044SHuie-Ying.Lee@Sun.COM 			return -1;
33011044SHuie-Ying.Lee@Sun.COM 	}
33111044SHuie-Ying.Lee@Sun.COM 
33211044SHuie-Ying.Lee@Sun.COM 	if (addr_pton(addrbuf, &tmp) == -1)
33311044SHuie-Ying.Lee@Sun.COM 		return -1;
33411044SHuie-Ying.Lee@Sun.COM 
33511044SHuie-Ying.Lee@Sun.COM 	if (mp == NULL)
33611044SHuie-Ying.Lee@Sun.COM 		masklen = addr_unicast_masklen(tmp.af);
33711044SHuie-Ying.Lee@Sun.COM 	if (masklen_valid(tmp.af, masklen) == -1)
33811044SHuie-Ying.Lee@Sun.COM 		return -2;
33911044SHuie-Ying.Lee@Sun.COM 	if (addr_host_is_all0s(&tmp, masklen) != 0)
34011044SHuie-Ying.Lee@Sun.COM 		return -2;
34111044SHuie-Ying.Lee@Sun.COM 
34211044SHuie-Ying.Lee@Sun.COM 	if (n != NULL)
34311044SHuie-Ying.Lee@Sun.COM 		memcpy(n, &tmp, sizeof(*n));
34411044SHuie-Ying.Lee@Sun.COM 	if (l != NULL)
34511044SHuie-Ying.Lee@Sun.COM 		*l = masklen;
34611044SHuie-Ying.Lee@Sun.COM 
34711044SHuie-Ying.Lee@Sun.COM 	return 0;
34811044SHuie-Ying.Lee@Sun.COM }
34911044SHuie-Ying.Lee@Sun.COM 
35011044SHuie-Ying.Lee@Sun.COM static int
addr_netmatch(const struct xaddr * host,const struct xaddr * net,u_int masklen)35111044SHuie-Ying.Lee@Sun.COM addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen)
35211044SHuie-Ying.Lee@Sun.COM {
35311044SHuie-Ying.Lee@Sun.COM 	struct xaddr tmp_mask, tmp_result;
35411044SHuie-Ying.Lee@Sun.COM 
35511044SHuie-Ying.Lee@Sun.COM 	if (host->af != net->af)
35611044SHuie-Ying.Lee@Sun.COM 		return -1;
35711044SHuie-Ying.Lee@Sun.COM 
35811044SHuie-Ying.Lee@Sun.COM 	if (addr_netmask(host->af, masklen, &tmp_mask) == -1)
35911044SHuie-Ying.Lee@Sun.COM 		return -1;
36011044SHuie-Ying.Lee@Sun.COM 	if (addr_and(&tmp_result, host, &tmp_mask) == -1)
36111044SHuie-Ying.Lee@Sun.COM 		return -1;
36211044SHuie-Ying.Lee@Sun.COM 	return addr_cmp(&tmp_result, net);
36311044SHuie-Ying.Lee@Sun.COM }
36411044SHuie-Ying.Lee@Sun.COM 
36511044SHuie-Ying.Lee@Sun.COM /*
36611044SHuie-Ying.Lee@Sun.COM  * Match "addr" against list pattern list "_list", which may contain a
36711044SHuie-Ying.Lee@Sun.COM  * mix of CIDR addresses and old-school wildcards.
36811044SHuie-Ying.Lee@Sun.COM  *
36911044SHuie-Ying.Lee@Sun.COM  * If addr is NULL, then no matching is performed, but _list is parsed
37011044SHuie-Ying.Lee@Sun.COM  * and checked for well-formedness.
37111044SHuie-Ying.Lee@Sun.COM  *
37211044SHuie-Ying.Lee@Sun.COM  * Returns 1 on match found (never returned when addr == NULL).
37311044SHuie-Ying.Lee@Sun.COM  * Returns 0 on if no match found, or no errors found when addr == NULL.
37411044SHuie-Ying.Lee@Sun.COM  * Returns -1 on negated match found (never returned when addr == NULL).
37511044SHuie-Ying.Lee@Sun.COM  * Returns -2 on invalid list entry.
37611044SHuie-Ying.Lee@Sun.COM  */
37711044SHuie-Ying.Lee@Sun.COM int
addr_match_list(const char * addr,const char * _list)37811044SHuie-Ying.Lee@Sun.COM addr_match_list(const char *addr, const char *_list)
37911044SHuie-Ying.Lee@Sun.COM {
38011044SHuie-Ying.Lee@Sun.COM 	char *list, *cp, *o;
38111044SHuie-Ying.Lee@Sun.COM 	struct xaddr try_addr, match_addr;
38211044SHuie-Ying.Lee@Sun.COM 	u_int masklen, neg;
38311044SHuie-Ying.Lee@Sun.COM 	int ret = 0, r;
38411044SHuie-Ying.Lee@Sun.COM 
38511044SHuie-Ying.Lee@Sun.COM 	if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
38611044SHuie-Ying.Lee@Sun.COM 		debug2("%s: couldn't parse address %.100s", __func__, addr);
38711044SHuie-Ying.Lee@Sun.COM 		return 0;
38811044SHuie-Ying.Lee@Sun.COM 	}
38911044SHuie-Ying.Lee@Sun.COM 	if ((o = list = strdup(_list)) == NULL)
39011044SHuie-Ying.Lee@Sun.COM 		return -1;
39111044SHuie-Ying.Lee@Sun.COM 	while ((cp = strsep(&list, ",")) != NULL) {
39211044SHuie-Ying.Lee@Sun.COM 		neg = *cp == '!';
39311044SHuie-Ying.Lee@Sun.COM 		if (neg)
39411044SHuie-Ying.Lee@Sun.COM 			cp++;
39511044SHuie-Ying.Lee@Sun.COM 		if (*cp == '\0') {
39611044SHuie-Ying.Lee@Sun.COM 			ret = -2;
39711044SHuie-Ying.Lee@Sun.COM 			break;
39811044SHuie-Ying.Lee@Sun.COM 		}
39911044SHuie-Ying.Lee@Sun.COM 		/* Prefer CIDR address matching */
40011044SHuie-Ying.Lee@Sun.COM 		r = addr_pton_cidr(cp, &match_addr, &masklen);
40111044SHuie-Ying.Lee@Sun.COM 		if (r == -2) {
40211044SHuie-Ying.Lee@Sun.COM 			error("Inconsistent mask length for "
40311044SHuie-Ying.Lee@Sun.COM 			    "network \"%.100s\"", cp);
40411044SHuie-Ying.Lee@Sun.COM 			ret = -2;
40511044SHuie-Ying.Lee@Sun.COM 			break;
40611044SHuie-Ying.Lee@Sun.COM 		} else if (r == 0) {
40711044SHuie-Ying.Lee@Sun.COM 			if (addr != NULL && addr_netmatch(&try_addr,
40811044SHuie-Ying.Lee@Sun.COM                            &match_addr, masklen) == 0) {
40911044SHuie-Ying.Lee@Sun.COM  foundit:
41011044SHuie-Ying.Lee@Sun.COM 				if (neg) {
41111044SHuie-Ying.Lee@Sun.COM 					ret = -1;
41211044SHuie-Ying.Lee@Sun.COM 					break;
41311044SHuie-Ying.Lee@Sun.COM 				}
41411044SHuie-Ying.Lee@Sun.COM 				ret = 1;
41511044SHuie-Ying.Lee@Sun.COM 			}
41611044SHuie-Ying.Lee@Sun.COM 			continue;
41711044SHuie-Ying.Lee@Sun.COM 		} else {
41811044SHuie-Ying.Lee@Sun.COM 			/* If CIDR parse failed, try wildcard string match */
41911044SHuie-Ying.Lee@Sun.COM 			if (addr != NULL && match_pattern(addr, cp) == 1)
42011044SHuie-Ying.Lee@Sun.COM 				goto foundit;
42111044SHuie-Ying.Lee@Sun.COM 		}
42211044SHuie-Ying.Lee@Sun.COM 	}
42311044SHuie-Ying.Lee@Sun.COM 	xfree(o);
42411044SHuie-Ying.Lee@Sun.COM 
42511044SHuie-Ying.Lee@Sun.COM 	return ret;
42611044SHuie-Ying.Lee@Sun.COM }
427