877 share_nfs cannot share to IPv6 subnets
authorYuri Pankov <yuri.pankov@nexenta.com>
Thu, 03 Nov 2011 11:18:52 +0400
changeset 13528 f077aa5fa57c
parent 13527 cb9d7a417470
child 13529 c3a7090dbc16
877 share_nfs cannot share to IPv6 subnets 1554 SMB has no concept of IPv6 host access lists 1592 NFS access checks shouldn't fail if client address can't be resolved Reviewed by: Gordon Ross <[email protected]> Reviewed by: Dan McDonald <[email protected]> Approved by: Richard Lowe <[email protected]>
usr/src/cmd/fs.d/nfs/mountd/mountd.c
usr/src/head/arpa/inet.h
usr/src/lib/libnsl/Makefile.com
usr/src/lib/libnsl/common/llib-lnsl
usr/src/lib/libnsl/common/mapfile-vers
usr/src/lib/libnsl/nss/inet_matchaddr.c
usr/src/lib/smbsrv/libmlsvc/common/smb_share.c
usr/src/lib/smbsrv/libsmb/common/smb_util.c
usr/src/uts/common/netinet/in.h
--- a/usr/src/cmd/fs.d/nfs/mountd/mountd.c	Tue Nov 01 10:17:46 2011 +0400
+++ b/usr/src/cmd/fs.d/nfs/mountd/mountd.c	Thu Nov 03 11:18:52 2011 +0400
@@ -17,10 +17,9 @@
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
- */
-
-/*
+ *
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -112,7 +111,6 @@
 static void sh_free(struct sh_list *);
 static void umount(struct svc_req *);
 static void umountall(struct svc_req *);
-static int netmatch(struct netbuf *, char *);
 static void sigexit(int);
 static int newopts(char *);
 static tsol_tpent_t *get_client_template(struct sockaddr *);
@@ -1711,10 +1709,10 @@
  * We match on aliases of the hostname as well as on the canonical name.
  * Names in the access list may be either hosts or netgroups;  they're
  * not distinguished syntactically.  We check for hosts first because
- * it's cheaper (just M*N strcmp()s), then try netgroups.
+ * it's cheaper, then try netgroups.
  *
  * If pnb and pclnames are NULL, it means that we have to use transp
- * to resolve client's IP address to host name. If they aren't NULL
+ * to resolve client IP address to hostname. If they aren't NULL
  * then transp argument won't be used and can be NULL.
  */
 int
@@ -1722,84 +1720,112 @@
     struct nd_hostservlist **pclnames,
     char *access_list)	/* N.B. we clobber this "input" parameter */
 {
-	int nentries;
-	char *gr;
-	char *lasts;
+	char addr[INET_ADDRSTRLEN];
+	char buff[256];
+	int nentries = 0;
+	char *cstr = access_list;
+	char *gr = access_list;
 	char *host;
 	int off;
 	int i;
-	int netgroup_match;
 	int response;
+	int sbr = 0;
 	struct nd_hostservlist *clnames;
+	struct netent n, *np;
 
-	/*
-	 * If no access list - then it's unrestricted
-	 */
+	/* If no access list - then it's unrestricted */
 	if (access_list == NULL || *access_list == '\0')
 		return (1);
 
 	assert(transp != NULL || (*pnb != NULL && *pclnames != NULL));
 
-	nentries = 0;
+	/* Get client address if it wasn't provided */
+	if (*pnb == NULL)
+		/* Don't grant access if client address isn't known */
+		if ((*pnb = svc_getrpccaller(transp)) == NULL)
+			return (0);
+
+	/* Try to lookup client hostname if it wasn't provided */
+	if (*pclnames == NULL)
+		getclientsnames(transp, pnb, pclnames);
+	clnames = *pclnames;
 
-	for (gr = strtok_r(access_list, ":", &lasts);
-	    gr != NULL; gr = strtok_r(NULL, ":", &lasts)) {
+	for (;;) {
+		if ((cstr = strpbrk(cstr, "[]:")) != NULL) {
+			switch (*cstr) {
+			case '[':
+			case ']':
+				sbr = !sbr;
+				cstr++;
+				continue;
+			case ':':
+				if (sbr) {
+					cstr++;
+					continue;
+				}
+				*cstr = '\0';
+			}
+		}
 
 		/*
-		 * If the list name has a '-' prepended
-		 * then a match of the following name
-		 * implies failure instead of success.
+		 * If the list name has a '-' prepended then a match of
+		 * the following name implies failure instead of success.
 		 */
 		if (*gr == '-') {
 			response = 0;
 			gr++;
-		} else
+		} else {
 			response = 1;
+		}
 
 		/*
-		 * If the list name begins with an at
-		 * sign then do a network comparison.
+		 * First check if we have '@' entry, as it doesn't
+		 * require client hostname.
 		 */
 		if (*gr == '@') {
-			/*
-			 * Just get the netbuf, avoiding the costly name
-			 * lookup. This will suffice for access based
-			 * solely on addresses.
-			 */
-			if (*pnb == NULL) {
-				/*
-				 * Don't grant access if client's address isn't
-				 * known.
-				 */
-				if ((*pnb = svc_getrpccaller(transp)) == NULL)
-					return (0);
+			gr++;
+
+			/* Netname support */
+			if (!isdigit(*gr) && *gr != '[') {
+				if ((np = getnetbyname_r(gr, &n, buff,
+				    sizeof (buff))) != NULL &&
+				    np->n_net != 0) {
+					while ((np->n_net & 0xFF000000u) == 0)
+						np->n_net <<= 8;
+					np->n_net = htonl(np->n_net);
+					if (inet_ntop(AF_INET, &np->n_net, addr,
+					    INET_ADDRSTRLEN) == NULL)
+						break;
+					if (inet_matchaddr((*pnb)->buf, addr))
+						return (response);
+				}
+			} else {
+				if (inet_matchaddr((*pnb)->buf, gr))
+					return (response);
 			}
 
-			if (netmatch(*pnb, gr + 1))
-				return (response);
+			if (cstr == NULL)
+				break;
+
+			gr = ++cstr;
+
 			continue;
 		}
 
 		/*
-		 * We need to get the host name if we haven't gotten
-		 * it by now!
+		 * No other checks can be performed if client address
+		 * can't be resolved.
 		 */
-		if (*pclnames == NULL) {
-			DTRACE_PROBE(mountd, name_by_addrlist);
-			/*
-			 * Do not grant access if we can't
-			 * get a name!
-			 */
-			if (getclientsnames(transp, pnb, pclnames) != 0)
-				return (0);
+		if (clnames == NULL) {
+			if (cstr == NULL)
+				break;
+
+			gr = ++cstr;
+
+			continue;
 		}
 
-		clnames = *pclnames;
-
-		/*
-		 * The following loops through all the
-		 * client's aliases.  Usually it's just one name.
-		 */
+		/* Otherwise loop through all client hostname aliases */
 		for (i = 0; i < clnames->h_cnt; i++) {
 			host = clnames->h_hostservs[i].h_host;
 
@@ -1820,120 +1846,25 @@
 						return (response);
 					}
 				}
-			} else
-
-			/*
-			 * Just do a hostname match
-			 */
-			if (strcasecmp(gr, host) == 0) {
-				return (response);	/* Matched a hostname */
+			} else {
+				/* Just do a hostname match */
+				if (strcasecmp(gr, host) == 0)
+					return (response);
 			}
 		}
 
 		nentries++;
-	}
 
-	/*
-	 * We need to get the host name if we haven't gotten
-	 * it by now!
-	 */
-	if (*pclnames == NULL) {
-		DTRACE_PROBE(mountd, name_by_netgroup);
-		/*
-		 * Do not grant access if we can't
-		 * get a name!
-		 */
-		if (getclientsnames(transp, pnb, pclnames) != 0)
-			return (0);
+		if (cstr == NULL)
+			break;
+
+		gr = ++cstr;
 	}
 
-	netgroup_match = netgroup_check(*pclnames, access_list, nentries);
-
-	return (netgroup_match);
-}
-
-int
-netmatch(struct netbuf *nb, char *name)
-{
-	uint_t claddr;
-	struct netent n, *np;
-	char *mp, *p;
-	uint_t addr, mask;
-	int i, bits;
-	char buff[256];
-
-	/*
-	 * Check if it's an IPv4 addr
-	 */
-	if (nb->len != sizeof (struct sockaddr_in))
+	if (clnames == NULL)
 		return (0);
 
-	(void) memcpy(&claddr,
-	    /* LINTED pointer alignment */
-	    &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr,
-	    sizeof (struct in_addr));
-	claddr = ntohl(claddr);
-
-	mp = strchr(name, '/');
-	if (mp)
-		*mp++ = '\0';
-
-	if (isdigit(*name)) {
-		/*
-		 * Convert a dotted IP address
-		 * to an IP address. The conversion
-		 * is not the same as that in inet_addr().
-		 */
-		p = name;
-		addr = 0;
-		for (i = 0; i < 4; i++) {
-			addr |= atoi(p) << ((3-i) * 8);
-			p = strchr(p, '.');
-			if (p == NULL)
-				break;
-			p++;
-		}
-	} else {
-		/*
-		 * Turn the netname into
-		 * an IP address.
-		 */
-		np = getnetbyname_r(name, &n, buff, sizeof (buff));
-		if (np == NULL) {
-			syslog(LOG_DEBUG, "getnetbyname_r: %s: %m", name);
-			return (0);
-		}
-		addr = np->n_net;
-	}
-
-	/*
-	 * If the mask is specified explicitly then
-	 * use that value, e.g.
-	 *
-	 *    @109.104.56/28
-	 *
-	 * otherwise assume a mask from the zero octets
-	 * in the least significant bits of the address, e.g.
-	 *
-	 *   @109.104  or  @109.104.0.0
-	 */
-	if (mp) {
-		bits = atoi(mp);
-		mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits)
-		    : 0;
-		addr &= mask;
-	} else {
-		if ((addr & IN_CLASSA_HOST) == 0)
-			mask = IN_CLASSA_NET;
-		else if ((addr & IN_CLASSB_HOST) == 0)
-			mask = IN_CLASSB_NET;
-		else if ((addr & IN_CLASSC_HOST) == 0)
-			mask = IN_CLASSC_NET;
-		else
-			mask = IN_CLASSE_NET;
-	}
-
-	return ((claddr & mask) == addr);
+	return (netgroup_check(clnames, access_list, nentries));
 }
 
 
--- a/usr/src/head/arpa/inet.h	Tue Nov 01 10:17:46 2011 +0400
+++ b/usr/src/head/arpa/inet.h	Thu Nov 03 11:18:52 2011 +0400
@@ -17,10 +17,11 @@
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
- */
-/*
+ *
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -56,6 +57,7 @@
 #ifdef __STDC__
 #if !defined(_XPG4_2) || defined(__EXTENSIONS__)
 extern int inet_net_pton(int, const char *, void *, size_t);
+extern boolean_t inet_matchaddr(const void *, const char *);
 #endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) */
 
 #if !defined(_XPG4_2) || defined(_XPG6) || defined(__EXTENSIONS__)
--- a/usr/src/lib/libnsl/Makefile.com	Tue Nov 01 10:17:46 2011 +0400
+++ b/usr/src/lib/libnsl/Makefile.com	Thu Nov 03 11:18:52 2011 +0400
@@ -21,6 +21,7 @@
 
 #
 # Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
 #
 
 LIBRARY= libnsl.a
@@ -41,8 +42,8 @@
 
 NSS= \
 gethostbyname_r.o gethostent.o gethostent_r.o gethostent6.o gethostby_door.o \
-getipnodeby_door.o getipnodeby.o getrpcent.o  getrpcent_r.o inet_pton.o \
-inet_ntop.o netdir_inet.o netdir_inet_sundry.o \
+getipnodeby_door.o getipnodeby.o getrpcent.o  getrpcent_r.o inet_matchaddr.o \
+inet_pton.o inet_ntop.o netdir_inet.o netdir_inet_sundry.o \
 parse.o getauthattr.o getprofattr.o getexecattr.o getuserattr.o getauuser.o
 
 NETSELECT= netselect.o
--- a/usr/src/lib/libnsl/common/llib-lnsl	Tue Nov 01 10:17:46 2011 +0400
+++ b/usr/src/lib/libnsl/common/llib-lnsl	Thu Nov 03 11:18:52 2011 +0400
@@ -22,6 +22,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  */
 
 
@@ -437,6 +439,9 @@
 void endrpcent(void);
 struct rpcent *getrpcent_r(struct rpcent *result, char *buffer, int buflen);
 
+/* inet_matchaddr.c */
+boolean_t inet_matchaddr(const void *, const char *);
+
 /* inet_ntop.c */
 const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
 
--- a/usr/src/lib/libnsl/common/mapfile-vers	Tue Nov 01 10:17:46 2011 +0400
+++ b/usr/src/lib/libnsl/common/mapfile-vers	Thu Nov 03 11:18:52 2011 +0400
@@ -20,7 +20,7 @@
 #
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-#
+# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
 #
 
 #
@@ -467,6 +467,7 @@
 
 SYMBOL_VERSION SUNWprivate_1.5 {
     global:
+	inet_matchaddr;
 	clnt_create_service_timed;
 } SUNWprivate_1.4;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libnsl/nss/inet_matchaddr.c	Thu Nov 03 11:18:52 2011 +0400
@@ -0,0 +1,150 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * inet_matchaddr
+ *
+ * Match IPv4 or IPv6 address provided in sa (sockaddr_in/sockaddr_in6)
+ * against standard text representation specified in name:
+ *
+ * IPv4:
+ *	IPv4
+ *	IPv4/netmask
+ *
+ * IPv6:
+ *	[IPv6]
+ *	[IPv6]/prefix
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <strings.h>
+
+
+boolean_t
+inet_matchaddr(const void *sa, const char *name)
+{
+	boolean_t ret = B_FALSE;
+	char *lname, *mp, *p;
+	uint32_t claddr4 = 0;
+
+	if ((p = lname = strdup(name)) == NULL)
+		err(1, "strdup");
+
+	if ((mp = strchr(p, '/')) != NULL)
+		*mp++ = '\0';
+
+	switch (((struct sockaddr_in *)sa)->sin_family) {
+	case AF_INET6: {
+		char *pp;
+		int prefix6;
+		ipaddr_t ipaddr4;
+		struct in6_addr hcaddr6;
+		struct in6_addr *claddr6 =
+		    &((struct sockaddr_in6 *)sa)->sin6_addr;
+
+		if (!IN6_IS_ADDR_V4MAPPED(claddr6)) {
+			/* IPv6 address */
+			if ((p = strchr(p, '[')) == NULL)
+				break;
+			p++;
+
+			if ((pp = strchr(p, ']')) == NULL)
+				break;
+			*pp = '\0';
+
+			if (inet_pton(AF_INET6, p, &hcaddr6) != 1)
+				break;
+
+			if (mp != NULL) {
+				/* Match only first prefix bits */
+				if ((prefix6 = (int)strtol(mp,
+				    (char **)NULL, 10)) == 0)
+					break;
+				ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6,
+				    &hcaddr6, prefix6);
+				break;
+			} else {
+				/* No prefix, exact match */
+				ret = IN6_ARE_ADDR_EQUAL(claddr6, &hcaddr6);
+				break;
+			}
+		} else {
+			/* IPv4-mapped IPv6 address, fallthrough to IPv4 */
+			IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4);
+			claddr4 = ntohl(ipaddr4);
+		}
+		/*FALLTHROUGH*/
+	}
+	case AF_INET: {
+		int bits, i;
+		uint32_t hcaddr4 = 0, mask4;
+
+		if (claddr4 == 0)
+			claddr4 = ntohl(
+			    ((struct sockaddr_in *)sa)->sin_addr.s_addr);
+
+		for (i = 0; i < 4; i++) {
+			hcaddr4 |= (int)strtol(p, (char **)NULL, 10) <<
+			    ((3 - i) * 8);
+			if ((p = strchr(p, '.')) == NULL)
+				break;
+			p++;
+		}
+
+		if (hcaddr4 == 0)
+			break;
+
+		if (mp != NULL) {
+			/* Mask is specified explicitly */
+			if ((bits = (int)strtol(mp, (char **)NULL, 10)) == 0)
+				break;
+			mask4 = bits ? ~0 << ((sizeof (struct in_addr) * NBBY)
+			    - bits) : 0;
+			hcaddr4 &= mask4;
+		} else {
+			/*
+			 * Use old-fashioned implicit netmasking by checking
+			 * for lower-end zeroes. On the off chance we don't
+			 * match any well-known prefixes, return an exact-
+			 * match prefix which is misleadingly labelled as
+			 * IN_CLASSE_NET.
+			 */
+			if ((hcaddr4 & IN_CLASSA_HOST) == 0)
+				mask4 = IN_CLASSA_NET;
+			else if ((hcaddr4 & IN_CLASSB_HOST) == 0)
+				mask4 = IN_CLASSB_NET;
+			else if ((hcaddr4 & IN_CLASSC_HOST) == 0)
+				mask4 = IN_CLASSC_NET;
+			else
+				mask4 = IN_CLASSE_NET;
+		}
+
+		ret = ((claddr4 & mask4) == hcaddr4);
+		break;
+	}
+	}
+
+	free(lname);
+
+	return (ret);
+}
--- a/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c	Tue Nov 01 10:17:46 2011 +0400
+++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c	Thu Nov 03 11:18:52 2011 +0400
@@ -17,9 +17,9 @@
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
- */
-/*
+ *
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -739,9 +739,6 @@
  * Precedence is none is checked first followed by ro then rw if
  * needed.  If x is wildcard (< 0) then check to see if the other
  * values are a match. If a match, that wins.
- *
- * ipv6 is wide open (returns SMB_SHRF_ACC_OPEN) for now until the underlying
- * functions support ipv6.
  */
 uint32_t
 smb_shr_hostaccess(smb_inaddr_t *ipaddr, char *none_list, char *ro_list,
@@ -753,10 +750,6 @@
 	int rw = 0;
 
 	if (!smb_inet_iszero(ipaddr)) {
-
-		if (ipaddr->a_family == AF_INET6)
-			return (SMB_SHRF_ACC_OPEN);
-
 		if ((flag & SMB_SHRF_ACC_NONE) != 0)
 			none = smb_chk_hostaccess(ipaddr, none_list);
 		if ((flag & SMB_SHRF_ACC_RO) != 0)
--- a/usr/src/lib/smbsrv/libsmb/common/smb_util.c	Tue Nov 01 10:17:46 2011 +0400
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_util.c	Thu Nov 03 11:18:52 2011 +0400
@@ -77,11 +77,9 @@
 static smb_log_t *smb_log_get(smb_log_hdl_t);
 static void smb_log_dump(smb_log_t *);
 
-static uint_t smb_make_mask(char *, uint_t);
-static boolean_t smb_netmatch(struct netbuf *, char *);
 static boolean_t smb_netgroup_match(struct nd_hostservlist *, char *, int);
 
-extern  int __multi_innetgr();
+extern int __multi_innetgr();
 extern int __netdir_getbyaddr_nosrv(struct netconfig *,
     struct nd_hostservlist **, struct netbuf *);
 
@@ -413,54 +411,80 @@
 int
 smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
 {
-	int nentries;
-	char *gr;
-	char *lasts;
+	char addr[INET_ADDRSTRLEN];
+	char buff[256];
+	char *cstr = access_list, *gr = access_list;
 	char *host;
-	int off;
+	int clres;
 	int i;
+	int nentries = 0;
+	int off;
 	int response;
-	int clres;
+	int sbr = 0;
 	struct nd_hostservlist *clnames;
 	struct in_addr inaddr;
 	struct sockaddr_in sa;
+	struct sockaddr_in6 sa6;
 	struct netbuf buf;
 	struct netconfig *config;
+	struct netent n, *np;
 
 	if (access_list == NULL)
 		return (0);
 
-	inaddr.s_addr = ipaddr->a_ipv4;
-
-	/*
-	 * If access list is empty or "*" - then it's "all"
-	 */
+	/* If access list is empty or "*" - then it's "all" */
 	if (*access_list == '\0' || strcmp(access_list, "*") == 0)
 		return (-1);
 
-	nentries = 0;
+	switch (ipaddr->a_family) {
+	case AF_INET:
+		inaddr.s_addr = ipaddr->a_ipv4;
+		sa.sin_family = AF_INET;
+		sa.sin_port = 0;
+		sa.sin_addr = inaddr;
+		buf.len = buf.maxlen = sizeof (sa);
+		buf.buf = (char *)&sa;
+		config = getnetconfigent("tcp");
+		break;
+	case AF_INET6:
+		sa6.sin6_family = AF_INET6;
+		sa6.sin6_port = 0;
+		sa6.sin6_addr = ipaddr->a_ipv6;
+		buf.len = buf.maxlen = sizeof (sa6);
+		buf.buf = (char *)&sa6;
+		config = getnetconfigent("tcp6");
+		break;
+	default:
+		return (1);
+	}
 
-	sa.sin_family = AF_INET;
-	sa.sin_port = 0;
-	sa.sin_addr = inaddr;
-
-	buf.len = buf.maxlen = sizeof (sa);
-	buf.buf = (char *)&sa;
-
-	config = getnetconfigent("tcp");
 	if (config == NULL)
 		return (1);
 
+	/* Try to lookup client hostname */
 	clres = __netdir_getbyaddr_nosrv(config, &clnames, &buf);
 	freenetconfigent(config);
 
-	for (gr = strtok_r(access_list, ":", &lasts);
-	    gr != NULL; gr = strtok_r(NULL, ":", &lasts)) {
+	for (;;) {
+		if ((cstr = strpbrk(cstr, "[]:")) != NULL) {
+			switch (*cstr) {
+			case '[':
+			case ']':
+				sbr = !sbr;
+				cstr++;
+				continue;
+			case ':':
+				if (sbr) {
+					cstr++;
+					continue;
+				}
+				*cstr = '\0';
+			}
+		}
 
 		/*
-		 * If the list name has a '-' prepended
-		 * then a match of the following name
-		 * implies failure instead of success.
+		 * If the list name has a '-' prepended then a match of
+		 * the following name implies failure instead of success.
 		 */
 		if (*gr == '-') {
 			response = 0;
@@ -470,21 +494,53 @@
 		}
 
 		/*
-		 * First check if we have '@' entry, as smb_netmatch doesn't
-		 * care if client address can be resolved.
+		 * First check if we have '@' entry, as it doesn't
+		 * require client hostname.
 		 */
-		if (*gr == '@')
-			if (smb_netmatch(&buf, gr + 1))
-				return (response);
+		if (*gr == '@') {
+			gr++;
+
+			if (!isdigit(*gr) && *gr != '[') {
+				/* Netname support */
+				if ((np = getnetbyname_r(gr, &n, buff,
+				    sizeof (buff))) != NULL &&
+				    np->n_net != 0) {
+					while ((np->n_net & 0xFF000000u) == 0)
+						np->n_net <<= 8;
+					np->n_net = htonl(np->n_net);
+					if (inet_ntop(AF_INET, &np->n_net, addr,
+					    INET_ADDRSTRLEN) == NULL)
+						break;
+					if (inet_matchaddr(buf.buf, addr))
+						return (response);
+				}
+			} else {
+				if (inet_matchaddr(buf.buf, gr))
+					return (response);
+			}
+
+			if (cstr == NULL)
+				break;
+
+			gr = ++cstr;
+
+			continue;
+		}
+
 		/*
 		 * No other checks can be performed if client address
 		 * can't be resolved.
 		 */
-		if (clres)
+		if (clres) {
+			if (cstr == NULL)
+				break;
+
+			gr = ++cstr;
+
 			continue;
-		/*
-		 * Otherwise loop through all client hostname aliases.
-		 */
+		}
+
+		/* Otherwise loop through all client hostname aliases */
 		for (i = 0; i < clnames->h_cnt; i++) {
 			host = clnames->h_hostservs[i].h_host;
 			/*
@@ -494,7 +550,7 @@
 			 * suffix.
 			 */
 			if (*gr == '.') {
-				if (*(gr + 1) == '\0') {  /* single dot */
+				if (*(gr + 1) == '\0') {
 					if (strchr(host, '.') == NULL)
 						return (response);
 				} else {
@@ -505,15 +561,18 @@
 					}
 				}
 			} else {
-				/*
-				 * Just do a hostname match
-				 */
+				/* Just do a hostname match */
 				if (strcasecmp(gr, host) == 0)
 					return (response);
 				}
 			}
 
 		nentries++;
+
+		if (cstr == NULL)
+			break;
+
+		gr = ++cstr;
 	}
 
 	if (clres)
@@ -523,116 +582,6 @@
 }
 
 /*
- * smb_make_mask
- *
- * Construct a mask for an IPv4 address using the @<dotted-ip>/<len>
- * syntax or use the default mask for the IP address.
- */
-static uint_t
-smb_make_mask(char *maskstr, uint_t addr)
-{
-	uint_t mask;
-	uint_t bits;
-
-	/*
-	 * If the mask is specified explicitly then
-	 * use that value, e.g.
-	 *
-	 *    @109.104.56/28
-	 *
-	 * otherwise assume a mask from the zero octets
-	 * in the least significant bits of the address, e.g.
-	 *
-	 *   @109.104  or  @109.104.0.0
-	 */
-	if (maskstr) {
-		bits = atoi(maskstr);
-		mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits)
-		    : 0;
-		addr &= mask;
-	} else {
-		if ((addr & IN_CLASSA_HOST) == 0)
-			mask = IN_CLASSA_NET;
-		else if ((addr & IN_CLASSB_HOST) == 0)
-			mask = IN_CLASSB_NET;
-		else if ((addr & IN_CLASSC_HOST) == 0)
-			mask = IN_CLASSC_NET;
-		else
-			mask = IN_CLASSE_NET;
-	}
-
-	return (mask);
-}
-
-/*
- * smb_netmatch
- *
- * Check to see if the address in the netbuf matches the "net"
- * specified by name.  The format of "name" can be:
- *	fully qualified domain name
- *	dotted IP address
- *	dotted IP address followed by '/<len>'
- *	See sharen_nfs(1M) for details.
- */
-
-static boolean_t
-smb_netmatch(struct netbuf *nb, char *name)
-{
-	uint_t claddr;
-	struct netent n, *np;
-	char *mp, *p;
-	uint_t addr, mask;
-	int i;
-	char buff[256];
-
-	/*
-	 * Check if it's an IPv4 addr
-	 */
-	if (nb->len != sizeof (struct sockaddr_in))
-		return (B_FALSE);
-
-	(void) memcpy(&claddr,
-	    /* LINTED pointer alignment */
-	    &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr,
-	    sizeof (struct in_addr));
-	claddr = ntohl(claddr);
-
-	mp = strchr(name, '/');
-	if (mp)
-		*mp++ = '\0';
-
-	if (isdigit(*name)) {
-		/*
-		 * Convert a dotted IP address
-		 * to an IP address. The conversion
-		 * is not the same as that in inet_addr().
-		 */
-		p = name;
-		addr = 0;
-		for (i = 0; i < 4; i++) {
-			addr |= atoi(p) << ((3-i) * 8);
-			p = strchr(p, '.');
-			if (p == NULL)
-				break;
-			p++;
-		}
-	} else {
-		/*
-		 * Turn the netname into
-		 * an IP address.
-		 */
-		np = getnetbyname_r(name, &n, buff, sizeof (buff));
-		if (np == NULL) {
-			return (B_FALSE);
-		}
-		addr = np->n_net;
-	}
-
-	mask = smb_make_mask(mp, addr);
-	return ((claddr & mask) == addr);
-}
-
-/*
  * smb_netgroup_match
  *
  * Check whether any of the hostnames in clnames are
--- a/usr/src/uts/common/netinet/in.h	Tue Nov 01 10:17:46 2011 +0400
+++ b/usr/src/uts/common/netinet/in.h	Thu Nov 03 11:18:52 2011 +0400
@@ -1,6 +1,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  */
 /*
  * Copyright (c) 1982, 1986 Regents of the University of California.
@@ -839,6 +841,38 @@
 	((addr1)->_S6_un._S6_u32[1] == (addr2)->_S6_un._S6_u32[1]) && \
 	((addr1)->_S6_un._S6_u32[0] == (addr2)->_S6_un._S6_u32[0]))
 
+/*
+ * IN6_ARE_PREFIXEDADDR_EQUAL (not defined in RFCs)
+ *	Compares if prefixed parts of IPv6 addresses are equal.
+ *
+ * uint32_t IN6_MASK_FROM_PREFIX(int, int);
+ * bool     IN6_ARE_PREFIXEDADDR_EQUAL(const struct in6_addr *,
+ *				       const struct in6_addr *,
+ *				       int);
+ */
+#define	IN6_MASK_FROM_PREFIX(qoctet, prefix) \
+	((((qoctet) + 1) * 32 < (prefix)) ? 0xFFFFFFFFu : \
+	((((qoctet) * 32) >= (prefix)) ? 0x00000000u : \
+	0xFFFFFFFFu << (((qoctet) + 1) * 32 - (prefix))))
+
+#define	IN6_ARE_PREFIXEDADDR_EQUAL(addr1, addr2, prefix) \
+	(((ntohl((addr1)->_S6_un._S6_u32[0]) & \
+	IN6_MASK_FROM_PREFIX(0, prefix)) == \
+	(ntohl((addr2)->_S6_un._S6_u32[0]) & \
+	IN6_MASK_FROM_PREFIX(0, prefix))) && \
+	((ntohl((addr1)->_S6_un._S6_u32[1]) & \
+	IN6_MASK_FROM_PREFIX(1, prefix)) == \
+	(ntohl((addr2)->_S6_un._S6_u32[1]) & \
+	IN6_MASK_FROM_PREFIX(1, prefix))) && \
+	((ntohl((addr1)->_S6_un._S6_u32[2]) & \
+	IN6_MASK_FROM_PREFIX(2, prefix)) == \
+	(ntohl((addr2)->_S6_un._S6_u32[2]) & \
+	IN6_MASK_FROM_PREFIX(2, prefix))) && \
+	((ntohl((addr1)->_S6_un._S6_u32[3]) & \
+	IN6_MASK_FROM_PREFIX(3, prefix)) == \
+	(ntohl((addr2)->_S6_un._S6_u32[3]) & \
+	IN6_MASK_FROM_PREFIX(3, prefix))))
+
 #endif /* !defined(_XPG4_2) || defined(_XPG6) || defined(__EXTENSIONS__) */