6582152 MMC remote service management support
authorJordan Brown <Jordan.Brown@Sun.COM>
Fri, 17 Jul 2009 17:54:42 -0700
changeset 10122 96eda55bfd54
parent 10121 3f4b7e36b09b
child 10123 d9be114e78c4
6582152 MMC remote service management support 6816841 name <-> SID interfaces 6843330 Create interposer library for SVCCTL and LOGR functionality. 6856791 Files dropped from large directory listing with long filenames 6859346 Query File Information errors when querying a named pipe 6860126 name<->SID interfaces use unacceptable style
usr/src/cmd/idmap/idmap/idmap.c
usr/src/cmd/idmap/idmapd/Makefile
usr/src/cmd/idmap/idmapd/adutils.c
usr/src/cmd/idmap/idmapd/dbutils.c
usr/src/cmd/idmap/idmapd/directory_provider_ad.c
usr/src/cmd/idmap/idmapd/directory_provider_builtin.c
usr/src/cmd/idmap/idmapd/directory_provider_nsswitch.c
usr/src/cmd/idmap/idmapd/directory_server.c
usr/src/cmd/idmap/idmapd/directory_server_impl.h
usr/src/cmd/idmap/idmapd/idmap_config.c
usr/src/cmd/idmap/idmapd/idmapd.c
usr/src/cmd/idmap/idmapd/idmapd.h
usr/src/cmd/idmap/idmapd/rpc_svc.c
usr/src/cmd/idmap/idmapd/server.c
usr/src/cmd/idmap/idmapd/wksids.c
usr/src/common/smbsrv/smb_xdr_utils.c
usr/src/lib/libadutils/common/addisc.c
usr/src/lib/libadutils/common/adutils.c
usr/src/lib/libadutils/common/libadutils.h
usr/src/lib/libadutils/common/mapfile-vers
usr/src/lib/libidmap/Makefile
usr/src/lib/libidmap/Makefile.com
usr/src/lib/libidmap/common/directory.h
usr/src/lib/libidmap/common/directory_client.c
usr/src/lib/libidmap/common/directory_error.c
usr/src/lib/libidmap/common/directory_helper.c
usr/src/lib/libidmap/common/directory_library_impl.h
usr/src/lib/libidmap/common/directory_private.h
usr/src/lib/libidmap/common/directory_rpc_clnt.c
usr/src/lib/libidmap/common/idmap_api.c
usr/src/lib/libidmap/common/idmap_impl.h
usr/src/lib/libidmap/common/llib-lidmap
usr/src/lib/libidmap/common/mapfile-vers
usr/src/lib/libidmap/common/miscutils.c
usr/src/lib/libidmap/common/miscutils.h
usr/src/lib/libidmap/common/sidutil.c
usr/src/lib/libidmap/common/sidutil.h
usr/src/lib/libidmap/common/sized_array.c
usr/src/lib/libidmap/common/sized_array.h
usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h
usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers
usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c
usr/src/lib/smbsrv/libmlsvc/common/eventlog.h
usr/src/lib/smbsrv/libmlsvc/common/eventlog_svc.c
usr/src/lib/smbsrv/libmlsvc/common/eventlog_syslog.c
usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h
usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c
usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c
usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
usr/src/lib/smbsrv/libmlsvc/common/smb_share.c
usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c
usr/src/lib/smbsrv/libmlsvc/common/srvsvc_svc.c
usr/src/lib/smbsrv/libmlsvc/common/svcctl_scm.c
usr/src/lib/smbsrv/libmlsvc/common/svcctl_scm.h
usr/src/lib/smbsrv/libmlsvc/common/svcctl_svc.c
usr/src/lib/smbsrv/libsmb/Makefile.com
usr/src/lib/smbsrv/libsmb/common/libsmb.h
usr/src/lib/smbsrv/libsmb/common/mapfile-vers
usr/src/lib/smbsrv/libsmb/common/smb_kmod.c
usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c
usr/src/lib/smbsrv/libsmb/common/smb_util.c
usr/src/pkgdefs/etc/exception_list_i386
usr/src/pkgdefs/etc/exception_list_sparc
usr/src/uts/common/fs/smbsrv/smb_common_open.c
usr/src/uts/common/fs/smbsrv/smb_delete.c
usr/src/uts/common/fs/smbsrv/smb_fsops.c
usr/src/uts/common/fs/smbsrv/smb_init.c
usr/src/uts/common/fs/smbsrv/smb_lock.c
usr/src/uts/common/fs/smbsrv/smb_node.c
usr/src/uts/common/fs/smbsrv/smb_ofile.c
usr/src/uts/common/fs/smbsrv/smb_opipe.c
usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c
usr/src/uts/common/fs/smbsrv/smb_query_information.c
usr/src/uts/common/fs/smbsrv/smb_query_information2.c
usr/src/uts/common/fs/smbsrv/smb_server.c
usr/src/uts/common/fs/smbsrv/smb_session.c
usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c
usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c
usr/src/uts/common/fs/smbsrv/smb_tree.c
usr/src/uts/common/fs/smbsrv/smb_user.c
usr/src/uts/common/rpc/xdr.c
usr/src/uts/common/rpc/xdr.h
usr/src/uts/common/rpcsvc/idmap_prot.x
usr/src/uts/common/smbsrv/ndl/srvsvc.ndl
usr/src/uts/common/smbsrv/smb_ioctl.h
usr/src/uts/common/smbsrv/smb_kproto.h
usr/src/uts/common/smbsrv/smb_ktypes.h
usr/src/uts/common/smbsrv/smb_xdr.h
usr/src/uts/common/sys/lvm/meta_basic.x
--- a/usr/src/cmd/idmap/idmap/idmap.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmap/idmap.c	Fri Jul 17 17:54:42 2009 -0700
@@ -34,7 +34,6 @@
 #include <sys/varargs.h>
 #include "idmap_engine.h"
 #include "idmap_priv.h"
-#include "idmap_impl.h"
 
 /* Initialization values for pids/rids: */
 
--- a/usr/src/cmd/idmap/idmapd/Makefile	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmapd/Makefile	Fri Jul 17 17:54:42 2009 -0700
@@ -25,8 +25,21 @@
 
 PROG =		idmapd
 MANIFEST =	idmap.xml
-SERVEROBJS =	idmapd.o init.o dbutils.o rpc_svc.o server.o adutils.o\
-	idmap_config.o nldaputils.o
+SERVEROBJS =				\
+	directory_provider_builtin.o	\
+	directory_provider_nsswitch.o	\
+	directory_provider_ad.o		\
+	directory_server.o		\
+	adutils.o			\
+	dbutils.o			\
+	idmap_config.o			\
+	idmapd.o			\
+	init.o				\
+	nldaputils.o			\
+	rpc_svc.o			\
+	server.o			\
+	wksids.o
+
 SERVERSRCS =	$(SERVEROBJS:%.o=%.c)
 OBJS =		$(SERVEROBJS)
 SRCS =		$(SERVERSRCS)
@@ -42,8 +55,17 @@
 
 include ../../Makefile.cmd
 
+TEXT_DOMAIN =	SUNW_OST_OSLIB
+XGETTEXT =	$(GNUXGETTEXT)
+XGETFLAGS =	--foreign-user --strict -n -E --width=72 \
+		--omit-header --keyword=directoryError:2 \
+		--language=C --force-po
+
+C99MODE = $(C99_ENABLE)
 POFILE =	$(PROG)_all.po
 
+RPC_MSGOUT_OPT = -DRPC_MSGOUT=idmap_rpc_msgout
+
 ROOTMANIFESTDIR = $(ROOTSVCSYSTEM)
 $(ROOTMANIFEST) := FILEMODE= 444
 
@@ -55,7 +77,18 @@
 $(POFILE) := CPPFLAGS += $(INCS)
 
 CFLAGS += -v
-LDLIBS += -lsecdb -lsocket -lnsl -lidmap -lscf -lsldap -lldap -luuid -ladutils
+LDLIBS += -lsecdb \
+	-lsocket \
+	-lnsl \
+	-lidmap \
+	-lscf \
+	-lsldap \
+	-lldap \
+	-luuid \
+	-ladutils \
+	-lumem
+
+rpc_svc.o := CFLAGS += $(RPC_MSGOUT_OPT)
 
 $(PROG) := MAPFILES = $(MAPFILE.INT) $(MAPFILE.NGB)
 $(PROG) := LDFLAGS += $(MAPFILES:%=-M%)
@@ -65,7 +98,7 @@
 OWNER = root
 GROUP = sys
 
-lint_SRCS := CPPFLAGS += $(INCS) -D_REENTRANT
+lint_SRCS := CPPFLAGS += $(INCS) -D_REENTRANT $(RPC_MSGOUT_OPT)
 lint := LDLIBS += $(SQLITELINT)
 
 .KEEP_STATE:
--- a/usr/src/cmd/idmap/idmapd/adutils.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmapd/adutils.c	Fri Jul 17 17:54:42 2009 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -235,7 +235,7 @@
 	char *sid, rid_t rid, int sid_type, char *unixname)
 {
 	char *domain;
-	int err1, err2;
+	int err1;
 
 	assert(dn != NULL);
 
@@ -252,8 +252,7 @@
 
 	if (q->edomain != NULL) {
 		/* Check that this is the domain that we were looking for */
-		if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER,
-		    U8_UNICODE_LATEST, &err2) != 0 || err2 != 0)
+		if (!domain_eq(q->edomain, domain))
 			goto out;
 	}
 
--- a/usr/src/cmd/idmap/idmapd/dbutils.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmapd/dbutils.c	Fri Jul 17 17:54:42 2009 -0700
@@ -74,8 +74,6 @@
 
 #define	IS_EPHEMERAL(pid)	(pid > INT32_MAX && pid != SENTINEL_PID)
 
-#define	LOCALRID_MIN	1000
-
 
 typedef enum init_db_option {
 	FAIL_IF_CORRUPT = 0,
@@ -83,21 +81,6 @@
 } init_db_option_t;
 
 /*
- * Data structure to store well-known SIDs and
- * associated mappings (if any)
- */
-typedef struct wksids_table {
-	const char	*sidprefix;
-	uint32_t	rid;
-	const char	*domain;
-	const char	*winname;
-	int		is_wuser;
-	uid_t		pid;
-	int		is_user;
-	int		direction;
-} wksids_table_t;
-
-/*
  * Thread specific data to hold the database handles so that the
  * databases are not opened and closed for every request. It also
  * contains the sqlite busy handler structure.
@@ -131,12 +114,6 @@
 
 static pthread_key_t	idmap_tsd_key;
 
-static const wksids_table_t *find_wksid_by_pid(uid_t pid, int is_user);
-static const wksids_table_t *find_wksid_by_sid(const char *sid, int rid,
-    int type);
-static const wksids_table_t *find_wksid_by_name(const char *name,
-    const char *domain, int type);
-
 void
 idmap_tsd_destroy(void *key)
 {
@@ -1203,179 +1180,6 @@
 	rule->direction = direction;
 }
 
-
-/*
- * Table for well-known SIDs.
- *
- * Background:
- *
- * Some of the well-known principals are stored under:
- * cn=WellKnown Security Principals, cn=Configuration, dc=<forestRootDomain>
- * They belong to objectClass "foreignSecurityPrincipal". They don't have
- * "samAccountName" nor "userPrincipalName" attributes. Their names are
- * available in "cn" and "name" attributes. Some of these principals have a
- * second entry under CN=ForeignSecurityPrincipals,dc=<forestRootDomain> and
- * these duplicate entries have the stringified SID in the "name" and "cn"
- * attributes instead of the actual name.
- *
- * Those of the form S-1-5-32-X are Builtin groups and are stored in the
- * cn=builtin container (except, Power Users which is not stored in AD)
- *
- * These principals are and will remain constant. Therefore doing AD lookups
- * provides no benefit. Also, using hard-coded table (and thus avoiding AD
- * lookup) improves performance and avoids additional complexity in the
- * adutils.c code. Moreover these SIDs can be used when no Active Directory
- * is available (such as the CIFS server's "workgroup" mode).
- *
- * Notes:
- * 1. Currently we don't support localization of well-known SID names,
- * unlike Windows.
- *
- * 2. Other well-known SIDs i.e. S-1-5-<domain>-<w-k RID> are not stored
- * here. AD does have normal user/group objects for these objects and
- * can be looked up using the existing AD lookup code.
- *
- * 3. See comments above lookup_wksids_sid2pid() for more information
- * on how we lookup the wksids table.
- *
- * 4. If this table contains two entries for a particular Windows name,
- * so as to offer both UID and GID mappings, the preferred mapping (the
- * one that matches Windows usage) must be listed first.  That is the
- * entry that will be used when the caller specifies IDMAP_POSIXID
- * ("don't care") as the target.
- *
- * Entries here come from KB243330, MS-LSAT, and
- * http://technet.microsoft.com/en-us/library/cc755854.aspx
- * http://technet.microsoft.com/en-us/library/cc755925.aspx
- * http://msdn.microsoft.com/en-us/library/cc980032(PROT.10).aspx
- */
-static wksids_table_t wksids[] = {
-	/* S-1-0	Null Authority */
-	{"S-1-0", 0, "", "Nobody", 1, SENTINEL_PID, -1, 1},
-
-	/* S-1-1	World Authority */
-	{"S-1-1", 0, "", "Everyone", 0, SENTINEL_PID, -1, -1},
-
-	/* S-1-2	Local Authority */
-	{"S-1-2", 0, "", "Local", 0, SENTINEL_PID, -1, -1},
-	{"S-1-2", 1, "", "Console Logon", 0, SENTINEL_PID, -1, -1},
-
-	/* S-1-3	Creator Authority */
-	{"S-1-3", 0, "", "Creator Owner", 1, IDMAP_WK_CREATOR_OWNER_UID, 1, 0},
-	{"S-1-3", 1, "", "Creator Group", 0, IDMAP_WK_CREATOR_GROUP_GID, 0, 0},
-	{"S-1-3", 2, "", "Creator Owner Server", 1, SENTINEL_PID, -1, -1},
-	{"S-1-3", 3, "", "Creator Group Server", 0, SENTINEL_PID, -1, 1},
-	{"S-1-3", 4, "", "Owner Rights", 0, SENTINEL_PID, -1, -1},
-
-	/* S-1-4	Non-unique Authority */
-
-	/* S-1-5	NT Authority */
-	{"S-1-5", 1, "", "Dialup", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 2, "", "Network", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 3, "", "Batch", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 4, "", "Interactive", 0, SENTINEL_PID, -1, -1},
-	/* S-1-5-5-X-Y	Logon Session */
-	{"S-1-5", 6, "", "Service", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 7, "", "Anonymous Logon", 0, GID_NOBODY, 0, 0},
-	{"S-1-5", 7, "", "Anonymous Logon", 0, UID_NOBODY, 1, 0},
-	{"S-1-5", 8, "", "Proxy", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 9, "", "Enterprise Domain Controllers", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5", 10, "", "Self", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 11, "", "Authenticated Users", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 12, "", "Restricted", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 13, "", "Terminal Server Users", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 14, "", "Remote Interactive Logon", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 15, "", "This Organization", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 17, "", "IUSR", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 18, "", "Local System", 0, IDMAP_WK_LOCAL_SYSTEM_GID, 0, 0},
-	{"S-1-5", 19, "", "Local Service", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5", 20, "", "Network Service", 0, SENTINEL_PID, -1, -1},
-
-	/* S-1-5-21-<domain>	Machine-local definitions */
-	{NULL, 498, NULL, "Enterprise Read-only Domain Controllers", 0,
-	    SENTINEL_PID, -1, -1},
-	{NULL, 500, NULL, "Administrator", 1, SENTINEL_PID, 1, -1},
-	{NULL, 501, NULL, "Guest", 1, SENTINEL_PID, 1, -1},
-	{NULL, 502, NULL, "KRBTGT", 1, SENTINEL_PID, 1, -1},
-	{NULL, 512, NULL, "Domain Admins", 0, SENTINEL_PID, -1, -1},
-	{NULL, 513, NULL, "Domain Users", 0, SENTINEL_PID, -1, -1},
-	{NULL, 514, NULL, "Domain Guests", 0, SENTINEL_PID, -1, -1},
-	{NULL, 515, NULL, "Domain Computers", 0, SENTINEL_PID, -1, -1},
-	{NULL, 516, NULL, "Domain Controllers", 0, SENTINEL_PID, -1, -1},
-	{NULL, 517, NULL, "Cert Publishers", 0, SENTINEL_PID, -1, -1},
-	{NULL, 518, NULL, "Schema Admins", 0, SENTINEL_PID, -1, -1},
-	{NULL, 519, NULL, "Enterprise Admins", 0, SENTINEL_PID, -1, -1},
-	{NULL, 520, NULL, "Global Policy Creator Owners", 0,
-	    SENTINEL_PID, -1, -1},
-	{NULL, 533, NULL, "RAS and IAS Servers", 0, SENTINEL_PID, -1, -1},
-
-	/* S-1-5-32	BUILTIN */
-	{"S-1-5-32", 544, "BUILTIN", "Administrators", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 545, "BUILTIN", "Users", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 546, "BUILTIN", "Guests", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 547, "BUILTIN", "Power Users", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 548, "BUILTIN", "Account Operators", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 549, "BUILTIN", "Server Operators", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 550, "BUILTIN", "Print Operators", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 551, "BUILTIN", "Backup Operators", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 552, "BUILTIN", "Replicator", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 554, "BUILTIN", "Pre-Windows 2000 Compatible Access", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 555, "BUILTIN", "Remote Desktop Users", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 556, "BUILTIN", "Network Configuration Operators", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 557, "BUILTIN", "Incoming Forest Trust Builders", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 558, "BUILTIN", "Performance Monitor Users", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 559, "BUILTIN", "Performance Log Users", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 560, "BUILTIN", "Windows Authorization Access Group", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 561, "BUILTIN", "Terminal Server License Servers", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 562, "BUILTIN", "Distributed COM Users", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 568, "BUILTIN", "IIS_IUSRS", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 569, "BUILTIN", "Cryptographic Operators", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 573, "BUILTIN", "Event Log Readers", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-32", 574, "BUILTIN", "Certificate Service DCOM Access", 0,
-	    SENTINEL_PID, -1, -1},
-
-	{"S-1-5", 33, "", "Write Restricted", 0, SENTINEL_PID, -1, -1},
-
-	/* S-1-5-64	NT Authority */
-	{"S-1-5-64", 10, "", "NTLM Authentication", 0, SENTINEL_PID, -1, -1},
-	{"S-1-5-64", 14, "", "SChannel Authentication", 0,
-	    SENTINEL_PID, -1, -1},
-	{"S-1-5-64", 21, "", "Digest Authentication", 0, SENTINEL_PID, -1, -1},
-
-	/* S-1-5-80-a-b-c-d NT Service */
-
-	{"S-1-5", 1000, "", "Other Organization", 0, SENTINEL_PID, -1, -1},
-
-	/* S-1-7 Internet$ */
-
-	/*
-	 * S-1-16	Mandatory Label
-	 * S-1-16-0	Untrusted Mandatory Level
-	 * S-1-16-4096	Low Mandatory Level
-	 * S-1-16-8192	Medium Mandatory Level
-	 * S-1-16-8448	Medium Plus Mandatory Level
-	 * S-1-16-12288	High Mandatory Level
-	 * S-1-16-16384	System Mandatory Level
-	 * S-1-16-20480	Protected Process Mandatory Level
-	 */
-};
-
 /*
  * Lookup well-known SIDs table either by winname or by SID.
  *
@@ -2634,8 +2438,8 @@
 		return (IDMAP_ERR_NOMAPPING);
 
 	/* Skip 1000 UIDs */
-	if (is_user && req->id1.idmap_id_u.uid >
-	    (INT32_MAX - LOCALRID_MIN))
+	if (is_user &&
+	    req->id1.idmap_id_u.uid + LOCALRID_UID_MIN > LOCALRID_UID_MAX)
 		return (IDMAP_ERR_NOMAPPING);
 
 	RDLOCK_CONFIG();
@@ -2652,8 +2456,8 @@
 	}
 	UNLOCK_CONFIG();
 	res->id.idmap_id_u.sid.rid =
-	    (is_user) ? req->id1.idmap_id_u.uid + LOCALRID_MIN :
-	    req->id1.idmap_id_u.gid + INT32_MAX + 1;
+	    (is_user) ? req->id1.idmap_id_u.uid + LOCALRID_UID_MIN :
+	    req->id1.idmap_id_u.gid + LOCALRID_GID_MIN;
 	res->direction = IDMAP_DIRECTION_BI;
 	if (res->id.idtype == IDMAP_SID)
 		res->id.idtype = is_user ? IDMAP_USID : IDMAP_GSID;
@@ -2702,24 +2506,24 @@
 
 	switch (res->id.idtype) {
 	case IDMAP_UID:
-		if (rid > INT32_MAX || rid < LOCALRID_MIN)
+		if (rid < LOCALRID_UID_MIN || rid > LOCALRID_UID_MAX)
 			return (IDMAP_ERR_ARG);
-		res->id.idmap_id_u.uid = rid - LOCALRID_MIN;
+		res->id.idmap_id_u.uid = rid - LOCALRID_UID_MIN;
 		break;
 	case IDMAP_GID:
-		if (rid <= INT32_MAX)
+		if (rid < LOCALRID_GID_MIN)
 			return (IDMAP_ERR_ARG);
-		res->id.idmap_id_u.gid = rid - INT32_MAX - 1;
+		res->id.idmap_id_u.gid = rid - LOCALRID_GID_MIN;
 		break;
 	case IDMAP_POSIXID:
-		if (rid > INT32_MAX) {
-			res->id.idmap_id_u.gid = rid - INT32_MAX - 1;
+		if (rid >= LOCALRID_GID_MIN) {
+			res->id.idmap_id_u.gid = rid - LOCALRID_GID_MIN;
 			res->id.idtype = IDMAP_GID;
-		} else if (rid < LOCALRID_MIN) {
-			return (IDMAP_ERR_ARG);
+		} else if (rid >= LOCALRID_UID_MIN) {
+			res->id.idmap_id_u.uid = rid - LOCALRID_UID_MIN;
+			res->id.idtype = IDMAP_UID;
 		} else {
-			res->id.idmap_id_u.uid = rid - LOCALRID_MIN;
-			res->id.idtype = IDMAP_UID;
+			return (IDMAP_ERR_ARG);
 		}
 		break;
 	default:
@@ -4851,161 +4655,3 @@
 	result.ids.ids_val = res;
 	return (ad_lookup_batch(state, &batch, &result));
 }
-
-/*
- * Find a wksid entry for the specified Windows name and domain, of the
- * specified type.
- *
- * Ignore entries intended only for U2W use.
- */
-static
-const
-wksids_table_t *
-find_wksid_by_name(const char *name, const char *domain, int type)
-{
-	int i;
-	char *myhostname;
-	int len;
-
-	RDLOCK_CONFIG();
-	len = strlen(_idmapdstate.hostname) + 1;
-	myhostname = alloca(len);
-	(void) memcpy(myhostname, _idmapdstate.hostname, len);
-	UNLOCK_CONFIG();
-
-	for (i = 0; i < NELEM(wksids); i++) {
-		/* Check to see if this entry yields the desired type */
-		switch (type) {
-		case IDMAP_UID:
-			if (wksids[i].is_user == 0)
-				continue;
-			break;
-		case IDMAP_GID:
-			if (wksids[i].is_user == 1)
-				continue;
-			break;
-		case IDMAP_POSIXID:
-			break;
-		default:
-			assert(FALSE);
-		}
-
-		if (strcasecmp(wksids[i].winname, name) != 0)
-			continue;
-
-		if (!EMPTY_STRING(domain)) {
-			const char *dom;
-
-			if (wksids[i].domain != NULL) {
-				dom = wksids[i].domain;
-			} else {
-				dom = myhostname;
-			}
-			if (strcasecmp(dom, domain) != 0) {
-				/* this is not our domain */
-				continue;
-			}
-		}
-
-		/*
-		 * We have a Windows name, so ignore entries that are only
-		 * usable for mapping UNIX->Windows.  (Note:  the current
-		 * table does not have any such entries.)
-		 */
-		if (wksids[i].direction == IDMAP_DIRECTION_U2W)
-			continue;
-
-		return (&wksids[i]);
-	}
-
-	return (NULL);
-}
-
-/*
- * Find a wksid entry for the specified SID, of the specified type.
- *
- * Ignore entries intended only for U2W use.
- */
-static
-const
-wksids_table_t *
-find_wksid_by_sid(const char *sid, int rid, int type)
-{
-	int i;
-	char *mymachinesid;
-	int len;
-
-	RDLOCK_CONFIG();
-	len = strlen(_idmapdstate.cfg->pgcfg.machine_sid) + 1;
-	mymachinesid = alloca(len);
-	(void) memcpy(mymachinesid, _idmapdstate.cfg->pgcfg.machine_sid, len);
-	UNLOCK_CONFIG();
-
-	for (i = 0; i < NELEM(wksids); i++) {
-		int sidcmp;
-
-		/* Check to see if this entry yields the desired type */
-		switch (type) {
-		case IDMAP_UID:
-			if (wksids[i].is_user == 0)
-				continue;
-			break;
-		case IDMAP_GID:
-			if (wksids[i].is_user == 1)
-				continue;
-			break;
-		case IDMAP_POSIXID:
-			break;
-		default:
-			assert(FALSE);
-		}
-
-		if (wksids[i].sidprefix != NULL) {
-			sidcmp = strcasecmp(wksids[i].sidprefix, sid);
-		} else {
-			sidcmp = strcasecmp(mymachinesid, sid);
-		}
-
-		if (sidcmp != 0)
-			continue;
-		if (wksids[i].rid != rid)
-			continue;
-
-		/*
-		 * We have a SID, so ignore entries that are only usable
-		 * for mapping UNIX->Windows.  (Note:  the current table
-		 * does not have any such entries.)
-		 */
-		if (wksids[i].direction == IDMAP_DIRECTION_U2W)
-			continue;
-
-		return (&wksids[i]);
-	}
-
-	return (NULL);
-}
-
-/*
- * Find a wksid entry for the specified pid, of the specified type.
- * Ignore entries that do not specify U2W mappings.
- */
-static
-const
-wksids_table_t *
-find_wksid_by_pid(uid_t pid, int is_user)
-{
-	int i;
-
-	if (pid == SENTINEL_PID)
-		return (NULL);
-
-	for (i = 0; i < NELEM(wksids); i++) {
-		if (wksids[i].pid == pid &&
-		    wksids[i].is_user == is_user &&
-		    (wksids[i].direction == IDMAP_DIRECTION_BI ||
-		    wksids[i].direction == IDMAP_DIRECTION_U2W)) {
-			return (&wksids[i]);
-		}
-	}
-	return (NULL);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/idmap/idmapd/directory_provider_ad.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,590 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Retrieve directory information for Active Directory users.
+ */
+
+#include <ldap.h>
+#include <lber.h>
+#include <pwd.h>
+#include <malloc.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <libadutils.h>
+#include <note.h>
+#include <assert.h>
+#include "directory.h"
+#include "directory_private.h"
+#include "idmapd.h"
+#include <rpcsvc/idmap_prot.h>
+#include "directory_server_impl.h"
+#include "miscutils.h"
+
+/*
+ * Information required by the function that handles the callback from LDAP
+ * when responses are received.
+ */
+struct cbinfo {
+	const char * const *attrs;
+	int nattrs;
+	directory_entry_rpc *entry;
+	const char *domain;
+};
+
+static void directory_provider_ad_cb(LDAP *ld, LDAPMessage **ldapres, int rc,
+    int qid, void *argp);
+static void directory_provider_ad_cb1(LDAP *ld, LDAPMessage *msg,
+    struct cbinfo *cbinfo);
+static directory_error_t bv_list_dav(directory_values_rpc *lvals,
+    struct berval **bv);
+static directory_error_t directory_provider_ad_lookup(
+    directory_entry_rpc *pent, const char * const * attrs, int nattrs,
+    const char *domain, const char *filter);
+static directory_error_t get_domain(LDAP *ld, LDAPMessage *ldapres,
+    char **domain);
+static directory_error_t directory_provider_ad_utils_error(char *func, int rc);
+
+#if	defined(DUMP_VALUES)
+static void dump_bv_list(const char *attr, struct berval **bv);
+#endif
+
+#define	MAX_EXTRA_ATTRS	1	/* sAMAccountName */
+
+/*
+ * Add an entry to a NULL-terminated list, if it's not already there.
+ * Assumes that the list has been allocated large enough for all additions,
+ * and prefilled with NULL.
+ */
+static
+void
+maybe_add_to_list(const char **list, const char *s)
+{
+	for (; *list != NULL; list++) {
+		if (strcaseeq(*list, s))
+			return;
+	}
+	*list = s;
+}
+
+/*
+ * Copy a counted attribute list to a NULL-terminated one.
+ * In the process, examine the requested attributes and augment
+ * the list as required to support any synthesized attributes
+ * requested.
+ */
+static
+const char **
+copy_and_augment_attr_list(char **req_list, int req_list_len)
+{
+	const char **new_list;
+	int i;
+
+	new_list =
+	    calloc(req_list_len + MAX_EXTRA_ATTRS + 1, sizeof (*new_list));
+	if (new_list == NULL)
+		return (NULL);
+
+	(void) memcpy(new_list, req_list, req_list_len * sizeof (char *));
+
+	for (i = 0; i < req_list_len; i++) {
+		const char *a = req_list[i];
+		/*
+		 * Note that you must update MAX_EXTRA_ATTRS above if you
+		 * add to this list.
+		 */
+		if (strcaseeq(a, "x-sun-canonicalName")) {
+			maybe_add_to_list(new_list, "sAMAccountName");
+			continue;
+		}
+		/* None needed for x-sun-provider */
+	}
+
+	return (new_list);
+}
+
+/*
+ * Retrieve information by name.
+ * Called indirectly through the Directory_provider_static structure.
+ */
+static
+directory_error_t
+directory_provider_ad_get(
+    directory_entry_rpc *del,
+    idmap_utf8str_list *ids,
+    char *types,
+    idmap_utf8str_list *attrs)
+{
+	int i;
+	const char **attrs2;
+	directory_error_t de = NULL;
+
+	/*
+	 * If we don't have any AD servers handy, we can't find anything.
+	 */
+	if (_idmapdstate.num_ads < 1) {
+		return (NULL);
+	}
+
+	RDLOCK_CONFIG()
+
+	/* 6835280 spurious lint error if the strlen is in the declaration */
+	int len = strlen(_idmapdstate.cfg->pgcfg.default_domain);
+	char default_domain[len + 1];
+	(void) strcpy(default_domain, _idmapdstate.cfg->pgcfg.default_domain);
+
+	UNLOCK_CONFIG();
+
+	/*
+	 * Turn our counted-array argument into a NULL-terminated array.
+	 * At the same time, add in any attributes that we need to support
+	 * any requested synthesized attributes.
+	 */
+	attrs2 = copy_and_augment_attr_list(attrs->idmap_utf8str_list_val,
+	    attrs->idmap_utf8str_list_len);
+	if (attrs2 == NULL)
+		goto nomem;
+
+	for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
+		char *vw[3];
+		int type;
+
+		/*
+		 * Extract the type for this particular ID.
+		 * Advance to the next type, if it's there, else keep
+		 * using this type until we run out of IDs.
+		 */
+		type = *types;
+		if (*(types+1) != '\0')
+			types++;
+
+		/*
+		 * If this entry has already been handled, one way or another,
+		 * skip it.
+		 */
+		if (del[i].status != DIRECTORY_NOT_FOUND)
+			continue;
+
+		char *id = ids->idmap_utf8str_list_val[i];
+
+		/*
+		 * Allow for expanding every character to \xx, plus some
+		 * space for the query syntax.
+		 */
+		int id_len = strlen(id);
+		char filter[1000 + id_len*3];
+
+		if (type == DIRECTORY_ID_SID[0]) {
+			/*
+			 * Mildly surprisingly, AD appears to allow searching
+			 * based on text SIDs.  Must be a special case on the
+			 * server end.
+			 */
+			ldap_build_filter(filter, sizeof (filter),
+			    "(objectSid=%v)", NULL, NULL, NULL, id, NULL);
+
+			de = directory_provider_ad_lookup(&del[i], attrs2,
+			    attrs->idmap_utf8str_list_len, NULL, filter);
+			if (de != NULL) {
+				directory_entry_set_error(&del[i], de);
+				de = NULL;
+			}
+		} else {
+			int id_len = strlen(id);
+			char name[id_len + 1];
+			char domain[id_len + 1];
+
+			split_name(name, domain, id);
+
+			vw[0] = name;
+
+			if (streq(domain, "")) {
+				vw[1] = default_domain;
+			} else {
+				vw[1] = domain;
+			}
+
+			if (type == DIRECTORY_ID_USER[0])
+				vw[2] = "user";
+			else if (type == DIRECTORY_ID_GROUP[0])
+				vw[2] = "group";
+			else
+				vw[2] = "*";
+
+			/*
+			 * Try samAccountName.
+			 * Note that here we rely on checking the returned
+			 * distinguishedName to make sure that we found an
+			 * entry from the right domain, because there's no
+			 * attribute we can straightforwardly filter for to
+			 * match domain.
+			 *
+			 * Eventually we should perhaps also try
+			 * userPrincipalName.
+			 */
+			ldap_build_filter(filter, sizeof (filter),
+			    "(&(samAccountName=%v1)(objectClass=%v3))",
+			    NULL, NULL, NULL, NULL, vw);
+
+			de = directory_provider_ad_lookup(&del[i], attrs2,
+			    attrs->idmap_utf8str_list_len, vw[1], filter);
+			if (de != NULL) {
+				directory_entry_set_error(&del[i], de);
+				de = NULL;
+			}
+		}
+	}
+
+	de = NULL;
+
+	goto out;
+
+nomem:
+	de = directory_error("ENOMEM.AD",
+	    "Out of memory during AD lookup", NULL);
+out:
+	free(attrs2);
+	return (de);
+}
+
+/*
+ * Note that attrs is NULL terminated, and that nattrs is the number
+ * of attributes requested by the user... which might be fewer than are
+ * in attrs because of attributes that we need for our own processing.
+ */
+static
+directory_error_t
+directory_provider_ad_lookup(
+    directory_entry_rpc *pent,
+    const char * const * attrs,
+    int nattrs,
+    const char *domain,
+    const char *filter)
+{
+	adutils_ad_t *ad;
+	adutils_rc batchrc;
+	struct cbinfo cbinfo;
+	adutils_query_state_t *qs;
+	int rc;
+
+	/*
+	 * NEEDSWORK:  Should eventually handle other forests.
+	 * NEEDSWORK:  Should eventually handle non-GC attributes.
+	 */
+	ad = _idmapdstate.ads[0];
+
+	/* Stash away information for the callback function. */
+	cbinfo.attrs = attrs;
+	cbinfo.nattrs = nattrs;
+	cbinfo.entry = pent;
+	cbinfo.domain = domain;
+
+	rc = adutils_lookup_batch_start(ad, 1, directory_provider_ad_cb,
+	    &cbinfo, &qs);
+	if (rc != ADUTILS_SUCCESS) {
+		return (directory_provider_ad_utils_error(
+		    "adutils_lookup_batch_start", rc));
+	}
+
+	rc = adutils_lookup_batch_add(qs, filter, attrs, domain,
+	    NULL, &batchrc);
+	if (rc != ADUTILS_SUCCESS) {
+		adutils_lookup_batch_release(&qs);
+		return (directory_provider_ad_utils_error(
+		    "adutils_lookup_batch_add", rc));
+	}
+
+	rc = adutils_lookup_batch_end(&qs);
+	if (rc != ADUTILS_SUCCESS) {
+		return (directory_provider_ad_utils_error(
+		    "adutils_lookup_batch_end", rc));
+	}
+
+	if (batchrc != ADUTILS_SUCCESS) {
+		/*
+		 * NEEDSWORK:  We're consistently getting -9997 here.
+		 * What does it mean?
+		 */
+		return (NULL);
+	}
+
+	return (NULL);
+}
+
+/*
+ * Callback from the LDAP functions when they get responses.
+ * We don't really need (nor want) asynchronous handling, but it's
+ * what libadutils gives us.
+ */
+static
+void
+directory_provider_ad_cb(
+    LDAP *ld,
+    LDAPMessage **ldapres,
+    int rc,
+    int qid,
+    void *argp)
+{
+	NOTE(ARGUNUSED(rc, qid))
+	struct cbinfo *cbinfo = (struct cbinfo *)argp;
+	LDAPMessage *msg = *ldapres;
+
+	for (msg = ldap_first_entry(ld, msg);
+	    msg != NULL;
+	    msg = ldap_next_entry(ld, msg)) {
+		directory_provider_ad_cb1(ld, msg, cbinfo);
+	}
+}
+
+/*
+ * Process a single entry returned by an LDAP callback.
+ * Note that this performs a function roughly equivalent to the
+ * directory*Populate() functions in the other providers.
+ * Given an LDAP response, populate the directory entry for return to
+ * the caller.  This one differs primarily in that we're working directly
+ * with LDAP, so we don't have to do any attribute translation.
+ */
+static
+void
+directory_provider_ad_cb1(
+    LDAP *ld,
+    LDAPMessage *msg,
+    struct cbinfo *cbinfo)
+{
+	int nattrs = cbinfo->nattrs;
+	const char * const *attrs = cbinfo->attrs;
+	directory_entry_rpc *pent = cbinfo->entry;
+
+	int i;
+	directory_values_rpc *llvals;
+	directory_error_t de;
+	char *domain = NULL;
+
+	/*
+	 * We don't have a way to filter for entries from the right domain
+	 * in the LDAP query, so we check for it here.  Searches based on
+	 * samAccountName might yield results from the wrong domain.
+	 */
+	de = get_domain(ld, msg, &domain);
+	if (de != NULL)
+		goto err;
+
+	if (cbinfo->domain != NULL && !domain_eq(cbinfo->domain, domain))
+		goto out;
+
+	/*
+	 * If we've already found a match, error.
+	 */
+	if (pent->status != DIRECTORY_NOT_FOUND) {
+		de = directory_error("Duplicate.AD",
+		    "Multiple matching entries found", NULL);
+		goto err;
+	}
+
+	llvals = calloc(nattrs, sizeof (directory_values_rpc));
+	if (llvals == NULL)
+		goto nomem;
+
+	pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
+	pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
+	pent->status = DIRECTORY_FOUND;
+
+	for (i = 0; i < nattrs; i++) {
+		struct berval **bv;
+		const char *a = attrs[i];
+		directory_values_rpc *val = &llvals[i];
+
+		bv = ldap_get_values_len(ld, msg, a);
+#if	defined(DUMP_VALUES)
+		dump_bv_list(attrs[i], bv);
+#endif
+		if (bv != NULL) {
+			de = bv_list_dav(val, bv);
+			ldap_value_free_len(bv);
+			if (de != NULL)
+				goto err;
+		} else if (strcaseeq(a, "x-sun-canonicalName")) {
+			bv = ldap_get_values_len(ld, msg, "sAMAccountName");
+			if (bv != NULL) {
+				int n = ldap_count_values_len(bv);
+				if (n > 0) {
+					char *tmp;
+					(void) asprintf(&tmp, "%.*s@%s",
+					    bv[0]->bv_len, bv[0]->bv_val,
+					    domain);
+					if (tmp == NULL)
+						goto nomem;
+					const char *ctmp = tmp;
+					de = str_list_dav(val, &ctmp, 1);
+					free(tmp);
+					if (de != NULL)
+						goto err;
+				}
+			}
+		} else if (strcaseeq(a, "x-sun-provider")) {
+			const char *provider = "LDAP-AD";
+			de = str_list_dav(val, &provider, 1);
+		}
+	}
+
+	goto out;
+
+nomem:
+	de = directory_error("ENOMEM.users",
+	    "No memory allocating return value for user lookup", NULL);
+
+err:
+	directory_entry_set_error(pent, de);
+	de = NULL;
+
+out:
+	free(domain);
+}
+
+/*
+ * Given a struct berval, populate a directory attribute value (which is a
+ * list of values).
+ * Note that here we populate the DAV with the exact bytes that LDAP returns.
+ * Back over in the client it appends a \0 so that strings are null
+ * terminated.
+ */
+static
+directory_error_t
+bv_list_dav(directory_values_rpc *lvals, struct berval **bv)
+{
+	directory_value_rpc *dav;
+	int n;
+	int i;
+
+	n = ldap_count_values_len(bv);
+
+	dav = calloc(n, sizeof (directory_value_rpc));
+	if (dav == NULL)
+		goto nomem;
+
+	lvals->directory_values_rpc_u.values.values_val = dav;
+	lvals->directory_values_rpc_u.values.values_len = n;
+	lvals->found = TRUE;
+
+	for (i = 0; i < n; i++) {
+		dav[i].directory_value_rpc_val =
+		    memdup(bv[i]->bv_val, bv[i]->bv_len);
+		if (dav[i].directory_value_rpc_val == NULL)
+			goto nomem;
+		dav[i].directory_value_rpc_len = bv[i]->bv_len;
+	}
+
+	return (NULL);
+
+nomem:
+	return (directory_error("ENOMEM.bv_list_dav",
+	    "Insufficient memory copying values"));
+}
+
+#if	defined(DUMP_VALUES)
+static
+void
+dump_bv_list(const char *attr, struct berval **bv)
+{
+	int i;
+
+	if (bv == NULL) {
+		(void) fprintf(stderr, "%s:  (empty)\n", attr);
+		return;
+	}
+	for (i = 0; bv[i] != NULL; i++) {
+		(void) fprintf(stderr, "%s[%d] =\n", attr, i);
+		dump(stderr, "    ", bv[i]->bv_val, bv[i]->bv_len);
+	}
+}
+#endif	/* DUMP_VALUES */
+
+/*
+ * Return the domain associated with the specified entry.
+ */
+static
+directory_error_t
+get_domain(
+    LDAP *ld,
+    LDAPMessage *msg,
+    char **domain)
+{
+	*domain = NULL;
+
+	char *dn = ldap_get_dn(ld, msg);
+	if (dn == NULL) {
+		char buf[100];	/* big enough for any int */
+		char *m;
+		char *s;
+		int err = ldap_get_lderrno(ld, &m, &s);
+		(void) snprintf(buf, sizeof (buf), "%d", err);
+
+		return directory_error("AD.get_domain.ldap_get_dn",
+		    "ldap_get_dn: %1 (%2)\n"
+		    "matched: %3\n"
+		    "error:   %4",
+		    ldap_err2string(err), buf,
+		    m == NULL ? "(null)" : m,
+		    s == NULL ? "(null)" : s,
+		    NULL);
+	}
+
+	*domain = adutils_dn2dns(dn);
+	if (*domain == NULL) {
+		directory_error_t de;
+
+		de = directory_error("Unknown.get_domain.adutils_dn2dns",
+		    "get_domain:  Unexpected error from adutils_dn2dns(%1)",
+		    dn, NULL);
+		free(dn);
+		return (de);
+	}
+	free(dn);
+
+	return (NULL);
+}
+
+/*
+ * Given an error report from libadutils, generate a directory_error_t.
+ */
+static
+directory_error_t
+directory_provider_ad_utils_error(char *func, int rc)
+{
+	char rcstr[100];	/* plenty for any int */
+	char code[100];		/* plenty for any int */
+	(void) snprintf(rcstr, sizeof (rcstr), "%d", rc);
+	(void) snprintf(code, sizeof (code), "ADUTILS.%d", rc);
+
+	return (directory_error(code,
+	    "Error %2 from adutils function %1", func, rcstr, NULL));
+}
+
+struct directory_provider_static directory_provider_ad = {
+	"AD",
+	directory_provider_ad_get,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/idmap/idmapd/directory_provider_builtin.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,319 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Retrieve directory information for built-in users and groups
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <sys/idmap.h>
+#include <sys/param.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <note.h>
+#include "idmapd.h"
+#include "directory.h"
+#include "directory_private.h"
+#include <rpcsvc/idmap_prot.h>
+#include "directory_server_impl.h"
+#include "miscutils.h"
+#include "sidutil.h"
+
+static directory_error_t sid_dav(directory_values_rpc *lvals,
+    const wksids_table_t *wksid);
+static directory_error_t directory_provider_builtin_populate(
+    directory_entry_rpc *pent, const wksids_table_t *wksid,
+    idmap_utf8str_list *attrs);
+
+/*
+ * Retrieve information by name.
+ * Called indirectly through the directory_provider_static structure.
+ */
+static
+directory_error_t
+directory_provider_builtin_get(
+    directory_entry_rpc *del,
+    idmap_utf8str_list *ids,
+    idmap_utf8str types,
+    idmap_utf8str_list *attrs)
+{
+	int i;
+
+	for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
+		const wksids_table_t *wksid;
+		directory_error_t de;
+		int type;
+
+		/*
+		 * Extract the type for this particular ID.
+		 * Advance to the next type, if it's there, else keep
+		 * using this type until we run out of IDs.
+		 */
+		type = *types;
+		if (*(types+1) != '\0')
+			types++;
+
+		/*
+		 * If this entry has already been handled, one way or another,
+		 * skip it.
+		 */
+		if (del[i].status != DIRECTORY_NOT_FOUND)
+			continue;
+
+		char *id = ids->idmap_utf8str_list_val[i];
+
+		/*
+		 * End-to-end error injection point.
+		 * NEEDSWORK:  should probably eliminate this for production
+		 */
+		if (streq(id, " DEBUG BUILTIN ERROR ")) {
+			directory_entry_set_error(&del[i],
+			    directory_error("Directory_provider_builtin.debug",
+			    "Directory_provider_builtin:  artificial error",
+			    NULL));
+			continue;
+		}
+
+		if (type == DIRECTORY_ID_SID[0])
+			wksid = find_wk_by_sid(id);
+		else {
+			int idmap_id_type;
+			if (type == DIRECTORY_ID_NAME[0])
+				idmap_id_type = IDMAP_POSIXID;
+			else if (type == DIRECTORY_ID_USER[0])
+				idmap_id_type = IDMAP_UID;
+			else if (type == DIRECTORY_ID_GROUP[0])
+				idmap_id_type = IDMAP_GID;
+			else {
+				directory_entry_set_error(&del[i],
+				    directory_error("invalid_arg.id_type",
+				    "Invalid ID type \"%1\"",
+				    types, NULL));
+				continue;
+			}
+
+			int id_len = strlen(id);
+			char name[id_len + 1];
+			char domain[id_len + 1];
+
+			split_name(name, domain, id);
+
+			wksid = find_wksid_by_name(name, domain, idmap_id_type);
+		}
+
+		if (wksid == NULL)
+			continue;
+
+		de = directory_provider_builtin_populate(&del[i], wksid, attrs);
+		if (de != NULL) {
+			directory_entry_set_error(&del[i], de);
+			de = NULL;
+		}
+	}
+
+	return (NULL);
+}
+
+/*
+ * Given a well-known name entry and a list of attributes that were
+ * requested, populate the structure to return to the caller.
+ */
+static
+directory_error_t
+directory_provider_builtin_populate(
+    directory_entry_rpc *pent,
+    const wksids_table_t *wksid,
+    idmap_utf8str_list *attrs)
+{
+	int j;
+	directory_values_rpc *llvals;
+	int nattrs;
+
+	nattrs = attrs->idmap_utf8str_list_len;
+
+	llvals = calloc(nattrs, sizeof (directory_values_rpc));
+	if (llvals == NULL)
+		goto nomem;
+
+	pent->status = DIRECTORY_FOUND;
+	pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
+	pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
+
+	for (j = 0; j < nattrs; j++) {
+		directory_values_rpc *val;
+		char *a;
+		directory_error_t de;
+
+		/*
+		 * We're going to refer to these a lot, so make a shorthand
+		 * copy.
+		 */
+		a = attrs->idmap_utf8str_list_val[j];
+		val = &llvals[j];
+
+		/*
+		 * Start by assuming no errors and that we don't have
+		 * the information.
+		 */
+		val->found = FALSE;
+		de = NULL;
+
+		if (strcaseeq(a, "uid")) {
+			de = str_list_dav(val, &wksid->winname, 1);
+		} else if (strcaseeq(a, "uidNumber")) {
+			if (wksid->pid != SENTINEL_PID && wksid->is_user) {
+				de = uint_list_dav(val, &wksid->pid, 1);
+			}
+		} else if (strcaseeq(a, "gidNumber")) {
+			if (wksid->pid != SENTINEL_PID && !wksid->is_user) {
+				de = uint_list_dav(val, &wksid->pid, 1);
+			}
+		} else if (strcaseeq(a, "displayName") || strcaseeq(a, "cn")) {
+			de = str_list_dav(val, &wksid->winname, 1);
+		} else if (strcaseeq(a, "distinguishedName")) {
+			char *container;
+			if (wksid->domain == NULL) {
+				container = "Users";
+			} else {
+				container = "Builtin";
+			}
+			RDLOCK_CONFIG();
+			char *dn;
+			(void) asprintf(&dn,
+			    "CN=%s,CN=%s,DC=%s",
+			    wksid->winname, container, _idmapdstate.hostname);
+			UNLOCK_CONFIG();
+			const char *cdn = dn;
+			de = str_list_dav(val, &cdn, 1);
+			free(dn);
+		} else if (strcaseeq(a, "objectClass")) {
+			if (wksid->is_wuser) {
+				static const char *objectClasses[] = {
+					"top",
+					"person",
+					"organizationalPerson",
+					"user",
+				};
+				de = str_list_dav(val, objectClasses,
+				    NELEM(objectClasses));
+			} else {
+				static const char *objectClasses[] = {
+					"top",
+					"group",
+				};
+				de = str_list_dav(val, objectClasses,
+				    NELEM(objectClasses));
+			}
+		} else if (strcaseeq(a, "objectSid")) {
+			de = sid_dav(val, wksid);
+		} else if (strcaseeq(a, "x-sun-canonicalName")) {
+			char *canon;
+
+			if (wksid->domain == NULL) {
+				RDLOCK_CONFIG();
+				(void) asprintf(&canon, "%s@%s",
+				    wksid->winname, _idmapdstate.hostname);
+				UNLOCK_CONFIG();
+			} else if (streq(wksid->domain, "")) {
+				canon = strdup(wksid->winname);
+			} else {
+				(void) asprintf(&canon, "%s@%s",
+				    wksid->winname, wksid->domain);
+			}
+
+			if (canon == NULL)
+				goto nomem;
+			const char *ccanon = canon;
+			de = str_list_dav(val, &ccanon, 1);
+			free(canon);
+		} else if (strcaseeq(a, "x-sun-provider")) {
+			const char *provider = "Builtin";
+			de = str_list_dav(val, &provider, 1);
+		}
+		if (de != NULL)
+			return (de);
+	}
+
+	return (NULL);
+
+nomem:
+	return (directory_error("ENOMEM.users",
+	    "No memory allocating return value for user lookup", NULL));
+}
+
+/*
+ * Given a well-known name structure, generate a binary-format SID.
+ * It's a bit perverse that we must take a text-format SID and turn it into
+ * a binary-format SID, only to have the caller probably turn it back into
+ * text format, but SIDs are carried across LDAP in binary format.
+ */
+static
+directory_error_t
+sid_dav(directory_values_rpc *lvals, const wksids_table_t *wksid)
+{
+	char *text_sid;
+	sid_t *sid;
+	directory_error_t de;
+
+	if (wksid->sidprefix == NULL) {
+		RDLOCK_CONFIG();
+		(void) asprintf(&text_sid, "%s-%d",
+		    _idmapdstate.cfg->pgcfg.machine_sid,
+		    wksid->rid);
+		UNLOCK_CONFIG();
+	} else {
+		(void) asprintf(&text_sid, "%s-%d",
+		    wksid->sidprefix, wksid->rid);
+	}
+
+	if (text_sid == NULL)
+		goto nomem;
+
+	sid = sid_fromstr(text_sid);
+	free(text_sid);
+
+	if (sid == NULL)
+		goto nomem;
+
+	sid_to_le(sid);
+
+	de = bin_list_dav(lvals, sid, 1, sid_len(sid));
+
+	sid_free(sid);
+
+	return (de);
+
+nomem:
+	return (directory_error("ENOMEM.sid_dav",
+	    "No memory allocating SID for user lookup", NULL));
+}
+
+struct directory_provider_static directory_provider_builtin = {
+	"builtin",
+	directory_provider_builtin_get,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/idmap/idmapd/directory_provider_nsswitch.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,472 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Retrieve directory information for standard UNIX users/groups.
+ * (NB:  not just from files, but all nsswitch sources.)
+ */
+
+#include <pwd.h>
+#include <grp.h>
+#include <malloc.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <note.h>
+#include <errno.h>
+#include "idmapd.h"
+#include "directory.h"
+#include "directory_private.h"
+#include <rpcsvc/idmap_prot.h>
+#include "directory_server_impl.h"
+#include "miscutils.h"
+#include "sidutil.h"
+
+static directory_error_t machine_sid_dav(directory_values_rpc *lvals,
+    unsigned int rid);
+static directory_error_t directory_provider_nsswitch_populate(
+    directory_entry_rpc *pent, struct passwd *pwd, struct group *grp,
+    idmap_utf8str_list *attrs);
+
+/*
+ * Retrieve information by name.
+ * Called indirectly through the directory_provider_static structure.
+ */
+static
+directory_error_t
+directory_provider_nsswitch_get(
+    directory_entry_rpc *del,
+    idmap_utf8str_list *ids,
+    idmap_utf8str types,
+    idmap_utf8str_list *attrs)
+{
+	int i;
+
+	RDLOCK_CONFIG();
+
+	/* 6835280 spurious lint error if the strlen is in the declaration */
+	int host_name_len = strlen(_idmapdstate.hostname);
+	char my_host_name[host_name_len + 1];
+	(void) strcpy(my_host_name, _idmapdstate.hostname);
+
+	/* We use len later, so this is not merely a workaround for 6835280 */
+	int machine_sid_len = strlen(_idmapdstate.cfg->pgcfg.machine_sid);
+	char my_machine_sid[machine_sid_len + 1];
+	(void) strcpy(my_machine_sid, _idmapdstate.cfg->pgcfg.machine_sid);
+
+	UNLOCK_CONFIG();
+
+	for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
+		struct passwd *pwd = NULL;
+		struct group *grp = NULL;
+		directory_error_t de;
+		int type;
+
+		/*
+		 * Extract the type for this particular ID.
+		 * Advance to the next type, if it's there, else keep
+		 * using this type until we run out of IDs.
+		 */
+		type = *types;
+		if (*(types+1) != '\0')
+			types++;
+
+		/*
+		 * If this entry has already been handled, one way or another,
+		 * skip it.
+		 */
+		if (del[i].status != DIRECTORY_NOT_FOUND)
+			continue;
+
+		char *id = ids->idmap_utf8str_list_val[i];
+
+		if (type == DIRECTORY_ID_SID[0]) {
+			/*
+			 * Is it our SID?
+			 * Check whether the first part matches, then a "-",
+			 * then a single RID.
+			 */
+			if (strncasecmp(id, my_machine_sid, machine_sid_len) !=
+			    0)
+				continue;
+			if (id[machine_sid_len] != '-')
+				continue;
+			char *p;
+			uint32_t rid =
+			    strtoul(id + machine_sid_len + 1, &p, 10);
+			if (*p != '\0')
+				continue;
+
+			if (rid < LOCALRID_UID_MIN) {
+				/* Builtin, not handled here */
+				continue;
+			}
+
+			if (rid <= LOCALRID_UID_MAX) {
+				/* User */
+				errno = 0;
+				pwd = getpwuid(rid - LOCALRID_UID_MIN);
+				if (pwd == NULL) {
+					if (errno == 0)		/* Not found */
+						continue;
+					char buf[40];
+					int err = errno;
+					(void) snprintf(buf, sizeof (buf),
+					    "%d", err);
+					directory_entry_set_error(&del[i],
+					    directory_error("errno.getpwuid",
+					    "getpwuid: %2 (%1)",
+					    buf, strerror(err), NULL));
+					continue;
+				}
+			} else if (rid >= LOCALRID_GID_MIN &&
+			    rid <= LOCALRID_GID_MAX) {
+				/* Group */
+				errno = 0;
+				grp = getgrgid(rid - LOCALRID_GID_MIN);
+				if (grp == NULL) {
+					if (errno == 0)		/* Not found */
+						continue;
+					char buf[40];
+					int err = errno;
+					(void) snprintf(buf, sizeof (buf),
+					    "%d", err);
+					directory_entry_set_error(&del[i],
+					    directory_error("errno.getgrgid",
+					    "getgrgid: %2 (%1)",
+					    buf, strerror(err), NULL));
+					continue;
+				}
+			} else
+				continue;
+
+		} else {
+			int id_len = strlen(id);
+			char name[id_len + 1];
+			char domain[id_len + 1];
+
+			split_name(name, domain, id);
+
+			if (domain[0] != '\0') {
+				if (!domain_eq(domain, my_host_name))
+					continue;
+			}
+
+			/*
+			 * If the caller has requested user or group
+			 * information specifically, we only set one of
+			 * pwd or grp.
+			 * If the caller has requested either type, we try
+			 * both in the hopes of getting one.
+			 * Note that directory_provider_nsswitch_populate
+			 * considers it to be an error if both are set.
+			 */
+			if (type != DIRECTORY_ID_GROUP[0]) {
+				/* prep for not found / error case */
+				errno = 0;
+
+				pwd = getpwnam(name);
+				if (pwd == NULL && errno != 0) {
+					char buf[40];
+					int err = errno;
+					(void) snprintf(buf, sizeof (buf),
+					    "%d", err);
+					directory_entry_set_error(&del[i],
+					    directory_error("errno.getpwnam",
+					    "getpwnam: %2 (%1)",
+					    buf, strerror(err), NULL));
+					continue;
+				}
+			}
+
+			if (type != DIRECTORY_ID_USER[0]) {
+				/* prep for not found / error case */
+				errno = 0;
+
+				grp = getgrnam(name);
+				if (grp == NULL && errno != 0) {
+					char buf[40];
+					int err = errno;
+					(void) snprintf(buf, sizeof (buf),
+					    "%d", err);
+					directory_entry_set_error(&del[i],
+					    directory_error("errno.getgrnam",
+					    "getgrnam: %2 (%1)",
+					    buf, strerror(err), NULL));
+					continue;
+				}
+			}
+		}
+
+		/*
+		 * Didn't find it, don't populate the structure.
+		 * Another provider might populate it.
+		 */
+		if (pwd == NULL && grp == NULL)
+			continue;
+
+		de = directory_provider_nsswitch_populate(&del[i], pwd, grp,
+		    attrs);
+		if (de != NULL) {
+			directory_entry_set_error(&del[i], de);
+			de = NULL;
+			continue;
+		}
+	}
+
+	return (NULL);
+}
+
+/*
+ * Given a pwd structure or a grp structure, and a list of attributes that
+ * were requested, populate the structure to return to the caller.
+ */
+static
+directory_error_t
+directory_provider_nsswitch_populate(
+    directory_entry_rpc *pent,
+    struct passwd *pwd,
+    struct group *grp,
+    idmap_utf8str_list *attrs)
+{
+	int j;
+	directory_values_rpc *llvals;
+	int nattrs;
+
+	/*
+	 * If it wasn't for this case, everything would be a lot simpler.
+	 * UNIX allows users and groups with the same name.  Windows doesn't.
+	 */
+	if (pwd != NULL && grp != NULL) {
+		return directory_error("Ambiguous.Name",
+		    "Ambiguous name, is both a user and a group",
+		    NULL);
+	}
+
+	nattrs = attrs->idmap_utf8str_list_len;
+
+	llvals = calloc(nattrs, sizeof (directory_values_rpc));
+	if (llvals == NULL)
+		goto nomem;
+
+	pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
+	pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
+	pent->status = DIRECTORY_FOUND;
+
+	for (j = 0; j < nattrs; j++) {
+		directory_values_rpc *val;
+		char *a;
+		directory_error_t de;
+
+		/*
+		 * We're going to refer to these a lot, so make a shorthand
+		 * copy.
+		 */
+		a = attrs->idmap_utf8str_list_val[j];
+		val = &llvals[j];
+
+		/*
+		 * Start by assuming no errors and that we don't have
+		 * the information
+		 */
+		val->found = FALSE;
+		de = NULL;
+
+		if (pwd != NULL) {
+			/*
+			 * Handle attributes for user entries.
+			 */
+			if (strcaseeq(a, "cn")) {
+				const char *p = pwd->pw_name;
+				de = str_list_dav(val, &p, 1);
+			} else if (strcaseeq(a, "objectClass")) {
+				static const char *objectClasses[] = {
+					"top",
+					"posixAccount",
+				};
+				de = str_list_dav(val, objectClasses,
+				    NELEM(objectClasses));
+			} else if (strcaseeq(a, "gidNumber")) {
+				de = uint_list_dav(val, &pwd->pw_gid, 1);
+			} else if (strcaseeq(a, "objectSid")) {
+				de = machine_sid_dav(val,
+				    pwd->pw_uid + LOCALRID_UID_MIN);
+			} else if (strcaseeq(a, "displayName")) {
+				const char *p = pwd->pw_gecos;
+				de = str_list_dav(val, &p, 1);
+			} else if (strcaseeq(a, "distinguishedName")) {
+				char *dn;
+				RDLOCK_CONFIG();
+				(void) asprintf(&dn,
+				    "uid=%s,ou=people,dc=%s",
+				    pwd->pw_name, _idmapdstate.hostname);
+				UNLOCK_CONFIG();
+				if (dn == NULL)
+					goto nomem;
+				const char *cdn = dn;
+				de = str_list_dav(val, &cdn, 1);
+				free(dn);
+			} else if (strcaseeq(a, "uid")) {
+				const char *p = pwd->pw_name;
+				de = str_list_dav(val, &p, 1);
+			} else if (strcaseeq(a, "uidNumber")) {
+				de = uint_list_dav(val, &pwd->pw_uid, 1);
+			} else if (strcaseeq(a, "gecos")) {
+				const char *p = pwd->pw_gecos;
+				de = str_list_dav(val, &p, 1);
+			} else if (strcaseeq(a, "homeDirectory")) {
+				const char *p = pwd->pw_dir;
+				de = str_list_dav(val, &p, 1);
+			} else if (strcaseeq(a, "loginShell")) {
+				const char *p = pwd->pw_shell;
+				de = str_list_dav(val, &p, 1);
+			} else if (strcaseeq(a, "x-sun-canonicalName")) {
+				char *canon;
+				RDLOCK_CONFIG();
+				(void) asprintf(&canon, "%s@%s",
+				    pwd->pw_name, _idmapdstate.hostname);
+				UNLOCK_CONFIG();
+				if (canon == NULL)
+					goto nomem;
+				const char *ccanon = canon;
+				de = str_list_dav(val, &ccanon, 1);
+				free(canon);
+			} else if (strcaseeq(a, "x-sun-provider")) {
+				const char *provider = "UNIX-passwd";
+				de = str_list_dav(val, &provider, 1);
+			}
+		} else if (grp != NULL)  {
+			/*
+			 * Handle attributes for group entries.
+			 */
+			if (strcaseeq(a, "cn")) {
+				const char *p = grp->gr_name;
+				de = str_list_dav(val, &p, 1);
+			} else if (strcaseeq(a, "objectClass")) {
+				static const char *objectClasses[] = {
+					"top",
+					"posixGroup",
+				};
+				de = str_list_dav(val, objectClasses,
+				    NELEM(objectClasses));
+			} else if (strcaseeq(a, "gidNumber")) {
+				de = uint_list_dav(val, &grp->gr_gid, 1);
+			} else if (strcaseeq(a, "objectSid")) {
+				de = machine_sid_dav(val,
+				    grp->gr_gid + LOCALRID_GID_MIN);
+			} else if (strcaseeq(a, "displayName")) {
+				const char *p = grp->gr_name;
+				de = str_list_dav(val, &p, 1);
+			} else if (strcaseeq(a, "distinguishedName")) {
+				char *dn;
+				RDLOCK_CONFIG();
+				(void) asprintf(&dn,
+				    "cn=%s,ou=group,dc=%s",
+				    grp->gr_name, _idmapdstate.hostname);
+				UNLOCK_CONFIG();
+				if (dn == NULL)
+					goto nomem;
+				const char *cdn = dn;
+				de = str_list_dav(val, &cdn, 1);
+				free(dn);
+			} else if (strcaseeq(a, "memberUid")) {
+				/*
+				 * NEEDSWORK:  There is probably a non-cast
+				 * way to do this, but I don't immediately
+				 * see it.
+				 */
+				const char * const *members =
+				    (const char * const *)grp->gr_mem;
+				de = str_list_dav(val, members, 0);
+			} else if (strcaseeq(a, "x-sun-canonicalName")) {
+				char *canon;
+				RDLOCK_CONFIG();
+				(void) asprintf(&canon, "%s@%s",
+				    grp->gr_name, _idmapdstate.hostname);
+				UNLOCK_CONFIG();
+				if (canon == NULL)
+					goto nomem;
+				const char *ccanon = canon;
+				de = str_list_dav(val, &ccanon, 1);
+				free(canon);
+			} else if (strcaseeq(a, "x-sun-provider")) {
+				const char *provider = "UNIX-group";
+				de = str_list_dav(val, &provider, 1);
+			}
+		}
+
+		if (de != NULL)
+			return (de);
+	}
+
+	return (NULL);
+
+nomem:
+	return (directory_error("ENOMEM.users",
+	    "No memory allocating return value for user lookup", NULL));
+}
+
+/*
+ * Populate a directory attribute value with a SID based on our machine SID
+ * and the specified RID.
+ *
+ * It's a bit perverse that we must take a text-format SID and turn it into
+ * a binary-format SID, only to have the caller probably turn it back into
+ * text format, but SIDs are carried across LDAP in binary format.
+ */
+static
+directory_error_t
+machine_sid_dav(directory_values_rpc *lvals, unsigned int rid)
+{
+	sid_t *sid;
+	directory_error_t de;
+
+	RDLOCK_CONFIG();
+	int len = strlen(_idmapdstate.cfg->pgcfg.machine_sid);
+	char buf[len + 100];	/* 100 is enough space for any RID */
+	(void) snprintf(buf, sizeof (buf), "%s-%u",
+	    _idmapdstate.cfg->pgcfg.machine_sid, rid);
+	UNLOCK_CONFIG();
+
+	sid = sid_fromstr(buf);
+	if (sid == NULL)
+		goto nomem;
+
+	sid_to_le(sid);
+
+	de = bin_list_dav(lvals, sid, 1, sid_len(sid));
+	sid_free(sid);
+	return (de);
+
+nomem:
+	return (directory_error("ENOMEM.machine_sid_dav",
+	    "Out of memory allocating return value for lookup", NULL));
+}
+
+struct directory_provider_static directory_provider_nsswitch = {
+	"files",
+	directory_provider_nsswitch_get,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/idmap/idmapd/directory_server.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,292 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Server-side support for directory information lookup functions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include <note.h>
+#include "idmapd.h"
+#include "directory.h"
+#include "directory_private.h"
+#include <rpcsvc/idmap_prot.h>
+#include "directory_library_impl.h"
+#include "directory_server_impl.h"
+#include "sized_array.h"
+#include "miscutils.h"
+
+/*
+ * Here's a list of all of the modules that provide directory
+ * information.  In the fullness of time this should probably be
+ * a plugin-able switch mechanism.
+ * Note that the list is in precedence order.
+ */
+extern struct directory_provider_static directory_provider_builtin;
+extern struct directory_provider_static directory_provider_nsswitch;
+extern struct directory_provider_static directory_provider_ad;
+struct directory_provider_static *providers[] = {
+	&directory_provider_builtin,
+	&directory_provider_nsswitch,
+	&directory_provider_ad,
+};
+
+/*
+ * This is the entry point for all directory lookup service requests.
+ */
+bool_t
+directory_get_common_1_svc(
+    idmap_utf8str_list ids,
+    idmap_utf8str types,
+    idmap_utf8str_list attrs,
+    directory_results_rpc *result,
+    struct svc_req *req)
+{
+	NOTE(ARGUNUSED(req))
+	int nids;
+	directory_entry_rpc *entries;
+	directory_error_t de;
+	int i;
+
+	nids = ids.idmap_utf8str_list_len;
+
+	entries = (directory_entry_rpc *)
+	    calloc(nids, sizeof (directory_entry_rpc));
+	if (entries == NULL)
+		goto nomem;
+	result->directory_results_rpc_u.entries.entries_val = entries;
+	result->directory_results_rpc_u.entries.entries_len = nids;
+	result->failed = FALSE;
+
+	for (i = 0; i < nids; i++) {
+		if (strlen(ids.idmap_utf8str_list_val[i]) >
+		    IDMAP_MAX_NAME_LEN) {
+			directory_entry_set_error(&entries[i],
+			    directory_error("invalid_arg.id.too_long",
+			    "Identifier too long", NULL));
+		}
+	}
+
+	for (i = 0; i < NELEM(providers); i++) {
+		de = providers[i]->get(entries, &ids, types,
+		    &attrs);
+		if (de != NULL)
+			goto err;
+	}
+
+	return (TRUE);
+
+nomem:
+	de = directory_error("ENOMEM.get_common",
+	    "Insufficient memory retrieving directory data", NULL);
+
+err:
+	xdr_free(xdr_directory_results_rpc, (char *)result);
+	result->failed = TRUE;
+	return (
+	    directory_error_to_rpc(&result->directory_results_rpc_u.err, de));
+}
+
+/*
+ * Split name into {domain, name}.
+ * Suggest allocating name and domain on the stack, same size as id,
+ * using variable length arrays.
+ */
+void
+split_name(char *name, char *domain, char *id)
+{
+	char *p;
+
+	if ((p = strchr(id, '@')) != NULL) {
+		(void) strlcpy(name, id, p - id + 1);
+		(void) strcpy(domain, p + 1);
+	} else if ((p = strchr(id, '\\')) != NULL) {
+		(void) strcpy(name, p + 1);
+		(void) strlcpy(domain, id, p - id + 1);
+	} else {
+		(void) strcpy(name, id);
+		(void) strcpy(domain, "");
+	}
+}
+
+/*
+ * Given a list of strings, return a set of directory attribute values.
+ *
+ * Mark that the attribute was found.
+ *
+ * Note that the terminating \0 is *not* included in the result, because
+ * that's the way that strings come from LDAP.
+ * (Note also that the client side stuff adds in a terminating \0.)
+ *
+ * Note that on error the array may have been partially populated and will
+ * need to be cleaned up by the caller.  This is normally not a problem
+ * because the caller will need to clean up several such arrays.
+ */
+directory_error_t
+str_list_dav(directory_values_rpc *lvals, const char * const *str_list, int n)
+{
+	directory_value_rpc *dav;
+	int i;
+
+	if (n == 0) {
+		for (n = 0; str_list[n] != NULL; n++)
+			/* LOOP */;
+	}
+
+	dav = calloc(n, sizeof (directory_value_rpc));
+	if (dav == NULL)
+		goto nomem;
+
+	lvals->directory_values_rpc_u.values.values_val = dav;
+	lvals->directory_values_rpc_u.values.values_len = n;
+	lvals->found = TRUE;
+
+	for (i = 0; i < n; i++) {
+		int len;
+
+		len = strlen(str_list[i]);
+		dav[i].directory_value_rpc_val = memdup(str_list[i], len);
+		if (dav[i].directory_value_rpc_val == NULL)
+			goto nomem;
+		dav[i].directory_value_rpc_len = len;
+	}
+
+	return (NULL);
+
+nomem:
+	return (directory_error("ENOMEM.str_list_dav",
+	    "Insufficient memory copying values"));
+}
+
+/*
+ * Given a list of unsigned integers, return a set of string directory
+ * attribute values.
+ *
+ * Mark that the attribute was found.
+ *
+ * Note that the terminating \0 is *not* included in the result, because
+ * that's the way that strings come from LDAP.
+ * (Note also that the client side stuff adds in a terminating \0.)
+ *
+ * Note that on error the array may have been partially populated and will
+ * need to be cleaned up by the caller.  This is normally not a problem
+ * because the caller will need to clean up several such arrays.
+ */
+directory_error_t
+uint_list_dav(directory_values_rpc *lvals, const unsigned int *array, int n)
+{
+	directory_value_rpc *dav;
+	int i;
+
+	dav = calloc(n, sizeof (directory_value_rpc));
+	if (dav == NULL)
+		goto nomem;
+
+	lvals->directory_values_rpc_u.values.values_val = dav;
+	lvals->directory_values_rpc_u.values.values_len = n;
+	lvals->found = TRUE;
+
+	for (i = 0; i < n; i++) {
+		char buf[100];	/* larger than any integer */
+		int len;
+
+		(void) snprintf(buf, sizeof (buf), "%u", array[i]);
+
+		len = strlen(buf);
+		dav[i].directory_value_rpc_val = memdup(buf, len);
+		if (dav[i].directory_value_rpc_val == NULL)
+			goto nomem;
+		dav[i].directory_value_rpc_len = len;
+	}
+
+	return (NULL);
+
+nomem:
+	return (directory_error("ENOMEM.uint_list_dav",
+	    "Insufficient memory copying values"));
+}
+
+/*
+ * Given a list of fixed-length binary chunks, return a set of binary
+ * directory attribute values.
+ *
+ * Mark that the attribute was found.
+ *
+ * Note that on error the array may have been partially populated and will
+ * need to be cleaned up by the caller.  This is normally not a problem
+ * because the caller will need to clean up several such arrays.
+ */
+directory_error_t
+bin_list_dav(directory_values_rpc *lvals, const void *array, int n, size_t sz)
+{
+	directory_value_rpc *dav;
+	char *inbuf = (char *)array;
+	int i;
+
+	dav = calloc(n, sizeof (directory_value_rpc));
+	if (dav == NULL)
+		goto nomem;
+
+	lvals->directory_values_rpc_u.values.values_val = dav;
+	lvals->directory_values_rpc_u.values.values_len = n;
+	lvals->found = TRUE;
+
+	for (i = 0; i < n; i++) {
+		dav[i].directory_value_rpc_val = memdup(inbuf, sz);
+		if (dav[i].directory_value_rpc_val == NULL)
+			goto nomem;
+		dav[i].directory_value_rpc_len = sz;
+		inbuf += sz;
+	}
+
+	return (NULL);
+
+nomem:
+	return (directory_error("ENOMEM.bin_list_dav",
+	    "Insufficient memory copying values"));
+}
+
+/*
+ * Set up to return an error on a particular directory entry.
+ * Note that the caller need not (and in fact must not) free
+ * the directory_error_t; it will be freed when the directory entry
+ * list is freed.
+ */
+void
+directory_entry_set_error(directory_entry_rpc *ent, directory_error_t de)
+{
+	xdr_free(xdr_directory_entry_rpc, (char *)&ent);
+	ent->status = DIRECTORY_ERROR;
+	(void) directory_error_to_rpc(&ent->directory_entry_rpc_u.err, de);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/idmap/idmapd/directory_server_impl.h	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,79 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+#ifndef _DIRECTORY_SERVER_IMPL_H
+#define	_DIRECTORY_SERVER_IMPL_H
+
+/*
+ * Internal implementation details for the server side of directory lookup.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Functions to populate Directory Attribute Value lists.
+ */
+directory_error_t str_list_dav(directory_values_rpc *lvals,
+    const char * const *str_list, int n);
+directory_error_t uint_list_dav(directory_values_rpc *lvals,
+    const unsigned int *uint_list, int n);
+directory_error_t bin_list_dav(directory_values_rpc *lvals,
+    const void *array, int n, size_t sz);
+
+/*
+ * Split a name@domain into name, domain.  Recommend allocating the
+ * destination buffers the same size as the input, on the stack,
+ * using variable length arrays.
+ */
+void split_name(char *name, char *domain, char *id);
+
+/*
+ * Insert a directory_error_t into a directory entry to be returned.
+ * Caller MUST NOT free the directory_error_t.
+ */
+void directory_entry_set_error(directory_entry_rpc *ent,
+    directory_error_t de);
+
+/*
+ * This is the structure by which a provider supplies its entry points.
+ * The name is not currently used.
+ */
+struct directory_provider_static {
+	char *name;
+	directory_error_t (*get)(
+	    directory_entry_rpc *ret,
+	    idmap_utf8str_list *ids,
+	    idmap_utf8str types,
+	    idmap_utf8str_list *attrs);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DIRECTORY_SERVER_IMPL_H */
--- a/usr/src/cmd/idmap/idmapd/idmap_config.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmapd/idmap_config.c	Fri Jul 17 17:54:42 2009 -0700
@@ -696,7 +696,6 @@
 	int		num_df1 = 0;
 	int		num_df2 = 0;
 	boolean_t	match;
-	int		err;
 
 	for (i = 0; df1[i].domain[0] != '\0'; i++)
 		if (df1[i].trusted)
@@ -714,10 +713,8 @@
 			match = B_FALSE;
 			for (j = 0; df2[j].domain[0] != '\0'; j++) {
 				if (df2[j].trusted &&
-				    u8_strcmp(df1[i].domain, df2[i].domain, 0,
-				    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err)
-				    == 0 && err == 0 &&
-				    strcmp(df1[i].sid, df2[i].sid) == 0) {
+				    domain_eq(df1[i].domain, df2[j].domain) &&
+				    strcmp(df1[i].sid, df2[j].sid) == 0) {
 					match = B_TRUE;
 					break;
 				}
@@ -756,10 +753,10 @@
 				    (*new)[j].forest_name) == 0 &&
 				    ad_disc_compare_ds(
 				    (*value)[i].global_catalog,
-				    (*new)[i].global_catalog) == 0 &&
+				    (*new)[j].global_catalog) == 0 &&
 				    compare_trusteddomainsinforest(
 				    (*value)[i].domains_in_forest,
-				    (*new)[i].domains_in_forest) == 0) {
+				    (*new)[j].domains_in_forest) == 0) {
 					match = B_TRUE;
 					break;
 				}
@@ -1274,7 +1271,6 @@
 	char *forestname;
 	int num_trusteddomains;
 	boolean_t new_forest;
-	int err;
 	char *trusteddomain;
 	idmap_ad_disc_ds_t *globalcatalog;
 	idmap_trustedforest_t *trustedforests;
@@ -1368,11 +1364,8 @@
 				/* Mark the domain as trusted */
 				for (l = 0;
 				    domainsinforest[l].domain[0] != '\0'; l++) {
-					if (u8_strcmp(trusteddomain,
-					    domainsinforest[l].domain, 0,
-					    U8_STRCMP_CI_LOWER,
-					    U8_UNICODE_LATEST, &err) == 0 &&
-					    err == 0) {
+					if (domain_eq(trusteddomain,
+					    domainsinforest[l].domain)) {
 						domainsinforest[l].trusted =
 						    TRUE;
 						break;
@@ -1420,11 +1413,8 @@
 			/* Mark the domain as trusted */
 			for (l = 0; domainsinforest[l].domain[0] != '\0';
 			    l++) {
-				if (u8_strcmp(trusteddomain,
-				    domainsinforest[l].domain, 0,
-				    U8_STRCMP_CI_LOWER,
-				    U8_UNICODE_LATEST, &err) == 0 &&
-				    err == 0) {
+				if (domain_eq(trusteddomain,
+				    domainsinforest[l].domain)) {
 					domainsinforest[l].trusted = TRUE;
 					break;
 				}
@@ -1434,6 +1424,8 @@
 		if (j > 0) {
 			pgcfg->num_trusted_forests = j;
 			pgcfg->trusted_forests = trustedforests;
+		} else {
+			free(trustedforests);
 		}
 	}
 
--- a/usr/src/cmd/idmap/idmapd/idmapd.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmapd/idmapd.c	Fri Jul 17 17:54:42 2009 -0700
@@ -59,12 +59,6 @@
 static void	init_idmapd();
 static void	fini_idmapd();
 
-
-#define	_RPCSVC_CLOSEDOWN 120
-
-int _rpcsvcstate = _IDLE;	/* Set when a request is serviced */
-int _rpcsvccount = 0;		/* Number of requests being serviced */
-mutex_t _svcstate_lock;		/* lock for _rpcsvcstate, _rpcsvccount */
 idmapd_state_t	_idmapdstate;
 
 SVCXPRT *xprt = NULL;
--- a/usr/src/cmd/idmap/idmapd/idmapd.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmapd/idmapd.h	Fri Jul 17 17:54:42 2009 -0700
@@ -47,15 +47,9 @@
 extern "C" {
 #endif
 
-/* States a server can be in wrt request */
-#define	_IDLE	0
-#define	_SERVED	1
-
 #define	SENTINEL_PID	UINT32_MAX
 #define	CHECK_NULL(s)	(s != NULL ? s : "null")
 
-extern int _rpcsvcstate;	/* set when a request is serviced */
-extern int _rpcsvccount;	/* number of requests being serviced */
 extern mutex_t _svcstate_lock;	/* lock for _rpcsvcstate, _rpcsvccount */
 
 typedef enum idmap_namemap_mode {
@@ -154,6 +148,21 @@
 	const char	*msg;
 } msg_table_t;
 
+/*
+ * Data structure to store well-known SIDs and
+ * associated mappings (if any)
+ */
+typedef struct wksids_table {
+	const char	*sidprefix;
+	uint32_t	rid;
+	const char	*domain;
+	const char	*winname;
+	int		is_wuser;
+	uid_t		pid;
+	int		is_user;
+	int		direction;
+} wksids_table_t;
+
 #define	IDMAPD_SEARCH_TIMEOUT		3   /* seconds */
 #define	IDMAPD_LDAP_OPEN_TIMEOUT	1   /* secs; initial, w/ exp backoff */
 
@@ -221,6 +230,14 @@
 #define	IS_REQUEST_GID(request) \
 	((request).id1.idtype == IDMAP_GID)
 
+/*
+ * Local RID ranges
+ */
+#define	LOCALRID_UID_MIN	1000U
+#define	LOCALRID_UID_MAX	((uint32_t)INT32_MAX)
+#define	LOCALRID_GID_MIN	(((uint32_t)INT32_MAX) + 1)
+#define	LOCALRID_GID_MAX	UINT32_MAX
+
 typedef idmap_retcode (*update_list_res_cb)(void *, const char **, uint64_t);
 typedef int (*list_svc_cb)(void *, int, char **, char **);
 
@@ -286,6 +303,13 @@
 extern void	idmap_log_syslog(boolean_t);
 extern void	idmap_log_degraded(boolean_t);
 
+extern const wksids_table_t *find_wksid_by_pid(uid_t pid, int is_user);
+extern const wksids_table_t *find_wksid_by_sid(const char *sid, int rid,
+    int type);
+extern const wksids_table_t *find_wksid_by_name(const char *name,
+    const char *domain, int type);
+extern const wksids_table_t *find_wk_by_sid(char *sid);
+
 #ifdef __cplusplus
 }
 #endif
--- a/usr/src/cmd/idmap/idmapd/rpc_svc.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmapd/rpc_svc.c	Fri Jul 17 17:54:42 2009 -0700
@@ -24,73 +24,152 @@
  */
 
 /*
- * RPC service routines
- * It was initially generated using rpcgen.
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ * Edit idmap_prot.x and rebuild this file with
+ * rpcgen -CMNm -o idmap_prot_svc.c.new ../../../uts/common/rpcsvc/idmap_prot.x
+ * then merge as required.  A recent version of rpcgen is needed to
+ * produce this exact file; when the revised rpcgen is available in the
+ * build environment this file can be built automatically.
  */
 
-#include "idmapd.h"
-#include <rpcsvc/idmap_prot.h>
-#include <stdlib.h>
+#include "../../../uts/common/rpcsvc/idmap_prot.h"
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
 #include <signal.h>
-#include <rpc/xdr.h>
-#include <rpc/rpc.h>
-#include <string.h>
-#include <thread.h>
-#include <synch.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <string.h> /* strcmp */
+#include <unistd.h> /* setsid */
+#include <sys/types.h>
+#include <memory.h>
+#include <stropts.h>
+#include <sys/resource.h> /* rlimit */
+#include <syslog.h>
+
+#ifndef SIG_PF
+#define	SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define	RPC_SVC_FG
+#endif
+
+#define	_RPCSVC_CLOSEDOWN 120
+extern int _rpcpmstart;		/* Started by a port monitor ? */
+
+/* States a server can be in wrt request */
 
+#define	_IDLE 0
+#define	_SERVED 1
+
+/* LINTED static unused if no main */
+static int _rpcsvcstate = _IDLE;	/* Set when a request is serviced */
+static int _rpcsvccount = 0;		/* Number of requests being serviced */
+mutex_t _svcstate_lock;		/* lock for _rpcsvcstate, _rpcsvccount */
+
+#if	defined(RPC_MSGOUT)
+extern void RPC_MSGOUT(const char *, ...);
+#else	/* defined(RPC_MSGOUT) */
+static void
+RPC_MSGOUT(const char *fmt, char *msg)
+{
+#ifdef RPC_SVC_FG
+	if (_rpcpmstart)
+		syslog(LOG_ERR, fmt, msg);
+	else {
+		(void) fprintf(stderr, fmt, msg);
+		(void) putc('\n', stderr);
+	}
+#else
+	syslog(LOG_ERR, fmt, msg);
+#endif
+}
+#endif	/* defined(RPC_MSGOUT) */
 
 /* ARGSUSED */
 int
-_idmap_null_1(void  *argp, void *result, struct svc_req *rqstp)
+_idmap_null_1(
+    void  *argp,
+    void *result,
+    struct svc_req *rqstp)
 {
 	return (idmap_null_1_svc(result, rqstp));
 }
 
 int
-_idmap_get_mapped_ids_1(idmap_mapping_batch  *argp, idmap_ids_res *result,
-		struct svc_req *rqstp)
+_idmap_get_mapped_ids_1(
+    idmap_mapping_batch  *argp,
+    idmap_ids_res *result,
+    struct svc_req *rqstp)
 {
 	return (idmap_get_mapped_ids_1_svc(*argp, result, rqstp));
 }
 
 int
-_idmap_list_mappings_1(idmap_list_mappings_1_argument *argp,
-		idmap_mappings_res *result, struct svc_req *rqstp)
+_idmap_list_mappings_1(
+    idmap_list_mappings_1_argument *argp,
+    idmap_mappings_res *result,
+    struct svc_req *rqstp)
 {
-	return (idmap_list_mappings_1_svc(argp->lastrowid,
-	    argp->limit, argp->flag, result, rqstp));
+	return (idmap_list_mappings_1_svc(
+	    argp->lastrowid,
+	    argp->limit,
+	    argp->flag,
+	    result, rqstp));
 }
 
 int
-_idmap_list_namerules_1(idmap_list_namerules_1_argument *argp,
-		idmap_namerules_res *result, struct svc_req *rqstp)
+_idmap_list_namerules_1(
+    idmap_list_namerules_1_argument *argp,
+    idmap_namerules_res *result,
+    struct svc_req *rqstp)
 {
-	return (idmap_list_namerules_1_svc(argp->rule, argp->lastrowid,
-	    argp->limit, result, rqstp));
+	return (idmap_list_namerules_1_svc(
+	    argp->rule,
+	    argp->lastrowid,
+	    argp->limit,
+	    result, rqstp));
 }
 
 int
-_idmap_update_1(idmap_update_batch  *argp, idmap_update_res *res,
-		struct svc_req *rqstp)
+_idmap_update_1(
+    idmap_update_batch  *argp,
+    idmap_update_res *result,
+    struct svc_req *rqstp)
 {
-	return (idmap_update_1_svc(*argp, res, rqstp));
+	return (idmap_update_1_svc(*argp, result, rqstp));
 }
 
 int
-_idmap_get_mapped_id_by_name_1(idmap_mapping  *argp,
-		idmap_mappings_res *result, struct svc_req *rqstp)
+_idmap_get_mapped_id_by_name_1(
+    idmap_mapping  *argp,
+    idmap_mappings_res *result,
+    struct svc_req *rqstp)
 {
 	return (idmap_get_mapped_id_by_name_1_svc(*argp, result, rqstp));
 }
 
 int
-_idmap_get_prop_1(idmap_prop_type  *argp,
-		idmap_prop_res *result, struct svc_req *rqstp)
+_idmap_get_prop_1(
+    idmap_prop_type  *argp,
+    idmap_prop_res *result,
+    struct svc_req *rqstp)
 {
 	return (idmap_get_prop_1_svc(*argp, result, rqstp));
 }
 
-
+int
+_directory_get_common_1(
+    directory_get_common_1_argument *argp,
+    directory_results_rpc *result,
+    struct svc_req *rqstp)
+{
+	return (directory_get_common_1_svc(
+	    argp->ids,
+	    argp->types,
+	    argp->attrs,
+	    result, rqstp));
+}
 
 void
 idmap_prog_1(struct svc_req *rqstp, register SVCXPRT *transp)
@@ -102,6 +181,7 @@
 		idmap_update_batch idmap_update_1_arg;
 		idmap_mapping idmap_get_mapped_id_by_name_1_arg;
 		idmap_prop_type idmap_get_prop_1_arg;
+		directory_get_common_1_argument directory_get_common_1_arg;
 	} argument;
 	union {
 		idmap_ids_res idmap_get_mapped_ids_1_res;
@@ -110,6 +190,7 @@
 		idmap_update_res idmap_update_1_res;
 		idmap_mappings_res idmap_get_mapped_id_by_name_1_res;
 		idmap_prop_res idmap_get_prop_1_res;
+		directory_results_rpc directory_get_common_1_res;
 	} result;
 	bool_t retval;
 	xdrproc_t _xdr_argument, _xdr_result;
@@ -120,61 +201,84 @@
 	(void) mutex_unlock(&_svcstate_lock);
 	switch (rqstp->rq_proc) {
 	case IDMAP_NULL:
-		_xdr_argument = (xdrproc_t)xdr_void;
-		_xdr_result = (xdrproc_t)xdr_void;
+		_xdr_argument = (xdrproc_t)
+		    xdr_void;
+		_xdr_result = (xdrproc_t)
+		    xdr_void;
 		local = (bool_t (*) (char *,  void *,  struct svc_req *))
 		    _idmap_null_1;
 		break;
 
 	case IDMAP_GET_MAPPED_IDS:
-		_xdr_argument = (xdrproc_t)xdr_idmap_mapping_batch;
-		_xdr_result = (xdrproc_t)xdr_idmap_ids_res;
+		_xdr_argument = (xdrproc_t)
+		    xdr_idmap_mapping_batch;
+		_xdr_result = (xdrproc_t)
+		    xdr_idmap_ids_res;
 		local = (bool_t (*) (char *,  void *,  struct svc_req *))
 		    _idmap_get_mapped_ids_1;
 		break;
 
 	case IDMAP_LIST_MAPPINGS:
-		_xdr_argument = (xdrproc_t)xdr_idmap_list_mappings_1_argument;
-		_xdr_result = (xdrproc_t)xdr_idmap_mappings_res;
+		_xdr_argument = (xdrproc_t)
+		    xdr_idmap_list_mappings_1_argument;
+		_xdr_result = (xdrproc_t)
+		    xdr_idmap_mappings_res;
 		local = (bool_t (*) (char *,  void *,  struct svc_req *))
 		    _idmap_list_mappings_1;
 		break;
 
 	case IDMAP_LIST_NAMERULES:
-		_xdr_argument = (xdrproc_t)xdr_idmap_list_namerules_1_argument;
-		_xdr_result = (xdrproc_t)xdr_idmap_namerules_res;
+		_xdr_argument = (xdrproc_t)
+		    xdr_idmap_list_namerules_1_argument;
+		_xdr_result = (xdrproc_t)
+		    xdr_idmap_namerules_res;
 		local = (bool_t (*) (char *,  void *,  struct svc_req *))
 		    _idmap_list_namerules_1;
 		break;
 
 	case IDMAP_UPDATE:
-		_xdr_argument = (xdrproc_t)xdr_idmap_update_batch;
-		_xdr_result = (xdrproc_t)xdr_idmap_update_res;
+		_xdr_argument = (xdrproc_t)
+		    xdr_idmap_update_batch;
+		_xdr_result = (xdrproc_t)
+		    xdr_idmap_update_res;
 		local = (bool_t (*) (char *,  void *,  struct svc_req *))
 		    _idmap_update_1;
 		break;
 
 	case IDMAP_GET_MAPPED_ID_BY_NAME:
-		_xdr_argument = (xdrproc_t)xdr_idmap_mapping;
-		_xdr_result = (xdrproc_t)xdr_idmap_mappings_res;
+		_xdr_argument = (xdrproc_t)
+		    xdr_idmap_mapping;
+		_xdr_result = (xdrproc_t)
+		    xdr_idmap_mappings_res;
 		local = (bool_t (*) (char *,  void *,  struct svc_req *))
 		    _idmap_get_mapped_id_by_name_1;
 		break;
 
 	case IDMAP_GET_PROP:
-		_xdr_argument = (xdrproc_t)xdr_idmap_prop_type;
-		_xdr_result = (xdrproc_t)xdr_idmap_prop_res;
+		_xdr_argument = (xdrproc_t)
+		    xdr_idmap_prop_type;
+		_xdr_result = (xdrproc_t)
+		    xdr_idmap_prop_res;
 		local = (bool_t (*) (char *,  void *,  struct svc_req *))
 		    _idmap_get_prop_1;
 		break;
 
+	case DIRECTORY_GET_COMMON:
+		_xdr_argument = (xdrproc_t)
+		    xdr_directory_get_common_1_argument;
+		_xdr_result = (xdrproc_t)
+		    xdr_directory_results_rpc;
+		local = (bool_t (*) (char *,  void *,  struct svc_req *))
+		    _directory_get_common_1;
+		break;
+
 	default:
 		svcerr_noproc(transp);
 		(void) mutex_lock(&_svcstate_lock);
 		_rpcsvccount--;
 		_rpcsvcstate = _SERVED;
 		(void) mutex_unlock(&_svcstate_lock);
-		return;
+		return; /* CSTYLED */
 	}
 	(void) memset((char *)&argument, 0, sizeof (argument));
 	if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) {
@@ -183,27 +287,28 @@
 		_rpcsvccount--;
 		_rpcsvcstate = _SERVED;
 		(void) mutex_unlock(&_svcstate_lock);
-		return;
+		return; /* CSTYLED */
 	}
 	retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp);
-	if (_xdr_result && retval > 0 && !svc_sendreply(transp, _xdr_result,
-	    (char *)&result)) {
+	if (_xdr_result && retval > 0 &&
+	    !svc_sendreply(transp, _xdr_result, (char *)&result)) {
 		svcerr_systemerr(transp);
 	}
 	if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) {
-		idmapdlog(LOG_ERR,
-		    "unable to free RPC arguments");
+		RPC_MSGOUT("%s",
+		    "unable to free arguments");
 		exit(1);
 	}
 	if (_xdr_result != NULL) {
 		if (!idmap_prog_1_freeresult(transp, _xdr_result,
 		    (caddr_t)&result))
-			idmapdlog(LOG_ERR,
-			    "unable to free RPC results");
+			RPC_MSGOUT("%s",
+			    "unable to free results");
 
 	}
 	(void) mutex_lock(&_svcstate_lock);
 	_rpcsvccount--;
 	_rpcsvcstate = _SERVED;
 	(void) mutex_unlock(&_svcstate_lock);
+	return; /* CSTYLED */
 }
--- a/usr/src/cmd/idmap/idmapd/server.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/cmd/idmap/idmapd/server.c	Fri Jul 17 17:54:42 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -44,6 +44,7 @@
 #include <auth_attr.h>
 #include <secdb.h>
 #include <sys/u8_textprep.h>
+#include <note.h>
 
 #define	_VALIDATE_LIST_CB_DATA(col, val, siz)\
 	retcode = validate_list_cb_data(cb_data, argc, argv, col,\
@@ -1097,3 +1098,20 @@
 	(void) xdr_free(xdr_result, result);
 	return (TRUE);
 }
+
+/*
+ * This function is called by rpc_svc.c when it encounters an error.
+ */
+NOTE(PRINTFLIKE(1))
+void
+idmap_rpc_msgout(const char *fmt, ...)
+{
+	va_list va;
+	char buf[1000];
+
+	va_start(va, fmt);
+	(void) vsnprintf(buf, sizeof (buf), fmt, va);
+	va_end(va);
+
+	idmapdlog(LOG_ERR, "idmap RPC:  %s", buf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/idmap/idmapd/wksids.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,407 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Information about well-known (builtin) names, and functions to retrieve
+ * information about them.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "idmapd.h"
+#include "miscutils.h"
+
+/*
+ * Table for well-known SIDs.
+ *
+ * Background:
+ *
+ * Some of the well-known principals are stored under:
+ * cn=WellKnown Security Principals, cn=Configuration, dc=<forestRootDomain>
+ * They belong to objectClass "foreignSecurityPrincipal". They don't have
+ * "samAccountName" nor "userPrincipalName" attributes. Their names are
+ * available in "cn" and "name" attributes. Some of these principals have a
+ * second entry under CN=ForeignSecurityPrincipals,dc=<forestRootDomain> and
+ * these duplicate entries have the stringified SID in the "name" and "cn"
+ * attributes instead of the actual name.
+ *
+ * Those of the form S-1-5-32-X are Builtin groups and are stored in the
+ * cn=builtin container (except, Power Users which is not stored in AD)
+ *
+ * These principals are and will remain constant. Therefore doing AD lookups
+ * provides no benefit. Also, using hard-coded table (and thus avoiding AD
+ * lookup) improves performance and avoids additional complexity in the
+ * adutils.c code. Moreover these SIDs can be used when no Active Directory
+ * is available (such as the CIFS server's "workgroup" mode).
+ *
+ * Notes:
+ * 1. Currently we don't support localization of well-known SID names,
+ * unlike Windows.
+ *
+ * 2. Other well-known SIDs i.e. S-1-5-<domain>-<w-k RID> are not stored
+ * here. AD does have normal user/group objects for these objects and
+ * can be looked up using the existing AD lookup code.
+ *
+ * 3. See comments above lookup_wksids_sid2pid() for more information
+ * on how we lookup the wksids table.
+ *
+ * 4. If this table contains two entries for a particular Windows name,
+ * so as to offer both UID and GID mappings, the preferred mapping (the
+ * one that matches Windows usage) must be listed first.  That is the
+ * entry that will be used when the caller specifies IDMAP_POSIXID
+ * ("don't care") as the target.
+ *
+ * Entries here come from KB243330, MS-LSAT, and
+ * http://technet.microsoft.com/en-us/library/cc755854.aspx
+ * http://technet.microsoft.com/en-us/library/cc755925.aspx
+ * http://msdn.microsoft.com/en-us/library/cc980032(PROT.10).aspx
+ */
+static wksids_table_t wksids[] = {
+	/* S-1-0	Null Authority */
+	{"S-1-0", 0, "", "Nobody", 1, SENTINEL_PID, -1, 1},
+
+	/* S-1-1	World Authority */
+	{"S-1-1", 0, "", "Everyone", 0, SENTINEL_PID, -1, -1},
+
+	/* S-1-2	Local Authority */
+	{"S-1-2", 0, "", "Local", 0, SENTINEL_PID, -1, -1},
+	{"S-1-2", 1, "", "Console Logon", 0, SENTINEL_PID, -1, -1},
+
+	/* S-1-3	Creator Authority */
+	{"S-1-3", 0, "", "Creator Owner", 1, IDMAP_WK_CREATOR_OWNER_UID, 1, 0},
+	{"S-1-3", 1, "", "Creator Group", 0, IDMAP_WK_CREATOR_GROUP_GID, 0, 0},
+	{"S-1-3", 2, "", "Creator Owner Server", 1, SENTINEL_PID, -1, -1},
+	{"S-1-3", 3, "", "Creator Group Server", 0, SENTINEL_PID, -1, 1},
+	{"S-1-3", 4, "", "Owner Rights", 0, SENTINEL_PID, -1, -1},
+
+	/* S-1-4	Non-unique Authority */
+
+	/* S-1-5	NT Authority */
+	{"S-1-5", 1, "", "Dialup", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 2, "", "Network", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 3, "", "Batch", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 4, "", "Interactive", 0, SENTINEL_PID, -1, -1},
+	/* S-1-5-5-X-Y	Logon Session */
+	{"S-1-5", 6, "", "Service", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 7, "", "Anonymous Logon", 0, GID_NOBODY, 0, 0},
+	{"S-1-5", 7, "", "Anonymous Logon", 0, UID_NOBODY, 1, 0},
+	{"S-1-5", 8, "", "Proxy", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 9, "", "Enterprise Domain Controllers", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5", 10, "", "Self", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 11, "", "Authenticated Users", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 12, "", "Restricted", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 13, "", "Terminal Server Users", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 14, "", "Remote Interactive Logon", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 15, "", "This Organization", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 17, "", "IUSR", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 18, "", "Local System", 0, IDMAP_WK_LOCAL_SYSTEM_GID, 0, 0},
+	{"S-1-5", 19, "", "Local Service", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5", 20, "", "Network Service", 0, SENTINEL_PID, -1, -1},
+
+	/* S-1-5-21-<domain>	Machine-local definitions */
+	{NULL, 498, NULL, "Enterprise Read-only Domain Controllers", 0,
+	    SENTINEL_PID, -1, -1},
+	{NULL, 500, NULL, "Administrator", 1, SENTINEL_PID, 1, -1},
+	{NULL, 501, NULL, "Guest", 1, SENTINEL_PID, 1, -1},
+	{NULL, 502, NULL, "KRBTGT", 1, SENTINEL_PID, 1, -1},
+	{NULL, 512, NULL, "Domain Admins", 0, SENTINEL_PID, -1, -1},
+	{NULL, 513, NULL, "Domain Users", 0, SENTINEL_PID, -1, -1},
+	{NULL, 514, NULL, "Domain Guests", 0, SENTINEL_PID, -1, -1},
+	{NULL, 515, NULL, "Domain Computers", 0, SENTINEL_PID, -1, -1},
+	{NULL, 516, NULL, "Domain Controllers", 0, SENTINEL_PID, -1, -1},
+	{NULL, 517, NULL, "Cert Publishers", 0, SENTINEL_PID, -1, -1},
+	{NULL, 518, NULL, "Schema Admins", 0, SENTINEL_PID, -1, -1},
+	{NULL, 519, NULL, "Enterprise Admins", 0, SENTINEL_PID, -1, -1},
+	{NULL, 520, NULL, "Global Policy Creator Owners", 0,
+	    SENTINEL_PID, -1, -1},
+	{NULL, 533, NULL, "RAS and IAS Servers", 0, SENTINEL_PID, -1, -1},
+
+	/* S-1-5-32	BUILTIN */
+	{"S-1-5-32", 544, "BUILTIN", "Administrators", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 545, "BUILTIN", "Users", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 546, "BUILTIN", "Guests", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 547, "BUILTIN", "Power Users", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 548, "BUILTIN", "Account Operators", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 549, "BUILTIN", "Server Operators", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 550, "BUILTIN", "Print Operators", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 551, "BUILTIN", "Backup Operators", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 552, "BUILTIN", "Replicator", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 554, "BUILTIN", "Pre-Windows 2000 Compatible Access", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 555, "BUILTIN", "Remote Desktop Users", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 556, "BUILTIN", "Network Configuration Operators", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 557, "BUILTIN", "Incoming Forest Trust Builders", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 558, "BUILTIN", "Performance Monitor Users", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 559, "BUILTIN", "Performance Log Users", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 560, "BUILTIN", "Windows Authorization Access Group", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 561, "BUILTIN", "Terminal Server License Servers", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 562, "BUILTIN", "Distributed COM Users", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 568, "BUILTIN", "IIS_IUSRS", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 569, "BUILTIN", "Cryptographic Operators", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 573, "BUILTIN", "Event Log Readers", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-32", 574, "BUILTIN", "Certificate Service DCOM Access", 0,
+	    SENTINEL_PID, -1, -1},
+
+	{"S-1-5", 33, "", "Write Restricted", 0, SENTINEL_PID, -1, -1},
+
+	/* S-1-5-64	NT Authority */
+	{"S-1-5-64", 10, "", "NTLM Authentication", 0, SENTINEL_PID, -1, -1},
+	{"S-1-5-64", 14, "", "SChannel Authentication", 0,
+	    SENTINEL_PID, -1, -1},
+	{"S-1-5-64", 21, "", "Digest Authentication", 0, SENTINEL_PID, -1, -1},
+
+	/* S-1-5-80-a-b-c-d NT Service */
+
+	{"S-1-5", 1000, "", "Other Organization", 0, SENTINEL_PID, -1, -1},
+
+	/* S-1-7 Internet$ */
+
+	/*
+	 * S-1-16	Mandatory Label
+	 * S-1-16-0	Untrusted Mandatory Level
+	 * S-1-16-4096	Low Mandatory Level
+	 * S-1-16-8192	Medium Mandatory Level
+	 * S-1-16-8448	Medium Plus Mandatory Level
+	 * S-1-16-12288	High Mandatory Level
+	 * S-1-16-16384	System Mandatory Level
+	 * S-1-16-20480	Protected Process Mandatory Level
+	 */
+};
+
+/*
+ * Find a wksid entry for the specified Windows name and domain, of the
+ * specified type.
+ *
+ * Ignore entries intended only for U2W use.
+ */
+const
+wksids_table_t *
+find_wksid_by_name(const char *name, const char *domain, int type)
+{
+	int i;
+
+	RDLOCK_CONFIG();
+	int len = strlen(_idmapdstate.hostname);
+	char my_host_name[len + 1];
+	(void) strcpy(my_host_name, _idmapdstate.hostname);
+	UNLOCK_CONFIG();
+
+	for (i = 0; i < NELEM(wksids); i++) {
+		/* Check to see if this entry yields the desired type */
+		switch (type) {
+		case IDMAP_UID:
+			if (wksids[i].is_user == 0)
+				continue;
+			break;
+		case IDMAP_GID:
+			if (wksids[i].is_user == 1)
+				continue;
+			break;
+		case IDMAP_POSIXID:
+			break;
+		default:
+			assert(FALSE);
+		}
+
+		if (strcasecmp(wksids[i].winname, name) != 0)
+			continue;
+
+		if (!EMPTY_STRING(domain)) {
+			const char *dom;
+
+			if (wksids[i].domain != NULL) {
+				dom = wksids[i].domain;
+			} else {
+				dom = my_host_name;
+			}
+			if (strcasecmp(dom, domain) != 0)
+				continue;
+		}
+
+		/*
+		 * We have a Windows name, so ignore entries that are only
+		 * usable for mapping UNIX->Windows.  (Note:  the current
+		 * table does not have any such entries.)
+		 */
+		if (wksids[i].direction == IDMAP_DIRECTION_U2W)
+			continue;
+
+		return (&wksids[i]);
+	}
+
+	return (NULL);
+}
+
+/*
+ * Find a wksid entry for the specified SID, of the specified type.
+ *
+ * Ignore entries intended only for U2W use.
+ */
+const
+wksids_table_t *
+find_wksid_by_sid(const char *sid, int rid, int type)
+{
+	int i;
+
+	RDLOCK_CONFIG();
+	int len = strlen(_idmapdstate.cfg->pgcfg.machine_sid);
+	char my_machine_sid[len + 1];
+	(void) strcpy(my_machine_sid, _idmapdstate.cfg->pgcfg.machine_sid);
+	UNLOCK_CONFIG();
+
+	for (i = 0; i < NELEM(wksids); i++) {
+		int sidcmp;
+
+		/* Check to see if this entry yields the desired type */
+		switch (type) {
+		case IDMAP_UID:
+			if (wksids[i].is_user == 0)
+				continue;
+			break;
+		case IDMAP_GID:
+			if (wksids[i].is_user == 1)
+				continue;
+			break;
+		case IDMAP_POSIXID:
+			break;
+		default:
+			assert(FALSE);
+		}
+
+		if (wksids[i].sidprefix != NULL) {
+			sidcmp = strcasecmp(wksids[i].sidprefix, sid);
+		} else {
+			sidcmp = strcasecmp(my_machine_sid, sid);
+		}
+
+		if (sidcmp != 0)
+			continue;
+		if (wksids[i].rid != rid)
+			continue;
+
+		/*
+		 * We have a SID, so ignore entries that are only usable
+		 * for mapping UNIX->Windows.  (Note:  the current table
+		 * does not have any such entries.)
+		 */
+		if (wksids[i].direction == IDMAP_DIRECTION_U2W)
+			continue;
+
+		return (&wksids[i]);
+	}
+
+	return (NULL);
+}
+
+/*
+ * Find a wksid entry for the specified pid, of the specified type.
+ * Ignore entries that do not specify U2W mappings.
+ */
+const
+wksids_table_t *
+find_wksid_by_pid(uid_t pid, int is_user)
+{
+	int i;
+
+	if (pid == SENTINEL_PID)
+		return (NULL);
+
+	for (i = 0; i < NELEM(wksids); i++) {
+		if (wksids[i].pid == pid &&
+		    wksids[i].is_user == is_user &&
+		    (wksids[i].direction == IDMAP_DIRECTION_BI ||
+		    wksids[i].direction == IDMAP_DIRECTION_U2W)) {
+			return (&wksids[i]);
+		}
+	}
+	return (NULL);
+}
+
+/*
+ * It is probably a bug that both this and find_wksid_by_sid exist,
+ * but for now the distinction is primarily that one takes {machinesid,rid}
+ * and the other takes a full SID.
+ */
+const
+wksids_table_t *
+find_wk_by_sid(char *sid)
+{
+	int i;
+
+	RDLOCK_CONFIG();
+	int len = strlen(_idmapdstate.cfg->pgcfg.machine_sid);
+	char my_machine_sid[len + 1];
+	(void) strcpy(my_machine_sid, _idmapdstate.cfg->pgcfg.machine_sid);
+	UNLOCK_CONFIG();
+
+	for (i = 0; i < NELEM(wksids); i++) {
+		int len;
+		const char *prefix;
+		char *p;
+		unsigned long rid;
+
+		if (wksids[i].sidprefix == NULL)
+			prefix = my_machine_sid;
+		else
+			prefix = wksids[i].sidprefix;
+
+		len = strlen(prefix);
+
+		/*
+		 * Check to see whether the SID we're looking for starts
+		 * with this prefix, then a -, then a single RID, and it's
+		 * the right RID.
+		 */
+		if (strncasecmp(sid, prefix, len) != 0)
+			continue;
+		if (sid[len] != '-')
+			continue;
+		rid = strtoul(sid + len + 1, &p, 10);
+		if (*p != '\0')
+			continue;
+
+		if (rid != wksids[i].rid)
+			continue;
+
+		return (&wksids[i]);
+	}
+	return (NULL);
+}
--- a/usr/src/common/smbsrv/smb_xdr_utils.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/common/smbsrv/smb_xdr_utils.c	Fri Jul 17 17:54:42 2009 -0700
@@ -30,57 +30,6 @@
 #endif /* _KERNEL */
 #include <smbsrv/smb_xdr.h>
 #include <sys/socket.h>
-#ifdef _KERNEL
-/*
- * xdr_vector():
- *
- * XDR a fixed length array. Unlike variable-length arrays,
- * the storage of fixed length arrays is static and unfreeable.
- * > basep: base of the array
- * > size: size of the array
- * > elemsize: size of each element
- * > xdr_elem: routine to XDR each element
- */
-#define	LASTUNSIGNED ((uint_t)0-1)
-bool_t
-xdr_vector(XDR *xdrs, char *basep, uint_t nelem,
-	uint_t elemsize, xdrproc_t xdr_elem)
-{
-	uint_t i;
-	char *elptr;
-
-	elptr = basep;
-	for (i = 0; i < nelem; i++) {
-		if (!(*xdr_elem)(xdrs, elptr, LASTUNSIGNED))
-			return (FALSE);
-		elptr += elemsize;
-	}
-	return (TRUE);
-}
-
-/*
- * XDR an unsigned char
- */
-bool_t
-xdr_u_char(XDR *xdrs, uchar_t *cp)
-{
-	int i;
-
-	switch (xdrs->x_op) {
-	case XDR_ENCODE:
-		i = (*cp);
-		return (XDR_PUTINT32(xdrs, &i));
-	case XDR_DECODE:
-		if (!XDR_GETINT32(xdrs, &i))
-			return (FALSE);
-		*cp = (uchar_t)i;
-		return (TRUE);
-	case XDR_FREE:
-		return (TRUE);
-	}
-	return (FALSE);
-}
-#endif /* _KERNEL */
 
 bool_t
 xdr_smb_dr_string_t(xdrs, objp)
@@ -160,45 +109,44 @@
 }
 
 /*
- * Encode an opipe context structure into a buffer.
+ * Encode an smb_netuserinfo_t into a buffer.
  */
 int
-smb_opipe_context_encode(smb_opipe_context_t *ctx, uint8_t *buf,
-    uint32_t buflen, uint_t *pbytes_encoded)
+smb_netuserinfo_encode(smb_netuserinfo_t *info, uint8_t *buf,
+    uint32_t buflen, uint_t *nbytes)
 {
 	XDR xdrs;
 	int rc = 0;
 
 	xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_ENCODE);
 
-	if (!smb_opipe_context_xdr(&xdrs, ctx))
+	if (!smb_netuserinfo_xdr(&xdrs, info))
 		rc = -1;
 
-	if (pbytes_encoded != NULL)
-		*pbytes_encoded = xdr_getpos(&xdrs);
-
+	if (nbytes != NULL)
+		*nbytes = xdr_getpos(&xdrs);
 	xdr_destroy(&xdrs);
 	return (rc);
 }
 
 /*
- * Decode an XDR buffer into an opipe context structure.
+ * Decode an XDR buffer into an smb_netuserinfo_t.
  */
 int
-smb_opipe_context_decode(smb_opipe_context_t *ctx, uint8_t *buf,
-    uint32_t buflen, uint_t *pbytes_decoded)
+smb_netuserinfo_decode(smb_netuserinfo_t *info, uint8_t *buf,
+    uint32_t buflen, uint_t *nbytes)
 {
 	XDR xdrs;
 	int rc = 0;
 
 	xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_DECODE);
 
-	bzero(ctx, sizeof (smb_opipe_context_t));
-	if (!smb_opipe_context_xdr(&xdrs, ctx))
+	bzero(info, sizeof (smb_netuserinfo_t));
+	if (!smb_netuserinfo_xdr(&xdrs, info))
 		rc = -1;
 
-	if (pbytes_decoded != NULL)
-		*pbytes_decoded = xdr_getpos(&xdrs);
+	if (nbytes != NULL)
+		*nbytes = xdr_getpos(&xdrs);
 	xdr_destroy(&xdrs);
 	return (rc);
 }
@@ -219,32 +167,175 @@
 	return (TRUE);
 }
 
+/*
+ * XDR encode/decode for smb_netuserinfo_t.
+ */
 bool_t
-smb_opipe_context_xdr(XDR *xdrs, smb_opipe_context_t *objp)
+smb_netuserinfo_xdr(XDR *xdrs, smb_netuserinfo_t *objp)
 {
-	if (!xdr_uint64_t(xdrs, &objp->oc_session_id))
+	if (!xdr_uint64_t(xdrs, &objp->ui_session_id))
+		return (FALSE);
+	if (!xdr_uint16_t(xdrs, &objp->ui_uid))
+		return (FALSE);
+	if (!xdr_uint16_t(xdrs, &objp->ui_domain_len))
+		return (FALSE);
+	if (!xdr_string(xdrs, &objp->ui_domain, ~0))
+		return (FALSE);
+	if (!xdr_uint16_t(xdrs, &objp->ui_account_len))
 		return (FALSE);
-	if (!xdr_uint16_t(xdrs, &objp->oc_uid))
+	if (!xdr_string(xdrs, &objp->ui_account, ~0))
+		return (FALSE);
+	if (!xdr_uint16_t(xdrs, &objp->ui_workstation_len))
 		return (FALSE);
-	if (!xdr_uint16_t(xdrs, &objp->oc_domain_len))
+	if (!xdr_string(xdrs, &objp->ui_workstation, ~0))
+		return (FALSE);
+	if (!xdr_smb_inaddr_t(xdrs, &objp->ui_ipaddr))
 		return (FALSE);
-	if (!xdr_string(xdrs, &objp->oc_domain, ~0))
+	if (!xdr_int32_t(xdrs, &objp->ui_native_os))
+		return (FALSE);
+	if (!xdr_int64_t(xdrs, &objp->ui_logon_time))
 		return (FALSE);
-	if (!xdr_uint16_t(xdrs, &objp->oc_account_len))
+	if (!xdr_uint32_t(xdrs, &objp->ui_numopens))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->ui_flags))
 		return (FALSE);
-	if (!xdr_string(xdrs, &objp->oc_account, ~0))
+	return (TRUE);
+}
+
+/*
+ * Encode an smb_netconnectinfo_t into a buffer.
+ */
+int
+smb_netconnectinfo_encode(smb_netconnectinfo_t *info, uint8_t *buf,
+    uint32_t buflen, uint_t *nbytes)
+{
+	XDR xdrs;
+	int rc = 0;
+
+	xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_ENCODE);
+
+	if (!smb_netconnectinfo_xdr(&xdrs, info))
+		rc = -1;
+
+	if (nbytes != NULL)
+		*nbytes = xdr_getpos(&xdrs);
+	xdr_destroy(&xdrs);
+	return (rc);
+}
+
+/*
+ * Decode an XDR buffer into an smb_netconnectinfo_t.
+ */
+int
+smb_netconnectinfo_decode(smb_netconnectinfo_t *info, uint8_t *buf,
+    uint32_t buflen, uint_t *nbytes)
+{
+	XDR xdrs;
+	int rc = 0;
+
+	xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_DECODE);
+
+	bzero(info, sizeof (smb_netconnectinfo_t));
+	if (!smb_netconnectinfo_xdr(&xdrs, info))
+		rc = -1;
+
+	if (nbytes != NULL)
+		*nbytes = xdr_getpos(&xdrs);
+	xdr_destroy(&xdrs);
+	return (rc);
+}
+
+/*
+ * XDR encode/decode for smb_netconnectinfo_t.
+ */
+bool_t
+smb_netconnectinfo_xdr(XDR *xdrs, smb_netconnectinfo_t *objp)
+{
+	if (!xdr_uint32_t(xdrs, &objp->ci_id))
 		return (FALSE);
-	if (!xdr_uint16_t(xdrs, &objp->oc_workstation_len))
+	if (!xdr_uint32_t(xdrs, &objp->ci_type))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->ci_numopens))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->ci_numusers))
 		return (FALSE);
-	if (!xdr_string(xdrs, &objp->oc_workstation, ~0))
+	if (!xdr_uint32_t(xdrs, &objp->ci_time))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->ci_namelen))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->ci_sharelen))
+		return (FALSE);
+	if (!xdr_string(xdrs, &objp->ci_username, MAXNAMELEN))
 		return (FALSE);
-	if (!xdr_smb_inaddr_t(xdrs, &objp->oc_ipaddr))
-		return (FALSE);
-	if (!xdr_int32_t(xdrs, &objp->oc_native_os))
+	if (!xdr_string(xdrs, &objp->ci_share, MAXNAMELEN))
 		return (FALSE);
-	if (!xdr_int64_t(xdrs, &objp->oc_logon_time))
+	return (TRUE);
+}
+
+/*
+ * Encode an smb_netfileinfo_t into a buffer.
+ */
+int
+smb_netfileinfo_encode(smb_netfileinfo_t *info, uint8_t *buf,
+    uint32_t buflen, uint_t *nbytes)
+{
+	XDR xdrs;
+	int rc = 0;
+
+	xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_ENCODE);
+
+	if (!smb_netfileinfo_xdr(&xdrs, info))
+		rc = -1;
+
+	if (nbytes != NULL)
+		*nbytes = xdr_getpos(&xdrs);
+	xdr_destroy(&xdrs);
+	return (rc);
+}
+
+/*
+ * Decode an XDR buffer into an smb_netfileinfo_t.
+ */
+int
+smb_netfileinfo_decode(smb_netfileinfo_t *info, uint8_t *buf,
+    uint32_t buflen, uint_t *nbytes)
+{
+	XDR xdrs;
+	int rc = 0;
+
+	xdrmem_create(&xdrs, (const caddr_t)buf, buflen, XDR_DECODE);
+
+	bzero(info, sizeof (smb_netfileinfo_t));
+	if (!smb_netfileinfo_xdr(&xdrs, info))
+		rc = -1;
+
+	if (nbytes != NULL)
+		*nbytes = xdr_getpos(&xdrs);
+	xdr_destroy(&xdrs);
+	return (rc);
+}
+
+/*
+ * XDR encode/decode for smb_netfileinfo_t.
+ */
+bool_t
+smb_netfileinfo_xdr(XDR *xdrs, smb_netfileinfo_t *objp)
+{
+	if (!xdr_uint16_t(xdrs, &objp->fi_fid))
 		return (FALSE);
-	if (!xdr_uint32_t(xdrs, &objp->oc_flags))
+	if (!xdr_uint32_t(xdrs, &objp->fi_uniqid))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->fi_permissions))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->fi_numlocks))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->fi_pathlen))
+		return (FALSE);
+	if (!xdr_uint32_t(xdrs, &objp->fi_namelen))
+		return (FALSE);
+	if (!xdr_string(xdrs, &objp->fi_path, MAXPATHLEN))
+		return (FALSE);
+	if (!xdr_string(xdrs, &objp->fi_username, MAXNAMELEN))
 		return (FALSE);
 	return (TRUE);
 }
--- a/usr/src/lib/libadutils/common/addisc.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libadutils/common/addisc.c	Fri Jul 17 17:54:42 2009 -0700
@@ -314,7 +314,6 @@
 	int		num_td1;
 	int		num_td2;
 	boolean_t	match;
-	int 		err;
 
 	for (i = 0; td1[i].domain[0] != '\0'; i++)
 		continue;
@@ -330,9 +329,7 @@
 	for (i = 0; i < num_td1; i++) {
 		match = B_FALSE;
 		for (j = 0; j < num_td2; j++) {
-			if (u8_strcmp(td1[i].domain, td2[j].domain, 0,
-			    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 &&
-			    err == 0) {
+			if (domain_eq(td1[i].domain, td2[j].domain)) {
 				match = B_TRUE;
 				break;
 			}
@@ -373,7 +370,6 @@
 	int		num_df1;
 	int		num_df2;
 	boolean_t	match;
-	int		err;
 
 	for (i = 0; df1[i].domain[0] != '\0'; i++)
 		continue;
@@ -389,9 +385,7 @@
 	for (i = 0; i < num_df1; i++) {
 		match = B_FALSE;
 		for (j = 0; j < num_df2; j++) {
-			if (u8_strcmp(df1[i].domain, df2[j].domain, 0,
-			    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 &&
-			    err == 0 &&
+			if (domain_eq(df1[i].domain, df2[j].domain) &&
 			    strcmp(df1[i].sid, df2[j].sid) == 0) {
 				match = B_TRUE;
 				break;
@@ -1077,7 +1071,7 @@
 
 	if (ndomains < nresults) {
 		ad_disc_domainsinforest_t *tmp;
-		tmp = realloc(domains, (ndomains+1) * sizeof (*domains));
+		tmp = realloc(domains, (ndomains + 1) * sizeof (*domains));
 		if (tmp == NULL)
 			goto err;
 		domains = tmp;
@@ -1241,8 +1235,8 @@
 
 	/* Eat any trailing dot */
 	len = strlen(dname);
-	if (len > 0 && dname[len-1] == '.')
-		dname[len-1] = '\0';
+	if (len > 0 && dname[len - 1] == '.')
+		dname[len - 1] = '\0';
 
 	update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl);
 
--- a/usr/src/lib/libadutils/common/adutils.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libadutils/common/adutils.c	Fri Jul 17 17:54:42 2009 -0700
@@ -863,12 +863,10 @@
 adutils_lookup_check_domain(adutils_query_state_t *qs, const char *domain)
 {
 	adutils_ad_t *ad = qs->qadh->owner;
-	int i, err;
+	int i;
 
 	for (i = 0; i < ad->num_known_domains; i++) {
-		if (u8_strcmp(domain, ad->known_domains[i].name, 0,
-		    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 &&
-		    err == 0)
+		if (domain_eq(domain, ad->known_domains[i].name))
 			return (1);
 	}
 
@@ -1116,7 +1114,7 @@
 	char		*attr = NULL, *dn = NULL, *domain = NULL;
 	adutils_entry_t	*ep;
 	adutils_attr_t	*ap;
-	int		i, j, b, err = 0, ret = -2;
+	int		i, j, b, ret = -2;
 
 	*entry = NULL;
 
@@ -1128,8 +1126,7 @@
 		return (-2);
 	}
 	if (q->edomain != NULL) {
-		if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER,
-		    U8_UNICODE_LATEST, &err) != 0 || err != 0) {
+		if (!domain_eq(q->edomain, domain)) {
 			ldap_memfree(dn);
 			free(domain);
 			return (-1);
@@ -1560,7 +1557,7 @@
  */
 adutils_rc
 adutils_lookup_batch_add(adutils_query_state_t *state,
-	const char *filter, const char **attrs, const char *edomain,
+	const char *filter, const char * const *attrs, const char *edomain,
 	adutils_result_t **result, adutils_rc *rc)
 {
 	adutils_rc	retcode = ADUTILS_SUCCESS;
@@ -1675,3 +1672,12 @@
 		return (rc);
 	return (brc);
 }
+
+boolean_t
+domain_eq(const char *a, const char *b)
+{
+	int err;
+
+	return (u8_strcmp(a, b, 0, U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err)
+	    == 0 && err == 0);
+}
--- a/usr/src/lib/libadutils/common/libadutils.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libadutils/common/libadutils.h	Fri Jul 17 17:54:42 2009 -0700
@@ -164,7 +164,7 @@
 				void *ldap_res_search_argp,
 				adutils_query_state_t **state);
 extern adutils_rc	adutils_lookup_batch_add(adutils_query_state_t *state,
-				const char *filter, const char **attrs,
+				const char *filter, const char * const *attrs,
 				const char *edomain, adutils_result_t **result,
 				adutils_rc *rc);
 extern adutils_rc	adutils_lookup_batch_end(
@@ -181,6 +181,8 @@
 				const char *sid);
 extern void		adutils_set_logger(adutils_logger logger);
 
+extern boolean_t	domain_eq(const char *a, const char *b);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/lib/libadutils/common/mapfile-vers	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libadutils/common/mapfile-vers	Fri Jul 17 17:54:42 2009 -0700
@@ -80,6 +80,7 @@
 	ad_disc_get_SiteName;
 	ad_disc_get_TrustedDomains;
 	ad_disc_get_DomainsInForest;
+	domain_eq;
     local:
 	*;
 };
--- a/usr/src/lib/libidmap/Makefile	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libidmap/Makefile	Fri Jul 17 17:54:42 2009 -0700
@@ -25,15 +25,18 @@
 
 include $(SRC)/lib/Makefile.lib
 
-HDRS =		idmap.h
+HDRS =		idmap.h directory.h
 HDRDIR =	common
 
 SUBDIRS	=	$(MACH)
 $(BUILD64)SUBDIRS += $(MACH64)
 
 POFILE =	libidmap.po
-MSGFILES =	`$(GREP) -l gettext common/*.[ch]`
-XGETFLAGS =	-a
+MSGFILES:sh =	echo common/*.[ch]
+XGETTEXT =	$(GNUXGETTEXT)
+XGETFLAGS =	--foreign-user --strict -n -E --width=72 \
+		--omit-header --keyword=directoryError:2 \
+		--language=C --force-po
 
 all := 		TARGET = all
 clean :=	TARGET = clean
@@ -49,7 +52,7 @@
 
 check:		$(CHECKHDRS)
 
-$(POFILE):	pofile_MSGFILES
+$(POFILE):	$(MSGFILES)
 		$(BUILDPO.msgfiles)
 
 _msg:		$(MSGDOMAINPOFILE)
--- a/usr/src/lib/libidmap/Makefile.com	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libidmap/Makefile.com	Fri Jul 17 17:54:42 2009 -0700
@@ -26,11 +26,24 @@
 
 LIBRARY =	libidmap.a
 VERS =		.1
-OBJECTS =	idmap_xdr.o utils.o idmap_api.o namemaps.o idmap_cache.o
-LINT_OBJECTS =	utils.o idmap_api.o namemaps.o idmap_cache.o
+LINT_OBJECTS =	\
+	directory_client.o	\
+	directory_error.o	\
+	directory_helper.o		\
+	directory_rpc_clnt.o	\
+	sidutil.o		\
+	sized_array.o		\
+	idmap_api.o		\
+	idmap_cache.o		\
+	miscutils.o		\
+	namemaps.o		\
+	utils.o
+
+OBJECTS = $(LINT_OBJECTS)	\
+	idmap_xdr.o
 
 include ../../Makefile.lib
-
+C99MODE = $(C99_ENABLE)
 
 LIBS =		$(DYNLIB) $(LINTLIB)
 LDLIBS +=	-lc -lldap -lsldap -lavl -ladutils -lnsl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/directory.h	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,167 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+#ifndef _DIRECTORY_H
+#define	_DIRECTORY_H
+
+/*
+ * A suite of functions for retrieving information about objects
+ * in a directory service.
+ *
+ * Currently it is limited to retrieving SIDs from names, and vice
+ * versa.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * This bitmap is a distillation of the objectClass attribute,
+ * reporting those classes that Solaris finds "interesting".
+ *
+ * All undefined bits are reserved and must be ignored.
+ */
+#define	DIRECTORY_CLASS_USER	0x0000000000000001
+#define	DIRECTORY_CLASS_GROUP	0x0000000000000002
+
+/*
+ * Opaque pointer to a directory search context.
+ */
+typedef struct directory *directory_t;
+
+/*
+ * Opaque pointer to a structure that describes an error.
+ * Note that NULL means no error.
+ */
+typedef struct directory_error *directory_error_t;
+
+/*
+ * Initialize a directory query session, returning in *d a directory_t
+ * that should be used for query transactions.
+ */
+directory_error_t directory_open(directory_t *d);
+
+/*
+ * Tear down a directory query session.
+ * There is an argument that this should return a directory_error_t, but
+ * then what state would the directory_t be in, and what should you do
+ * if you were doing the directory_close as a result of encountering an error?
+ *
+ * Does nothing if d==NULL.
+ */
+void directory_close(directory_t d);
+
+/*
+ * All directory_t functions return NULL on success or a pointer to a
+ * directory_error_t structure on failure.  The caller must call
+ * directory_error_free() on any non-NULL directory_error_t structures returned.
+ *
+ * Strings returned from the directory_error functions are are
+ * invalidated when the directory_error_t itself is freed.
+ */
+
+directory_error_t directory_error(const char *code, const char *fmt, ...);
+
+/*
+ * Determines whether this directory_error_t is an instance of the
+ * particular error, or a subclass of that error.
+ */
+boolean_t directory_error_is_instance_of(directory_error_t de,
+    char *error_string);
+
+/*
+ * Returns a printable version of this directory_error_t, suitable for
+ * human consumption.
+ *
+ * The string returned is valid as long as the directory_error_t itself is
+ * valid, and is freed when the directory_error_t is freed.
+ */
+const char *directory_error_printable(directory_error_t de);
+
+/*
+ * Returns the error code for the particular error, as a string.
+ * Note that this function should not normally be used to answer
+ * the question "did error X happen", since the value returned
+ * could be a subclass of X.  directory_error_is_instance_of is intended
+ * to answer that question.  This function is more appropriate for
+ * logging, where one would want to log the error code and the list
+ * of parameters so as to allow structured analysis of the error
+ * after the fact.
+ *
+ * The string returned is valid as long as the directory_error_t itself is
+ * valid, and is freed when the directory_error_t is freed.
+ */
+const char *directory_error_code(directory_error_t de);
+
+/*
+ * Returns one of the parameters of the directory_error_t, or NULL if
+ * the parameter does not exist.
+ *
+ * Note that by definition error subclasses have initial parameters
+ * the same as their superclasses.
+ *
+ * The string returned is valid as long as the directory_error_t itself is
+ * valid, and is freed when the directory_error_t is freed.
+ */
+const char *directory_error_param(directory_error_t de, int param);
+
+/*
+ * Frees the memory (if any) allocated for the directory_error_t.
+ * This frees all strings that might have been derived from this
+ * directory_error_t through directory_error_code, directory_error_printable,
+ * et cetera.
+ *
+ * Does nothing if de==NULL.
+ */
+void directory_error_free(directory_error_t de);
+
+/*
+ * Utility functions to look up a SID given a name, and vice versa.
+ * Caller must free() the result (sid or name, respectively).
+ */
+directory_error_t directory_sid_from_name(directory_t d, char *name, char **sid,
+    uint64_t *classes);
+directory_error_t directory_sid_from_user_name(directory_t d, char *name,
+    char **sid);
+directory_error_t directory_sid_from_group_name(directory_t d, char *name,
+    char **sid);
+directory_error_t directory_name_from_sid(directory_t d, char *sid, char **name,
+    uint64_t *classes);
+directory_error_t directory_canon_from_name(directory_t d, char *name,
+    char **canon, uint64_t *classes);
+directory_error_t directory_canon_from_user_name(directory_t d, char *name,
+    char **canon);
+directory_error_t directory_canon_from_group_name(directory_t d, char *name,
+    char **canon);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DIRECTORY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/directory_client.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,366 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Directory lookup functions.  These are shims that translate from the API
+ * into the RPC protocol.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include "directory.h"
+#include "directory_private.h"
+#include <rpcsvc/idmap_prot.h>
+#include "directory_library_impl.h"
+#include "sized_array.h"
+
+static directory_error_t copy_directory_attribute_value(
+    directory_attribute_value_t *dav,
+    directory_values_rpc *dav_rpc);
+static directory_error_t copy_directory_entry(directory_entry_t *ent,
+    directory_entry_rpc *ent_rpc);
+static void directory_results_free(directory_results_rpc *dr);
+static directory_datum_t directory_datum(void *data, size_t len);
+static void directory_datum_free(directory_datum_t d);
+
+/*
+ * This is the actual implementation of the opaque directory_t structure.
+ */
+struct directory {
+	CLIENT	*client;
+};
+
+/*
+ * Set up a directory search context.
+ */
+directory_error_t
+directory_open(directory_t *ret)
+{
+	directory_t d;
+	directory_error_t de;
+	char host[] = "localhost";
+
+	*ret = NULL;
+
+	d = calloc(1, sizeof (*d));
+	if (d == NULL)
+		goto nomem;
+
+	d->client = clnt_door_create(IDMAP_PROG, IDMAP_V1, 0);
+	if (d->client == NULL) {
+		de = directory_error("clnt_create.directory_open",
+		    "Error: %1",
+		    clnt_spcreateerror(host),
+		    NULL);
+		goto err;
+	}
+
+	*ret = d;
+	return (NULL);
+
+nomem:
+	de = directory_error("ENOMEM.directory_open",
+	    "Insufficient memory setting up directory access", NULL);
+err:
+	directory_close(d);
+	return (de);
+}
+
+/*
+ * Tear down a directory search context.
+ *
+ * Does nothing if d==NULL.
+ */
+void
+directory_close(directory_t d)
+{
+	if (d == NULL)
+		return;
+
+	if (d->client != NULL)
+		clnt_destroy(d->client);
+
+	free(d);
+}
+
+/*
+ * Given a list of identifiers, a list of their types, and a list of attributes,
+ * return the information.
+ */
+directory_error_t
+directory_get_v(
+    directory_t d,
+    directory_entry_list_t *ret,
+    char **ids,
+    int nids,
+    char *types,
+    char **attr_list)
+{
+	int nattrs;
+	directory_entry_list_t del;
+	directory_error_t de;
+	directory_results_rpc dr;
+	idmap_utf8str_list sl_ids;
+	idmap_utf8str_list sl_attrs;
+	directory_entry_rpc *users;
+	int i;
+	enum clnt_stat cs;
+
+	*ret = NULL;
+	del = NULL;
+
+	if (nids == 0) {
+		for (nids = 0; ids[nids] != NULL; nids++)
+			/* LOOP */;
+	}
+
+	for (nattrs = 0; attr_list[nattrs] != NULL; nattrs++)
+		/* LOOP */;
+
+	sl_ids.idmap_utf8str_list_len = nids;
+	sl_ids.idmap_utf8str_list_val = ids;
+	sl_attrs.idmap_utf8str_list_len = nattrs;
+	sl_attrs.idmap_utf8str_list_val = attr_list;
+
+	(void) memset(&dr, 0, sizeof (dr));
+	cs = directory_get_common_1(sl_ids, types, sl_attrs, &dr, d->client);
+	if (cs != RPC_SUCCESS) {
+		char errbuf[100];	/* well long enough for any integer */
+		(void) sprintf(errbuf, "%d", cs);
+		de = directory_error("RPC.Get_common",
+		    "Get_common RPC (%1)%2", errbuf,
+		    clnt_sperror(d->client, ""), NULL);
+		goto err;
+	}
+
+	if (dr.failed) {
+		de = directory_error_from_rpc(
+		    &dr.directory_results_rpc_u.err);
+		goto err;
+	}
+
+	assert(dr.directory_results_rpc_u.entries.entries_len == nids);
+
+	users = dr.directory_results_rpc_u.entries.entries_val;
+
+	del = sized_array(nids, sizeof (directory_entry_t));
+
+	for (i = 0; i < nids; i++) {
+		de = copy_directory_entry(&del[i], &users[i]);
+		if (de != NULL)
+			goto err;
+	}
+
+	directory_results_free(&dr);
+
+	*ret = del;
+	return (NULL);
+
+err:
+	directory_results_free(&dr);
+	directory_free(del);
+	return (de);
+}
+
+/*
+ * Free the results from a directory_get_*() request.
+ */
+void
+directory_free(directory_entry_list_t del)
+{
+	directory_entry_t *ent;
+	directory_attribute_value_t dav;
+	int i;
+	int j;
+	int k;
+
+	if (del == NULL)
+		return;
+
+	/* For each directory entry returned */
+	for (i = 0; i < sized_array_n(del); i++) {
+		ent = &del[i];
+
+		if (ent->attrs != NULL) {
+			/* For each attribute */
+			for (j = 0; j < sized_array_n(ent->attrs); j++) {
+				dav = ent->attrs[j];
+				if (dav != NULL) {
+					for (k = 0; k < sized_array_n(dav); k++)
+						directory_datum_free(dav[k]);
+
+					sized_array_free(dav);
+				}
+			}
+			sized_array_free(ent->attrs);
+		}
+
+		directory_error_free(ent->err);
+	}
+
+	sized_array_free(del);
+}
+
+/*
+ * Create a directory datum.  Note that we allocate an extra byte and
+ * zero it, so that strings get null-terminated.  Return NULL on error.
+ */
+static
+directory_datum_t
+directory_datum(void *data, size_t len)
+{
+	void *p;
+
+	p = sized_array(len + 1, 1);
+	if (p == NULL)
+		return (NULL);
+	(void) memcpy(p, data, len);
+	*((char *)p + len) = '\0';
+	return (p);
+}
+
+/*
+ * Return the size of a directory_datum_t.  Note that this does not include
+ * the terminating \0, so it represents the value as returned by LDAP.
+ */
+size_t
+directory_datum_len(directory_datum_t d)
+{
+	/*
+	 * Deduct the terminal \0, so that binary data gets the
+	 * expected length.
+	 */
+	return (sized_array_n(d) - 1);
+}
+
+static
+void
+directory_datum_free(directory_datum_t d)
+{
+	sized_array_free(d);
+}
+
+/*
+ * Unmarshall an RPC directory entry into an API directory entry.
+ */
+static
+directory_error_t
+copy_directory_entry(
+    directory_entry_t *ent,
+    directory_entry_rpc *ent_rpc)
+{
+	int nattrs;
+	int i;
+	directory_error_t de;
+
+	/* If the entry wasn't found, leave the entry attrs and err NULL. */
+	if (ent_rpc->status == DIRECTORY_NOT_FOUND)
+		return (NULL);
+
+	if (ent_rpc->status == DIRECTORY_ERROR) {
+		ent->err = directory_error_from_rpc(
+		    &ent_rpc->directory_entry_rpc_u.err);
+		return (NULL);
+	}
+
+	nattrs = ent_rpc->directory_entry_rpc_u.attrs.attrs_len;
+
+	ent->attrs = sized_array(nattrs, sizeof (directory_attribute_value_t));
+	if (ent->attrs == NULL) {
+		return (directory_error("ENOMEM.copy_directory_entry",
+		    "Insufficient memory copying directory entry", NULL));
+	}
+	for (i = 0; i < nattrs; i++) {
+		de = copy_directory_attribute_value(&ent->attrs[i],
+		    &ent_rpc->directory_entry_rpc_u.attrs.attrs_val[i]);
+		if (de != NULL)
+			return (de);
+	}
+
+	return (NULL);
+}
+
+/*
+ * Unmarshall an RPC directory attribute value into the API equivalent.
+ *
+ * Note that on error some entries may have been copied, and so
+ * the caller needs to clean up dav.  This is normally not a problem
+ * since the caller will have called this function several times and
+ * will need to clean up the results from the other calls too.
+ */
+static
+directory_error_t
+copy_directory_attribute_value(
+    directory_attribute_value_t *dav,
+    directory_values_rpc *dav_rpc)
+{
+	int i;
+	int nvals;
+	directory_value_rpc *vals;
+
+	/* If it wasn't found, leave the corresponding entry NULL */
+	if (!dav_rpc->found)
+		return (NULL);
+
+	nvals = dav_rpc->directory_values_rpc_u.values.values_len;
+	*dav = sized_array(nvals + 1, sizeof (directory_datum_t));
+	if (*dav == NULL) {
+		return (directory_error("ENOMEM.copy_directory_attribute_value",
+		    "Insufficient memory copying directory entry", NULL));
+	}
+
+	vals = dav_rpc->directory_values_rpc_u.values.values_val;
+	for (i = 0; i < nvals; i++) {
+		(*dav)[i] = directory_datum(vals[i].directory_value_rpc_val,
+		    vals[i].directory_value_rpc_len);
+		if ((*dav)[i] == NULL) {
+			return (directory_error(
+			    "ENOMEM.copy_directory_attribute_value",
+			    "Insufficient memory copying directory entry",
+			    NULL));
+		}
+	}
+
+	return (NULL);
+}
+
+/*
+ * Free the results of a directory RPC request.
+ */
+static
+void
+directory_results_free(directory_results_rpc *dr)
+{
+	xdr_free(xdr_directory_results_rpc, (char *)&dr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/directory_error.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,498 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Error handling support for directory lookup.
+ * Actually, this is intended to be a very generic and extensible error
+ * reporting mechanism.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <thread.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <malloc.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <idmap_impl.h>
+#include <rpcsvc/idmap_prot.h>
+#include <libintl.h>
+#include "directory.h"
+
+/*
+ * This is the actual implementation of the opaque directory_error_t structure.
+ */
+struct directory_error {
+	/*
+	 * True if this directory_error_t is statically allocated.  Used to
+	 * handle out of memory errors during error reporting.
+	 */
+	boolean_t	is_static;
+
+	/*
+	 * The error code.  This is a locale-independent string that
+	 * represents the precise error (to some level of granularity)
+	 * that occurred.  Internationalization processing could map it
+	 * to an message.  Errors may be subclassed by appending a dot
+	 * and a name for the subclass.
+	 *
+	 * Note that this code plus the parameters allows for structured
+	 * processing of error results.
+	 */
+	char		*code;
+
+	/*
+	 * The default (in the absence of internationalization) format for
+	 * the error message.  %n interposes params[n - 1].
+	 */
+	char		*fmt;
+
+	/*
+	 * Parameters to the error message.  Note that subclasses are
+	 * required to have the same initial parameters as their superclasses,
+	 * so that code that processes the superclass can work on the subclass.
+	 */
+	int		nparams;
+	char		**params;
+
+	/*
+	 * Cached printable form (that is, with params[] interpolated into
+	 * fmt) of the error message.  Created when requested.
+	 */
+	char		*printable;
+};
+
+static directory_error_t directory_error_internal_error(int err);
+
+/*
+ * For debugging, reference count of directory_error instances still in
+ * existence.  When the system is idle, this should be zero.
+ * Note that no attempt is made to make this MT safe, so it is not reliable
+ * in an MT environment.
+ */
+static int directory_errors_outstanding = 0;
+
+/*
+ * Free the specified directory_error_t.  Note that this invalidates all strings
+ * returned based on it.
+ *
+ * Does nothing when de==NULL.
+ */
+void
+directory_error_free(directory_error_t de)
+{
+	int i;
+
+	if (de == NULL)
+		return;
+
+	/* Don't free our internal static directory_error_ts! */
+	if (de->is_static)
+		return;
+
+	free(de->code);
+	de->code = NULL;
+	free(de->fmt);
+	de->fmt = NULL;
+
+	/* Free parameters, if any */
+	if (de->params != NULL) {
+		for (i = 0; i < de->nparams; i++) {
+			free(de->params[i]);
+			de->params[i] = NULL;
+		}
+		free(de->params);
+		de->params = NULL;
+	}
+
+	/* Free cached printable */
+	free(de->printable);
+	de->printable = NULL;
+
+	free(de);
+
+	directory_errors_outstanding--;
+}
+
+/*
+ * de = directory_error(code, fmt [, arg1 ... ]);
+ * Code, fmt, and arguments must be strings and will be copied.
+ */
+directory_error_t
+directory_error(const char *code, const char *fmt, ...)
+{
+	directory_error_t de = NULL;
+	va_list va;
+	int i;
+
+	de = calloc(1, sizeof (*de));
+	if (de == NULL)
+		goto nomem;
+
+	directory_errors_outstanding++;
+
+	de->is_static = B_FALSE;
+
+	de->code = strdup(code);
+	if (de->code == NULL)
+		goto nomem;
+
+	de->fmt = strdup(fmt);
+	if (de->fmt == NULL)
+		goto nomem;
+
+	/* Count our parameters */
+	va_start(va, fmt);
+	for (i = 0; va_arg(va, char *) != NULL; i++)
+		/* LOOP */;
+	va_end(va);
+
+	de->nparams = i;
+
+	/*
+	 * Note that we do not copy the terminating NULL because we have
+	 * a count.
+	 */
+	de->params = calloc(de->nparams, sizeof (char *));
+	if (de->params == NULL)
+		goto nomem;
+
+	va_start(va, fmt);
+	for (i = 0; i < de->nparams; i++) {
+		de->params[i] = strdup((char *)va_arg(va, char *));
+		if (de->params[i] == NULL) {
+			va_end(va);
+			goto nomem;
+		}
+	}
+	va_end(va);
+
+	return (de);
+
+nomem:;
+	int err = errno;
+	directory_error_free(de);
+	return (directory_error_internal_error(err));
+}
+
+/*
+ * Transform a directory_error returned by RPC into a directory_error_t.
+ */
+directory_error_t
+directory_error_from_rpc(directory_error_rpc *de_rpc)
+{
+	directory_error_t de;
+	int i;
+
+	de = calloc(1, sizeof (*de));
+	if (de == NULL)
+		goto nomem;
+
+	directory_errors_outstanding++;
+
+	de->is_static = B_FALSE;
+	de->code = strdup(de_rpc->code);
+	if (de->code == NULL)
+		goto nomem;
+	de->fmt = strdup(de_rpc->fmt);
+	if (de->fmt == NULL)
+		goto nomem;
+
+	de->nparams = de_rpc->params.params_len;
+
+	de->params = calloc(de->nparams, sizeof (char *));
+	if (de->params == NULL)
+		goto nomem;
+
+	for (i = 0; i < de->nparams; i++) {
+		de->params[i] = strdup(de_rpc->params.params_val[i]);
+		if (de->params[i] == NULL)
+			goto nomem;
+	}
+
+	return (de);
+
+nomem:;
+	int err = errno;
+	directory_error_free(de);
+	return (directory_error_internal_error(err));
+}
+
+/*
+ * Convert a directory_error_t into a directory_error to send over RPC.
+ *
+ * Returns TRUE on successful conversion, FALSE on failure.
+ *
+ * Frees the directory_error_t.
+ *
+ * Note that most functions in this suite return boolean_t, as defined
+ * by types.h.  This function is intended to be used directly as the
+ * return value from an RPC service function, and so it returns bool_t.
+ */
+bool_t
+directory_error_to_rpc(directory_error_rpc *de_rpc, directory_error_t de)
+{
+	int i;
+	idmap_utf8str *params;
+
+	de_rpc->code = strdup(de->code);
+	if (de_rpc->code == NULL)
+		goto nomem;
+
+	de_rpc->fmt = strdup(de->fmt);
+	if (de_rpc->fmt == NULL)
+		goto nomem;
+
+	params = calloc(de->nparams, sizeof (idmap_utf8str));
+	if (params == NULL)
+		goto nomem;
+	de_rpc->params.params_val = params;
+	de_rpc->params.params_len = de->nparams;
+
+	for (i = 0; i < de->nparams; i++) {
+		params[i] = strdup(de->params[i]);
+		if (params[i] == NULL)
+			goto nomem;
+	}
+
+	directory_error_free(de);
+	return (TRUE);
+
+nomem:
+	logger(LOG_ERR, "Warning:  failed to convert error for RPC\n"
+	    "Original error:  %s\n"
+	    "Conversion error:  %s\n",
+	    strerror(errno),
+	    directory_error_printable(de));
+	directory_error_free(de);
+	return (FALSE);
+}
+
+/*
+ * Determines whether this directory_error_t is an instance of the
+ * particular error, or a subclass of that error.
+ */
+boolean_t
+directory_error_is_instance_of(directory_error_t de, char *code)
+{
+	int len;
+
+	if (de == NULL || de->code == NULL)
+		return (B_FALSE);
+
+	len = strlen(code);
+
+	if (strncasecmp(de->code, code, len) != 0)
+		return (B_FALSE);
+
+	if (de->code[len] == '\0' || de->code[len] == '.')
+		return (B_TRUE);
+
+	return (B_FALSE);
+}
+
+/*
+ * Expand the directory_error_t in de into buf, returning the size of the
+ * resulting string including terminating \0.  If buf is NULL, just
+ * return the size.
+ *
+ * Return -1 if there are no substitutions, so that the caller can
+ * avoid memory allocation.
+ */
+static
+int
+directory_error_expand(char *buf, directory_error_t de)
+{
+	int bufsiz;
+	boolean_t has_subst;
+	const char *p;
+	char c;
+	long n;
+	const char *s;
+	char *newp;
+
+	bufsiz = 0;
+	has_subst = B_FALSE;
+
+	for (p = dgettext(TEXT_DOMAIN, de->fmt); *p != '\0'; ) {
+		c = *p++;
+		if (c == '%') {
+			has_subst = B_TRUE;
+			if (isdigit(*p)) {
+				n = strtol(p, &newp, 10);
+				p = newp;
+				if (de->params == NULL ||
+				    n < 1 ||
+				    n > de->nparams)
+					s = dgettext(TEXT_DOMAIN, "(missing)");
+				else
+					s = de->params[n - 1];
+				if (buf != NULL)
+					(void) strcpy(buf + bufsiz, s);
+				bufsiz += strlen(s);
+				continue;
+			}
+		}
+		if (buf != NULL)
+			buf[bufsiz] = c;
+		bufsiz++;
+	}
+
+	if (buf != NULL)
+		buf[bufsiz] = '\0';
+	bufsiz++;
+
+	return (has_subst ? bufsiz : -1);
+}
+
+/*
+ * Returns a printable version of this directory_error_t, suitable for
+ * human consumption.
+ *
+ * The value returned is valid as long as the directory_error_t is valid,
+ * and is freed when the directory_error_t is freed.
+ */
+const char *
+directory_error_printable(directory_error_t de)
+{
+	char *s;
+	int bufsiz;
+
+	if (de->printable != NULL)
+		return (de->printable);
+
+	bufsiz = directory_error_expand(NULL, de);
+
+	/*
+	 * Short circuit case to avoid memory allocation when there is
+	 * no parameter substitution.
+	 */
+	if (bufsiz < 0)
+		return (dgettext(TEXT_DOMAIN, de->fmt));
+
+	s = malloc(bufsiz);
+	if (s == NULL) {
+		return (dgettext(TEXT_DOMAIN,
+		    "Out of memory while expanding directory_error_t"));
+	}
+
+	(void) directory_error_expand(s, de);
+
+	/*
+	 * Stash the expansion away for later free, and to short-circuit
+	 * repeated expansions.
+	 */
+	de->printable = s;
+
+	return (de->printable);
+}
+
+/*
+ * Returns the error code for the particular error, as a string.
+ * Note that this function should not normally be used to answer
+ * the question "did error X happen", since the value returned
+ * could be a subclass of X.  directory_error_is_instance_of is intended
+ * to answer that question.
+ *
+ * The value returned is valid as long as the directory_error_t is valid,
+ * and is freed when the directory_error_t is freed.
+ */
+const char *
+directory_error_code(directory_error_t de)
+{
+	return (de->code);
+}
+
+/*
+ * Returns one of the parameters of the directory_error_t, or NULL if
+ * the parameter does not exist.
+ *
+ * Note that it is required that error subclasses have initial parameters
+ * the same as their superclasses.
+ *
+ * The value returned is valid as long as the directory_error_t is valid,
+ * and is freed when the directory_error_t is freed.
+ */
+const char *
+directory_error_param(directory_error_t de, int param)
+{
+	if (param >= de->nparams)
+		return (NULL);
+	return (de->params[param]);
+}
+
+/*
+ * Here are some (almost) constant directory_error_t structures
+ * for use in reporting errors encountered while creating a
+ * directory_error_t structure.  Unfortunately, the original error
+ * report is lost.
+ */
+#define	gettext(x)	x	/* let xgettext see these messages */
+static struct directory_error directory_error_ENOMEM = {
+	B_TRUE,
+	"ENOMEM.directory_error_t",
+	gettext("Out of memory while creating a directory_error_t"),
+	0, NULL,
+	NULL,
+};
+
+static struct directory_error directory_error_EAGAIN = {
+	B_TRUE,
+	"EAGAIN.directory_error_t",
+	gettext("Out of resources while creating a directory_error_t"),
+	0, NULL,
+	NULL,
+};
+
+/* 40 is big enough for even 128 bits */
+static char directory_error_unknown_errno[40] = "0";
+static char *directory_error_unknown_params[] = {
+    directory_error_unknown_errno
+};
+static struct directory_error directory_error_unknown = {
+	B_TRUE,
+	"Unknown.directory_error_t",
+	gettext("Unknown error (%1) while creating a directory_error_t"),
+	1, directory_error_unknown_params,
+	NULL,
+};
+#undef	gettext
+
+static
+directory_error_t
+directory_error_internal_error(int err)
+{
+	switch (err) {
+	case ENOMEM:	return (&directory_error_ENOMEM);
+	case EAGAIN:	return (&directory_error_EAGAIN);
+	default:
+		/* Pray that we don't have a reentrancy problem ... */
+		(void) sprintf(directory_error_unknown_errno, "%u", err);
+		return (&directory_error_unknown);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/directory_helper.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,302 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Some helper routines for directory lookup.  These offer functions that
+ * you could implement yourself on top of the generic routines, but since
+ * they're a common request we implement them here.  (Well, OK, we cheat a bit
+ * and call an internal routine to do the dirty work to reduce code
+ * duplication, but you could still implement them using the generic routines.)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <libadutils.h>
+#include <rpcsvc/idmap_prot.h>
+#include "directory.h"
+#include "directory_private.h"
+#include "directory_library_impl.h"
+#include "miscutils.h"
+#include "sidutil.h"
+
+/*
+ * Given a username, return a text-form SID.
+ *
+ * The SID must be free()ed by the caller.
+ *
+ * d, if non-NULL, specifies an existing directory-search context.
+ * If NULL, a temporary one will be created.
+ */
+directory_error_t
+directory_sid_from_name_common(
+    directory_t d,
+    char *name,
+    char *type,
+    char **sid,
+    uint64_t *classes)
+{
+	directory_t d1 = NULL;
+	static char *attrs[] = {
+		"objectSid",
+		"objectClass",
+		NULL,
+	};
+	directory_entry_t *ret_list = NULL;
+	directory_error_t de;
+	struct ret_sid {
+		sid_t **objectSid;
+		char **objectClass;
+	} *ret_sid;
+
+	/* Prep for error cases. */
+	*sid = NULL;
+	if (classes != NULL)
+		*classes = 0;
+
+	if (d == NULL) {
+		de = directory_open(&d1);
+		if (de != NULL)
+			goto out;
+	} else {
+		d1 = d;
+	}
+
+	de = directory_get_v(d1, &ret_list, &name, 1, type, attrs);
+	if (de != NULL)
+		goto out;
+	if (ret_list[0].err != NULL) {
+		de = ret_list[0].err;
+		ret_list[0].err = NULL;
+		goto out;
+	}
+
+	ret_sid = (struct ret_sid *)ret_list[0].attrs;
+	if (ret_sid == NULL)
+		goto out;
+
+	if (ret_sid->objectSid != NULL &&
+	    ret_sid->objectSid[0] != NULL) {
+		char text_sid[SID_STRSZ+1];
+		sid_from_le(ret_sid->objectSid[0]);
+		sid_tostr(ret_sid->objectSid[0], text_sid);
+		*sid = strdup(text_sid);
+		if (*sid == NULL)
+			goto nomem;
+	}
+
+	if (ret_sid->objectClass != NULL &&
+	    classes != NULL)
+		*classes = class_bitmap(ret_sid->objectClass);
+
+	goto out;
+
+nomem:
+	de = directory_error("ENOMEM.directory_sid_from_name_common",
+	    "Insufficient memory retrieving data about SID", NULL);
+
+out:
+	directory_free(ret_list);
+	if (d == NULL)
+		directory_close(d1);
+	return (de);
+}
+
+directory_error_t
+directory_sid_from_name(
+    directory_t d,
+    char *name,
+    char **sid,
+    uint64_t *classes)
+{
+	return (directory_sid_from_name_common(d, name, DIRECTORY_ID_NAME, sid,
+	    classes));
+}
+
+directory_error_t
+directory_sid_from_user_name(directory_t d, char *name, char **sid)
+{
+	return (directory_sid_from_name_common(d, name, DIRECTORY_ID_USER, sid,
+	    NULL));
+}
+
+directory_error_t
+directory_sid_from_group_name(directory_t d, char *name, char **sid)
+{
+	return (directory_sid_from_name_common(d, name, DIRECTORY_ID_GROUP, sid,
+	    NULL));
+}
+
+/*
+ * Given a name or text-format SID, return a user@domain.
+ *
+ * The user@domain returned must be free()ed by the caller.
+ *
+ * Returns NULL and sets *name to NULL if no error occurred but the specified
+ * entity does not exist.
+ *
+ * d, if non-NULL, specifies an existing directory-search context.
+ * If NULL, a temporary one will be created.
+ */
+static
+directory_error_t
+directory_canon_common(
+    directory_t d,
+    char *id,
+    char *id_type,
+    char **canon,
+    uint64_t *classes)
+{
+	directory_t d1 = NULL;
+	directory_entry_t *ret_list = NULL;
+	directory_error_t de;
+	/*
+	 * Attributes required to generate a canonical name, in named-list and
+	 * structure form.
+	 */
+	static char *attrs[] = {
+		"x-sun-canonicalName",
+		"objectClass",
+		NULL,
+	};
+
+	struct canon_name_ret {
+		char **x_sun_canonicalName;
+		char **objectClass;
+	} *ret_name;
+
+	/* Prep for error cases. */
+	*canon = NULL;
+	if (classes != NULL)
+		*classes = 0;
+
+	if (d == NULL) {
+		de = directory_open(&d1);
+		if (de != NULL)
+			goto out;
+	} else {
+		d1 = d;
+	}
+
+	de = directory_get_v(d1, &ret_list, &id, 1, id_type, attrs);
+	if (de != NULL)
+		goto out;
+	if (ret_list[0].err != NULL) {
+		de = ret_list[0].err;
+		ret_list[0].err = NULL;
+		goto out;
+	}
+
+	ret_name = (struct canon_name_ret *)ret_list[0].attrs;
+	if (ret_name == NULL)
+		goto out;
+
+	if (ret_name->x_sun_canonicalName != NULL &&
+	    ret_name->x_sun_canonicalName[0] != NULL) {
+		*canon = strdup(ret_name->x_sun_canonicalName[0]);
+		if (*canon == NULL)
+			goto nomem;
+	}
+
+	if (ret_name->objectClass != NULL &&
+	    classes != NULL)
+		*classes = class_bitmap(ret_name->objectClass);
+
+	goto out;
+
+nomem:
+	de = directory_error("ENOMEM.directory_canon_common",
+	    "Insufficient memory retrieving data about name", NULL);
+
+out:
+	directory_free(ret_list);
+	if (d == NULL)
+		directory_close(d1);
+	return (de);
+}
+
+directory_error_t
+directory_name_from_sid(
+    directory_t d,
+    char *sid,
+    char **canon,
+    uint64_t *classes)
+{
+	return (directory_canon_common(d, sid, DIRECTORY_ID_SID, canon,
+	    classes));
+}
+
+directory_error_t
+directory_canon_from_name(
+    directory_t d,
+    char *name,
+    char **canon,
+    uint64_t *classes)
+{
+	return (directory_canon_common(d, name, DIRECTORY_ID_NAME, canon,
+	    classes));
+}
+
+directory_error_t
+directory_canon_from_user_name(directory_t d, char *name, char **canon)
+{
+	return (
+	    directory_canon_common(d, name, DIRECTORY_ID_USER, canon, NULL));
+}
+
+directory_error_t
+directory_canon_from_group_name(directory_t d, char *name, char **canon)
+{
+	return (
+	    directory_canon_common(d, name, DIRECTORY_ID_GROUP, canon, NULL));
+}
+
+boolean_t
+is_in_list(char **list, char *val)
+{
+	for (; *list != NULL; list++) {
+		if (strcaseeq(*list, val))
+			return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+uint64_t
+class_bitmap(char **objectClass)
+{
+	uint64_t ret = 0;
+
+	for (; *objectClass != NULL; objectClass++) {
+		if (strcaseeq(*objectClass, "user") ||
+		    strcaseeq(*objectClass, "posixAccount"))
+			ret |= DIRECTORY_CLASS_USER;
+
+		if (strcaseeq(*objectClass, "group") ||
+		    strcaseeq(*objectClass, "posixGroup"))
+			ret |= DIRECTORY_CLASS_GROUP;
+	}
+
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/directory_library_impl.h	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+#ifndef _DIRECTORY_LIBRARY_IMPL_H
+#define	_DIRECTORY_LIBRARY_IMPL_H
+
+/*
+ * Internal implementation of the client side of directory lookup.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+directory_error_t directory_error_from_rpc(directory_error_rpc *de_rpc);
+bool_t directory_error_to_rpc(directory_error_rpc *de_rpc,
+    directory_error_t de);
+
+
+directory_error_t directory_get_v(directory_t d, directory_entry_list_t *ret,
+    char **ids, int nids, char *types, char **attr_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DIRECTORY_LIBRARY_IMPL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/directory_private.h	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,101 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+#ifndef _DIRECTORY_PRIVATE_H
+#define	_DIRECTORY_PRIVATE_H
+
+/*
+ * A suite of functions for retrieving information about objects
+ * in a directory service.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+#define	DIRECTORY_ID_NAME	"n"
+#define	DIRECTORY_ID_USER	"u"
+#define	DIRECTORY_ID_GROUP	"g"
+#define	DIRECTORY_ID_SID	"s"
+
+/*
+ * Structure of the returned data.
+ * Note that this is constructed from the bottom up; what is returned is
+ * a directory_entry_list_t.
+ */
+typedef void *directory_datum_t;
+typedef directory_datum_t *directory_attribute_value_t;
+typedef struct {
+	directory_attribute_value_t *attrs;
+	directory_error_t err;
+} directory_entry_t;
+typedef directory_entry_t *directory_entry_list_t;
+
+/*
+ * Retrieve information about a user or group.  By way of analogy to exec(2),
+ * the _v variants accept a list of attributes as an array, while
+ * the _l variants accept the attribute list as arguments.
+ * All variations accept a list of identifiers, and return a
+ * directory_entry_list_t in the same order.  The length of the list of user
+ * identifiers can be specified either explicitly, or by a terminating
+ * NULL if the associated count is zero.  Attributes are returned in the
+ * order they were requested, with missing attributes yielding NULL
+ * entries.
+ */
+directory_error_t directory_get_v(directory_t d, directory_entry_list_t *ret,
+    char **ids, int nids, char *types, char **attrlist);
+
+directory_error_t directory_get_l(directory_t d, directory_entry_list_t *ret,
+    char **ids, int nids, char *types, char *attr1, ...);
+
+/*
+ * Free the data structure returned by directory_get_by*().
+ *
+ * Does nothing if list==NULL.
+ */
+void directory_free(directory_entry_list_t list);
+
+/* Return the number of bytes in a directory_datum_t */
+size_t directory_datum_len(directory_datum_t d);
+
+/*
+ * Search a list, case-insensitively, for a string
+ */
+boolean_t is_in_list(char **list, char *value);
+
+/*
+ * Examine an objectClass list and distill it into a bitmap of "interesting"
+ * classes.
+ */
+uint64_t class_bitmap(char **objectClass);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DIRECTORY_PRIVATE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/directory_rpc_clnt.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,57 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * RPC shims for directory lookup.  Originally generated using rpcgen.
+ */
+
+#include <memory.h> /* for memset */
+#include <rpcsvc/idmap_prot.h>
+#ifndef _KERNEL
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#endif /* !_KERNEL */
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+enum clnt_stat
+directory_get_common_1(
+    idmap_utf8str_list ids,
+    idmap_utf8str types,
+    idmap_utf8str_list attrs,
+    directory_results_rpc *clnt_res,
+    CLIENT *clnt)
+{
+	directory_get_common_1_argument arg;
+	arg.ids = ids;
+	arg.attrs = attrs;
+	arg.types = types;
+	return (clnt_call(clnt, DIRECTORY_GET_COMMON,
+	    (xdrproc_t)xdr_directory_get_common_1_argument, (caddr_t)&arg,
+	    (xdrproc_t)xdr_directory_results_rpc, (caddr_t)clnt_res,
+	    TIMEOUT));
+}
--- a/usr/src/lib/libidmap/common/idmap_api.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libidmap/common/idmap_api.c	Fri Jul 17 17:54:42 2009 -0700
@@ -2072,9 +2072,9 @@
 
 	for (i = 0; stattable[i].msg; i++) {
 		if (stattable[i].retcode == status)
-			return (gettext(stattable[i].msg));
+			return (dgettext(TEXT_DOMAIN, stattable[i].msg));
 	}
-	return (gettext("Unknown error"));
+	return (dgettext(TEXT_DOMAIN, "Unknown error"));
 }
 
 
--- a/usr/src/lib/libidmap/common/idmap_impl.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libidmap/common/idmap_impl.h	Fri Jul 17 17:54:42 2009 -0700
@@ -134,6 +134,7 @@
 extern idmap_stat idmap_get_prop_str(idmap_handle_t *, idmap_prop_type,
     char **);
 
+extern idmap_logger logger;
 
 #ifdef __cplusplus
 }
--- a/usr/src/lib/libidmap/common/llib-lidmap	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libidmap/common/llib-lidmap	Fri Jul 17 17:54:42 2009 -0700
@@ -19,13 +19,17 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /* LINTLIBRARY */
 /* PROTOLIB1 */
 
 #include "idmap_impl.h"
+#include "miscutils.h"
+#include "directory.h"
+#include "directory_private.h"
+#include "sized_array.h"
+#include "directory_library_impl.h"
+#include "sidutil.h"
--- a/usr/src/lib/libidmap/common/mapfile-vers	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/libidmap/common/mapfile-vers	Fri Jul 17 17:54:42 2009 -0700
@@ -40,70 +40,105 @@
 
 SUNWprivate {
     global:
-	xdr_idmap_retcode;
+	directory_canon_from_group_name;
+	directory_canon_from_name;
+	directory_canon_from_user_name;
+	directory_close;
+	directory_error;
+	directory_error_code;
+	directory_error_free;
+	directory_error_from_rpc;
+	directory_error_is_instance_of;
+	directory_error_param;
+	directory_error_printable;
+	directory_error_to_rpc;
+	directory_free;
+	directory_get_v;
+	directory_name_from_sid;
+	directory_open;
+	directory_sid_from_group_name;
+	directory_sid_from_name;
+	directory_sid_from_user_name;
+	idmap_cache_get_data;
+	idmap_fini;
+	idmap_fini_namemaps;
+	idmap_free;
+	idmap_getext_gidbysid;
+	idmap_getext_pidbysid;
+	idmap_getext_sidbygid;
+	idmap_getext_sidbyuid;
+	idmap_getext_uidbysid;
+	idmap_getgidbywinname;
+	idmap_getuidbywinname;
+	idmap_getwinnamebygid;
+	idmap_getwinnamebyuid;
+	idmap_get_create;
+	idmap_get_destroy;
+	idmap_get_gidbysid;
+	idmap_get_mappings;
+	idmap_get_namemap;
+	idmap_get_pidbysid;
+	idmap_get_sidbygid;
+	idmap_get_sidbyuid;
+	idmap_get_u2w_mapping;
+	idmap_get_uidbysid;
+	idmap_get_w2u_mapping;
+	idmap_info_cpy;
+	idmap_info_free;
+	idmap_info_mov;
+	idmap_init;
+	idmap_init_namemaps;
+	idmap_iter_destroy;
+	idmap_iter_mappings;
+	idmap_iter_namerules;
+	idmap_iter_next_mapping;
+	idmap_iter_next_namerule;
+	idmap_namerule_cpy;
+	idmap_set_logger;
+	idmap_set_namemap;
+	idmap_stat2string;
+	idmap_stat4prot;
+	idmap_string2stat;
+	idmap_udt_add_namerule;
+	idmap_udt_commit;
+	idmap_udt_create;
+	idmap_udt_destroy;
+	idmap_udt_flush_namerules;
+	idmap_udt_get_conflict_rule;
+	idmap_udt_get_error_index;
+	idmap_udt_get_error_rule;
+	idmap_udt_rm_namerule;
+	idmap_unset_namemap;
+	memdup;
+	sid_free;
+	sid_from_le;
+	sid_fromstr;
+	sid_len;
+	sid_to_le;
+	sid_tostr;
+	sized_array;
+	sized_array_free;
+	sized_array_n;
+	strcaseeq;
+	streq;
+	strndup;
+	xdr_directory_entry_rpc;
+	xdr_directory_get_common_1_argument;
+	xdr_directory_results_rpc;
+	xdr_idmap_ids_res;
+	xdr_idmap_id_res;
+	xdr_idmap_list_mappings_1_argument;
+	xdr_idmap_list_namerules_1_argument;
+	xdr_idmap_mapping;
+	xdr_idmap_mappings_res;
+	xdr_idmap_mapping_batch;
 	xdr_idmap_namerule;
 	xdr_idmap_namerules_res;
-	xdr_idmap_ids_res;
-	xdr_idmap_mapping_batch;
-	xdr_idmap_list_mappings_1_argument;
-	xdr_idmap_mappings_res;
-	xdr_idmap_update_batch;
-	xdr_idmap_list_namerules_1_argument;
-	xdr_idmap_mapping;
-	xdr_idmap_id_res;
-	xdr_idmap_update_res;
 	xdr_idmap_prop_res;
 	xdr_idmap_prop_type;
-	idmap_init;
-	idmap_fini;
-	idmap_free;
-	idmap_stat2string;
-	idmap_string2stat;
-	idmap_stat4prot;
-	idmap_iter_namerules;
-	idmap_iter_next_namerule;
-	idmap_iter_mappings;
-	idmap_iter_next_mapping;
-	idmap_iter_destroy;
-	idmap_get_create;
-	idmap_get_uidbysid;
-	idmap_get_gidbysid;
-	idmap_get_pidbysid;
-	idmap_get_sidbyuid;
-	idmap_get_sidbygid;
-	idmap_get_mappings;
-	idmap_get_destroy;
-	idmap_get_w2u_mapping;
-	idmap_get_u2w_mapping;
-	idmap_udt_add_namerule;
-	idmap_udt_rm_namerule;
-	idmap_udt_destroy;
-	idmap_udt_commit;
-	idmap_udt_create;
-	idmap_udt_get_error_index;
-	idmap_udt_get_error_rule;
-	idmap_udt_get_conflict_rule;
-	idmap_udt_flush_namerules;
-	idmap_getwinnamebyuid;
-	idmap_getwinnamebygid;
-	idmap_getuidbywinname;
-	idmap_getgidbywinname;
-	idmap_namerule_cpy;
-	idmap_info_free;
-	idmap_info_cpy;
-	idmap_info_mov;
-	idmap_getext_sidbygid;
-	idmap_getext_uidbysid;
-	idmap_getext_pidbysid;
-	idmap_getext_gidbysid;
-	idmap_getext_sidbyuid;
-	idmap_set_namemap;
-	idmap_unset_namemap;
-	idmap_get_namemap;
-	idmap_init_namemaps;
-	idmap_fini_namemaps;
-	idmap_cache_get_data;
-	idmap_set_logger;
+	xdr_idmap_retcode;
+	xdr_idmap_update_batch;
+	xdr_idmap_update_res;
     local:
 	*;
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/miscutils.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,124 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Miscellaneous utility functions not specifically related to
+ * the application.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <ctype.h>
+#include "miscutils.h"
+
+/* Return true if strings are equal */
+boolean_t
+streq(const char *a, const char *b)
+{
+	return (strcmp(a, b) == 0);
+}
+
+/* Return true if strings are equal, case-insensitively */
+boolean_t
+strcaseeq(const char *a, const char *b)
+{
+	return (strcasecmp(a, b) == 0);
+}
+
+/* Return true if string a Begins With string b */
+boolean_t
+strbw(const char *a, const char *b)
+{
+	return (strncmp(a, b, strlen(b)) == 0);
+}
+
+/*
+ * Duplicate up to n bytes of a string.  Kind of sort of like
+ * strdup(strlcpy(s, n)).
+ */
+char *
+strndup(const char *s, int n)
+{
+	int len;
+	char *p;
+
+	len = strnlen(s, n);
+	p = malloc(len + 1);
+	if (p == NULL)
+		return (NULL);
+
+	if (len > 0)
+		(void) memcpy(p, s, len);
+	p[len] = '\0';
+
+	return (p);
+}
+
+/*
+ * Duplicate a block of memory.  Combines malloc with memcpy, much as
+ * strdup combines malloc, strlen, and strcpy.
+ */
+void *
+memdup(const void *buf, size_t sz)
+{
+	void *p;
+
+	p = malloc(sz);
+	if (p == NULL)
+		return (NULL);
+	(void) memcpy(p, buf, sz);
+	return (p);
+}
+
+/*
+ * Dump a block of memory in hex+ascii, for debugging
+ */
+void
+dump(FILE *out, const char *prefix, const void *buf, size_t len)
+{
+	const unsigned char *p = buf;
+	int i;
+
+	for (i = 0; i < len; i += 16) {
+		int j;
+
+		(void) fprintf(out, "%s", prefix);
+		for (j = 0; j < 16 && i + j < len; j++) {
+			(void) fprintf(out, "%2.2x ", p[i + j]);
+		}
+		for (; j < 16; j++) {
+			(void) fprintf(out, "   ");
+		}
+		for (j = 0; j < 16 && i + j < len; j++) {
+			(void) fprintf(out, "%c",
+			    isprint(p[i + j]) ? p[i + j] : '.');
+		}
+		(void) fprintf(out, "\n");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/miscutils.h	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+#ifndef _MISCUTILS_H
+#define	_MISCUTILS_H
+
+/*
+ * Miscellaneous functions and macros not directly related to the application.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	NELEM(a)	(sizeof (a) / sizeof ((a)[0]))
+
+boolean_t strcaseeq(const char *a, const char *b);
+boolean_t streq(const char *a, const char *b);
+char *strndup(const char *s, int n);
+boolean_t strbw(const char *a, const char *b);
+void *memdup(const void *buf, size_t sz);
+void dump(FILE *out, const char *prefix, const void *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MISCUTILS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/sidutil.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,189 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * This is an extract from usr/src/common/smbsrv/smb_sid.c,
+ * with functions renamed as part of a tentative plan for convergence.
+ */
+#ifndef _KERNEL
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <syslog.h>
+#else /* _KERNEL */
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#endif /* _KERNEL */
+
+#include <sidutil.h>
+
+/*
+ * sid_len
+ *
+ * Returns the number of bytes required to hold the sid.
+ */
+int
+sid_len(sid_t *sid)
+{
+	if (sid == NULL)
+		return (0);
+
+	return (sizeof (sid_t) - sizeof (uint32_t)
+	    + (sid->sid_subauthcnt * sizeof (uint32_t)));
+}
+
+/*
+ * sid_tostr
+ *
+ * Fill in the passed buffer with the string form of the given
+ * binary sid.
+ */
+void
+sid_tostr(sid_t *sid, char *strsid)
+{
+	char *p = strsid;
+	int i;
+
+	if (sid == NULL || strsid == NULL)
+		return;
+
+	(void) sprintf(p, "S-%d-", sid->sid_revision);
+	while (*p)
+		p++;
+
+	for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
+		if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
+			(void) sprintf(p, "%d", sid->sid_authority[i]);
+			while (*p)
+				p++;
+		}
+	}
+
+	for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
+		(void) sprintf(p, "-%u", sid->sid_subauth[i]);
+		while (*p)
+			p++;
+	}
+}
+
+/*
+ * sid_fromstr
+ *
+ * Converts a SID in string form to a SID structure. There are lots of
+ * simplifying assumptions in here. The memory for the SID is allocated
+ * as if it was the largest possible SID; the caller is responsible for
+ * freeing the memory when it is no longer required. We assume that the
+ * string starts with "S-1-" and that the authority is held in the last
+ * byte, which should be okay for most situations. It also assumes the
+ * sub-authorities are in decimal format.
+ *
+ * On success, a pointer to a SID is returned. Otherwise a null pointer
+ * is returned.
+ */
+sid_t *
+sid_fromstr(char *sidstr)
+{
+	sid_t *sid;
+	char *p;
+	int size;
+	uint8_t i;
+
+	if (sidstr == NULL)
+		return (NULL);
+
+	if (strncmp(sidstr, "S-1-", 4) != 0)
+		return (NULL);
+
+	size = sizeof (sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
+
+	if ((sid = malloc(size)) == NULL)
+		return (NULL);
+
+	bzero(sid, size);
+	sid->sid_revision = NT_SID_REVISION;
+	sid->sid_authority[5] = atoi(&sidstr[4]);
+
+	for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
+		while (*p && *p == '-')
+			++p;
+
+		if (*p < '0' || *p > '9') {
+			free(sid);
+			return (NULL);
+		}
+
+		sid->sid_subauth[i] = strtoul(p, NULL, 10);
+
+		while (*p && *p != '-')
+			++p;
+	}
+
+	sid->sid_subauthcnt = i;
+	return (sid);
+}
+
+void
+sid_free(sid_t *sid)
+{
+#ifdef _KERNEL
+	if (sid == NULL)
+		return;
+
+	kmem_free(sid, sid_len(sid));
+#else
+	free(sid);
+#endif
+}
+
+void
+sid_to_le(sid_t *sid)
+{
+	int i;
+
+	for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
+		uint32_t v = sid->sid_subauth[i];
+		uint8_t *p = (uint8_t *)&sid->sid_subauth[i];
+
+		p[0] = v & 0xff;
+		p[1] = (v >> 8) & 0xff;
+		p[2] = (v >> 16) & 0xff;
+		p[3] = (v >> 24) & 0xff;
+	}
+}
+
+void
+sid_from_le(sid_t *sid)
+{
+	int i;
+
+	for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
+		uint32_t v;
+		uint8_t *p = (uint8_t *)&sid->sid_subauth[i];
+
+		v = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+
+		sid->sid_subauth[i] = v;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/sidutil.h	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,123 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+#ifndef _SIDUTIL_H
+#define	_SIDUTIL_H
+
+/*
+ * Security Identifier (SID) interface definition.
+ *
+ * This is an extract from uts/common/smbsrv/smb_sid.h, with functions
+ * renamed as part of a tentative plan for convergence.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Common definition for a SID.
+ */
+#define	NT_SID_REVISION		1
+#define	NT_SID_AUTH_MAX		6
+#define	NT_SID_SUBAUTH_MAX	15
+
+#if	!defined(ANY_SIZE_ARRAY)
+#define	ANY_SIZE_ARRAY	1
+#endif
+
+/*
+ * Security Identifier (SID)
+ *
+ * The security identifier (SID) uniquely identifies a user, group or
+ * a domain. It consists of a revision number, the identifier authority,
+ * and a list of sub-authorities. The revision number is currently 1.
+ * The identifier authority identifies which system issued the SID. The
+ * sub-authorities of a domain SID uniquely identify a domain. A user
+ * or group SID consists of a domain SID with the user or group id
+ * appended. The user or group id (also known as a relative id (RID)
+ * uniquely identifies a user within a domain. A user or group SID
+ * uniquely identifies a user or group across all domains. The SidType
+ * values identify the various types of SID.
+ *
+ *      1   1   1   1   1   1
+ *      5   4   3   2   1   0   9   8   7   6   5   4   3   2   1   0
+ *   +---------------------------------------------------------------+
+ *   |      SubAuthorityCount        |Reserved1 (SBZ)|   Revision    |
+ *   +---------------------------------------------------------------+
+ *   |                   IdentifierAuthority[0]                      |
+ *   +---------------------------------------------------------------+
+ *   |                   IdentifierAuthority[1]                      |
+ *   +---------------------------------------------------------------+
+ *   |                   IdentifierAuthority[2]                      |
+ *   +---------------------------------------------------------------+
+ *   |                                                               |
+ *   +- -  -  -  -  -  -  -  SubAuthority[]  -  -  -  -  -  -  -  - -+
+ *   |                                                               |
+ *   +---------------------------------------------------------------+
+ *
+ */
+/*
+ * Note: NT defines the Identifier Authority as a separate
+ * structure (SID_IDENTIFIER_AUTHORITY) containing a literal
+ * definition of a 6 byte vector but the effect is the same
+ * as defining it as a member value.
+ */
+typedef struct sid {
+	uint8_t sid_revision;
+	uint8_t sid_subauthcnt;
+	uint8_t sid_authority[NT_SID_AUTH_MAX];
+	uint32_t sid_subauth[ANY_SIZE_ARRAY];
+} sid_t;
+
+/*
+ * The maximum size of a SID in string format
+ */
+#define	SID_STRSZ		256
+
+/* Given a SID, return its length in bytes. */
+int sid_len(sid_t *);
+
+/* Given a dynamically allocated SID (e.g. from sid_fromstr), free it. */
+void sid_free(sid_t *);
+
+/* Translate a binary-format SID into the supplied SID_STRSZ buffer. */
+void sid_tostr(sid_t *, char *);
+
+/* Translate a text-format SID into an allocated binary-format SID. */
+sid_t *sid_fromstr(char *);
+
+/* In-place, translate a host-order SID into MS-native little endian. */
+void sid_to_le(sid_t *);
+
+/* In-place, translate a MS-native little endian SID into host order. */
+void sid_from_le(sid_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _SIDUTIL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/sized_array.c	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+/*
+ * Much like calloc, but with functions to report the size of the
+ * allocation given only the pointer.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <malloc.h>
+#include "sized_array.h"
+
+/*
+ * Assumes that int is at least 32 bits and that nothing needs more than
+ * 8-byte alignment.
+ */
+
+/* COOKIE provides some bad-pointer protection. */
+#define	COOKIE	"SACOOKIE"
+
+struct sized_array {
+	int	n;
+	int	sz;
+#if	defined(COOKIE)
+	char	cookie[8];
+#endif
+};
+
+
+void *
+sized_array(size_t n, size_t sz)
+{
+	struct sized_array *sa;
+	size_t total;
+
+	total = sizeof (struct sized_array) + n*sz;
+
+	sa = malloc(total);
+
+	if (sa == NULL)
+		return (NULL);
+
+	(void) memset(sa, 0, total);
+
+	sa->n = n;
+	sa->sz = sz;
+
+#if	defined(COOKIE)
+	(void) memcpy(sa->cookie, COOKIE, sizeof (sa->cookie));
+#endif
+
+	return ((void *)(sa + 1));
+}
+
+void
+sized_array_free(void *p)
+{
+	struct sized_array *sa;
+
+	if (p == NULL)
+		return;
+
+	sa = ((struct sized_array *)p)-1;
+
+#if	defined(COOKIE)
+	assert(memcmp(sa->cookie, COOKIE, sizeof (sa->cookie)) == 0);
+#endif
+
+	free(sa);
+}
+
+size_t
+sized_array_n(void *p)
+{
+	struct sized_array *sa;
+
+	sa = ((struct sized_array *)p)-1;
+
+#if	defined(COOKIE)
+	assert(memcmp(sa->cookie, COOKIE, sizeof (sa->cookie)) == 0);
+#endif
+
+	return (sa->n);
+}
+
+size_t
+sized_array_sz(void *p)
+{
+	struct sized_array *sa;
+
+	sa = ((struct sized_array *)p)-1;
+
+#if	defined(COOKIE)
+	assert(memcmp(sa->cookie, COOKIE, sizeof (sa->cookie)) == 0);
+#endif
+
+	return (sa->sz);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libidmap/common/sized_array.h	Fri Jul 17 17:54:42 2009 -0700
@@ -0,0 +1,48 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * 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.
+ */
+
+#ifndef _SIZED_ARRAY_H
+#define	_SIZED_ARRAY_H
+
+/*
+ * Like calloc, but with mechanisms to get the size of the allocated
+ * area given only the pointer.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *sized_array(size_t n, size_t sz);
+void sized_array_free(void *p);
+size_t sized_array_n(void *p);
+size_t sized_array_sz(void *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SIZED_ARRAY_H */
--- a/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h	Fri Jul 17 17:54:42 2009 -0700
@@ -245,7 +245,7 @@
 
 typedef struct ndr_pipe {
 	int			np_fid;
-	smb_opipe_context_t	np_ctx;
+	smb_netuserinfo_t	np_user;
 	char			*np_buf;
 	struct uio		np_uio;
 	iovec_t			np_iov;
@@ -516,7 +516,6 @@
 int ndr_pipe_close(int);
 int ndr_pipe_read(int, uint8_t *, uint32_t *, uint32_t *);
 int ndr_pipe_write(int, uint8_t *, uint32_t);
-int ndr_pipe_getinfo(int, ndr_pipe_info_t *);
 
 int ndr_generic_call_stub(ndr_xa_t *);
 
--- a/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers	Fri Jul 17 17:54:42 2009 -0700
@@ -67,7 +67,6 @@
 	ndr_mbtowc;
 	ndr_native_os;
         ndr_params;
-	ndr_pipe_getinfo;
 	ndr_pipe_open;
 	ndr_pipe_close;
 	ndr_pipe_read;
--- a/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c	Fri Jul 17 17:54:42 2009 -0700
@@ -89,7 +89,7 @@
 		return (ENOMEM);
 	}
 
-	if (smb_opipe_context_decode(&np->np_ctx, data, datalen, NULL) == -1) {
+	if (smb_netuserinfo_decode(&np->np_user, data, datalen, NULL) == -1) {
 		ndr_pipe_release(np);
 		(void) mutex_unlock(&ndr_pipe_lock);
 		return (EINVAL);
@@ -257,37 +257,6 @@
 }
 
 /*
- * Return information about the specified pipe.
- */
-int
-ndr_pipe_getinfo(int ndx, ndr_pipe_info_t *npi)
-{
-	ndr_pipe_t *np;
-
-	if ((ndx < 0) || (ndx >= NDR_PIPE_MAX) || (npi == NULL))
-		return (-1);
-
-	(void) mutex_lock(&ndr_pipe_lock);
-	np = &ndr_pipe_table[ndx];
-
-	if (np->np_fid == 0) {
-		(void) mutex_unlock(&ndr_pipe_lock);
-		return (-1);
-	}
-
-	npi->npi_fid = np->np_fid;
-	npi->npi_permissions = FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE;
-	npi->npi_num_locks = 0;
-	(void) snprintf(npi->npi_username, MAXNAMELEN, "%s\\%s",
-	    np->np_ctx.oc_domain, np->np_ctx.oc_account);
-	(void) snprintf(npi->npi_pathname, MAXPATHLEN, "%s",
-	    np->np_binding->service->sec_addr_port);
-
-	(void) mutex_unlock(&ndr_pipe_lock);
-	return (0);
-}
-
-/*
  * Must be called with ndr_pipe_lock held.
  */
 static ndr_pipe_t *
@@ -365,9 +334,9 @@
 		ndr_pipe_rewind(np);
 		ndr_pipe_flush(np);
 		free(np->np_buf);
-		free(np->np_ctx.oc_domain);
-		free(np->np_ctx.oc_account);
-		free(np->np_ctx.oc_workstation);
+		free(np->np_user.ui_domain);
+		free(np->np_user.ui_account);
+		free(np->np_user.ui_workstation);
 		bzero(np, sizeof (ndr_pipe_t));
 	}
 }
@@ -412,9 +381,9 @@
 boolean_t
 ndr_is_admin(ndr_xa_t *xa)
 {
-	smb_opipe_context_t *ctx = &xa->pipe->np_ctx;
+	smb_netuserinfo_t *ctx = &xa->pipe->np_user;
 
-	return (ctx->oc_flags & SMB_ATF_ADMIN);
+	return (ctx->ui_flags & SMB_ATF_ADMIN);
 }
 
 /*
@@ -426,18 +395,18 @@
 boolean_t
 ndr_is_poweruser(ndr_xa_t *xa)
 {
-	smb_opipe_context_t *ctx = &xa->pipe->np_ctx;
+	smb_netuserinfo_t *ctx = &xa->pipe->np_user;
 
-	return ((ctx->oc_flags & SMB_ATF_ADMIN) ||
-	    (ctx->oc_flags & SMB_ATF_POWERUSER));
+	return ((ctx->ui_flags & SMB_ATF_ADMIN) ||
+	    (ctx->ui_flags & SMB_ATF_POWERUSER));
 }
 
 int32_t
 ndr_native_os(ndr_xa_t *xa)
 {
-	smb_opipe_context_t *ctx = &xa->pipe->np_ctx;
+	smb_netuserinfo_t *ctx = &xa->pipe->np_user;
 
-	return (ctx->oc_native_os);
+	return (ctx->ui_native_os);
 }
 
 /*
--- a/usr/src/lib/smbsrv/libmlsvc/common/eventlog.h	Fri Jul 17 16:57:52 2009 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * 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.
- */
-
-#ifndef _eventlog_H
-#define	_eventlog_H
-
-#include <netdb.h>
-#include <smbsrv/libsmb.h>
-#include <smbsrv/ndl/eventlog.ndl>
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-#define	LOGR_NMSGMASK	1023
-
-typedef struct logr_entry {
-	struct timeval	le_timestamp;			/* Time of log entry */
-	int		le_pri;				/* Message priority */
-	char		le_hostname[MAXHOSTNAMELEN];	/* Log hostname */
-	char		le_msg[LOGR_MAXENTRYLEN];	/* Log message text */
-} logr_entry_t;
-
-typedef struct logr_info {
-	logr_entry_t	li_entry[LOGR_NMSGMASK+1];	/* Array of log entry */
-	int		li_idx;				/* Index */
-} logr_info_t;
-
-typedef struct logr_read_data {
-	int		rd_tot_recnum;		/* Total no. of record read */
-	int		rd_last_sentrec;	/* Last sentence read */
-	char		rd_first_read;		/* First sentence read */
-	logr_info_t	*rd_log;		/* Log information read */
-} logr_read_data_t;
-
-/* This structure provides the context for eventlog calls from clients. */
-typedef struct logr_context {
-	logr_read_data_t *lc_cached_read_data;
-	char *lc_source_name;
-} logr_context_t;
-
-int logr_syslog_snapshot(logr_info_t *);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* _eventlog_H */
--- a/usr/src/lib/smbsrv/libmlsvc/common/eventlog_svc.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/eventlog_svc.c	Fri Jul 17 17:54:42 2009 -0700
@@ -29,15 +29,15 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 #include <strings.h>
-
 #include <smbsrv/libsmb.h>
 #include <smbsrv/libmlrpc.h>
 #include <smbsrv/ntstatus.h>
 #include <smbsrv/nmpipes.h>
 #include <smbsrv/libmlsvc.h>
-#include "eventlog.h"
+#include <smbsrv/ndl/eventlog.ndl>
 #include <smbsrv/nterror.h>
 
+
 #define	LOGR_FWD		+1
 #define	LOGR_REW		-1
 #define	LOGR_RECORD_SIGNATURE	0x654C664C
@@ -162,8 +162,6 @@
 	logr_stub_table			/* stub_table */
 };
 
-static int logr_get_snapshot(logr_context_t *);
-
 /*
  * logr_initialize
  *
@@ -175,6 +173,13 @@
 logr_initialize(void)
 {
 	(void) ndr_svc_register(&logr_service);
+	logr_init();
+}
+
+void
+logr_finalize(void)
+{
+	logr_fini();
 }
 
 /*
@@ -222,12 +227,12 @@
 }
 
 /*
- * logr_mgr_hdalloc
+ * logr_hdalloc
  *
  * Handle allocation wrapper to setup the local manager context.
  */
 static ndr_hdid_t *
-logr_hdalloc(ndr_xa_t *mxa)
+logr_hdalloc(ndr_xa_t *mxa, char *logname)
 {
 	logr_context_t *ctx;
 
@@ -235,8 +240,13 @@
 		return (NULL);
 	bzero(ctx, sizeof (logr_context_t));
 
-	ctx->lc_source_name = strdup("eventlog");
-	if ((ctx->lc_source_name != NULL) && (logr_get_snapshot(ctx) < 0)) {
+	ctx->lc_source_name = strdup(logname);
+	if (ctx->lc_source_name == NULL) {
+		free(ctx);
+		return (NULL);
+	}
+
+	if (logr_get_snapshot(ctx) != 0) {
 		free(ctx->lc_source_name);
 		free(ctx);
 		return (NULL);
@@ -287,16 +297,22 @@
 	ndr_handle_t *hd;
 	char *log_name = NULL;
 
-	if (param->log_name.length != 0)
-		log_name = (char *)param->log_name.str;
-
-	if ((log_name == NULL) || strcasecmp(log_name, "System") != 0) {
+	if (!ndr_is_admin(mxa)) {
 		bzero(&param->handle, sizeof (logr_handle_t));
 		param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
 		return (NDR_DRC_OK);
 	}
 
-	id = logr_hdalloc(mxa);
+	if (param->log_name.length != 0)
+		log_name = (char *)param->log_name.str;
+
+	if (!logr_is_supported(log_name)) {
+		bzero(&param->handle, sizeof (logr_handle_t));
+		param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+		return (NDR_DRC_OK);
+	}
+
+	id = logr_hdalloc(mxa, log_name);
 	if (id && ((hd = logr_hdlookup(mxa, id)) != NULL)) {
 		hd->nh_data_free = logr_context_data_free;
 		bcopy(id, &param->handle, sizeof (logr_handle_t));
@@ -310,42 +326,6 @@
 }
 
 /*
- * logr_get_snapshot
- *
- * Allocate memory and make a copy, as a snapshot, from system log.
- */
-static int
-logr_get_snapshot(logr_context_t *ctx)
-{
-	logr_read_data_t *data = NULL;
-
-	ctx->lc_cached_read_data = malloc(sizeof (logr_read_data_t));
-	if (ctx->lc_cached_read_data != NULL) {
-		data = ctx->lc_cached_read_data;
-
-		data->rd_log = (logr_info_t *)malloc(sizeof (logr_info_t));
-		if (data->rd_log == NULL) {
-			free(data);
-			return (-1);
-		}
-		bzero(data->rd_log, sizeof (logr_info_t));
-
-		data->rd_tot_recnum = logr_syslog_snapshot(data->rd_log);
-		if (data->rd_tot_recnum < 0) {
-			free(data->rd_log);
-			free(data);
-			return (-1);
-		}
-
-		data->rd_first_read = 1;
-
-		return (0);
-	}
-
-	return (-1);
-}
-
-/*
  * logr_s_EventLogQueryCount
  *
  * take a snapshot from system log, assign it to the given handle.
--- a/usr/src/lib/smbsrv/libmlsvc/common/eventlog_syslog.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/eventlog_syslog.c	Fri Jul 17 17:54:42 2009 -0700
@@ -32,11 +32,13 @@
 #include <string.h>
 #include <strings.h>
 #include <stdarg.h>
+#include <dlfcn.h>
 #include <sys/synch.h>
 #include <sys/stat.h>
 #include <sys/errno.h>
 #include <ctype.h>
-#include "eventlog.h"
+#include <smbsrv/ndl/eventlog.ndl>
+#include <smbsrv/libmlsvc.h>
 
 typedef enum {
 	LOGR_MONTH = 0,
@@ -77,6 +79,12 @@
 	char		ln_logline[LOGR_MAXENTRYLEN];
 } logr_syslog_node_t;
 
+static void *logr_interposer_hdl = NULL;
+static struct {
+	boolean_t (*logr_op_supported)(char *);
+	int (*logr_op_snapshot)(logr_context_t *);
+} logr_interposer_ops;
+
 /*
  * Set the syslog timestamp.
  *
@@ -289,12 +297,12 @@
  * provided by the caller. Returns the number of entries in
  * the log.
  */
-int
-logr_syslog_snapshot(logr_info_t *loginfo)
+static int
+logr_syslog_snapshot(char *logname, logr_info_t *loginfo)
 {
 	FILE *fp;
 
-	if (loginfo == NULL)
+	if ((loginfo == NULL) || (!logr_is_supported(logname)))
 		return (-1);
 
 	if ((fp = fopen("/var/adm/messages", "r")) == 0)
@@ -311,3 +319,107 @@
 
 	return (LOGR_NMSGMASK+1);
 }
+
+/*
+ * logr_is_supported
+ *
+ * Determines if a given log is supported or not.
+ * Returns B_TRUE on success, B_FALSE on failure.
+ */
+boolean_t
+logr_is_supported(char *log_name)
+{
+	if (log_name == NULL)
+		return (B_FALSE);
+
+	if (logr_interposer_ops.logr_op_supported != NULL)
+		return (logr_interposer_ops.logr_op_supported(log_name));
+
+	if (strcasecmp(log_name, LOGR_SYSTEM_LOG) != 0)
+		return (B_FALSE);
+
+	return (B_TRUE);
+}
+
+/*
+ * logr_get_snapshot
+ *
+ * Allocate memory and make a copy, as a snapshot, from system log.
+ * Returns 0 on success, -1 on failure.
+ */
+int
+logr_get_snapshot(logr_context_t *ctx)
+{
+	logr_read_data_t *data = NULL;
+
+	if (logr_interposer_ops.logr_op_snapshot != NULL)
+		return (logr_interposer_ops.logr_op_snapshot(ctx));
+
+	ctx->lc_cached_read_data = malloc(sizeof (logr_read_data_t));
+	if (ctx->lc_cached_read_data != NULL) {
+		data = ctx->lc_cached_read_data;
+
+		data->rd_log = (logr_info_t *)malloc(sizeof (logr_info_t));
+		if (data->rd_log == NULL) {
+			free(data);
+			return (-1);
+		}
+		bzero(data->rd_log, sizeof (logr_info_t));
+
+		data->rd_tot_recnum = logr_syslog_snapshot(ctx->lc_source_name,
+		    data->rd_log);
+		if (data->rd_tot_recnum < 0) {
+			free(data->rd_log);
+			free(data);
+			return (-1);
+		}
+
+		data->rd_first_read = 1;
+
+		return (0);
+	}
+
+	return (-1);
+}
+
+/*
+ * logr_init
+ *
+ * Initializes the Eventlog service.
+ * Checks to see if a event log utility library
+ * is interposed. If yes then it'll initializes logr_interposer_ops
+ * structure with function pointers from this library.
+ */
+void
+logr_init(void)
+{
+	logr_interposer_hdl = smb_dlopen();
+	if (logr_interposer_hdl == NULL)
+		return;
+
+	bzero((void *)&logr_interposer_ops, sizeof (logr_interposer_ops));
+
+	logr_interposer_ops.logr_op_supported =
+	    (boolean_t (*)())dlsym(logr_interposer_hdl, "logr_is_supported");
+
+	logr_interposer_ops.logr_op_snapshot =
+	    (int (*)())dlsym(logr_interposer_hdl, "logr_get_snapshot");
+
+	if (logr_interposer_ops.logr_op_supported == NULL ||
+	    logr_interposer_ops.logr_op_snapshot == NULL)
+		logr_fini();
+}
+
+/*
+ * logr_fini
+ *
+ * Finalizes the Eventlog service.
+ * Closes handle to interposed library.
+ */
+void
+logr_fini(void)
+{
+	smb_dlclose(logr_interposer_hdl);
+	logr_interposer_hdl = NULL;
+	bzero((void *)&logr_interposer_ops, sizeof (logr_interposer_ops));
+}
--- a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h	Fri Jul 17 17:54:42 2009 -0700
@@ -26,12 +26,15 @@
 #ifndef	_LIBMLSVC_H
 #define	_LIBMLSVC_H
 
+#include <uuid/uuid.h>
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <sys/ksynch.h>
 #include <stdio.h>
 #include <string.h>
+#include <netdb.h>
+#include <libuutil.h>
 #include <smbsrv/wintypes.h>
 #include <smbsrv/hash_table.h>
 #include <smbsrv/smb_token.h>
@@ -59,12 +62,9 @@
 extern boolean_t smb_domain_getinfo(smb_domain_t *);
 
 
-extern uint64_t mlsvc_get_num_users(void);
-extern int mlsvc_get_user_list(smb_ulist_t *);
 extern void dssetup_clear_domain_info(void);
 extern int mlsvc_init(void);
 extern void mlsvc_fini(void);
-extern int mlsvc_set_share(int, char *, char *);
 extern DWORD mlsvc_netlogon(char *, char *);
 extern DWORD mlsvc_join(smb_domain_t *, char *, char *);
 
@@ -126,6 +126,21 @@
 } ms_luid_t;
 
 /*
+ * Information about a server as reported by NetServerGetInfo.
+ * The SV_PLATFORM and SV_TYPE definitions are in srvsvc.ndl.
+ */
+typedef struct srvsvc_server_info {
+	uint32_t	sv_platform_id;
+	char		*sv_name;
+	uint32_t	sv_version_major;
+	uint32_t	sv_version_minor;
+	uint32_t	sv_type;
+	char		*sv_comment;
+} srvsvc_server_info_t;
+
+int srvsvc_net_server_getinfo(char *, char *, srvsvc_server_info_t *);
+
+/*
  * A client_t is created while binding a client connection to hold the
  * context for calls made using that connection.
  *
@@ -137,11 +152,14 @@
 	ndr_hdid_t			handle;
 	ndr_client_t			*clnt;
 	int				remote_os;
+	srvsvc_server_info_t		svinfo;
 } mlsvc_handle_t;
 
 int ndr_rpc_bind(mlsvc_handle_t *, char *, char *, char *, const char *);
 void ndr_rpc_unbind(mlsvc_handle_t *);
 int ndr_rpc_call(mlsvc_handle_t *, int, void *);
+void ndr_rpc_server_setinfo(mlsvc_handle_t *, const srvsvc_server_info_t *);
+void ndr_rpc_server_getinfo(mlsvc_handle_t *, srvsvc_server_info_t *);
 int ndr_rpc_server_os(mlsvc_handle_t *);
 void *ndr_rpc_malloc(mlsvc_handle_t *, size_t);
 ndr_heap_t *ndr_rpc_get_heap(mlsvc_handle_t *);
@@ -151,6 +169,108 @@
 void ndr_inherit_handle(mlsvc_handle_t *, mlsvc_handle_t *);
 void ndr_rpc_status(mlsvc_handle_t *, int, uint32_t);
 
+/* SVCCTL service */
+/*
+ * Calculate the wide-char equivalent string length required to
+ * store a string - including the terminating null wide-char.
+ */
+#define	SVCCTL_WNSTRLEN(S)	((strlen((S)) + 1) * sizeof (mts_wchar_t))
+
+/* An AVL-storable node representing each service in the SCM database. */
+typedef struct svcctl_svc_node {
+	uu_avl_node_t		sn_node;
+	char			*sn_name;	/* Service Name (Key) */
+	char			*sn_fmri;	/* Display Name (FMRI) */
+	char			*sn_desc;	/* Description */
+	char			*sn_state;	/* State */
+} svcctl_svc_node_t;
+
+/* This structure provides context for each svcctl_s_OpenManager call. */
+typedef struct svcctl_manager_context {
+	scf_handle_t		*mc_scf_hdl;	  /* SCF handle */
+	scf_propertygroup_t	*mc_scf_gpg;	  /* Property group */
+	scf_property_t		*mc_scf_gprop;	  /* Property */
+	scf_value_t		*mc_scf_gval;	  /* Value */
+	uint32_t		mc_scf_numsvcs;   /* Number of SMF services */
+	ssize_t			mc_scf_max_fmri_len;  /* Max FMRI length */
+	ssize_t			mc_scf_max_value_len; /* Max Value length */
+	uint32_t		mc_bytes_needed;  /* Number of bytes needed */
+	uu_avl_pool_t		*mc_svcs_pool;	  /* AVL pool */
+	uu_avl_t		*mc_svcs;	  /* AVL tree of SMF services */
+} svcctl_manager_context_t;
+
+/* This structure provides context for each svcctl_s_OpenService call. */
+typedef struct svcctl_service_context {
+	ndr_hdid_t		*sc_mgrid;	/* Manager ID */
+	char			*sc_svcname;    /* Service Name */
+} svcctl_service_context_t;
+
+typedef enum {
+	SVCCTL_MANAGER_CONTEXT = 0,
+	SVCCTL_SERVICE_CONTEXT
+} svcctl_context_type_t;
+
+/* This structure provides abstraction for service and manager context call. */
+typedef struct svcctl_context {
+	svcctl_context_type_t	c_type;
+	union {
+		svcctl_manager_context_t *uc_mgr;
+		svcctl_service_context_t *uc_svc;
+		void *uc_cp;
+	} c_ctx;
+} svcctl_context_t;
+
+/* Service Control Manager (SCM) functions */
+void svcctl_init(void);
+void svcctl_fini(void);
+int svcctl_scm_init(svcctl_manager_context_t *);
+void svcctl_scm_fini(svcctl_manager_context_t *);
+int svcctl_scm_scf_handle_init(svcctl_manager_context_t *);
+void svcctl_scm_scf_handle_fini(svcctl_manager_context_t *);
+int svcctl_scm_refresh(svcctl_manager_context_t *);
+uint32_t svcctl_scm_enum_services(svcctl_manager_context_t *, uint8_t *,
+    size_t, uint32_t *, boolean_t);
+uint32_t svcctl_scm_validate_service(svcctl_manager_context_t *, char *);
+svcctl_svc_node_t *svcctl_scm_find_service(svcctl_manager_context_t *, char *);
+uint32_t svcctl_scm_map_status(const char *);
+
+/* LOGR service */
+#define	LOGR_APPLICATION_LOG		"Application"
+#define	LOGR_SECURITY_LOG		"Security"
+#define	LOGR_SYSTEM_LOG			"System"
+#define	LOGR_NMSGMASK			1023
+#define	LOGR_MAXMSGLEN			800
+
+typedef struct logr_entry {
+	struct timeval	le_timestamp;			/* Time of log entry */
+	int		le_pri;				/* Message priority */
+	char		le_hostname[MAXHOSTNAMELEN];	/* Log hostname */
+	char		le_msg[LOGR_MAXMSGLEN];		/* Log message text */
+} logr_entry_t;
+
+typedef struct logr_info {
+	logr_entry_t	li_entry[LOGR_NMSGMASK+1];	/* Array of log entry */
+	int		li_idx;				/* Index */
+} logr_info_t;
+
+typedef struct logr_read_data {
+	int		rd_tot_recnum;		/* Total no. of record read */
+	int		rd_last_sentrec;	/* Last sentence read */
+	char		rd_first_read;		/* First sentence read */
+	logr_info_t	*rd_log;		/* Log information read */
+} logr_read_data_t;
+
+/* This structure provides the context for eventlog calls from clients. */
+typedef struct logr_context {
+	logr_read_data_t *lc_cached_read_data;
+	char *lc_source_name;
+} logr_context_t;
+
+void logr_init(void);
+void logr_fini(void);
+boolean_t logr_is_supported(char *);
+int logr_get_snapshot(logr_context_t *);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers	Fri Jul 17 17:54:42 2009 -0700
@@ -41,15 +41,12 @@
 SUNWprivate {
     global:
 	dssetup_clear_domain_info;
-	mlsvc_get_num_users;
-	mlsvc_get_user_list;
 	mlsvc_fini;
 	mlsvc_init;
 	mlsvc_join;
 	mlsvc_lookup_name;
 	mlsvc_lookup_sid;
 	mlsvc_netlogon;
-	mlsvc_set_share;
 	smb_autohome_add;
 	smb_autohome_remove;
 	smb_domain_getinfo;
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h	Fri Jul 17 17:54:42 2009 -0700
@@ -47,6 +47,9 @@
 void msgsvcsend_initialize(void);
 void spoolss_initialize(void);
 
+void logr_finalize(void);
+void svcctl_finalize(void);
+
 int netr_open(char *, char *, mlsvc_handle_t *);
 int netr_close(mlsvc_handle_t *);
 DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD);
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c	Fri Jul 17 17:54:42 2009 -0700
@@ -170,6 +170,36 @@
 }
 
 /*
+ * Set information about the remote RPC server in the handle.
+ */
+void
+ndr_rpc_server_setinfo(mlsvc_handle_t *handle,
+    const srvsvc_server_info_t *svinfo)
+{
+	bcopy(svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t));
+	handle->svinfo.sv_name = NULL;
+	handle->svinfo.sv_comment = NULL;
+
+	if (svinfo->sv_version_major > 4)
+		handle->remote_os = NATIVE_OS_WIN2000;
+	else
+		handle->remote_os = NATIVE_OS_WINNT;
+
+	smb_tracef("NdrRpcServerSetInfo: %s (version %d.%d)",
+	    svinfo->sv_name ? svinfo->sv_name : "<unknown>",
+	    svinfo->sv_version_major, svinfo->sv_version_minor);
+}
+
+/*
+ * Get information about the remote RPC server from the handle.
+ */
+void
+ndr_rpc_server_getinfo(mlsvc_handle_t *handle, srvsvc_server_info_t *svinfo)
+{
+	bcopy(&handle->svinfo, svinfo, sizeof (srvsvc_server_info_t));
+}
+
+/*
  * Returns the Native-OS of the RPC server.
  */
 int
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c	Fri Jul 17 17:54:42 2009 -0700
@@ -82,6 +82,8 @@
 mlsvc_fini(void)
 {
 	smb_logon_fini();
+	svcctl_finalize();
+	logr_finalize();
 }
 
 /*ARGSUSED*/
@@ -100,43 +102,3 @@
 	/*NOTREACHED*/
 	return (NULL);
 }
-
-uint64_t
-mlsvc_get_num_users(void)
-{
-	uint32_t n_users = 0;
-
-	(void) smb_kmod_get_usernum(&n_users);
-	return ((uint64_t)n_users);
-}
-
-/*
- * The calling function must free the output parameter 'users'.
- */
-int
-mlsvc_get_user_list(smb_ulist_t *ulist)
-{
-	return (smb_kmod_get_userlist(ulist));
-}
-
-/*
- * Downcall to the kernel that is executed upon share enable and disable.
- */
-int
-mlsvc_set_share(int shrop, char *path, char *name)
-{
-	int rc;
-
-	switch (shrop) {
-	case SMB_SHROP_ADD:
-		rc = smb_kmod_share(path, name);
-		break;
-	case SMB_SHROP_DELETE:
-		rc = smb_kmod_unshare(path, name);
-		break;
-	default:
-		rc = EINVAL;
-		break;
-	}
-	return (rc);
-}
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c	Fri Jul 17 17:54:42 2009 -0700
@@ -518,7 +518,7 @@
 lsarpc_s_GetConnectedUser(void *arg, ndr_xa_t *mxa)
 {
 	struct mslsa_GetConnectedUser *param = arg;
-	smb_opipe_context_t *ctx = &mxa->pipe->np_ctx;
+	smb_netuserinfo_t *user = &mxa->pipe->np_user;
 	DWORD status = NT_STATUS_SUCCESS;
 	smb_domain_t di;
 	int rc1;
@@ -546,8 +546,9 @@
 		return (NDR_DRC_OK);
 	}
 
-	rc1 = NDR_MSTRING(mxa, ctx->oc_account, (ndr_mstring_t *)param->owner);
-	rc2 = NDR_MSTRING(mxa, ctx->oc_domain,
+	rc1 = NDR_MSTRING(mxa, user->ui_account,
+	    (ndr_mstring_t *)param->owner);
+	rc2 = NDR_MSTRING(mxa, user->ui_domain,
 	    (ndr_mstring_t *)param->domain->name);
 
 	if (rc1 == -1 || rc2 == -1)
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c	Fri Jul 17 17:54:42 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -118,19 +118,27 @@
 /*
  * netr_open
  *
- * Open an anonymous session to the NETLOGON pipe on a domain
- * controller and bind to the NETR RPC interface. We store the
- * remote server's native OS type - we may need it due to
- * differences between versions of Windows.
+ * Open an anonymous session to the NETLOGON pipe on a domain controller
+ * and bind to the NETR RPC interface.
+ *
+ * We store the remote server information, which is used to drive Windows
+ * version specific behavior.
  */
 int
 netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle)
 {
+	srvsvc_server_info_t svinfo;
 	char *user = smbrdr_ipc_get_user();
 
+	if (srvsvc_net_server_getinfo(server, domain, &svinfo) < 0)
+		return (-1);
+
 	if (ndr_rpc_bind(netr_handle, server, domain, user, "NETR") < 0)
 		return (-1);
 
+	ndr_rpc_server_setinfo(netr_handle, &svinfo);
+	free(svinfo.sv_name);
+	free(svinfo.sv_comment);
 	return (0);
 }
 
--- a/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c	Fri Jul 17 17:54:42 2009 -0700
@@ -406,7 +406,7 @@
 	smb_shr_cache_unlock();
 
 	/* call kernel to take a hold on the shared file system */
-	rc = mlsvc_set_share(SMB_SHROP_ADD, si->shr_path, si->shr_name);
+	rc = smb_kmod_share(si->shr_path, si->shr_name);
 
 	if (rc == 0) {
 		smb_shr_publish(si->shr_name, si->shr_container);
@@ -483,7 +483,7 @@
 	smb_shr_unpublish(sharename, container);
 
 	/* call kernel to release the hold on the shared file system */
-	(void) mlsvc_set_share(SMB_SHROP_DELETE, path, sharename);
+	(void) smb_kmod_unshare(path, sharename);
 
 	return (NERR_Success);
 }
--- a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c	Fri Jul 17 17:54:42 2009 -0700
@@ -347,6 +347,52 @@
 	return (0);
 }
 
+int
+srvsvc_net_server_getinfo(char *server, char *domain,
+    srvsvc_server_info_t *svinfo)
+{
+	mlsvc_handle_t handle;
+	struct mslm_NetServerGetInfo arg;
+	struct mslm_SERVER_INFO_101 *sv101;
+	int len, opnum, rc;
+	char *user = smbrdr_ipc_get_user();
+
+	if (srvsvc_open(server, domain, user, &handle) != 0)
+		return (-1);
+
+	opnum = SRVSVC_OPNUM_NetServerGetInfo;
+	bzero(&arg, sizeof (arg));
+
+	len = strlen(server) + 4;
+	arg.servername = ndr_rpc_malloc(&handle, len);
+	if (arg.servername == NULL)
+		return (-1);
+
+	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+	arg.level = 101;
+
+	rc = ndr_rpc_call(&handle, opnum, &arg);
+	if ((rc != 0) || (arg.status != 0)) {
+		srvsvc_close(&handle);
+		return (-1);
+	}
+
+	sv101 = arg.result.bufptr.bufptr101;
+
+	bzero(svinfo, sizeof (srvsvc_server_info_t));
+	svinfo->sv_platform_id = sv101->sv101_platform_id;
+	svinfo->sv_version_major = sv101->sv101_version_major;
+	svinfo->sv_version_minor = sv101->sv101_version_minor;
+	svinfo->sv_type = sv101->sv101_type;
+	if (sv101->sv101_name)
+		svinfo->sv_name = strdup((char *)sv101->sv101_name);
+	if (sv101->sv101_comment)
+		svinfo->sv_comment = strdup((char *)sv101->sv101_comment);
+
+	srvsvc_close(&handle);
+	return (0);
+}
+
 /*
  * Synchronize the local system clock with the domain controller.
  */
@@ -490,6 +536,7 @@
 srvsvc_net_test(char *server, char *domain, char *netname)
 {
 	smb_domain_t di;
+	srvsvc_server_info_t svinfo;
 
 	(void) smb_tracef("%s %s %s", server, domain, netname);
 
@@ -498,6 +545,17 @@
 		domain = di.d_info.di_nbname;
 	}
 
+	if (srvsvc_net_server_getinfo(server, domain, &svinfo) == 0) {
+		smb_tracef("NetServerGetInfo: %s %s (%d.%d) id=%d type=0x%08x",
+		    svinfo.sv_name ? svinfo.sv_name : "NULL",
+		    svinfo.sv_comment ? svinfo.sv_comment : "NULL",
+		    svinfo.sv_version_major, svinfo.sv_version_minor,
+		    svinfo.sv_platform_id, svinfo.sv_type);
+
+		free(svinfo.sv_name);
+		free(svinfo.sv_comment);
+	}
+
 	(void) srvsvc_net_share_get_info(server, domain, netname);
 #if 0
 	/*
--- a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_svc.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_svc.c	Fri Jul 17 17:54:42 2009 -0700
@@ -65,26 +65,8 @@
 #define	SRVSVC_CONNECT_ENUM_SHARE	1
 #define	SRVSVC_CONNECT_ENUM_WKSTN	2
 
-#define	SMB_SRVSVC_MAXBUFLEN	(8 * 1024 * 1024)
-#define	SMB_SRVSVC_MAXPREFLEN	((uint32_t)(-1))
-
-/*
- * prefmaxlen:    Client specified response buffer limit.
- * resume_handle: Cookie used to track enumeration across multiple calls.
- * n_total:       Total number of entries.
- * n_enum:        Number of entries to enumerate (derived from prefmaxlen).
- * n_skip:        Number of entries to skip (from incoming resume handle).
- * n_read:        Number of objects returned for current enumeration request.
- */
-typedef struct srvsvc_enum {
-	uint32_t se_level;
-	uint32_t se_prefmaxlen;
-	uint32_t se_resume_handle;
-	uint32_t se_n_total;
-	uint32_t se_n_enum;
-	uint32_t se_n_skip;
-	uint32_t se_n_read;
-} srvsvc_enum_t;
+#define	SMB_SRVSVC_MAXBUFLEN		(8 * 1024 * 1024)
+#define	SMB_SRVSVC_MAXPREFLEN		((uint32_t)(-1))
 
 typedef struct srvsvc_sd {
 	uint8_t *sd_buf;
@@ -112,46 +94,45 @@
 	struct mslm_NetShareInfo_1501 nsg_info1501;
 } srvsvc_netshare_getinfo_t;
 
-static DWORD srvsvc_s_NetConnectEnumLevel0(ndr_xa_t *,
-    srvsvc_NetConnectInfo0_t *);
-static DWORD srvsvc_s_NetConnectEnumLevel1(ndr_xa_t *,
-    srvsvc_NetConnectInfo1_t *);
-
-static DWORD srvsvc_NetFileEnum2(ndr_xa_t *,
-    struct mslm_NetFileEnum *);
-static DWORD srvsvc_NetFileEnum3(ndr_xa_t *,
-    struct mslm_NetFileEnum *);
-
-static DWORD mlsvc_NetSessionEnumLevel0(struct mslm_infonres *, DWORD,
-    ndr_xa_t *);
-static DWORD mlsvc_NetSessionEnumLevel1(struct mslm_infonres *, DWORD,
-    ndr_xa_t *);
-static DWORD mlsvc_NetSessionEnumLevel2(struct mslm_infonres *, DWORD,
-    ndr_xa_t *);
-static DWORD mlsvc_NetSessionEnumLevel10(struct mslm_infonres *, DWORD,
-    ndr_xa_t *);
-static DWORD mlsvc_NetSessionEnumLevel502(struct mslm_infonres *, DWORD,
-    ndr_xa_t *);
-
-static DWORD mlsvc_NetShareEnumLevel0(ndr_xa_t *,
-    struct mslm_infonres *, srvsvc_enum_t *, int);
-static DWORD mlsvc_NetShareEnumLevel1(ndr_xa_t *,
-    struct mslm_infonres *, srvsvc_enum_t *, int);
-static DWORD mlsvc_NetShareEnumLevel2(ndr_xa_t *,
-    struct mslm_infonres *, srvsvc_enum_t *, int);
-static DWORD mlsvc_NetShareEnumLevel501(ndr_xa_t *,
-    struct mslm_infonres *, srvsvc_enum_t *, int);
-static DWORD mlsvc_NetShareEnumLevel502(ndr_xa_t *,
-    struct mslm_infonres *, srvsvc_enum_t *, int);
-static DWORD mlsvc_NetShareEnumCommon(ndr_xa_t *,
-    srvsvc_enum_t *, smb_share_t *, void *);
-static boolean_t srvsvc_add_autohome(ndr_xa_t *, srvsvc_enum_t *,
-    void *);
+typedef struct mslm_infonres srvsvc_infonres_t;
+typedef struct mslm_NetConnectEnum srvsvc_NetConnectEnum_t;
+
+static uint32_t srvsvc_netconnectenum_level0(ndr_xa_t *, smb_svcenum_t *,
+    srvsvc_NetConnectEnum_t *);
+static uint32_t srvsvc_netconnectenum_level1(ndr_xa_t *, smb_svcenum_t *,
+    srvsvc_NetConnectEnum_t *);
+static uint32_t srvsvc_netconnectenum_common(ndr_xa_t *,
+    srvsvc_NetConnectInfo_t *, smb_netsvc_t *, smb_svcenum_t *);
+
+static DWORD srvsvc_NetFileEnum2(ndr_xa_t *, struct mslm_NetFileEnum *,
+    smb_svcenum_t *se);
+static DWORD srvsvc_NetFileEnum3(ndr_xa_t *, struct mslm_NetFileEnum *,
+    smb_svcenum_t *se);
+
+static uint32_t srvsvc_NetSessionEnumCommon(ndr_xa_t *, srvsvc_infonres_t *,
+    smb_netsvc_t *, smb_svcenum_t *);
+
+static DWORD mlsvc_NetShareEnumLevel0(ndr_xa_t *, srvsvc_infonres_t *,
+    smb_svcenum_t *, int);
+static DWORD mlsvc_NetShareEnumLevel1(ndr_xa_t *, srvsvc_infonres_t *,
+    smb_svcenum_t *, int);
+static DWORD mlsvc_NetShareEnumLevel2(ndr_xa_t *, srvsvc_infonres_t *,
+    smb_svcenum_t *, int);
+static DWORD mlsvc_NetShareEnumLevel501(ndr_xa_t *, srvsvc_infonres_t *,
+    smb_svcenum_t *, int);
+static DWORD mlsvc_NetShareEnumLevel502(ndr_xa_t *, srvsvc_infonres_t *,
+    smb_svcenum_t *, int);
+static DWORD mlsvc_NetShareEnumCommon(ndr_xa_t *, smb_svcenum_t *,
+    smb_share_t *, void *);
+static boolean_t srvsvc_add_autohome(ndr_xa_t *, smb_svcenum_t *, void *);
 static char *srvsvc_share_mkpath(ndr_xa_t *, char *);
 static uint32_t srvsvc_share_getsd(ndr_xa_t *, smb_share_t *, srvsvc_sd_t *);
 
 static int srvsvc_netconnect_qualifier(const char *);
-static uint32_t srvsvc_estimate_objcnt(uint32_t, uint32_t, uint32_t);
+static void srvsvc_estimate_limit(smb_svcenum_t *, uint32_t);
+static uint32_t srvsvc_open_sessions(void);
+static uint32_t srvsvc_open_connections(uint32_t, const char *);
+static uint32_t srvsvc_open_files(void);
 
 static uint32_t srvsvc_modify_share(smb_share_t *,
     srvsvc_netshare_setinfo_t *);
@@ -224,112 +205,222 @@
 static int
 srvsvc_s_NetConnectEnum(void *arg, ndr_xa_t *mxa)
 {
-	struct mslm_NetConnectEnum *param = arg;
-	srvsvc_NetConnectInfo0_t *info0;
-	srvsvc_NetConnectInfo1_t *info1;
-	char *qualifier;
-	int qualtype;
-	DWORD status = ERROR_SUCCESS;
+	srvsvc_NetConnectEnum_t		*param = arg;
+	smb_netsvc_t			*ns;
+	smb_svcenum_t			se;
+	char				*qualifier;
+	int				qualtype;
+	DWORD				status = ERROR_SUCCESS;
 
 	if (!ndr_is_poweruser(mxa)) {
-		bzero(param, sizeof (struct mslm_NetConnectEnum));
-		param->status = ERROR_ACCESS_DENIED;
-		return (NDR_DRC_OK);
+		status = ERROR_ACCESS_DENIED;
+		goto srvsvc_netconnectenum_error;
 	}
 
 	qualifier = (char *)param->qualifier;
 	qualtype = srvsvc_netconnect_qualifier(qualifier);
-
 	if (qualtype == SRVSVC_CONNECT_ENUM_NULL) {
-		bzero(param, sizeof (struct mslm_NetConnectEnum));
-		param->status = NERR_NetNameNotFound;
+		status = NERR_NetNameNotFound;
+		goto srvsvc_netconnectenum_error;
+	}
+
+	param->total_entries = srvsvc_open_connections(qualtype, qualifier);
+	if (param->total_entries == 0) {
+		bzero(param, sizeof (srvsvc_NetConnectEnum_t));
+		param->status = ERROR_SUCCESS;
 		return (NDR_DRC_OK);
 	}
 
+	bzero(&se, sizeof (smb_svcenum_t));
+	se.se_type = SMB_SVCENUM_TYPE_TREE;
+	se.se_level = param->info.level;
+	se.se_ntotal = param->total_entries;
+	se.se_nlimit = se.se_ntotal;
+
+	if (param->pref_max_len == SMB_SRVSVC_MAXPREFLEN ||
+	    param->pref_max_len > SMB_SRVSVC_MAXBUFLEN)
+		se.se_prefmaxlen = SMB_SRVSVC_MAXBUFLEN;
+	else
+		se.se_prefmaxlen = param->pref_max_len;
+
+	if (param->resume_handle) {
+		se.se_resume = *param->resume_handle;
+		se.se_nskip = se.se_resume;
+		*param->resume_handle = 0;
+	}
+
 	switch (param->info.level) {
 	case 0:
-		info0 = NDR_NEW(mxa, srvsvc_NetConnectInfo0_t);
-		if (info0 == NULL) {
-			status = ERROR_NOT_ENOUGH_MEMORY;
-			break;
-		}
-
-		bzero(info0, sizeof (srvsvc_NetConnectInfo0_t));
-		param->info.ru.info0 = info0;
-
-		status = srvsvc_s_NetConnectEnumLevel0(mxa, info0);
-
-		param->total_entries = info0->entries_read;
-		param->resume_handle = NULL;
+		status = srvsvc_netconnectenum_level0(mxa, &se, param);
 		break;
-
 	case 1:
-		info1 = NDR_NEW(mxa, srvsvc_NetConnectInfo1_t);
-		if (info1 == NULL) {
-			status = ERROR_NOT_ENOUGH_MEMORY;
-			break;
-		}
-
-		bzero(info1, sizeof (srvsvc_NetConnectInfo1_t));
-		param->info.ru.info1 = info1;
-
-		status = srvsvc_s_NetConnectEnumLevel1(mxa, info1);
-
-		param->total_entries = info1->entries_read;
-		param->resume_handle = NULL;
+		status = srvsvc_netconnectenum_level1(mxa, &se, param);
 		break;
-
 	case 50:
 		status = ERROR_NOT_SUPPORTED;
 		break;
-
 	default:
 		status = ERROR_INVALID_LEVEL;
 		break;
 	}
 
 	if (status != ERROR_SUCCESS)
-		bzero(param, sizeof (struct mslm_NetConnectEnum));
-
+		goto srvsvc_netconnectenum_error;
+
+	if ((ns = smb_kmod_enum_init(&se)) == NULL) {
+		status = ERROR_NOT_ENOUGH_MEMORY;
+		goto srvsvc_netconnectenum_error;
+	}
+
+	status = srvsvc_netconnectenum_common(mxa, &param->info, ns, &se);
+	smb_kmod_enum_fini(ns);
+
+	if (status != ERROR_SUCCESS)
+		goto srvsvc_netconnectenum_error;
+
+	if (param->resume_handle &&
+	    param->pref_max_len != SMB_SRVSVC_MAXPREFLEN) {
+		if (se.se_resume < param->total_entries) {
+			*param->resume_handle = se.se_resume;
+			status = ERROR_MORE_DATA;
+		}
+	}
+
+	param->status = status;
+	return (NDR_DRC_OK);
+
+srvsvc_netconnectenum_error:
+	bzero(param, sizeof (srvsvc_NetConnectEnum_t));
 	param->status = status;
 	return (NDR_DRC_OK);
 }
 
-static DWORD
-srvsvc_s_NetConnectEnumLevel0(ndr_xa_t *mxa, srvsvc_NetConnectInfo0_t *info0)
+/*
+ * Allocate memory and estimate the number of objects that can
+ * be returned for NetConnectEnum level 0.
+ */
+static uint32_t
+srvsvc_netconnectenum_level0(ndr_xa_t *mxa, smb_svcenum_t *se,
+    srvsvc_NetConnectEnum_t *param)
 {
-	srvsvc_NetConnectInfoBuf0_t *ci0;
-
-	ci0 = NDR_NEW(mxa, srvsvc_NetConnectInfoBuf0_t);
+	srvsvc_NetConnectInfo0_t	*info0;
+	srvsvc_NetConnectInfoBuf0_t	*ci0;
+
+	if ((info0 = NDR_NEW(mxa, srvsvc_NetConnectInfo0_t)) == NULL)
+		return (ERROR_NOT_ENOUGH_MEMORY);
+
+	bzero(info0, sizeof (srvsvc_NetConnectInfo0_t));
+	param->info.ru.info0 = info0;
+
+	srvsvc_estimate_limit(se, sizeof (srvsvc_NetConnectInfoBuf0_t));
+	if (se->se_nlimit == 0)
+		return (NERR_BufTooSmall);
+
+	do {
+		ci0 = NDR_NEWN(mxa, srvsvc_NetConnectInfoBuf0_t, se->se_nlimit);
+		if (ci0 == NULL)
+			se->se_nlimit >>= 1;
+	} while ((se->se_nlimit > 0) && (ci0 == NULL));
+
 	if (ci0 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
-	ci0->coni0_id = 0x17;
-
 	info0->ci0 = ci0;
-	info0->entries_read = 1;
+	info0->entries_read = 0;
 	return (ERROR_SUCCESS);
 }
 
-static DWORD
-srvsvc_s_NetConnectEnumLevel1(ndr_xa_t *mxa, srvsvc_NetConnectInfo1_t *info1)
+/*
+ * Allocate memory and estimate the number of objects that can
+ * be returned for NetConnectEnum level 1.
+ */
+static uint32_t
+srvsvc_netconnectenum_level1(ndr_xa_t *mxa, smb_svcenum_t *se,
+    srvsvc_NetConnectEnum_t *param)
 {
-	srvsvc_NetConnectInfoBuf1_t *ci1;
-
-	ci1 = NDR_NEW(mxa, srvsvc_NetConnectInfoBuf1_t);
+	srvsvc_NetConnectInfo1_t	*info1;
+	srvsvc_NetConnectInfoBuf1_t	*ci1;
+
+	if ((info1 = NDR_NEW(mxa, srvsvc_NetConnectInfo1_t)) == NULL)
+		return (ERROR_NOT_ENOUGH_MEMORY);
+
+	bzero(info1, sizeof (srvsvc_NetConnectInfo1_t));
+	param->info.ru.info1 = info1;
+
+	srvsvc_estimate_limit(se,
+	    sizeof (srvsvc_NetConnectInfoBuf1_t) + MAXNAMELEN);
+	if (se->se_nlimit == 0)
+		return (NERR_BufTooSmall);
+
+	do {
+		ci1 = NDR_NEWN(mxa, srvsvc_NetConnectInfoBuf1_t, se->se_nlimit);
+		if (ci1 == NULL)
+			se->se_nlimit >>= 1;
+	} while ((se->se_nlimit > 0) && (ci1 == NULL));
+
 	if (ci1 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
-	ci1->coni1_id = 0x17;
-	ci1->coni1_type = STYPE_IPC;
-	ci1->coni1_num_opens = 1;
-	ci1->coni1_num_users = 1;
-	ci1->coni1_time = 16;
-	ci1->coni1_username = (uint8_t *)NDR_STRDUP(mxa, "Administrator");
-	ci1->coni1_netname = (uint8_t *)NDR_STRDUP(mxa, "IPC$");
-
 	info1->ci1 = ci1;
-	info1->entries_read = 1;
+	info1->entries_read = 0;
+	return (ERROR_SUCCESS);
+}
+
+/*
+ * Request a list of connections from the kernel and set up
+ * the connection information to be returned to the client.
+ */
+static uint32_t
+srvsvc_netconnectenum_common(ndr_xa_t *mxa, srvsvc_NetConnectInfo_t *info,
+    smb_netsvc_t *ns, smb_svcenum_t *se)
+{
+	srvsvc_NetConnectInfo0_t	*info0;
+	srvsvc_NetConnectInfo1_t	*info1;
+	srvsvc_NetConnectInfoBuf0_t	*ci0;
+	srvsvc_NetConnectInfoBuf1_t	*ci1;
+	smb_netsvcitem_t		*item;
+	smb_netconnectinfo_t		*tree;
+
+	if (smb_kmod_enum(ns) != 0)
+		return (ERROR_INTERNAL_ERROR);
+
+	info0 = info->ru.info0;
+	ci0 = info0->ci0;
+
+	info1 = info->ru.info1;
+	ci1 = info1->ci1;
+
+	item = list_head(&ns->ns_list);
+	while (item != NULL) {
+		tree = &item->nsi_un.nsi_tree;
+
+		switch (se->se_level) {
+		case 0:
+			ci0->coni0_id = tree->ci_id;
+			++ci0;
+			++info0->entries_read;
+			break;
+		case 1:
+			ci1->coni1_id = tree->ci_id;
+			ci1->coni1_type = tree->ci_type;
+			ci1->coni1_num_opens = tree->ci_numopens;
+			ci1->coni1_num_users = tree->ci_numusers;
+			ci1->coni1_time = tree->ci_time;
+			ci1->coni1_username = (uint8_t *)
+			    NDR_STRDUP(mxa, tree->ci_username);
+			ci1->coni1_netname = (uint8_t *)
+			    NDR_STRDUP(mxa, tree->ci_share);
+			++ci1;
+			++info1->entries_read;
+			break;
+		default:
+			return (ERROR_INVALID_LEVEL);
+		}
+
+		++se->se_resume;
+		item = list_next(&ns->ns_list, item);
+	}
+
 	return (ERROR_SUCCESS);
 }
 
@@ -361,6 +452,45 @@
 	}
 }
 
+static uint32_t
+srvsvc_open_sessions(void)
+{
+	smb_opennum_t	opennum;
+
+	bzero(&opennum, sizeof (smb_opennum_t));
+	if (smb_kmod_get_open_num(&opennum) != 0)
+		return (0);
+
+	return (opennum.open_users);
+}
+
+static uint32_t
+srvsvc_open_connections(uint32_t qualtype, const char *qualifier)
+{
+	smb_opennum_t	opennum;
+
+	bzero(&opennum, sizeof (smb_opennum_t));
+	opennum.qualtype = qualtype;
+	(void) strlcpy(opennum.qualifier, qualifier, MAXNAMELEN);
+
+	if (smb_kmod_get_open_num(&opennum) != 0)
+		return (0);
+
+	return (opennum.open_trees);
+}
+
+static uint32_t
+srvsvc_open_files(void)
+{
+	smb_opennum_t	opennum;
+
+	bzero(&opennum, sizeof (smb_opennum_t));
+	if (smb_kmod_get_open_num(&opennum) != 0)
+		return (0);
+
+	return (opennum.open_files);
+}
+
 /*
  * srvsvc_s_NetFileEnum
  *
@@ -416,8 +546,9 @@
 static int
 srvsvc_s_NetFileEnum(void *arg, ndr_xa_t *mxa)
 {
-	struct mslm_NetFileEnum *param = arg;
-	DWORD status;
+	struct mslm_NetFileEnum	*param = arg;
+	smb_svcenum_t		se;
+	DWORD			status;
 
 	if (!ndr_is_admin(mxa)) {
 		bzero(param, sizeof (struct mslm_NetFileEnum));
@@ -425,13 +556,37 @@
 		return (NDR_DRC_OK);
 	}
 
+	if ((param->total_entries = srvsvc_open_files()) == 0) {
+		bzero(param, sizeof (struct mslm_NetFileEnum));
+		param->status = ERROR_SUCCESS;
+		return (NDR_DRC_OK);
+	}
+
+	bzero(&se, sizeof (smb_svcenum_t));
+	se.se_type = SMB_SVCENUM_TYPE_FILE;
+	se.se_level = param->info.switch_value;
+	se.se_ntotal = param->total_entries;
+	se.se_nlimit = se.se_ntotal;
+
+	if (param->pref_max_len == SMB_SRVSVC_MAXPREFLEN ||
+	    param->pref_max_len > SMB_SRVSVC_MAXBUFLEN)
+		se.se_prefmaxlen = SMB_SRVSVC_MAXBUFLEN;
+	else
+		se.se_prefmaxlen = param->pref_max_len;
+
+	if (param->resume_handle) {
+		se.se_resume = *param->resume_handle;
+		se.se_nskip = se.se_resume;
+		*param->resume_handle = 0;
+	}
+
 	switch (param->info.switch_value) {
 	case 2:
-		status = srvsvc_NetFileEnum2(mxa, param);
+		status = srvsvc_NetFileEnum2(mxa, param, &se);
 		break;
 
 	case 3:
-		status = srvsvc_NetFileEnum3(mxa, param);
+		status = srvsvc_NetFileEnum3(mxa, param, &se);
 		break;
 
 	case 50:
@@ -449,92 +604,144 @@
 		return (NDR_DRC_OK);
 	}
 
-	if (param->resume_handle)
-		*param->resume_handle = 0;
-
-	param->status = ERROR_SUCCESS;
+	if (param->resume_handle &&
+	    param->pref_max_len != SMB_SRVSVC_MAXPREFLEN) {
+		if (se.se_resume < param->total_entries) {
+			*param->resume_handle = se.se_resume;
+			status = ERROR_MORE_DATA;
+		}
+	}
+
+	param->status = status;
 	return (NDR_DRC_OK);
 }
 
 /*
  * Build level 2 file information.
  *
+ * SMB fids are 16-bit values but this interface expects 32-bit file ids.
+ * So we use the uniqid here.
+ *
  * On success, the caller expects that the info2, fi2 and entries_read
  * fields have been set up.
  */
 static DWORD
-srvsvc_NetFileEnum2(ndr_xa_t *mxa, struct mslm_NetFileEnum *param)
+srvsvc_NetFileEnum2(ndr_xa_t *mxa, struct mslm_NetFileEnum *param,
+    smb_svcenum_t *se)
 {
-	struct mslm_NetFileInfoBuf2 *fi2;
-	ndr_pipe_info_t pi;
-	uint32_t entries_read = 0;
-	int i;
+	struct mslm_NetFileInfoBuf2	*fi2;
+	smb_netsvc_t			*ns;
+	smb_netsvcitem_t		*item;
+	smb_netfileinfo_t		*ofile;
+	uint32_t			entries_read = 0;
 
 	param->info.ru.info2 = NDR_NEW(mxa, struct mslm_NetFileInfo2);
 	if (param->info.ru.info2 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
-	fi2 = NDR_NEWN(mxa, struct mslm_NetFileInfoBuf2, 128);
+	srvsvc_estimate_limit(se, sizeof (struct mslm_NetFileInfoBuf2));
+	if (se->se_nlimit == 0)
+		return (NERR_BufTooSmall);
+
+	do {
+		fi2 = NDR_NEWN(mxa, struct mslm_NetFileInfoBuf2, se->se_nlimit);
+		if (fi2 == NULL)
+			se->se_nlimit >>= 1;
+	} while ((se->se_nlimit > 0) && (fi2 == NULL));
+
 	if (fi2 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
 	param->info.ru.info2->fi2 = fi2;
 
-	for (i = 0; i < 128; ++i) {
-		if (ndr_pipe_getinfo(i, &pi) == -1)
-			continue;
-
-		fi2->fi2_id = pi.npi_fid;
+	if ((ns = smb_kmod_enum_init(se)) == NULL)
+		return (ERROR_NOT_ENOUGH_MEMORY);
+
+	if (smb_kmod_enum(ns) != 0) {
+		smb_kmod_enum_fini(ns);
+		return (ERROR_INTERNAL_ERROR);
+	}
+
+	item = list_head(&ns->ns_list);
+	while (item != NULL) {
+		ofile = &item->nsi_un.nsi_ofile;
+		fi2->fi2_id = ofile->fi_uniqid;
 
 		++entries_read;
 		++fi2;
+		item = list_next(&ns->ns_list, item);
 	}
 
+	se->se_resume += entries_read;
 	param->info.ru.info2->entries_read = entries_read;
-	param->total_entries = entries_read;
+	smb_kmod_enum_fini(ns);
 	return (ERROR_SUCCESS);
 }
 
 /*
  * Build level 3 file information.
  *
+ * SMB fids are 16-bit values but this interface expects 32-bit file ids.
+ * So we use the uniqid here.
+ *
  * On success, the caller expects that the info3, fi3 and entries_read
  * fields have been set up.
  */
 static DWORD
-srvsvc_NetFileEnum3(ndr_xa_t *mxa, struct mslm_NetFileEnum *param)
+srvsvc_NetFileEnum3(ndr_xa_t *mxa, struct mslm_NetFileEnum *param,
+    smb_svcenum_t *se)
 {
-	struct mslm_NetFileInfoBuf3 *fi3;
-	ndr_pipe_info_t pi;
-	uint32_t entries_read = 0;
-	int i;
+	struct mslm_NetFileInfoBuf3	*fi3;
+	smb_netsvc_t			*ns;
+	smb_netsvcitem_t		*item;
+	smb_netfileinfo_t		*ofile;
+	uint32_t			entries_read = 0;
 
 	param->info.ru.info3 = NDR_NEW(mxa, struct mslm_NetFileInfo3);
 	if (param->info.ru.info3 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
-	fi3 = NDR_NEWN(mxa, struct mslm_NetFileInfoBuf3, 128);
+	srvsvc_estimate_limit(se,
+	    sizeof (struct mslm_NetFileInfoBuf3) + MAXNAMELEN);
+	if (se->se_nlimit == 0)
+		return (NERR_BufTooSmall);
+
+	do {
+		fi3 = NDR_NEWN(mxa, struct mslm_NetFileInfoBuf3, se->se_nlimit);
+		if (fi3 == NULL)
+			se->se_nlimit >>= 1;
+	} while ((se->se_nlimit > 0) && (fi3 == NULL));
+
 	if (fi3 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
 	param->info.ru.info3->fi3 = fi3;
 
-	for (i = 0; i < 128; ++i) {
-		if (ndr_pipe_getinfo(i, &pi) == -1)
-			continue;
-
-		fi3->fi3_id = pi.npi_fid;
-		fi3->fi3_permissions = pi.npi_permissions;
-		fi3->fi3_num_locks = pi.npi_num_locks;
+	if ((ns = smb_kmod_enum_init(se)) == NULL)
+		return (ERROR_NOT_ENOUGH_MEMORY);
+
+	if (smb_kmod_enum(ns) != 0) {
+		smb_kmod_enum_fini(ns);
+		return (ERROR_INTERNAL_ERROR);
+	}
+
+	item = list_head(&ns->ns_list);
+	while (item != NULL) {
+		ofile = &item->nsi_un.nsi_ofile;
+		fi3->fi3_id = ofile->fi_uniqid;
+		fi3->fi3_permissions = ofile->fi_permissions;
+		fi3->fi3_num_locks = ofile->fi_numlocks;
 		fi3->fi3_pathname = (uint8_t *)
-		    NDR_STRDUP(mxa, pi.npi_pathname);
+		    NDR_STRDUP(mxa, ofile->fi_path);
 		fi3->fi3_username = (uint8_t *)
-		    NDR_STRDUP(mxa, pi.npi_username);
+		    NDR_STRDUP(mxa, ofile->fi_username);
 
 		++entries_read;
 		++fi3;
+		item = list_next(&ns->ns_list, item);
 	}
 
+	se->se_resume += entries_read;
 	param->info.ru.info3->entries_read = entries_read;
 	param->total_entries = entries_read;
 	return (ERROR_SUCCESS);
@@ -544,32 +751,56 @@
  * srvsvc_s_NetFileClose
  *
  * NetFileClose forces a file to close. This function can be used when
- * an error prevents closure by any other means.  Use NetFileClose with
+ * an error prevents closure by other means.  Use NetFileClose with
  * caution because it does not flush data, cached on a client, to the
  * file before closing the file.
  *
+ * SMB fids are 16-bit values but this interface expects 32-bit file ids.
+ * So we use the uniqid here.
+ *
  * Return Values
  * ERROR_SUCCESS            Operation succeeded.
  * ERROR_ACCESS_DENIED      Operation denied.
  * NERR_FileIdNotFound      No open file with the specified id.
  *
- * Note: MSDN suggests that the error code should be ERROR_FILE_NOT_FOUND
- * but network captures using NT show NERR_FileIdNotFound.
- * The NetFileClose2 MSDN page has the right error code.
+ * Note: MSDN suggests ERROR_FILE_NOT_FOUND for NetFileClose but network
+ * captures using NT show NERR_FileIdNotFound, which is consistent with
+ * the NetFileClose2 page on MSDN.
  */
 static int
 srvsvc_s_NetFileClose(void *arg, ndr_xa_t *mxa)
 {
+	static struct {
+		int errnum;
+		int nerr;
+	} errmap[] = {
+		0,	ERROR_SUCCESS,
+		EACCES,	ERROR_ACCESS_DENIED,
+		EPERM,	ERROR_ACCESS_DENIED,
+		EINVAL,	ERROR_INVALID_PARAMETER,
+		ENOMEM,	ERROR_NOT_ENOUGH_MEMORY,
+		ENOENT,	NERR_FileIdNotFound
+	};
+
 	struct mslm_NetFileClose *param = arg;
+	int		i;
+	int		rc;
 
 	if (!ndr_is_admin(mxa)) {
-		bzero(param, sizeof (struct mslm_NetFileClose));
 		param->status = ERROR_ACCESS_DENIED;
 		return (NDR_DRC_OK);
 	}
 
-	bzero(param, sizeof (struct mslm_NetFileClose));
-	param->status = ERROR_SUCCESS;
+	rc = smb_kmod_file_close(param->file_id);
+
+	for (i = 0; i < (sizeof (errmap) / sizeof (errmap[0])); ++i) {
+		if (rc == errmap[i].errnum) {
+			param->status = errmap[i].nerr;
+			return (NDR_DRC_OK);
+		}
+	}
+
+	param->status = ERROR_INTERNAL_ERROR;
 	return (NDR_DRC_OK);
 }
 
@@ -1059,393 +1290,222 @@
 static int
 srvsvc_s_NetSessionEnum(void *arg, ndr_xa_t *mxa)
 {
-	struct mslm_NetSessionEnum *param = arg;
-	struct mslm_infonres *infonres;
-	DWORD status;
-	DWORD n_sessions;
-
-	infonres = NDR_NEW(mxa, struct mslm_infonres);
-	if (infonres == NULL) {
-		bzero(param, sizeof (struct mslm_NetSessionEnum));
-		param->status = ERROR_NOT_ENOUGH_MEMORY;
+	struct mslm_NetSessionEnum	*param = arg;
+	srvsvc_infonres_t		*info;
+	smb_netsvc_t			*ns;
+	smb_svcenum_t			se;
+	DWORD				status = ERROR_SUCCESS;
+
+	if (!ndr_is_admin(mxa)) {
+		status = ERROR_ACCESS_DENIED;
+		goto srvsvc_netsessionenum_error;
+	}
+
+	if ((info = NDR_NEW(mxa, srvsvc_infonres_t)) == NULL) {
+		status = ERROR_NOT_ENOUGH_MEMORY;
+		goto srvsvc_netsessionenum_error;
+	}
+
+	info->entriesread = 0;
+	info->entries = NULL;
+	param->result.level = param->level;
+	param->result.bufptr.p = info;
+
+	if ((param->total_entries = srvsvc_open_sessions()) == 0) {
+		param->resume_handle = NULL;
+		param->status = ERROR_SUCCESS;
 		return (NDR_DRC_OK);
 	}
 
-	infonres->entriesread = 0;
-	infonres->entries = NULL;
-	param->result.level = param->level;
-	param->result.bufptr.p = infonres;
-	param->total_entries = 0;
-	param->resume_handle = NULL;
-	param->status = ERROR_SUCCESS;
-
-	if ((n_sessions = (DWORD) mlsvc_get_num_users()) == 0)
-		return (NDR_DRC_OK);
+	bzero(&se, sizeof (smb_svcenum_t));
+	se.se_type = SMB_SVCENUM_TYPE_USER;
+	se.se_level = param->level;
+	se.se_ntotal = param->total_entries;
+	se.se_nlimit = se.se_ntotal;
+
+	if (param->resume_handle) {
+		se.se_resume = *param->resume_handle;
+		se.se_nskip = se.se_resume;
+		*param->resume_handle = 0;
+	}
 
 	switch (param->level) {
 	case 0:
-		status = mlsvc_NetSessionEnumLevel0(infonres, n_sessions, mxa);
-		break;
-
-	case 1:
-		status = mlsvc_NetSessionEnumLevel1(infonres, n_sessions, mxa);
+		info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_0,
+		    se.se_nlimit);
 		break;
-
-	case 2:
-		status = mlsvc_NetSessionEnumLevel2(infonres, n_sessions, mxa);
+	case 1:
+		info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_1,
+		    se.se_nlimit);
 		break;
-
-	case 10:
-		status = mlsvc_NetSessionEnumLevel10(infonres, n_sessions, mxa);
+	case 2:
+		info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_2,
+		    se.se_nlimit);
 		break;
-
+	case 10:
+		info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_10,
+		    se.se_nlimit);
+		break;
 	case 502:
-		status = mlsvc_NetSessionEnumLevel502(infonres, n_sessions,
-		    mxa);
+		info->entries = NDR_NEWN(mxa, struct mslm_SESSION_INFO_502,
+		    se.se_nlimit);
 		break;
-
 	default:
-		status = ERROR_INVALID_LEVEL;
-		break;
-	}
-
-	if (status != 0) {
 		bzero(param, sizeof (struct mslm_NetSessionEnum));
-		param->status = status;
+		param->status = ERROR_INVALID_LEVEL;
 		return (NDR_DRC_OK);
 	}
 
-	param->total_entries = infonres->entriesread;
+	if (info->entries == NULL) {
+		status = ERROR_NOT_ENOUGH_MEMORY;
+		goto srvsvc_netsessionenum_error;
+	}
+
+	if ((ns = smb_kmod_enum_init(&se)) == NULL) {
+		status = ERROR_NOT_ENOUGH_MEMORY;
+		goto srvsvc_netsessionenum_error;
+	}
+
+	status = srvsvc_NetSessionEnumCommon(mxa, info, ns, &se);
+	smb_kmod_enum_fini(ns);
+
+	if (status != ERROR_SUCCESS)
+		goto srvsvc_netsessionenum_error;
+
+	if (param->resume_handle &&
+	    param->pref_max_len != SMB_SRVSVC_MAXPREFLEN) {
+		if (se.se_resume < param->total_entries) {
+			*param->resume_handle = se.se_resume;
+			status = ERROR_MORE_DATA;
+		}
+	}
+
+	param->total_entries = info->entriesread;
+	param->status = status;
+	return (NDR_DRC_OK);
+
+srvsvc_netsessionenum_error:
+	bzero(param, sizeof (struct mslm_NetSessionEnum));
 	param->status = status;
 	return (NDR_DRC_OK);
 }
 
-/*
- * mlsvc_NetSessionEnumLevel0
- *
- * Build the level 0 session information.
- */
-static DWORD
-mlsvc_NetSessionEnumLevel0(struct mslm_infonres *infonres, DWORD n_sessions,
-    ndr_xa_t *mxa)
+static uint32_t
+srvsvc_NetSessionEnumCommon(ndr_xa_t *mxa, srvsvc_infonres_t *info,
+    smb_netsvc_t *ns, smb_svcenum_t *se)
 {
-	struct mslm_SESSION_INFO_0 *info0;
-	smb_ulist_t *ulist;
-	smb_opipe_context_t *user;
-	char *workstation;
-	char ipaddr_buf[INET6_ADDRSTRLEN];
-	int i;
-
-	ulist = smb_ulist_alloc();
-	if (ulist == NULL)
-		return (ERROR_NOT_ENOUGH_MEMORY);
-
-	if (mlsvc_get_user_list(ulist) != 0) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	if (ulist->ul_cnt < n_sessions)
-		n_sessions = ulist->ul_cnt;
-
-	info0 = NDR_NEWN(mxa, struct mslm_SESSION_INFO_0, n_sessions);
-	if (info0 == NULL) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	user = ulist->ul_users;
-	for (i = 0; i < n_sessions; ++i, user++) {
-		workstation = user->oc_workstation;
+	struct mslm_SESSION_INFO_0	*info0 = info->entries;
+	struct mslm_SESSION_INFO_1	*info1 = info->entries;
+	struct mslm_SESSION_INFO_2	*info2 = info->entries;
+	struct mslm_SESSION_INFO_10	*info10 = info->entries;
+	struct mslm_SESSION_INFO_502	*info502 = info->entries;
+	smb_netsvcitem_t		*item;
+	smb_netuserinfo_t		*user;
+	char				*workstation;
+	char				account[MAXNAMELEN];
+	char				ipaddr_buf[INET6_ADDRSTRLEN];
+	uint32_t			logon_time;
+	uint32_t			flags;
+	uint32_t			entries_read = 0;
+
+	if (smb_kmod_enum(ns) != 0)
+		return (ERROR_INTERNAL_ERROR);
+
+	item = list_head(&ns->ns_list);
+	while (item != NULL) {
+		user = &item->nsi_un.nsi_user;
+
+		workstation = user->ui_workstation;
 		if (workstation == NULL || *workstation == '\0') {
-			(void) smb_inet_ntop(&user->oc_ipaddr,
-			    ipaddr_buf, SMB_IPSTRLEN(user->oc_ipaddr.a_family));
-			workstation = ipaddr_buf;
-		}
-
-		info0[i].sesi0_cname = NDR_STRDUP(mxa, workstation);
-		if (info0[i].sesi0_cname == NULL) {
-			smb_ulist_free(ulist);
-			return (ERROR_NOT_ENOUGH_MEMORY);
-		}
-	}
-
-	smb_ulist_free(ulist);
-	infonres->entriesread = n_sessions;
-	infonres->entries = info0;
-	return (ERROR_SUCCESS);
-}
-
-/*
- * mlsvc_NetSessionEnumLevel1
- *
- * Build the level 1 session information.
- */
-static DWORD
-mlsvc_NetSessionEnumLevel1(struct mslm_infonres *infonres, DWORD n_sessions,
-    ndr_xa_t *mxa)
-{
-	struct mslm_SESSION_INFO_1 *info1;
-	smb_ulist_t *ulist;
-	smb_opipe_context_t *user;
-	char *workstation;
-	char account[MAXNAMELEN];
-	char ipaddr_buf[INET6_ADDRSTRLEN];
-	int i;
-
-	ulist = smb_ulist_alloc();
-	if (ulist == NULL)
-		return (ERROR_NOT_ENOUGH_MEMORY);
-
-	if (mlsvc_get_user_list(ulist) != 0) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	if (ulist->ul_cnt < n_sessions)
-		n_sessions = ulist->ul_cnt;
-
-	info1 = NDR_NEWN(mxa, struct mslm_SESSION_INFO_1, n_sessions);
-	if (info1 == NULL) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	user = ulist->ul_users;
-	for (i = 0; i < n_sessions; ++i, user++) {
-		workstation = user->oc_workstation;
-		if (workstation == NULL || *workstation == '\0') {
-			(void) smb_inet_ntop(&user->oc_ipaddr,
-			    ipaddr_buf, SMB_IPSTRLEN(user->oc_ipaddr.a_family));
+			(void) smb_inet_ntop(&user->ui_ipaddr, ipaddr_buf,
+			    SMB_IPSTRLEN(user->ui_ipaddr.a_family));
 			workstation = ipaddr_buf;
 		}
 
 		(void) snprintf(account, MAXNAMELEN, "%s\\%s",
-		    user->oc_domain, user->oc_account);
-
-		info1[i].sesi1_cname = NDR_STRDUP(mxa, workstation);
-		info1[i].sesi1_uname = NDR_STRDUP(mxa, account);
-
-		if (info1[i].sesi1_cname == NULL ||
-		    info1[i].sesi1_uname == NULL) {
-			smb_ulist_free(ulist);
-			return (ERROR_NOT_ENOUGH_MEMORY);
+		    user->ui_domain, user->ui_account);
+
+		logon_time = time(0) - user->ui_logon_time;
+		flags = (user->ui_flags & SMB_ATF_GUEST) ? SESS_GUEST : 0;
+
+		switch (se->se_level) {
+		case 0:
+			info0->sesi0_cname = NDR_STRDUP(mxa, workstation);
+			if (info0->sesi0_cname == NULL)
+				return (ERROR_NOT_ENOUGH_MEMORY);
+			++info0;
+			break;
+
+		case 1:
+			info1->sesi1_cname = NDR_STRDUP(mxa, workstation);
+			info1->sesi1_uname = NDR_STRDUP(mxa, account);
+
+			if (info1->sesi1_cname == NULL ||
+			    info1->sesi1_uname == NULL)
+				return (ERROR_NOT_ENOUGH_MEMORY);
+
+			info1->sesi1_nopens = user->ui_numopens;
+			info1->sesi1_time = logon_time;
+			info1->sesi1_itime = 0;
+			info1->sesi1_uflags = flags;
+			++info1;
+			break;
+
+		case 2:
+			info2->sesi2_cname = NDR_STRDUP(mxa, workstation);
+			info2->sesi2_uname = NDR_STRDUP(mxa, account);
+
+			if (info2->sesi2_cname == NULL ||
+			    info2->sesi2_uname == NULL)
+				return (ERROR_NOT_ENOUGH_MEMORY);
+
+			info2->sesi2_nopens = user->ui_numopens;
+			info2->sesi2_time = logon_time;
+			info2->sesi2_itime = 0;
+			info2->sesi2_uflags = flags;
+			info2->sesi2_cltype_name = (uint8_t *)"";
+			++info2;
+			break;
+
+		case 10:
+			info10->sesi10_cname = NDR_STRDUP(mxa, workstation);
+			info10->sesi10_uname = NDR_STRDUP(mxa, account);
+
+			if (info10->sesi10_cname == NULL ||
+			    info10->sesi10_uname == NULL)
+				return (ERROR_NOT_ENOUGH_MEMORY);
+
+			info10->sesi10_time = logon_time;
+			info10->sesi10_itime = 0;
+			++info10;
+			break;
+
+		case 502:
+			info502->sesi502_cname = NDR_STRDUP(mxa, workstation);
+			info502->sesi502_uname = NDR_STRDUP(mxa, account);
+
+			if (info502->sesi502_cname == NULL ||
+			    info502->sesi502_uname == NULL)
+				return (ERROR_NOT_ENOUGH_MEMORY);
+
+			info502->sesi502_nopens = user->ui_numopens;
+			info502->sesi502_time = logon_time;
+			info502->sesi502_itime = 0;
+			info502->sesi502_uflags = flags;
+			info502->sesi502_cltype_name = (uint8_t *)"";
+			info502->sesi502_transport = (uint8_t *)"";
+			++info502;
+			break;
+
+		default:
+			return (ERROR_INVALID_LEVEL);
 		}
 
-		info1[i].sesi1_nopens = 1;
-		info1[i].sesi1_time = time(0) - user->oc_logon_time;
-		info1[i].sesi1_itime = 0;
-		info1[i].sesi1_uflags =
-		    (user->oc_flags & SMB_ATF_GUEST) ? SESS_GUEST : 0;
-	}
-
-	smb_ulist_free(ulist);
-	infonres->entriesread = n_sessions;
-	infonres->entries = info1;
-	return (ERROR_SUCCESS);
-}
-
-/*
- * mlsvc_NetSessionEnumLevel2
- *
- * Build the level 2 session information.
- */
-static DWORD
-mlsvc_NetSessionEnumLevel2(struct mslm_infonres *infonres, DWORD n_sessions,
-    ndr_xa_t *mxa)
-{
-	struct mslm_SESSION_INFO_2 *info2;
-	smb_ulist_t *ulist;
-	smb_opipe_context_t *user;
-	char *workstation;
-	char account[MAXNAMELEN];
-	char ipaddr_buf[INET6_ADDRSTRLEN];
-	int i;
-
-	if ((ulist = smb_ulist_alloc()) == NULL)
-		return (ERROR_NOT_ENOUGH_MEMORY);
-
-	if (mlsvc_get_user_list(ulist) != 0) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	if (ulist->ul_cnt < n_sessions)
-		n_sessions = ulist->ul_cnt;
-
-	info2 = NDR_NEWN(mxa, struct mslm_SESSION_INFO_2, n_sessions);
-	if (info2 == NULL) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	user = ulist->ul_users;
-	for (i = 0; i < n_sessions; ++i, user++) {
-		workstation = user->oc_workstation;
-		if (workstation == NULL || *workstation == '\0') {
-			(void) smb_inet_ntop(&user->oc_ipaddr,
-			    ipaddr_buf, SMB_IPSTRLEN(user->oc_ipaddr.a_family));
-			workstation = ipaddr_buf;
-		}
-
-		(void) snprintf(account, MAXNAMELEN, "%s\\%s",
-		    user->oc_domain, user->oc_account);
-
-		info2[i].sesi2_cname = NDR_STRDUP(mxa, workstation);
-		info2[i].sesi2_uname = NDR_STRDUP(mxa, account);
-
-		if (info2[i].sesi2_cname == NULL ||
-		    info2[i].sesi2_uname == NULL) {
-			smb_ulist_free(ulist);
-			return (ERROR_NOT_ENOUGH_MEMORY);
-		}
-
-		info2[i].sesi2_nopens = 1;
-		info2[i].sesi2_time = time(0) - user->oc_logon_time;
-		info2[i].sesi2_itime = 0;
-		info2[i].sesi2_uflags =
-		    (user->oc_flags & SMB_ATF_GUEST) ? SESS_GUEST : 0;
-		info2[i].sesi2_cltype_name = (uint8_t *)"";
+		++entries_read;
+		item = list_next(&ns->ns_list, item);
 	}
 
-	smb_ulist_free(ulist);
-	infonres->entriesread = n_sessions;
-	infonres->entries = info2;
-	return (ERROR_SUCCESS);
-}
-
-/*
- * mlsvc_NetSessionEnumLevel10
- *
- * Build the level 10 session information.
- */
-static DWORD
-mlsvc_NetSessionEnumLevel10(struct mslm_infonres *infonres, DWORD n_sessions,
-    ndr_xa_t *mxa)
-{
-	struct mslm_SESSION_INFO_10 *info10;
-	smb_ulist_t *ulist;
-	smb_opipe_context_t *user;
-	char *workstation;
-	char account[MAXNAMELEN];
-	char ipaddr_buf[INET6_ADDRSTRLEN];
-	int i;
-
-	if ((ulist = smb_ulist_alloc()) == NULL)
-		return (ERROR_NOT_ENOUGH_MEMORY);
-
-	if (mlsvc_get_user_list(ulist) != 0) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	if (ulist->ul_cnt < n_sessions)
-		n_sessions = ulist->ul_cnt;
-
-	info10 = NDR_NEWN(mxa, struct mslm_SESSION_INFO_10, n_sessions);
-	if (info10 == NULL) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	user = ulist->ul_users;
-	for (i = 0; i < n_sessions; ++i, user++) {
-		workstation = user->oc_workstation;
-		if (workstation == NULL || *workstation == '\0') {
-			(void) smb_inet_ntop(&user->oc_ipaddr,
-			    ipaddr_buf, SMB_IPSTRLEN(user->oc_ipaddr.a_family));
-			workstation = ipaddr_buf;
-		}
-
-		(void) snprintf(account, MAXNAMELEN, "%s\\%s",
-		    user->oc_domain, user->oc_account);
-
-		info10[i].sesi10_cname = NDR_STRDUP(mxa, workstation);
-		info10[i].sesi10_uname = NDR_STRDUP(mxa, account);
-
-		if (info10[i].sesi10_cname == NULL ||
-		    info10[i].sesi10_uname == NULL) {
-			smb_ulist_free(ulist);
-			return (ERROR_NOT_ENOUGH_MEMORY);
-		}
-
-		info10[i].sesi10_time = time(0) - user->oc_logon_time;
-		info10[i].sesi10_itime = 0;
-	}
-
-	smb_ulist_free(ulist);
-	infonres->entriesread = n_sessions;
-	infonres->entries = info10;
-	return (ERROR_SUCCESS);
-}
-
-/*
- * mlsvc_NetSessionEnumLevel502
- *
- * Build the level 502 session information.
- */
-static DWORD
-mlsvc_NetSessionEnumLevel502(struct mslm_infonres *infonres, DWORD n_sessions,
-    ndr_xa_t *mxa)
-{
-	struct mslm_SESSION_INFO_502 *info502;
-	smb_ulist_t *ulist;
-	smb_opipe_context_t *user;
-	char *workstation;
-	char account[MAXNAMELEN];
-	char ipaddr_buf[INET6_ADDRSTRLEN];
-	int i;
-
-	if ((ulist = smb_ulist_alloc()) == NULL)
-		return (ERROR_NOT_ENOUGH_MEMORY);
-
-	if (mlsvc_get_user_list(ulist) != 0) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	if (ulist->ul_cnt < n_sessions)
-		n_sessions = ulist->ul_cnt;
-
-	info502 = NDR_NEWN(mxa, struct mslm_SESSION_INFO_502, n_sessions);
-	if (info502 == NULL) {
-		smb_ulist_free(ulist);
-		return (ERROR_NOT_ENOUGH_MEMORY);
-	}
-
-	user = ulist->ul_users;
-	for (i = 0; i < n_sessions; ++i, user++) {
-		workstation = user->oc_workstation;
-		if (workstation == NULL || *workstation == '\0') {
-			(void) smb_inet_ntop(&user->oc_ipaddr,
-			    ipaddr_buf, SMB_IPSTRLEN(user->oc_ipaddr.a_family));
-			workstation = ipaddr_buf;
-		}
-
-		(void) snprintf(account, MAXNAMELEN, "%s\\%s",
-		    user->oc_domain, user->oc_account);
-
-		info502[i].sesi502_cname = NDR_STRDUP(mxa, workstation);
-		info502[i].sesi502_uname = NDR_STRDUP(mxa, account);
-
-		if (info502[i].sesi502_cname == NULL ||
-		    info502[i].sesi502_uname == NULL) {
-			smb_ulist_free(ulist);
-			return (ERROR_NOT_ENOUGH_MEMORY);
-		}
-
-		info502[i].sesi502_nopens = 1;
-		info502[i].sesi502_time = time(0) - user->oc_logon_time;
-		info502[i].sesi502_itime = 0;
-		info502[i].sesi502_uflags =
-		    (user->oc_flags & SMB_ATF_GUEST) ? SESS_GUEST : 0;
-		info502[i].sesi502_cltype_name = (uint8_t *)"";
-		info502[i].sesi502_transport = (uint8_t *)"";
-	}
-
-	smb_ulist_free(ulist);
-	infonres->entriesread = n_sessions;
-	infonres->entries = info502;
+	info->entriesread = entries_read;
 	return (ERROR_SUCCESS);
 }
 
@@ -1456,29 +1516,58 @@
  * On NT only members of the Administrators or Account Operators
  * local groups are permitted to use NetSessionDel.
  *
+ * If unc_clientname is NULL, all sessions associated with the
+ * specified user will be disconnected.
+ *
+ * If username is NULL, all sessions from the specified client
+ * will be disconnected.
+ *
  * Return Values
- * If the function succeeds, the return value is NERR_Success/
- * ERROR_SUCCESS. If the function fails, the return value can be
- * one of the following error codes:
+ * On success, the return value is NERR_Success/ERROR_SUCCESS.
+ * On failure, the return value can be one of the following errors:
  *
  * ERROR_ACCESS_DENIED 		The user does not have access to the
- * 							requested information.
+ * 				requested information.
  * ERROR_INVALID_PARAMETER	The specified parameter is invalid.
  * ERROR_NOT_ENOUGH_MEMORY	Insufficient memory is available.
  * NERR_ClientNameNotFound	A session does not exist with that
- *                          computer name.
+ *				computer name.
  */
 static int
 srvsvc_s_NetSessionDel(void *arg, ndr_xa_t *mxa)
 {
+	static struct {
+		int errnum;
+		int nerr;
+	} errmap[] = {
+		0,	ERROR_SUCCESS,
+		EACCES,	ERROR_ACCESS_DENIED,
+		EPERM,	ERROR_ACCESS_DENIED,
+		EINVAL,	ERROR_INVALID_PARAMETER,
+		ENOMEM,	ERROR_NOT_ENOUGH_MEMORY,
+		ENOENT,	NERR_ClientNameNotFound
+	};
+
 	struct mslm_NetSessionDel *param = arg;
-
-	if (!ndr_is_poweruser(mxa)) {
+	int	i;
+	int	rc;
+
+	if (!ndr_is_admin(mxa)) {
 		param->status = ERROR_ACCESS_DENIED;
 		return (NDR_DRC_OK);
 	}
 
-	param->status = ERROR_ACCESS_DENIED;
+	rc = smb_kmod_session_close((char *)param->unc_clientname,
+	    (char *)param->username);
+
+	for (i = 0; i < (sizeof (errmap) / sizeof (errmap[0])); ++i) {
+		if (rc == errmap[i].errnum) {
+			param->status = errmap[i].nerr;
+			return (NDR_DRC_OK);
+		}
+	}
+
+	param->status = ERROR_INTERNAL_ERROR;
 	return (NDR_DRC_OK);
 }
 
@@ -1853,25 +1942,30 @@
 }
 
 /*
- * srvsvc_estimate_objcnt
+ * srvsvc_estimate_limit
  *
  * Estimate the number of objects that will fit in prefmaxlen.
+ * nlimit is adjusted here.
  */
-static uint32_t
-srvsvc_estimate_objcnt(uint32_t prefmaxlen, uint32_t n_obj, uint32_t obj_size)
+static void
+srvsvc_estimate_limit(smb_svcenum_t *se, uint32_t obj_size)
 {
 	DWORD max_cnt;
 
-	if (obj_size == 0)
-		return (0);
-
-	if ((max_cnt = (prefmaxlen / obj_size)) == 0)
-		return (0);
-
-	if (n_obj > max_cnt)
-		n_obj = max_cnt;
-
-	return (n_obj);
+	if (obj_size == 0) {
+		se->se_nlimit = 0;
+		return;
+	}
+
+	if ((max_cnt = (se->se_prefmaxlen / obj_size)) == 0) {
+		se->se_nlimit = 0;
+		return;
+	}
+
+	if (se->se_ntotal > max_cnt)
+		se->se_nlimit = max_cnt;
+	else
+		se->se_nlimit = se->se_ntotal;
 }
 
 /*
@@ -1890,11 +1984,11 @@
 srvsvc_s_NetShareEnum(void *arg, ndr_xa_t *mxa)
 {
 	struct mslm_NetShareEnum *param = arg;
-	struct mslm_infonres *infonres;
-	srvsvc_enum_t se;
+	srvsvc_infonres_t *infonres;
+	smb_svcenum_t se;
 	DWORD status;
 
-	infonres = NDR_NEW(mxa, struct mslm_infonres);
+	infonres = NDR_NEW(mxa, srvsvc_infonres_t);
 	if (infonres == NULL) {
 		bzero(param, sizeof (struct mslm_NetShareEnum));
 		param->status = ERROR_NOT_ENOUGH_MEMORY;
@@ -1906,9 +2000,11 @@
 	param->result.level = param->level;
 	param->result.bufptr.p = infonres;
 
-	bzero(&se, sizeof (srvsvc_enum_t));
+	bzero(&se, sizeof (smb_svcenum_t));
+	se.se_type = SMB_SVCENUM_TYPE_SHARE;
 	se.se_level = param->level;
-	se.se_n_total = smb_shr_count();
+	se.se_ntotal = smb_shr_count();
+	se.se_nlimit = se.se_ntotal;
 
 	if (param->prefmaxlen == SMB_SRVSVC_MAXPREFLEN ||
 	    param->prefmaxlen > SMB_SRVSVC_MAXBUFLEN)
@@ -1917,8 +2013,9 @@
 		se.se_prefmaxlen = param->prefmaxlen;
 
 	if (param->resume_handle) {
-		se.se_resume_handle = *param->resume_handle;
-		se.se_n_skip = se.se_resume_handle;
+		se.se_resume = *param->resume_handle;
+		se.se_nskip = se.se_resume;
+		*param->resume_handle = 0;
 	}
 
 	switch (param->level) {
@@ -1953,24 +2050,20 @@
 		return (NDR_DRC_OK);
 	}
 
-	if (se.se_n_enum == 0) {
-		if (param->resume_handle)
-			*param->resume_handle = 0;
+	if (se.se_nlimit == 0) {
 		param->status = ERROR_SUCCESS;
 		return (NDR_DRC_OK);
 	}
 
 	if (param->resume_handle &&
 	    param->prefmaxlen != SMB_SRVSVC_MAXPREFLEN) {
-		if (se.se_resume_handle < se.se_n_total) {
-			*param->resume_handle = se.se_resume_handle;
+		if (se.se_resume < se.se_ntotal) {
+			*param->resume_handle = se.se_resume;
 			status = ERROR_MORE_DATA;
-		} else {
-			*param->resume_handle = 0;
 		}
 	}
 
-	param->totalentries = se.se_n_total;
+	param->totalentries = se.se_ntotal;
 	param->status = status;
 	return (NDR_DRC_OK);
 }
@@ -1996,11 +2089,11 @@
 srvsvc_s_NetShareEnumSticky(void *arg, ndr_xa_t *mxa)
 {
 	struct mslm_NetShareEnum *param = arg;
-	struct mslm_infonres *infonres;
-	srvsvc_enum_t se;
+	srvsvc_infonres_t *infonres;
+	smb_svcenum_t se;
 	DWORD status;
 
-	infonres = NDR_NEW(mxa, struct mslm_infonres);
+	infonres = NDR_NEW(mxa, srvsvc_infonres_t);
 	if (infonres == NULL) {
 		bzero(param, sizeof (struct mslm_NetShareEnum));
 		param->status = ERROR_NOT_ENOUGH_MEMORY;
@@ -2012,9 +2105,11 @@
 	param->result.level = param->level;
 	param->result.bufptr.p = infonres;
 
-	bzero(&se, sizeof (srvsvc_enum_t));
+	bzero(&se, sizeof (smb_svcenum_t));
+	se.se_type = SMB_SVCENUM_TYPE_SHARE;
 	se.se_level = param->level;
-	se.se_n_total = smb_shr_count();
+	se.se_ntotal = smb_shr_count();
+	se.se_nlimit = se.se_ntotal;
 
 	if (param->prefmaxlen == SMB_SRVSVC_MAXPREFLEN ||
 	    param->prefmaxlen > SMB_SRVSVC_MAXBUFLEN)
@@ -2023,8 +2118,9 @@
 		se.se_prefmaxlen = param->prefmaxlen;
 
 	if (param->resume_handle) {
-		se.se_resume_handle = *param->resume_handle;
-		se.se_n_skip = se.se_resume_handle;
+		se.se_resume = *param->resume_handle;
+		se.se_nskip = se.se_resume;
+		*param->resume_handle = 0;
 	}
 
 	switch (param->level) {
@@ -2056,24 +2152,20 @@
 		return (NDR_DRC_OK);
 	}
 
-	if (se.se_n_enum == 0) {
-		if (param->resume_handle)
-			*param->resume_handle = 0;
+	if (se.se_nlimit == 0) {
 		param->status = ERROR_SUCCESS;
 		return (NDR_DRC_OK);
 	}
 
 	if (param->resume_handle &&
 	    param->prefmaxlen != SMB_SRVSVC_MAXPREFLEN) {
-		if (se.se_resume_handle < se.se_n_total) {
-			*param->resume_handle = se.se_resume_handle;
+		if (se.se_resume < se.se_ntotal) {
+			*param->resume_handle = se.se_resume;
 			status = ERROR_MORE_DATA;
-		} else {
-			*param->resume_handle = 0;
 		}
 	}
 
-	param->totalentries = se.se_n_total;
+	param->totalentries = se.se_ntotal;
 	param->status = status;
 	return (NDR_DRC_OK);
 }
@@ -2082,33 +2174,33 @@
  * NetShareEnum Level 0
  */
 static DWORD
-mlsvc_NetShareEnumLevel0(ndr_xa_t *mxa,
-    struct mslm_infonres *infonres, srvsvc_enum_t *se, int sticky)
+mlsvc_NetShareEnumLevel0(ndr_xa_t *mxa, srvsvc_infonres_t *infonres,
+    smb_svcenum_t *se, int sticky)
 {
 	struct mslm_NetShareInfo_0 *info0;
 	smb_shriter_t iterator;
 	smb_share_t *si;
 	DWORD status;
 
-	se->se_n_enum = srvsvc_estimate_objcnt(se->se_prefmaxlen,
-	    se->se_n_total, sizeof (struct mslm_NetShareInfo_0) + MAXNAMELEN);
-	if (se->se_n_enum == 0)
+	srvsvc_estimate_limit(se,
+	    sizeof (struct mslm_NetShareInfo_0) + MAXNAMELEN);
+	if (se->se_nlimit == 0)
 		return (ERROR_SUCCESS);
 
-	info0 = NDR_NEWN(mxa, struct mslm_NetShareInfo_0, se->se_n_enum);
+	info0 = NDR_NEWN(mxa, struct mslm_NetShareInfo_0, se->se_nlimit);
 	if (info0 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
 	smb_shr_iterinit(&iterator);
 
-	se->se_n_read = 0;
+	se->se_nitems = 0;
 	while ((si = smb_shr_iterate(&iterator)) != NULL) {
-		if (se->se_n_skip > 0) {
-			--se->se_n_skip;
+		if (se->se_nskip > 0) {
+			--se->se_nskip;
 			continue;
 		}
 
-		++se->se_resume_handle;
+		++se->se_resume;
 
 		if (sticky && (si->shr_flags & SMB_SHRF_TRANS))
 			continue;
@@ -2116,8 +2208,8 @@
 		if (si->shr_flags & SMB_SHRF_AUTOHOME)
 			continue;
 
-		if (se->se_n_read >= se->se_n_enum) {
-			se->se_n_read = se->se_n_enum;
+		if (se->se_nitems >= se->se_nlimit) {
+			se->se_nitems = se->se_nlimit;
 			break;
 		}
 
@@ -2125,15 +2217,15 @@
 		if (status != ERROR_SUCCESS)
 			break;
 
-		++se->se_n_read;
+		++se->se_nitems;
 	}
 
-	if (se->se_n_read < se->se_n_enum) {
+	if (se->se_nitems < se->se_nlimit) {
 		if (srvsvc_add_autohome(mxa, se, (void *)info0))
-			++se->se_n_read;
+			++se->se_nitems;
 	}
 
-	infonres->entriesread = se->se_n_read;
+	infonres->entriesread = se->se_nitems;
 	infonres->entries = info0;
 	return (ERROR_SUCCESS);
 }
@@ -2142,33 +2234,33 @@
  * NetShareEnum Level 1
  */
 static DWORD
-mlsvc_NetShareEnumLevel1(ndr_xa_t *mxa,
-    struct mslm_infonres *infonres, srvsvc_enum_t *se, int sticky)
+mlsvc_NetShareEnumLevel1(ndr_xa_t *mxa, srvsvc_infonres_t *infonres,
+    smb_svcenum_t *se, int sticky)
 {
 	struct mslm_NetShareInfo_1 *info1;
 	smb_shriter_t iterator;
 	smb_share_t *si;
 	DWORD status;
 
-	se->se_n_enum = srvsvc_estimate_objcnt(se->se_prefmaxlen,
-	    se->se_n_total, sizeof (struct mslm_NetShareInfo_1) + MAXNAMELEN);
-	if (se->se_n_enum == 0)
+	srvsvc_estimate_limit(se,
+	    sizeof (struct mslm_NetShareInfo_1) + MAXNAMELEN);
+	if (se->se_nlimit == 0)
 		return (ERROR_SUCCESS);
 
-	info1 = NDR_NEWN(mxa, struct mslm_NetShareInfo_1, se->se_n_enum);
+	info1 = NDR_NEWN(mxa, struct mslm_NetShareInfo_1, se->se_nlimit);
 	if (info1 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
 	smb_shr_iterinit(&iterator);
 
-	se->se_n_read = 0;
+	se->se_nitems = 0;
 	while ((si = smb_shr_iterate(&iterator)) != 0) {
-		if (se->se_n_skip > 0) {
-			--se->se_n_skip;
+		if (se->se_nskip > 0) {
+			--se->se_nskip;
 			continue;
 		}
 
-		++se->se_resume_handle;
+		++se->se_resume;
 
 		if (sticky && (si->shr_flags & SMB_SHRF_TRANS))
 			continue;
@@ -2176,8 +2268,8 @@
 		if (si->shr_flags & SMB_SHRF_AUTOHOME)
 			continue;
 
-		if (se->se_n_read >= se->se_n_enum) {
-			se->se_n_read = se->se_n_enum;
+		if (se->se_nitems >= se->se_nlimit) {
+			se->se_nitems = se->se_nlimit;
 			break;
 		}
 
@@ -2185,15 +2277,15 @@
 		if (status != ERROR_SUCCESS)
 			break;
 
-		++se->se_n_read;
+		++se->se_nitems;
 	}
 
-	if (se->se_n_read < se->se_n_enum) {
+	if (se->se_nitems < se->se_nlimit) {
 		if (srvsvc_add_autohome(mxa, se, (void *)info1))
-			++se->se_n_read;
+			++se->se_nitems;
 	}
 
-	infonres->entriesread = se->se_n_read;
+	infonres->entriesread = se->se_nitems;
 	infonres->entries = info1;
 	return (ERROR_SUCCESS);
 }
@@ -2202,33 +2294,33 @@
  * NetShareEnum Level 2
  */
 static DWORD
-mlsvc_NetShareEnumLevel2(ndr_xa_t *mxa,
-    struct mslm_infonres *infonres, srvsvc_enum_t *se, int sticky)
+mlsvc_NetShareEnumLevel2(ndr_xa_t *mxa, srvsvc_infonres_t *infonres,
+    smb_svcenum_t *se, int sticky)
 {
 	struct mslm_NetShareInfo_2 *info2;
 	smb_shriter_t iterator;
 	smb_share_t *si;
 	DWORD status;
 
-	se->se_n_enum = srvsvc_estimate_objcnt(se->se_prefmaxlen,
-	    se->se_n_total, sizeof (struct mslm_NetShareInfo_2) + MAXNAMELEN);
-	if (se->se_n_enum == 0)
+	srvsvc_estimate_limit(se,
+	    sizeof (struct mslm_NetShareInfo_2) + MAXNAMELEN);
+	if (se->se_nlimit == 0)
 		return (ERROR_SUCCESS);
 
-	info2 = NDR_NEWN(mxa, struct mslm_NetShareInfo_2, se->se_n_enum);
+	info2 = NDR_NEWN(mxa, struct mslm_NetShareInfo_2, se->se_nlimit);
 	if (info2 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
 	smb_shr_iterinit(&iterator);
 
-	se->se_n_read = 0;
+	se->se_nitems = 0;
 	while ((si = smb_shr_iterate(&iterator)) != 0) {
-		if (se->se_n_skip > 0) {
-			--se->se_n_skip;
+		if (se->se_nskip > 0) {
+			--se->se_nskip;
 			continue;
 		}
 
-		++se->se_resume_handle;
+		++se->se_resume;
 
 		if (sticky && (si->shr_flags & SMB_SHRF_TRANS))
 			continue;
@@ -2236,8 +2328,8 @@
 		if (si->shr_flags & SMB_SHRF_AUTOHOME)
 			continue;
 
-		if (se->se_n_read >= se->se_n_enum) {
-			se->se_n_read = se->se_n_enum;
+		if (se->se_nitems >= se->se_nlimit) {
+			se->se_nitems = se->se_nlimit;
 			break;
 		}
 
@@ -2245,15 +2337,15 @@
 		if (status != ERROR_SUCCESS)
 			break;
 
-		++se->se_n_read;
+		++se->se_nitems;
 	}
 
-	if (se->se_n_read < se->se_n_enum) {
+	if (se->se_nitems < se->se_nlimit) {
 		if (srvsvc_add_autohome(mxa, se, (void *)info2))
-			++se->se_n_read;
+			++se->se_nitems;
 	}
 
-	infonres->entriesread = se->se_n_read;
+	infonres->entriesread = se->se_nitems;
 	infonres->entries = info2;
 	return (ERROR_SUCCESS);
 }
@@ -2262,34 +2354,34 @@
  * NetShareEnum Level 501
  */
 static DWORD
-mlsvc_NetShareEnumLevel501(ndr_xa_t *mxa,
-    struct mslm_infonres *infonres, srvsvc_enum_t *se, int sticky)
+mlsvc_NetShareEnumLevel501(ndr_xa_t *mxa, srvsvc_infonres_t *infonres,
+    smb_svcenum_t *se, int sticky)
 {
 	struct mslm_NetShareInfo_501 *info501;
 	smb_shriter_t iterator;
 	smb_share_t *si;
 	DWORD status;
 
-	se->se_n_enum = srvsvc_estimate_objcnt(se->se_prefmaxlen,
-	    se->se_n_total, sizeof (struct mslm_NetShareInfo_501) + MAXNAMELEN);
-	if (se->se_n_enum == 0)
+	srvsvc_estimate_limit(se,
+	    sizeof (struct mslm_NetShareInfo_501) + MAXNAMELEN);
+	if (se->se_nlimit == 0)
 		return (ERROR_SUCCESS);
 
 	info501 = NDR_NEWN(mxa, struct mslm_NetShareInfo_501,
-	    se->se_n_enum);
+	    se->se_nlimit);
 	if (info501 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
 	smb_shr_iterinit(&iterator);
 
-	se->se_n_read = 0;
+	se->se_nitems = 0;
 	while ((si = smb_shr_iterate(&iterator)) != 0) {
-		if (se->se_n_skip > 0) {
-			--se->se_n_skip;
+		if (se->se_nskip > 0) {
+			--se->se_nskip;
 			continue;
 		}
 
-		++se->se_resume_handle;
+		++se->se_resume;
 
 		if (sticky && (si->shr_flags & SMB_SHRF_TRANS))
 			continue;
@@ -2297,8 +2389,8 @@
 		if (si->shr_flags & SMB_SHRF_AUTOHOME)
 			continue;
 
-		if (se->se_n_read >= se->se_n_enum) {
-			se->se_n_read = se->se_n_enum;
+		if (se->se_nitems >= se->se_nlimit) {
+			se->se_nitems = se->se_nlimit;
 			break;
 		}
 
@@ -2306,15 +2398,15 @@
 		if (status != ERROR_SUCCESS)
 			break;
 
-		++se->se_n_read;
+		++se->se_nitems;
 	}
 
-	if (se->se_n_read < se->se_n_enum) {
+	if (se->se_nitems < se->se_nlimit) {
 		if (srvsvc_add_autohome(mxa, se, (void *)info501))
-			++se->se_n_read;
+			++se->se_nitems;
 	}
 
-	infonres->entriesread = se->se_n_read;
+	infonres->entriesread = se->se_nitems;
 	infonres->entries = info501;
 	return (ERROR_SUCCESS);
 }
@@ -2323,34 +2415,34 @@
  * NetShareEnum Level 502
  */
 static DWORD
-mlsvc_NetShareEnumLevel502(ndr_xa_t *mxa,
-    struct mslm_infonres *infonres, srvsvc_enum_t *se, int sticky)
+mlsvc_NetShareEnumLevel502(ndr_xa_t *mxa, srvsvc_infonres_t *infonres,
+    smb_svcenum_t *se, int sticky)
 {
 	struct mslm_NetShareInfo_502 *info502;
 	smb_shriter_t iterator;
 	smb_share_t *si;
 	DWORD status;
 
-	se->se_n_enum = srvsvc_estimate_objcnt(se->se_prefmaxlen,
-	    se->se_n_total, sizeof (struct mslm_NetShareInfo_502) + MAXNAMELEN);
-	if (se->se_n_enum == 0)
+	srvsvc_estimate_limit(se,
+	    sizeof (struct mslm_NetShareInfo_502) + MAXNAMELEN);
+	if (se->se_nlimit == 0)
 		return (ERROR_SUCCESS);
 
 	info502 = NDR_NEWN(mxa, struct mslm_NetShareInfo_502,
-	    se->se_n_enum);
+	    se->se_nlimit);
 	if (info502 == NULL)
 		return (ERROR_NOT_ENOUGH_MEMORY);
 
 	smb_shr_iterinit(&iterator);
 
-	se->se_n_read = 0;
+	se->se_nitems = 0;
 	while ((si = smb_shr_iterate(&iterator)) != NULL) {
-		if (se->se_n_skip > 0) {
-			--se->se_n_skip;
+		if (se->se_nskip > 0) {
+			--se->se_nskip;
 			continue;
 		}
 
-		++se->se_resume_handle;
+		++se->se_resume;
 
 		if (sticky && (si->shr_flags & SMB_SHRF_TRANS))
 			continue;
@@ -2358,8 +2450,8 @@
 		if (si->shr_flags & SMB_SHRF_AUTOHOME)
 			continue;
 
-		if (se->se_n_read >= se->se_n_enum) {
-			se->se_n_read = se->se_n_enum;
+		if (se->se_nitems >= se->se_nlimit) {
+			se->se_nitems = se->se_nlimit;
 			break;
 		}
 
@@ -2367,15 +2459,15 @@
 		if (status != ERROR_SUCCESS)
 			break;
 
-		++se->se_n_read;
+		++se->se_nitems;
 	}
 
-	if (se->se_n_read < se->se_n_enum) {
+	if (se->se_nitems < se->se_nlimit) {
 		if (srvsvc_add_autohome(mxa, se, (void *)info502))
-			++se->se_n_read;
+			++se->se_nitems;
 	}
 
-	infonres->entriesread = se->se_n_read;
+	infonres->entriesread = se->se_nitems;
 	infonres->entries = info502;
 	return (ERROR_SUCCESS);
 }
@@ -2396,7 +2488,7 @@
  *	ERROR_INVALID_LEVEL
  */
 static DWORD
-mlsvc_NetShareEnumCommon(ndr_xa_t *mxa, srvsvc_enum_t *se,
+mlsvc_NetShareEnumCommon(ndr_xa_t *mxa, smb_svcenum_t *se,
     smb_share_t *si, void *infop)
 {
 	struct mslm_NetShareInfo_0 *info0;
@@ -2409,7 +2501,7 @@
 	uint8_t *comment;
 	uint8_t *passwd;
 	uint8_t *path;
-	int i = se->se_n_read;
+	int i = se->se_nitems;
 
 	netname = (uint8_t *)NDR_STRDUP(mxa, si->shr_name);
 	comment = (uint8_t *)NDR_STRDUP(mxa, si->shr_cmnt);
@@ -2487,10 +2579,10 @@
  * share to avoid duplicates.
  */
 static boolean_t
-srvsvc_add_autohome(ndr_xa_t *mxa, srvsvc_enum_t *se, void *infop)
+srvsvc_add_autohome(ndr_xa_t *mxa, smb_svcenum_t *se, void *infop)
 {
-	smb_opipe_context_t *ctx = &mxa->pipe->np_ctx;
-	char *username = ctx->oc_account;
+	smb_netuserinfo_t *user = &mxa->pipe->np_user;
+	char *username = user->ui_account;
 	smb_share_t si;
 	DWORD status;
 
@@ -2580,8 +2672,8 @@
 /*
  * srvsvc_s_NetShareDel
  *
- * Delete a share. Only the administrator, or a member of the domain
- * administrators group, is allowed to delete shares.
+ * Delete a share.  Only members of the Administrators, Server Operators
+ * or Power Users local groups are allowed to delete shares.
  *
  * This interface is used by the rmtshare command from the NT resource
  * kit. Rmtshare allows a client to add or remove shares on a server
--- a/usr/src/lib/smbsrv/libmlsvc/common/svcctl_scm.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/svcctl_scm.c	Fri Jul 17 17:54:42 2009 -0700
@@ -30,7 +30,6 @@
  * of Solaris SMF service are displayed on the Server/Connection Manager
  * Windows client.
  */
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -40,9 +39,12 @@
 #include <libscf.h>
 #include <libscf_priv.h>
 #include <time.h>
+#include <dlfcn.h>
 #include <sys/types.h>
-
-#include "svcctl_scm.h"
+#include <smbsrv/winsvc.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/ndl/svcctl.ndl>
+#include <smbsrv/libmlsvc.h>
 
 #define	LEGACY_UNKNOWN	"unknown"
 #define	SVC_NAME_PROP	"name"
@@ -51,6 +53,12 @@
 #define	EMPTY_OK	0x01
 #define	MULTI_OK	0x02
 
+static void *svcctl_scm_interposer_hdl = NULL;
+static struct {
+	int (*svcctl_op_scm_init)(svcctl_manager_context_t *);
+	int (*svcctl_op_scf_init)(svcctl_manager_context_t *);
+} svcctl_scm_ops;
+
 /*
  * svcctl_scm_avl_nodecmp
  *
@@ -659,7 +667,7 @@
  *
  * Calculates bytes needed to enumerate SMF services.
  */
-void
+static void
 svcctl_scm_bytes_needed(svcctl_manager_context_t *mgr_ctx)
 {
 	int bytes_needed = 0, svc_enum_status_size = 0;
@@ -761,6 +769,10 @@
 svcctl_scm_refresh(svcctl_manager_context_t *mgr_ctx)
 {
 	svcctl_scm_fini(mgr_ctx);
+
+	if (svcctl_scm_ops.svcctl_op_scm_init != NULL)
+		return (svcctl_scm_ops.svcctl_op_scm_init(mgr_ctx));
+
 	return (svcctl_scm_init(mgr_ctx));
 }
 
@@ -772,6 +784,10 @@
 int
 svcctl_scm_scf_handle_init(svcctl_manager_context_t *mgr_ctx)
 {
+	if (svcctl_scm_ops.svcctl_op_scf_init != NULL)
+		return (svcctl_scm_ops.
+		    svcctl_op_scf_init(mgr_ctx));
+
 	mgr_ctx->mc_scf_hdl = scf_handle_create(SCF_VERSION);
 	if (mgr_ctx->mc_scf_hdl == NULL)
 		return (-1);
@@ -810,8 +826,11 @@
 	scf_value_destroy(mgr_ctx->mc_scf_gval);
 	scf_property_destroy(mgr_ctx->mc_scf_gprop);
 	scf_pg_destroy(mgr_ctx->mc_scf_gpg);
-	(void) scf_handle_unbind(mgr_ctx->mc_scf_hdl);
-	scf_handle_destroy(mgr_ctx->mc_scf_hdl);
+
+	if (mgr_ctx->mc_scf_hdl != NULL) {
+		(void) scf_handle_unbind(mgr_ctx->mc_scf_hdl);
+		scf_handle_destroy(mgr_ctx->mc_scf_hdl);
+	}
 }
 
 /*
@@ -829,6 +848,9 @@
 	assert(mgr_ctx->mc_svcs_pool == NULL);
 	assert(mgr_ctx->mc_svcs == NULL);
 
+	if (svcctl_scm_ops.svcctl_op_scm_init != NULL)
+		return (svcctl_scm_ops.svcctl_op_scm_init(mgr_ctx));
+
 	mgr_ctx->mc_svcs_pool = uu_avl_pool_create("smf_svcs_pool",
 	    sizeof (svcctl_svc_node_t), offsetof(svcctl_svc_node_t, sn_node),
 	    svcctl_scm_avl_nodecmp, UU_AVL_DEBUG);
@@ -890,3 +912,47 @@
 	mgr_ctx->mc_svcs_pool = NULL;
 	mgr_ctx->mc_svcs = NULL;
 }
+
+/*
+ * svcctl_init
+ *
+ * Initializes the SVCCTL service.
+ * Initializes handle and ops structure to interposed library.
+ */
+void
+svcctl_init(void)
+{
+	svcctl_scm_interposer_hdl = smb_dlopen();
+	if (svcctl_scm_interposer_hdl == NULL)
+		return;
+
+	bzero((void *)&svcctl_scm_ops,
+	    sizeof (svcctl_scm_ops));
+
+	svcctl_scm_ops.svcctl_op_scm_init =
+	    (int (*)())dlsym(svcctl_scm_interposer_hdl, "svcctl_scm_init");
+
+	svcctl_scm_ops.svcctl_op_scf_init =
+	    (int (*)())dlsym(svcctl_scm_interposer_hdl,
+	    "svcctl_scm_scf_handle_init");
+
+	if (svcctl_scm_ops.svcctl_op_scm_init == NULL ||
+	    svcctl_scm_ops.svcctl_op_scf_init == NULL)
+		svcctl_fini();
+
+}
+
+/*
+ * svcctl_fini
+ *
+ * Finalizes the SVCCTL service.
+ * Closes handle to interposed library.
+ */
+void
+svcctl_fini(void)
+{
+	smb_dlclose(svcctl_scm_interposer_hdl);
+	svcctl_scm_interposer_hdl = NULL;
+	bzero((void *)&svcctl_scm_ops,
+	    sizeof (svcctl_scm_ops));
+}
--- a/usr/src/lib/smbsrv/libmlsvc/common/svcctl_scm.h	Fri Jul 17 16:57:52 2009 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * 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.
- */
-
-#ifndef	_SVCCTL_H
-#define	_SVCCTL_H
-
-#include <libuutil.h>
-#include <smbsrv/libsmb.h>
-#include <smbsrv/libmlrpc.h>
-#include <smbsrv/libmlsvc.h>
-#include <smbsrv/nterror.h>
-#include <smbsrv/winsvc.h>
-#include <smbsrv/ndl/svcctl.ndl>
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/*
- * Calculate the wide-char equivalent string length required to
- * store a string - including the terminating null wide-char.
- */
-#define	SVCCTL_WNSTRLEN(S)	((strlen((S)) + 1) * sizeof (mts_wchar_t))
-
-/* An AVL-storable node representing each service in the SCM database. */
-typedef struct svcctl_svc_node {
-	uu_avl_node_t		sn_node;
-	char			*sn_name;	/* Service Name (Key) */
-	char			*sn_fmri;	/* Display Name (FMRI) */
-	char			*sn_desc;	/* Description */
-	char			*sn_state;	/* State */
-} svcctl_svc_node_t;
-
-/* This structure provides context for each svcctl_s_OpenManager call. */
-typedef struct svcctl_manager_context {
-	scf_handle_t		*mc_scf_hdl;	  /* SCF handle */
-	scf_propertygroup_t	*mc_scf_gpg;	  /* Property group */
-	scf_property_t		*mc_scf_gprop;	  /* Property */
-	scf_value_t		*mc_scf_gval;	  /* Value */
-	uint32_t		mc_scf_numsvcs;   /* Number of SMF services */
-	ssize_t			mc_scf_max_fmri_len;  /* Max FMRI length */
-	ssize_t			mc_scf_max_value_len; /* Max Value length */
-	uint32_t		mc_bytes_needed;  /* Number of bytes needed */
-	uu_avl_pool_t		*mc_svcs_pool;	  /* AVL pool */
-	uu_avl_t		*mc_svcs;	  /* AVL tree of SMF services */
-} svcctl_manager_context_t;
-
-/* This structure provides context for each svcctl_s_OpenService call. */
-typedef struct svcctl_service_context {
-	ndr_hdid_t		*sc_mgrid;	/* Manager ID */
-	char			*sc_svcname;    /* Service Name */
-} svcctl_service_context_t;
-
-typedef enum {
-	SVCCTL_MANAGER_CONTEXT = 0,
-	SVCCTL_SERVICE_CONTEXT
-} svcctl_context_type_t;
-
-/* This structure provides abstraction for service and manager context call. */
-typedef struct svcctl_context {
-	svcctl_context_type_t	c_type;
-	union {
-		svcctl_manager_context_t *uc_mgr;
-		svcctl_service_context_t *uc_svc;
-		void *uc_cp;
-	} c_ctx;
-} svcctl_context_t;
-
-/* Service Control Manager (SCM) functions */
-int svcctl_scm_init(svcctl_manager_context_t *);
-void svcctl_scm_fini(svcctl_manager_context_t *);
-int svcctl_scm_scf_handle_init(svcctl_manager_context_t *);
-void svcctl_scm_scf_handle_fini(svcctl_manager_context_t *);
-int svcctl_scm_refresh(svcctl_manager_context_t *);
-void svcctl_scm_bytes_needed(svcctl_manager_context_t *);
-uint32_t svcctl_scm_enum_services(svcctl_manager_context_t *, uint8_t *,
-    size_t, uint32_t *, boolean_t);
-uint32_t svcctl_scm_validate_service(svcctl_manager_context_t *, char *);
-svcctl_svc_node_t *svcctl_scm_find_service(svcctl_manager_context_t *, char *);
-uint32_t svcctl_scm_map_status(const char *);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* _SVCCTL_H */
--- a/usr/src/lib/smbsrv/libmlsvc/common/svcctl_svc.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libmlsvc/common/svcctl_svc.c	Fri Jul 17 17:54:42 2009 -0700
@@ -37,7 +37,10 @@
 #include <smbsrv/ntstatus.h>
 #include <smbsrv/nmpipes.h>
 #include <smbsrv/ntifs.h>
-#include "svcctl_scm.h"
+#include <smbsrv/winsvc.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/ndl/svcctl.ndl>
+#include <smbsrv/libmlsvc.h>
 
 #define	SVCCTL_SECURITY_BUFSIZE		256
 #define	SVCCTL_ENUMSERVICES_MINBUFSIZE	1024
@@ -120,6 +123,13 @@
 svcctl_initialize(void)
 {
 	(void) ndr_svc_register(&svcctl_service);
+	svcctl_init();
+}
+
+void
+svcctl_finalize(void)
+{
+	svcctl_fini();
 }
 
 /*
--- a/usr/src/lib/smbsrv/libsmb/Makefile.com	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libsmb/Makefile.com	Fri Jul 17 17:54:42 2009 -0700
@@ -79,7 +79,7 @@
 INCS += -I$(SRC)/common/smbsrv
 
 LDLIBS +=	$(MACH_LDLIBS)
-LDLIBS +=	-lscf -lmd -lnsl -lpkcs11 -lsec -lsocket -lresolv
+LDLIBS +=	-lscf -lmd -luuid -lnsl -lpkcs11 -lsec -lsocket -lresolv
 LDLIBS +=	-lidmap -lavl -lc
 CPPFLAGS +=	$(INCS) -D_REENTRANT
 
--- a/usr/src/lib/smbsrv/libsmb/common/libsmb.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h	Fri Jul 17 17:54:42 2009 -0700
@@ -41,6 +41,7 @@
 #include <libscf.h>
 #include <libshare.h>
 #include <sqlite/sqlite.h>
+#include <uuid/uuid.h>
 
 #include <smbsrv/string.h>
 #include <smbsrv/smb_idmap.h>
@@ -65,7 +66,6 @@
 #define	SMB_CCACHE_PATH SMB_VARRUN_DIR "/" SMB_CCACHE_FILE
 
 
-
 /* Max value length of all SMB properties */
 #define	MAX_VALUE_BUFLEN	512
 
@@ -272,9 +272,6 @@
 extern int smb_chk_hostaccess(smb_inaddr_t *, char *);
 
 extern int smb_getnameinfo(smb_inaddr_t *, char *, int, int);
-extern smb_ulist_t *smb_ulist_alloc(void);
-extern void smb_ulist_free(smb_ulist_t *);
-extern void smb_ulist_cleanup(smb_ulist_t *);
 
 void smb_trace(const char *s);
 void smb_tracef(const char *fmt, ...);
@@ -848,8 +845,24 @@
 void smb_kmod_unbind(void);
 int smb_kmod_share(char *, char *);
 int smb_kmod_unshare(char *, char *);
-int smb_kmod_get_usernum(uint32_t *);
-int smb_kmod_get_userlist(smb_ulist_t *);
+int smb_kmod_get_open_num(smb_opennum_t *);
+int smb_kmod_enum(smb_netsvc_t *);
+smb_netsvc_t *smb_kmod_enum_init(smb_svcenum_t *);
+void smb_kmod_enum_fini(smb_netsvc_t *);
+int smb_kmod_session_close(const char *, const char *);
+int smb_kmod_file_close(uint32_t);
+
+/*
+ * Interposer library validation
+ */
+#define	SMBEX_VERSION	1
+#define	SMBEX_KEY	"82273fdc-e32a-18c3-3f78-827929dc23ea"
+typedef struct smbex_version {
+	uint32_t v_version;
+	uuid_t v_uuid;
+} smbex_version_t;
+void *smb_dlopen(void);
+void smb_dlclose(void *);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers	Fri Jul 17 17:54:42 2009 -0700
@@ -147,6 +147,8 @@
 	smb_ctxbuf_init;
 	smb_ctxbuf_len;
 	smb_ctxbuf_printf;
+	smb_dlclose;
+	smb_dlopen;
 	smb_dr_clnt_call;
 	smb_dr_clnt_cleanup;
 	smb_dr_clnt_setup;
@@ -219,10 +221,14 @@
 	smb_inet_iszero;
 	smb_inet_ntop;
 	smb_kmod_bind;
-        smb_kmod_get_userlist;
-        smb_kmod_get_usernum;
+	smb_kmod_enum;
+	smb_kmod_enum_init;
+	smb_kmod_enum_fini;
+	smb_kmod_file_close;
+        smb_kmod_get_open_num;
 	smb_kmod_nbtlisten;
 	smb_kmod_nbtreceive;
+	smb_kmod_session_close;
 	smb_kmod_setcfg;
 	smb_kmod_setgmtoff;
 	smb_kmod_share;
@@ -275,6 +281,12 @@
 	smb_msgbuf_term;
 	smb_msgbuf_used;
 	smb_msgbuf_word_align;
+	smb_netconnectinfo_decode;
+	smb_netconnectinfo_encode;
+	smb_netfileinfo_decode;
+	smb_netfileinfo_encode;
+	smb_netuserinfo_decode;
+	smb_netuserinfo_encode;
 	smb_nic_addhost;
 	smb_nic_delhost;
 	smb_nic_is_local;
@@ -289,9 +301,6 @@
 	smb_opipe_hdr_encode;
 	smb_opipe_hdr_decode;
 	smb_opipe_hdr_xdr;
-	smb_opipe_context_encode;
-	smb_opipe_context_decode;
-	smb_opipe_context_xdr;
 	smb_priv_getbyname;
 	smb_priv_getbyvalue;
 	smb_priv_presentable_ids;
@@ -348,8 +357,6 @@
 	smb_tonetbiosname;
 	smb_trace;
 	smb_tracef;
-	smb_ulist_alloc;
-	smb_ulist_free;
 	smb_update_netlogon_seqnum;
 	smb_wka_fini;
 	smb_wka_get_domain;
--- a/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c	Fri Jul 17 17:54:42 2009 -0700
@@ -181,83 +181,213 @@
 }
 
 int
-smb_kmod_get_usernum(uint32_t *punum)
+smb_kmod_get_open_num(smb_opennum_t *opennum)
 {
-	smb_ioc_usernum_t ioc;
+	smb_ioc_opennum_t ioc;
 	int rc;
 
-	ioc.num = 0;
-	rc = smb_kmod_ioctl(SMB_IOC_USER_NUMBER, &ioc.hdr, sizeof (ioc));
-	if (rc == 0)
-		*punum = ioc.num;
+	bzero(&ioc, sizeof (ioc));
+	ioc.qualtype = opennum->qualtype;
+	(void) strlcpy(ioc.qualifier, opennum->qualifier, MAXNAMELEN);
+
+	rc = smb_kmod_ioctl(SMB_IOC_NUMOPEN, &ioc.hdr, sizeof (ioc));
+	if (rc == 0) {
+		opennum->open_users = ioc.open_users;
+		opennum->open_trees = ioc.open_trees;
+		opennum->open_files = ioc.open_files;
+	}
+
+	return (rc);
+}
+
+/*
+ * Initialization for an smb_kmod_enum request.  If this call succeeds,
+ * smb_kmod_enum_fini() must be called later to deallocate resources.
+ */
+smb_netsvc_t *
+smb_kmod_enum_init(smb_svcenum_t *request)
+{
+	smb_netsvc_t		*ns;
+	smb_svcenum_t		*svcenum;
+	smb_ioc_svcenum_t	*ioc;
+	uint32_t		ioclen;
+
+	if ((ns = calloc(1, sizeof (smb_netsvc_t))) == NULL)
+		return (NULL);
+
+	ioclen = sizeof (smb_ioc_svcenum_t) + SMB_IOC_DATA_SIZE;
+	if ((ioc = malloc(ioclen)) == NULL) {
+		free(ns);
+		return (NULL);
+	}
+
+	bzero(ioc, ioclen);
+	svcenum = &ioc->svcenum;
+	svcenum->se_type   = request->se_type;
+	svcenum->se_level  = request->se_level;
+	svcenum->se_bavail = SMB_IOC_DATA_SIZE;
+	svcenum->se_nlimit = request->se_nlimit;
+	svcenum->se_nskip = request->se_nskip;
+	svcenum->se_buflen = SMB_IOC_DATA_SIZE;
+
+	list_create(&ns->ns_list, sizeof (smb_netsvcitem_t),
+	    offsetof(smb_netsvcitem_t, nsi_lnd));
+
+	ns->ns_ioc = ioc;
+	ns->ns_ioclen = ioclen;
+	return (ns);
+}
+
+/*
+ * Cleanup resources allocated via smb_kmod_enum_init and smb_kmod_enum.
+ */
+void
+smb_kmod_enum_fini(smb_netsvc_t *ns)
+{
+	list_t			*lst;
+	smb_netsvcitem_t	*item;
+	smb_netuserinfo_t	*user;
+	smb_netconnectinfo_t	*tree;
+	smb_netfileinfo_t	*ofile;
+	uint32_t		se_type;
+
+	if (ns == NULL)
+		return;
+
+	lst = &ns->ns_list;
+	se_type = ns->ns_ioc->svcenum.se_type;
+
+	while ((item = list_head(lst)) != NULL) {
+		list_remove(lst, item);
 
+		switch (se_type) {
+		case SMB_SVCENUM_TYPE_USER:
+			user = &item->nsi_un.nsi_user;
+			free(user->ui_domain);
+			free(user->ui_account);
+			free(user->ui_workstation);
+			break;
+		case SMB_SVCENUM_TYPE_TREE:
+			tree = &item->nsi_un.nsi_tree;
+			free(tree->ci_username);
+			free(tree->ci_share);
+			break;
+		case SMB_SVCENUM_TYPE_FILE:
+			ofile = &item->nsi_un.nsi_ofile;
+			free(ofile->fi_path);
+			free(ofile->fi_username);
+			break;
+		default:
+			break;
+		}
+	}
+
+	list_destroy(&ns->ns_list);
+	free(ns->ns_items);
+	free(ns->ns_ioc);
+	free(ns);
+}
+
+/*
+ * Enumerate users, connections or files.
+ */
+int
+smb_kmod_enum(smb_netsvc_t *ns)
+{
+	smb_ioc_svcenum_t	*ioc;
+	uint32_t		ioclen;
+	smb_svcenum_t		*svcenum;
+	smb_netsvcitem_t	*items;
+	smb_netuserinfo_t	*user;
+	smb_netconnectinfo_t	*tree;
+	smb_netfileinfo_t	*ofile;
+	uint8_t			*data;
+	uint32_t		len;
+	uint32_t		se_type;
+	uint_t			nbytes;
+	int			i;
+	int			rc;
+
+	ioc = ns->ns_ioc;
+	ioclen = ns->ns_ioclen;
+	rc = smb_kmod_ioctl(SMB_IOC_SVCENUM, &ioc->hdr, ioclen);
+	if (rc != 0)
+		return (rc);
+
+	svcenum = &ioc->svcenum;
+	items = calloc(svcenum->se_nitems, sizeof (smb_netsvcitem_t));
+	if (items == NULL)
+		return (ENOMEM);
+
+	ns->ns_items = items;
+	se_type = ns->ns_ioc->svcenum.se_type;
+	data = svcenum->se_buf;
+	len = svcenum->se_bused;
+
+	for (i = 0; i < svcenum->se_nitems; ++i) {
+		switch (se_type) {
+		case SMB_SVCENUM_TYPE_USER:
+			user = &items->nsi_un.nsi_user;
+			rc = smb_netuserinfo_decode(user, data, len, &nbytes);
+			break;
+		case SMB_SVCENUM_TYPE_TREE:
+			tree = &items->nsi_un.nsi_tree;
+			rc = smb_netconnectinfo_decode(tree, data, len,
+			    &nbytes);
+			break;
+		case SMB_SVCENUM_TYPE_FILE:
+			ofile = &items->nsi_un.nsi_ofile;
+			rc = smb_netfileinfo_decode(ofile, data, len, &nbytes);
+			break;
+		default:
+			rc = -1;
+			break;
+		}
+
+		if (rc != 0)
+			return (EINVAL);
+
+		list_insert_tail(&ns->ns_list, items);
+
+		++items;
+		data += nbytes;
+		len -= nbytes;
+	}
+
+	return (0);
+}
+
+/*
+ * A NULL pointer is a wildcard indicator, which we pass on
+ * as an empty string (by virtue of the bzero).
+ */
+int
+smb_kmod_session_close(const char *client, const char *username)
+{
+	smb_ioc_session_t ioc;
+	int rc;
+
+	bzero(&ioc, sizeof (ioc));
+
+	if (client != NULL)
+		(void) strlcpy(ioc.client, client, MAXNAMELEN);
+	if (username != NULL)
+		(void) strlcpy(ioc.username, username, MAXNAMELEN);
+
+	rc = smb_kmod_ioctl(SMB_IOC_SESSION_CLOSE, &ioc.hdr, sizeof (ioc));
 	return (rc);
 }
 
 int
-smb_kmod_get_userlist(smb_ulist_t *ulist)
+smb_kmod_file_close(uint32_t uniqid)
 {
-	smb_opipe_context_t	*ctx;
-	smb_ioc_ulist_t		*ioc;
-	uint32_t		ioc_len;
-	uint8_t			*data;
-	uint32_t		data_len;
-	uint32_t		unum;
-	int			rc;
-
-	smb_ulist_cleanup(ulist);
-
-	rc = smb_kmod_get_usernum(&unum);
-	if ((rc != 0) || (unum == 0))
-		return (rc);
-
-	ioc_len = sizeof (smb_ioc_ulist_t) + SMB_IOC_DATA_SIZE;
-	ioc = malloc(ioc_len);
-	if (ioc == NULL)
-		return (ENOMEM);
-
-	ctx = malloc(sizeof (smb_opipe_context_t) * unum);
-	if (ctx == NULL) {
-		free(ioc);
-		return (ENOMEM);
-	}
-	ulist->ul_users = ctx;
+	smb_ioc_fileid_t ioc;
+	int rc;
 
-	while (ulist->ul_cnt < unum) {
-		ioc->cookie = ulist->ul_cnt;
-		ioc->data_len = SMB_IOC_DATA_SIZE;
-		rc = smb_kmod_ioctl(SMB_IOC_USER_LIST, &ioc->hdr,
-		    ioc_len);
-		if (rc != 0)
-			break;
-
-		if ((ulist->ul_cnt + ioc->num) > unum)
-			ioc->num = unum - ulist->ul_cnt;
-
-		if (ioc->num == 0)
-			break;
+	bzero(&ioc, sizeof (ioc));
+	ioc.uniqid = uniqid;
 
-		data = ioc->data;
-		data_len = ioc->data_len;
-		while (ioc->num > 0) {
-			uint_t	bd = 0;
-
-			rc = smb_opipe_context_decode(ctx, data, data_len, &bd);
-			if (rc != 0)
-				break;
-
-			ctx++;
-			ioc->num--;
-			ulist->ul_cnt++;
-			data += bd;
-			data_len -= bd;
-		}
-	}
-
-	if (rc != 0)
-		smb_ulist_cleanup(ulist);
-
-	free(ioc);
+	rc = smb_kmod_ioctl(SMB_IOC_FILE_CLOSE, &ioc.hdr, sizeof (ioc));
 	return (rc);
 }
 
--- a/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c	Fri Jul 17 17:54:42 2009 -0700
@@ -45,8 +45,6 @@
 #define	SMB_PASSTEMP	"/var/smb/ptmp"
 #define	SMB_PASSLCK	"/var/smb/.pwd.lock"
 
-#define	SMB_LIB_ALT	"/usr/lib/smbsrv/libsmb_pwd.so"
-
 #define	SMB_PWD_DISABLE	"*DIS*"
 #define	SMB_PWD_BUFSIZE 256
 
@@ -172,11 +170,9 @@
 		smb_lucache_update();
 	}
 
-	smb_pwd_hdl = dlopen(SMB_LIB_ALT, RTLD_NOW | RTLD_LOCAL);
-	if (smb_pwd_hdl == NULL) {
-		/* No library is interposed */
+	smb_pwd_hdl = smb_dlopen();
+	if (smb_pwd_hdl == NULL)
 		return;
-	}
 
 	bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
 
@@ -212,7 +208,7 @@
 	    smb_pwd_ops.pwop_iteropen == NULL ||
 	    smb_pwd_ops.pwop_iterclose == NULL ||
 	    smb_pwd_ops.pwop_iterate == NULL) {
-		(void) dlclose(smb_pwd_hdl);
+		smb_dlclose(smb_pwd_hdl);
 		smb_pwd_hdl = NULL;
 
 		/* If error or function(s) are missing, use original lib */
@@ -230,11 +226,9 @@
 smb_pwd_fini(void)
 {
 	smb_lucache_destroy();
-
-	if (smb_pwd_hdl) {
-		(void) dlclose(smb_pwd_hdl);
-		smb_pwd_hdl = NULL;
-	}
+	smb_dlclose(smb_pwd_hdl);
+	smb_pwd_hdl = NULL;
+	bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
 }
 
 /*
--- a/usr/src/lib/smbsrv/libsmb/common/smb_util.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_util.c	Fri Jul 17 17:54:42 2009 -0700
@@ -26,6 +26,7 @@
 #include <ctype.h>
 #include <stdio.h>
 #include <string.h>
+#include <strings.h>
 #include <stdlib.h>
 #include <pthread.h>
 #include <sys/varargs.h>
@@ -37,9 +38,12 @@
 #include <sys/systeminfo.h>
 #include <sys/utsname.h>
 #include <libzfs.h>
+#include <dlfcn.h>
 #include <smbsrv/string.h>
 #include <smbsrv/libsmb.h>
 
+#define	SMB_LIB_ALT	"/usr/lib/smbsrv/libsmbex.so"
+
 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);
@@ -755,6 +759,64 @@
 }
 
 /*
+ * smb_dlopen
+ *
+ * Check to see if an interposer library exists.  If it exists
+ * and reports a valid version number and key (UUID), return
+ * a handle to the library.  Otherwise, return NULL.
+ */
+void *
+smb_dlopen(void)
+{
+	uuid_t uuid;
+	void *interposer_hdl;
+	typedef int (*smbex_versionfn_t)(smbex_version_t *);
+	smbex_versionfn_t getversion;
+	smbex_version_t *version;
+
+	bzero(&uuid, sizeof (uuid_t));
+	if (uuid_parse(SMBEX_KEY, uuid) < 0)
+		return (NULL);
+
+	interposer_hdl = dlopen(SMB_LIB_ALT, RTLD_NOW | RTLD_LOCAL);
+	if (interposer_hdl == NULL)
+		return (NULL);
+
+	bzero(&getversion, sizeof (smbex_versionfn_t));
+	getversion = (smbex_versionfn_t)dlsym(interposer_hdl,
+	    "smbex_get_version");
+	if ((getversion == NULL) ||
+	    (version = malloc(sizeof (smbex_version_t))) == NULL) {
+		(void) dlclose(interposer_hdl);
+		return (NULL);
+	}
+	bzero(version, sizeof (smbex_version_t));
+
+	if ((getversion(version) != 0) ||
+	    (version->v_version != SMBEX_VERSION) ||
+	    (uuid_compare(version->v_uuid, uuid) != 0)) {
+		free(version);
+		(void) dlclose(interposer_hdl);
+		return (NULL);
+	}
+
+	free(version);
+	return (interposer_hdl);
+}
+
+/*
+ * smb_dlclose
+ *
+ * Closes handle to the interposed library.
+ */
+void
+smb_dlclose(void *handle)
+{
+	if (handle)
+		(void) dlclose(handle);
+}
+
+/*
  * Returns the hostname given the IP address.  Wrapper for getnameinfo.
  */
 int
@@ -782,47 +844,3 @@
 	return (getnameinfo((struct sockaddr *)sp, salen,
 	    hostname, hostlen, NULL, 0, flags));
 }
-
-smb_ulist_t *
-smb_ulist_alloc(void)
-{
-	smb_ulist_t *ulist;
-
-	ulist = malloc(sizeof (smb_ulist_t));
-	if (ulist != NULL) {
-		ulist->ul_cnt = 0;
-		ulist->ul_users = NULL;
-	}
-	return (ulist);
-}
-
-void
-smb_ulist_free(smb_ulist_t *ulist)
-{
-	if (ulist != NULL) {
-		smb_ulist_cleanup(ulist);
-		free(ulist);
-	}
-}
-
-void
-smb_ulist_cleanup(smb_ulist_t *ulist)
-{
-	smb_opipe_context_t *ctx;
-
-	if (ulist->ul_users != NULL) {
-		ctx = ulist->ul_users;
-		while (ulist->ul_cnt != 0) {
-			free(ctx->oc_domain);
-			free(ctx->oc_account);
-			free(ctx->oc_workstation);
-			ctx->oc_domain = NULL;
-			ctx->oc_account = NULL;
-			ctx->oc_workstation = NULL;
-			ulist->ul_cnt--;
-			ctx++;
-		}
-		free(ulist->ul_users);
-		ulist->ul_users = NULL;
-	}
-}
--- a/usr/src/pkgdefs/etc/exception_list_i386	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/pkgdefs/etc/exception_list_i386	Fri Jul 17 17:54:42 2009 -0700
@@ -1256,3 +1256,6 @@
 # Private idmap RPC protocol
 usr/include/rpcsvc/idmap_prot.h		i386
 usr/include/rpcsvc/idmap_prot.x		i386
+
+# Private idmap directory API
+usr/include/directory.h			i386
--- a/usr/src/pkgdefs/etc/exception_list_sparc	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/pkgdefs/etc/exception_list_sparc	Fri Jul 17 17:54:42 2009 -0700
@@ -1355,3 +1355,6 @@
 # Private idmap RPC protocol
 usr/include/rpcsvc/idmap_prot.h		sparc
 usr/include/rpcsvc/idmap_prot.x		sparc
+
+# Private idmap directory API
+usr/include/directory.h			sparc
--- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c	Fri Jul 17 17:54:42 2009 -0700
@@ -414,7 +414,7 @@
 		    sizeof (op->fqi.fq_last_comp),
 		    "%s%s", cur_node->od_name, pn->pn_sname);
 
-		op->fqi.fq_dnode = cur_node->dir_snode;
+		op->fqi.fq_dnode = cur_node->n_dnode;
 		smb_node_ref(op->fqi.fq_dnode);
 	} else {
 		if (rc = smb_pathname_reduce(sr, sr->user_cr, pn->pn_path,
--- a/usr/src/uts/common/fs/smbsrv/smb_delete.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_delete.c	Fri Jul 17 17:54:42 2009 -0700
@@ -519,7 +519,7 @@
 	if (SMB_TREE_SUPPORTS_CATIA(sr))
 		flags |= SMB_CATIA;
 
-	rc = smb_fsop_remove(sr, sr->user_cr, node->dir_snode,
+	rc = smb_fsop_remove(sr, sr->user_cr, node->n_dnode,
 	    node->od_name, flags);
 	if (rc != 0) {
 		if (rc == ENOENT)
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c	Fri Jul 17 17:54:42 2009 -0700
@@ -694,7 +694,7 @@
 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
 
 	if (dnode->flags & NODE_XATTR_DIR) {
-		rc = smb_vop_stream_remove(dnode->dir_snode->vp,
+		rc = smb_vop_stream_remove(dnode->n_dnode->vp,
 		    name, flags, cr);
 	} else if (smb_is_stream_name(name)) {
 		smb_stream_parse_name(name, fname, sname);
@@ -1453,7 +1453,7 @@
 	faccess &= sr->tid_tree->t_access;
 
 	if (acl_check) {
-		dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL;
+		dir_vp = (snode->n_dnode) ? snode->n_dnode->vp : NULL;
 		error = smb_vop_access(snode->vp, faccess, V_ACE_MASK, dir_vp,
 		    cr);
 	} else {
@@ -2297,7 +2297,7 @@
 	}
 
 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_ACEMASKONACCESS)) {
-		dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL;
+		dir_vp = (snode->n_dnode) ? snode->n_dnode->vp : NULL;
 		smb_vop_eaccess(snode->vp, (int *)eaccess, V_ACE_MASK, dir_vp,
 		    cr);
 		return;
--- a/usr/src/uts/common/fs/smbsrv/smb_init.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_init.c	Fri Jul 17 17:54:42 2009 -0700
@@ -245,14 +245,20 @@
 	case SMB_IOC_UNSHARE:
 		rc = smb_server_share_unexport(&ioc->ioc_share);
 		break;
-	case SMB_IOC_USER_NUMBER:
-		rc = smb_server_user_number(&ioc->ioc_unum);
+	case SMB_IOC_NUMOPEN:
+		rc = smb_server_numopen(&ioc->ioc_opennum);
 		copyout = B_TRUE;
 		break;
-	case SMB_IOC_USER_LIST:
-		rc = smb_server_user_list(&ioc->ioc_ulist);
+	case SMB_IOC_SVCENUM:
+		rc = smb_server_enum(&ioc->ioc_svcenum);
 		copyout = B_TRUE;
 		break;
+	case SMB_IOC_SESSION_CLOSE:
+		rc = smb_server_session_close(&ioc->ioc_session);
+		break;
+	case SMB_IOC_FILE_CLOSE:
+		rc = smb_server_file_close(&ioc->ioc_fileid);
+		break;
 	default:
 		rc = ENOTTY;
 		break;
--- a/usr/src/uts/common/fs/smbsrv/smb_lock.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_lock.c	Fri Jul 17 17:54:42 2009 -0700
@@ -52,7 +52,22 @@
 static void smb_lock_destroy(smb_lock_t *);
 static void smb_lock_free(smb_lock_t *);
 
+/*
+ * Return the number of range locks on the specified node.
+ */
+uint32_t
+smb_lock_get_lock_count(smb_node_t *node)
+{
+	uint32_t	count;
 
+	SMB_NODE_VALID(node);
+
+	smb_llist_enter(&node->n_lock_list, RW_READER);
+	count = smb_llist_get_count(&node->n_ofile_list);
+	smb_llist_exit(&node->n_lock_list);
+
+	return (count);
+}
 
 /*
  * smb_unlock_range
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_node.c	Fri Jul 17 17:54:42 2009 -0700
@@ -153,7 +153,7 @@
 #define	VALIDATE_DIR_NODE(_dir_, _node_) \
     ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \
     ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \
-    ASSERT((_dir_)->dir_snode != (_node_));
+    ASSERT((_dir_)->n_dnode != (_node_));
 
 static kmem_cache_t	*smb_node_cache = NULL;
 static boolean_t	smb_node_initialized = B_FALSE;
@@ -246,7 +246,7 @@
  * or newly created.
  *
  * If an smb_node needs to be created, a reference is also taken on the
- * dir_snode (if passed in).
+ * dnode (if passed in).
  *
  * See smb_node_release() for details on the release of these references.
  */
@@ -259,8 +259,8 @@
     cred_t		*cred,
     vnode_t		*vp,
     char		*od_name,
-    smb_node_t		*dir_snode,
-    smb_node_t		*unnamed_node)
+    smb_node_t		*dnode,
+    smb_node_t		*unode)
 {
 	smb_llist_t		*node_hdr;
 	smb_node_t		*node;
@@ -277,8 +277,8 @@
 	 * it with the list lock held.
 	 */
 
-	if (unnamed_node)
-		unnamed_vp = unnamed_node->vp;
+	if (unode)
+		unnamed_vp = unode->vp;
 
 	/*
 	 * This getattr is performed on behalf of the server
@@ -323,14 +323,13 @@
 				case SMB_NODE_STATE_AVAILABLE:
 					/* The node was found. */
 					node->n_refcnt++;
-					if ((node->dir_snode == NULL) &&
-					    (dir_snode != NULL) &&
+					if ((node->n_dnode == NULL) &&
+					    (dnode != NULL) &&
 					    (strcmp(od_name, "..") != 0) &&
 					    (strcmp(od_name, ".") != 0)) {
-						VALIDATE_DIR_NODE(dir_snode,
-						    node);
-						node->dir_snode = dir_snode;
-						smb_node_ref(dir_snode);
+						VALIDATE_DIR_NODE(dnode, node);
+						node->n_dnode = dnode;
+						smb_node_ref(dnode);
 					}
 
 					smb_node_audit(node);
@@ -371,17 +370,17 @@
 	if (op)
 		node->flags |= smb_is_executable(op->fqi.fq_last_comp);
 
-	if (dir_snode) {
-		smb_node_ref(dir_snode);
-		node->dir_snode = dir_snode;
-		ASSERT(dir_snode->dir_snode != node);
-		ASSERT((dir_snode->vp->v_xattrdir) ||
-		    (dir_snode->vp->v_type == VDIR));
+	if (dnode) {
+		smb_node_ref(dnode);
+		node->n_dnode = dnode;
+		ASSERT(dnode->n_dnode != node);
+		ASSERT((dnode->vp->v_xattrdir) ||
+		    (dnode->vp->v_type == VDIR));
 	}
 
-	if (unnamed_node) {
-		smb_node_ref(unnamed_node);
-		node->unnamed_stream_node = unnamed_node;
+	if (unode) {
+		smb_node_ref(unode);
+		node->n_unode = unode;
 	}
 
 	DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node);
@@ -472,8 +471,8 @@
  * then the caller with the local variable should call smb_node_release()
  * directly.
  *
- * smb_node_release() itself will call smb_node_release() on a node's dir_snode,
- * as smb_node_lookup() takes a hold on dir_snode.
+ * smb_node_release() itself will call smb_node_release() on a node's n_dnode,
+ * as smb_node_lookup() takes a hold on dnode.
  */
 void
 smb_node_release(smb_node_t *node)
@@ -499,16 +498,16 @@
 			 */
 			smb_node_delete_on_close(node);
 
-			if (node->dir_snode) {
-				ASSERT(node->dir_snode->n_magic ==
+			if (node->n_dnode) {
+				ASSERT(node->n_dnode->n_magic ==
 				    SMB_NODE_MAGIC);
-				smb_node_release(node->dir_snode);
+				smb_node_release(node->n_dnode);
 			}
 
-			if (node->unnamed_stream_node) {
-				ASSERT(node->unnamed_stream_node->n_magic ==
+			if (node->n_unode) {
+				ASSERT(node->n_unode->n_magic ==
 				    SMB_NODE_MAGIC);
-				smb_node_release(node->unnamed_stream_node);
+				smb_node_release(node->n_unode);
 			}
 
 			smb_node_free(node);
@@ -529,7 +528,7 @@
 	int		rc = 0;
 	uint32_t	flags = 0;
 
-	d_snode = node->dir_snode;
+	d_snode = node->n_dnode;
 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
 		flags = node->n_delete_on_close_flags;
@@ -570,9 +569,9 @@
 	case SMB_NODE_STATE_AVAILABLE:
 	case SMB_NODE_STATE_OPLOCK_GRANTED:
 	case SMB_NODE_STATE_OPLOCK_BREAKING:
-		ret_node->dir_snode = to_dnode;
+		ret_node->n_dnode = to_dnode;
 		mutex_exit(&ret_node->n_mutex);
-		ASSERT(to_dnode->dir_snode != ret_node);
+		ASSERT(to_dnode->n_dnode != ret_node);
 		ASSERT((to_dnode->vp->v_xattrdir) ||
 		    (to_dnode->vp->v_type == VDIR));
 		smb_node_release(from_dnode);
@@ -956,10 +955,9 @@
 	node->n_orig_uid = 0;
 	node->readonly_creator = NULL;
 	node->waiting_event = 0;
-	node->what = 0;
 	node->n_open_count = 0;
-	node->dir_snode = NULL;
-	node->unnamed_stream_node = NULL;
+	node->n_dnode = NULL;
+	node->n_unode = NULL;
 	node->delete_on_close_cred = NULL;
 	node->n_delete_on_close_flags = 0;
 
--- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c	Fri Jul 17 17:54:42 2009 -0700
@@ -164,10 +164,14 @@
 #include <smbsrv/smb_kproto.h>
 #include <smbsrv/smb_fsops.h>
 
-/* Static functions defined further down this file. */
-static void smb_ofile_delete(smb_ofile_t *of);
-static smb_ofile_t *smb_ofile_close_and_next(smb_ofile_t *of);
+static boolean_t smb_ofile_is_open_locked(smb_ofile_t *);
+static void smb_ofile_delete(smb_ofile_t *);
+static smb_ofile_t *smb_ofile_close_and_next(smb_ofile_t *);
 static void smb_ofile_set_close_attrs(smb_ofile_t *, uint32_t);
+static int smb_ofile_netinfo_encode(smb_ofile_t *, uint8_t *, size_t,
+    uint32_t *);
+static int smb_ofile_netinfo_init(smb_ofile_t *, smb_netfileinfo_t *);
+static void smb_ofile_netinfo_fini(smb_netfileinfo_t *);
 
 /*
  * smb_ofile_open
@@ -277,6 +281,7 @@
 	smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
 	smb_llist_insert_tail(&tree->t_ofile_list, of);
 	smb_llist_exit(&tree->t_ofile_list);
+	atomic_inc_32(&tree->t_open_files);
 	atomic_inc_32(&tree->t_server->sv_open_files);
 	atomic_inc_32(&of->f_session->s_file_cnt);
 
@@ -328,6 +333,7 @@
 			if (of->f_node->flags & NODE_FLAGS_NOTIFY_CHANGE)
 				smb_process_file_notify_change_queue(of);
 		}
+		atomic_dec_32(&of->f_tree->t_open_files);
 		atomic_dec_32(&of->f_tree->t_server->sv_open_files);
 
 		mutex_enter(&of->f_mutex);
@@ -409,6 +415,70 @@
 }
 
 /*
+ * If the enumeration request is for ofile data, handle it here.
+ * Otherwise, return.
+ *
+ * This function should be called with a hold on the ofile.
+ */
+int
+smb_ofile_enum(smb_ofile_t *of, smb_svcenum_t *svcenum)
+{
+	uint8_t *pb;
+	uint_t nbytes;
+	int rc;
+
+	ASSERT(of);
+	ASSERT(of->f_magic == SMB_OFILE_MAGIC);
+	ASSERT(of->f_refcnt);
+
+	if (svcenum->se_type != SMB_SVCENUM_TYPE_FILE)
+		return (0);
+
+	if (svcenum->se_nskip > 0) {
+		svcenum->se_nskip--;
+		return (0);
+	}
+
+	if (svcenum->se_nitems >= svcenum->se_nlimit) {
+		svcenum->se_nitems = svcenum->se_nlimit;
+		return (0);
+	}
+
+	pb = &svcenum->se_buf[svcenum->se_bused];
+
+	rc = smb_ofile_netinfo_encode(of, pb, svcenum->se_bavail,
+	    &nbytes);
+	if (rc == 0) {
+		svcenum->se_bavail -= nbytes;
+		svcenum->se_bused += nbytes;
+		svcenum->se_nitems++;
+	}
+
+	return (rc);
+}
+
+/*
+ * Take a reference on an open file.
+ */
+boolean_t
+smb_ofile_hold(smb_ofile_t *of)
+{
+	ASSERT(of);
+	ASSERT(of->f_magic == SMB_OFILE_MAGIC);
+
+	mutex_enter(&of->f_mutex);
+
+	if (smb_ofile_is_open_locked(of)) {
+		of->f_refcnt++;
+		mutex_exit(&of->f_mutex);
+		return (B_TRUE);
+	}
+
+	mutex_exit(&of->f_mutex);
+	return (B_FALSE);
+}
+
+/*
  * smb_ofile_release
  *
  */
@@ -487,6 +557,71 @@
 }
 
 /*
+ * smb_ofile_lookup_by_uniqid
+ *
+ * Find the open file whose uniqid matches the one specified in the request.
+ */
+smb_ofile_t *
+smb_ofile_lookup_by_uniqid(smb_tree_t *tree, uint32_t uniqid)
+{
+	smb_llist_t	*of_list;
+	smb_ofile_t	*of;
+
+	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
+
+	of_list = &tree->t_ofile_list;
+	smb_llist_enter(of_list, RW_READER);
+	of = smb_llist_head(of_list);
+
+	while (of) {
+		ASSERT(of->f_magic == SMB_OFILE_MAGIC);
+		ASSERT(of->f_tree == tree);
+
+		if (of->f_uniqid == uniqid) {
+			if (smb_ofile_hold(of)) {
+				smb_llist_exit(of_list);
+				return (of);
+			}
+		}
+
+		of = smb_llist_next(of_list, of);
+	}
+
+	smb_llist_exit(of_list);
+	return (NULL);
+}
+
+/*
+ * Disallow NetFileClose on certain ofiles to avoid side-effects.
+ * Closing a tree root is not allowed: use NetSessionDel or NetShareDel.
+ * Closing SRVSVC connections is not allowed because this NetFileClose
+ * request may depend on this ofile.
+ */
+boolean_t
+smb_ofile_disallow_fclose(smb_ofile_t *of)
+{
+	ASSERT(of);
+	ASSERT(of->f_magic == SMB_OFILE_MAGIC);
+	ASSERT(of->f_refcnt);
+
+	switch (of->f_ftype) {
+	case SMB_FTYPE_DISK:
+		ASSERT(of->f_tree);
+		return (of->f_node == of->f_tree->t_snode);
+
+	case SMB_FTYPE_MESG_PIPE:
+		ASSERT(of->f_pipe);
+		if (utf8_strcasecmp(of->f_pipe->p_name, "SRVSVC") == 0)
+			return (B_TRUE);
+		break;
+	default:
+		break;
+	}
+
+	return (B_FALSE);
+}
+
+/*
  * smb_ofile_set_flags
  *
  * Return value:
@@ -588,14 +723,12 @@
 boolean_t
 smb_ofile_is_open(smb_ofile_t *of)
 {
-	boolean_t	rc = B_FALSE;
+	boolean_t	rc;
 
 	SMB_OFILE_VALID(of);
 
 	mutex_enter(&of->f_mutex);
-	if (of->f_state == SMB_OFILE_STATE_OPEN) {
-		rc = B_TRUE;
-	}
+	rc = smb_ofile_is_open_locked(of);
 	mutex_exit(&of->f_mutex);
 	return (rc);
 }
@@ -674,6 +807,27 @@
 /* *************************** Static Functions ***************************** */
 
 /*
+ * Determine whether or not an ofile is open.
+ * This function must be called with the mutex held.
+ */
+static boolean_t
+smb_ofile_is_open_locked(smb_ofile_t *of)
+{
+	switch (of->f_state) {
+	case SMB_OFILE_STATE_OPEN:
+		return (B_TRUE);
+
+	case SMB_OFILE_STATE_CLOSING:
+	case SMB_OFILE_STATE_CLOSED:
+		return (B_FALSE);
+
+	default:
+		ASSERT(0);
+		return (B_FALSE);
+	}
+}
+
+/*
  * smb_ofile_set_close_attrs
  *
  * Updates timestamps, size and readonly bit.
@@ -1038,3 +1192,107 @@
 	of->f_flags |= SMB_OFLAGS_SET_DELETE_ON_CLOSE;
 	mutex_exit(&of->f_mutex);
 }
+
+/*
+ * Encode open file information into a buffer; needed in user space to
+ * support RPC requests.
+ */
+static int
+smb_ofile_netinfo_encode(smb_ofile_t *of, uint8_t *buf, size_t buflen,
+    uint32_t *nbytes)
+{
+	smb_netfileinfo_t	fi;
+	int			rc;
+
+	rc = smb_ofile_netinfo_init(of, &fi);
+	if (rc == 0) {
+		rc = smb_netfileinfo_encode(&fi, buf, buflen, nbytes);
+		smb_ofile_netinfo_fini(&fi);
+	}
+
+	return (rc);
+}
+
+static int
+smb_ofile_netinfo_init(smb_ofile_t *of, smb_netfileinfo_t *fi)
+{
+	smb_user_t	*user;
+	smb_tree_t	*tree;
+	smb_node_t	*node;
+	char		*path;
+	char		*buf;
+	int		rc;
+
+	ASSERT(of);
+	user = of->f_user;
+	tree = of->f_tree;
+	ASSERT(user);
+	ASSERT(tree);
+
+	buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+
+	switch (of->f_ftype) {
+	case SMB_FTYPE_DISK:
+		node = of->f_node;
+		ASSERT(node);
+
+		fi->fi_permissions = of->f_granted_access;
+		fi->fi_numlocks = smb_lock_get_lock_count(node);
+
+		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+
+		if (node != tree->t_snode) {
+			rc = vnodetopath(tree->t_snode->vp, node->vp, path,
+			    MAXPATHLEN, kcred);
+			if (rc == 0)
+				(void) strsubst(path, '/', '\\');
+			else
+				(void) strlcpy(path, node->od_name, MAXPATHLEN);
+		}
+
+		(void) snprintf(buf, MAXPATHLEN, "%s:%s", tree->t_sharename,
+		    path);
+		kmem_free(path, MAXPATHLEN);
+		break;
+
+	case SMB_FTYPE_MESG_PIPE:
+		ASSERT(of->f_pipe);
+
+		fi->fi_permissions = FILE_READ_DATA | FILE_WRITE_DATA |
+		    FILE_EXECUTE;
+		fi->fi_numlocks = 0;
+		(void) snprintf(buf, MAXPATHLEN, "\\PIPE\\%s",
+		    of->f_pipe->p_name);
+		break;
+
+	default:
+		kmem_free(buf, MAXPATHLEN);
+		return (-1);
+	}
+
+	fi->fi_fid = of->f_fid;
+	fi->fi_uniqid = of->f_uniqid;
+	fi->fi_pathlen = strlen(buf) + 1;
+	fi->fi_path = smb_kstrdup(buf, fi->fi_pathlen);
+	kmem_free(buf, MAXPATHLEN);
+
+	fi->fi_namelen = user->u_domain_len + user->u_name_len + 2;
+	fi->fi_username = kmem_alloc(fi->fi_namelen, KM_SLEEP);
+	(void) snprintf(fi->fi_username, fi->fi_namelen, "%s\\%s",
+	    user->u_domain, user->u_name);
+	return (0);
+}
+
+static void
+smb_ofile_netinfo_fini(smb_netfileinfo_t *fi)
+{
+	if (fi == NULL)
+		return;
+
+	if (fi->fi_path)
+		kmem_free(fi->fi_path, fi->fi_pathlen);
+	if (fi->fi_username)
+		kmem_free(fi->fi_username, fi->fi_namelen);
+
+	bzero(fi, sizeof (smb_netfileinfo_t));
+}
--- a/usr/src/uts/common/fs/smbsrv/smb_opipe.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_opipe.c	Fri Jul 17 17:54:42 2009 -0700
@@ -143,13 +143,14 @@
  * The full pipe path will be in the form \\PIPE\\SERVICE.  The first part
  * can be assumed, so all we need here are the service names.
  *
- * Returns a pointer to the pipe name (without any leading \'s) on sucess.
+ * Returns a pointer to the pipe name (without any leading \'s) on success.
  * Otherwise returns a null pointer.
  */
 static char *
 smb_opipe_lookup(const char *path)
 {
 	static char *named_pipes[] = {
+		"lsass",
 		"LSARPC",
 		"NETLOGON",
 		"SAMR",
@@ -188,14 +189,14 @@
 static int
 smb_opipe_do_open(smb_request_t *sr, smb_opipe_t *opipe)
 {
-	smb_opipe_context_t *ctx = &opipe->p_context;
+	smb_netuserinfo_t *userinfo = &opipe->p_user;
 	smb_user_t *user = sr->uid_user;
 	uint8_t *buf = opipe->p_doorbuf;
 	uint32_t buflen = SMB_OPIPE_DOOR_BUFSIZE;
 	uint32_t len;
 
-	smb_user_context_init(user, ctx);
-	len = xdr_sizeof(smb_opipe_context_xdr, ctx);
+	smb_user_netinfo_init(user, userinfo);
+	len = xdr_sizeof(smb_netuserinfo_xdr, userinfo);
 
 	bzero(&opipe->p_hdr, sizeof (smb_opipe_hdr_t));
 	opipe->p_hdr.oh_magic = SMB_OPIPE_HDR_MAGIC;
@@ -208,7 +209,7 @@
 	buf += len;
 	buflen -= len;
 
-	if (smb_opipe_context_encode(ctx, buf, buflen, NULL) == -1)
+	if (smb_netuserinfo_encode(userinfo, buf, buflen, NULL) == -1)
 		return (-1);
 
 	return (smb_opipe_door_call(opipe));
@@ -266,7 +267,7 @@
 		kmem_free(opipe->p_doorbuf, SMB_OPIPE_DOOR_BUFSIZE);
 	}
 
-	smb_user_context_fini(&opipe->p_context);
+	smb_user_netinfo_fini(&opipe->p_user);
 	smb_opipe_exit(opipe);
 	cv_destroy(&opipe->p_cv);
 	mutex_destroy(&opipe->p_mutex);
@@ -620,48 +621,3 @@
 	opipe->p_hdr.oh_resid = hdr.oh_resid;
 	return (0);
 }
-
-void
-smb_user_context_init(smb_user_t *user, smb_opipe_context_t *ctx)
-{
-	smb_session_t *session;
-
-	ASSERT(user);
-	ASSERT(user->u_domain);
-	ASSERT(user->u_name);
-
-	session = user->u_session;
-	ASSERT(session);
-	ASSERT(session->workstation);
-
-	ctx->oc_session_id = session->s_kid;
-	ctx->oc_native_os = session->native_os;
-	ctx->oc_ipaddr = session->ipaddr;
-	ctx->oc_uid = user->u_uid;
-	ctx->oc_logon_time = user->u_logon_time;
-	ctx->oc_flags = user->u_flags;
-
-	ctx->oc_domain_len = user->u_domain_len;
-	ctx->oc_domain = smb_kstrdup(user->u_domain, ctx->oc_domain_len);
-
-	ctx->oc_account_len = user->u_name_len;
-	ctx->oc_account = smb_kstrdup(user->u_name, ctx->oc_account_len);
-
-	ctx->oc_workstation_len = strlen(session->workstation) + 1;
-	ctx->oc_workstation = smb_kstrdup(session->workstation,
-	    ctx->oc_workstation_len);
-}
-
-void
-smb_user_context_fini(smb_opipe_context_t *ctx)
-{
-	if (ctx) {
-		if (ctx->oc_domain)
-			kmem_free(ctx->oc_domain, ctx->oc_domain_len);
-		if (ctx->oc_account)
-			kmem_free(ctx->oc_account, ctx->oc_account_len);
-		if (ctx->oc_workstation)
-			kmem_free(ctx->oc_workstation, ctx->oc_workstation_len);
-		bzero(ctx, sizeof (smb_opipe_context_t));
-	}
-}
--- a/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c	Fri Jul 17 17:54:42 2009 -0700
@@ -367,7 +367,7 @@
  * ENOENT will be returned.
  *
  * Path components are processed one at a time so that smb_nodes can be
- * created for each component.  This allows the dir_snode field in the
+ * created for each component.  This allows the n_dnode field in the
  * smb_node to be properly populated.
  *
  * Because of the above, links are also processed in this routine
--- a/usr/src/uts/common/fs/smbsrv/smb_query_information.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_query_information.c	Fri Jul 17 17:54:42 2009 -0700
@@ -102,9 +102,9 @@
 	timestruc_t	*mtime;
 
 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
-		rc = smbsr_encode_result(sr, 10, 0, "bwll10.w",
-		    10, FILE_ATTRIBUTE_NORMAL, 0, 0, 0);
-		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
+		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
+		    ERROR_ACCESS_DENIED);
+		return (SDRC_ERROR);
 	}
 
 	if ((rc = smb_pathname_reduce(sr, sr->user_cr, path,
--- a/usr/src/uts/common/fs/smbsrv/smb_query_information2.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_query_information2.c	Fri Jul 17 17:54:42 2009 -0700
@@ -93,8 +93,9 @@
 	}
 
 	if (sr->fid_ofile->f_ftype != SMB_FTYPE_DISK) {
-		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
-		return (SDRC_ERROR);
+		rc = smbsr_encode_result(sr, 11, 0, "blllllww",
+		    11, 0, 0, 0, 0, 0, 0, 0);
+		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 	}
 
 	node = sr->fid_ofile->f_node;
--- a/usr/src/uts/common/fs/smbsrv/smb_server.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_server.c	Fri Jul 17 17:54:42 2009 -0700
@@ -236,8 +236,6 @@
     in_port_t, int, int);
 static int smb_server_lookup(smb_server_t **);
 static void smb_server_release(smb_server_t *);
-static int smb_server_ulist_geti(smb_session_list_t *, uint32_t,
-    uint8_t *, uint32_t, uint_t *);
 static void smb_server_store_cfg(smb_server_t *, smb_ioc_cfg_t *);
 static void smb_server_stop(smb_server_t *);
 static int smb_server_fsop_start(smb_server_t *);
@@ -246,6 +244,11 @@
 static void smb_server_disconnect_share(char *, smb_server_t *);
 static void smb_server_thread_unexport(smb_thread_t *, void *);
 
+static void smb_server_enum_private(smb_session_list_t *, smb_svcenum_t *);
+static int smb_server_sesion_disconnect(smb_session_list_t *, const char *,
+    const char *);
+static int smb_server_fclose(smb_session_list_t *, uint32_t);
+
 static smb_llist_t	smb_servers;
 
 /*
@@ -253,7 +256,8 @@
  * **************** Functions called from the device interface *****************
  * *****************************************************************************
  *
- * These functions determine the relevant smb server to which the call apply.
+ * These functions typically have to determine the relevant smb server
+ * to which the call applies.
  */
 
 /*
@@ -759,75 +763,117 @@
 }
 
 int
-smb_server_user_number(smb_ioc_usernum_t *ioc)
+smb_server_numopen(smb_ioc_opennum_t *ioc)
 {
 	smb_server_t	*sv;
 	int		rc;
 
 	if ((rc = smb_server_lookup(&sv)) == 0) {
-		ioc->num = sv->sv_open_users;
-		smb_server_release(sv);
-	}
-	return (rc);
-}
-
-int
-smb_server_user_list(smb_ioc_ulist_t *ioc)
-{
-	smb_server_t *sv;
-	uint8_t *data;
-	uint32_t data_len;
-	uint_t bytes_encoded;
-	int rc;
-
-	if ((rc = smb_server_lookup(&sv)) == 0) {
-
-		bytes_encoded = 0;
-		data = ioc->data;
-		data_len = ioc->data_len;
-		ioc->num =
-		    smb_server_ulist_geti(&sv->sv_nbt_daemon.ld_session_list,
-		    ioc->cookie, data, data_len, &bytes_encoded);
-
-		data += bytes_encoded;
-		data_len -= bytes_encoded;
-		ioc->data_len = bytes_encoded;
-		ioc->cookie += ioc->num;
-
-		ioc->num +=
-		    smb_server_ulist_geti(&sv->sv_tcp_daemon.ld_session_list,
-		    ioc->cookie, data, data_len, &bytes_encoded);
-
-		ioc->data_len += bytes_encoded;
-
+		ioc->open_users = sv->sv_open_users;
+		ioc->open_trees = sv->sv_open_trees;
+		ioc->open_files = sv->sv_open_files;
 		smb_server_release(sv);
 	}
 	return (rc);
 }
 
 /*
- * *****************************************************************************
- * ****************** Functions called from the door interface *****************
- * *****************************************************************************
- *
+ * Enumerate objects within the server.  The svcenum provides the
+ * enumeration context, i.e. what the caller want to get back.
+ */
+int
+smb_server_enum(smb_ioc_svcenum_t *ioc)
+{
+	smb_svcenum_t		*svcenum = &ioc->svcenum;
+	smb_server_t		*sv;
+	smb_session_list_t	*se;
+	int			rc;
+
+	switch (svcenum->se_type) {
+	case SMB_SVCENUM_TYPE_USER:
+	case SMB_SVCENUM_TYPE_TREE:
+	case SMB_SVCENUM_TYPE_FILE:
+		break;
+	default:
+		return (EINVAL);
+	}
+
+	if ((rc = smb_server_lookup(&sv)) != 0)
+		return (rc);
+
+	svcenum->se_bavail = svcenum->se_buflen;
+	svcenum->se_bused = 0;
+	svcenum->se_nitems = 0;
+
+	se = &sv->sv_nbt_daemon.ld_session_list;
+	smb_server_enum_private(se, svcenum);
+
+	se = &sv->sv_tcp_daemon.ld_session_list;
+	smb_server_enum_private(se, svcenum);
+
+	smb_server_release(sv);
+	return (0);
+}
+
+/*
+ * Look for sessions to disconnect by client and user name.
+ */
+int
+smb_server_session_close(smb_ioc_session_t *ioc)
+{
+	smb_session_list_t	*se;
+	smb_server_t		*sv;
+	int			nbt_cnt;
+	int			tcp_cnt;
+	int			rc;
+
+	if ((rc = smb_server_lookup(&sv)) != 0)
+		return (rc);
+
+	se = &sv->sv_nbt_daemon.ld_session_list;
+	nbt_cnt = smb_server_sesion_disconnect(se, ioc->client, ioc->username);
+
+	se = &sv->sv_tcp_daemon.ld_session_list;
+	tcp_cnt = smb_server_sesion_disconnect(se, ioc->client, ioc->username);
+
+	smb_server_release(sv);
+
+	if ((nbt_cnt == 0) && (tcp_cnt == 0))
+		return (ENOENT);
+	return (0);
+}
+
+/*
+ * Close a file by uniqid.
+ */
+int
+smb_server_file_close(smb_ioc_fileid_t *ioc)
+{
+	uint32_t		uniqid = ioc->uniqid;
+	smb_session_list_t	*se;
+	smb_server_t		*sv;
+	int			rc;
+
+	if ((rc = smb_server_lookup(&sv)) != 0)
+		return (rc);
+
+	se = &sv->sv_nbt_daemon.ld_session_list;
+	rc = smb_server_fclose(se, uniqid);
+
+	if (rc == ENOENT) {
+		se = &sv->sv_tcp_daemon.ld_session_list;
+		rc = smb_server_fclose(se, uniqid);
+	}
+
+	smb_server_release(sv);
+	return (rc);
+}
+
+/*
  * These functions determine the relevant smb server to which the call apply.
  */
 
 uint32_t
-smb_server_get_user_count(void)
-{
-	smb_server_t	*sv;
-	uint32_t	counter = 0;
-
-	if (smb_server_lookup(&sv) == 0) {
-		counter = (uint32_t)sv->sv_open_users;
-		smb_server_release(sv);
-	}
-
-	return (counter);
-}
-
-uint32_t
 smb_server_get_session_count(void)
 {
 	smb_server_t	*sv;
@@ -1422,51 +1468,140 @@
 	mutex_exit(&sv->sv_mutex);
 }
 
-static int
-smb_server_ulist_geti(smb_session_list_t *se, uint32_t cookie,
-    uint8_t *buf, uint32_t buf_len, uint_t *pbe)
+/*
+ * Enumerate the users associated with a session list.
+ */
+static void
+smb_server_enum_private(smb_session_list_t *se, smb_svcenum_t *svcenum)
 {
-	smb_session_t *sn = NULL;
-	smb_user_t *user;
-	smb_llist_t *ulist;
-	smb_opipe_context_t ctx;
-	uint_t bytes_encoded;
-	int rc = 0;
-	int cnt = 0;
+	smb_session_t	*sn;
+	smb_llist_t	*ulist;
+	smb_user_t	*user;
+	int		rc = 0;
 
 	rw_enter(&se->se_lock, RW_READER);
 	sn = list_head(&se->se_act.lst);
-	while ((sn != NULL) && (rc == 0)) {
+
+	while (sn != NULL) {
 		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
 		ulist = &sn->s_user_list;
 		smb_llist_enter(ulist, RW_READER);
 		user = smb_llist_head(ulist);
-		while ((user != NULL) && (rc == 0)) {
-			ASSERT(user->u_magic == SMB_USER_MAGIC);
-			mutex_enter(&user->u_mutex);
-			if ((user->u_state == SMB_USER_STATE_LOGGED_IN) &&
-			    (cookie == 0)) {
-				smb_user_context_init(user, &ctx);
-				rc = smb_opipe_context_encode(&ctx, buf,
-				    buf_len, &bytes_encoded);
-				smb_user_context_fini(&ctx);
-				if (rc == 0) {
-					*pbe += bytes_encoded;
-					buf += bytes_encoded;
-					buf_len -= bytes_encoded;
-					cnt++;
+
+		while (user != NULL) {
+			if (smb_user_hold(user)) {
+				rc = smb_user_enum(user, svcenum);
+				smb_user_release(user);
+			}
+
+			user = smb_llist_next(ulist, user);
+		}
+
+		smb_llist_exit(ulist);
+
+		if (rc != 0)
+			break;
+
+		sn = list_next(&se->se_act.lst, sn);
+	}
+
+	rw_exit(&se->se_lock);
+}
+
+/*
+ * Disconnect sessions associated with the specified client and username.
+ * Empty strings are treated as wildcards.
+ */
+static int
+smb_server_sesion_disconnect(smb_session_list_t *se,
+    const char *client, const char *name)
+{
+	smb_session_t	*sn;
+	smb_llist_t	*ulist;
+	smb_user_t	*user;
+	boolean_t	match;
+	int		count = 0;
+
+	rw_enter(&se->se_lock, RW_READER);
+	sn = list_head(&se->se_act.lst);
+
+	while (sn != NULL) {
+		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
+
+		if ((*client != '\0') && (!smb_session_isclient(sn, client))) {
+			sn = list_next(&se->se_act.lst, sn);
+			continue;
+		}
+
+		ulist = &sn->s_user_list;
+		smb_llist_enter(ulist, RW_READER);
+		user = smb_llist_head(ulist);
+
+		while (user != NULL) {
+			if (smb_user_hold(user)) {
+				match = (*name == '\0');
+				if (!match)
+					match = smb_user_namecmp(user, name);
+
+				if (match) {
+					smb_llist_exit(ulist);
+					smb_user_logoff(user);
+					++count;
+					smb_user_release(user);
+					smb_llist_enter(ulist, RW_READER);
+					user = smb_llist_head(ulist);
+					continue;
 				}
+
+				smb_user_release(user);
 			}
-			mutex_exit(&user->u_mutex);
+
 			user = smb_llist_next(ulist, user);
-			if (cookie > 0)
-				cookie--;
 		}
+
 		smb_llist_exit(ulist);
 		sn = list_next(&se->se_act.lst, sn);
 	}
+
 	rw_exit(&se->se_lock);
-	return (cnt);
+	return (count);
+}
+
+/*
+ * Close a file by its unique id.
+ */
+static int
+smb_server_fclose(smb_session_list_t *se, uint32_t uniqid)
+{
+	smb_session_t	*sn;
+	smb_llist_t	*ulist;
+	smb_user_t	*user;
+	int		rc = ENOENT;
+
+	rw_enter(&se->se_lock, RW_READER);
+	sn = list_head(&se->se_act.lst);
+
+	while ((sn != NULL) && (rc == ENOENT)) {
+		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
+		ulist = &sn->s_user_list;
+		smb_llist_enter(ulist, RW_READER);
+		user = smb_llist_head(ulist);
+
+		while ((user != NULL) && (rc == ENOENT)) {
+			if (smb_user_hold(user)) {
+				rc = smb_user_fclose(user, uniqid);
+				smb_user_release(user);
+			}
+
+			user = smb_llist_next(ulist, user);
+		}
+
+		smb_llist_exit(ulist);
+		sn = list_next(&se->se_act.lst, sn);
+	}
+
+	rw_exit(&se->se_lock);
+	return (rc);
 }
 
 static void
--- a/usr/src/uts/common/fs/smbsrv/smb_session.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_session.c	Fri Jul 17 17:54:42 2009 -0700
@@ -1041,13 +1041,8 @@
 		ASSERT(user->u_magic == SMB_USER_MAGIC);
 		if (!utf8_strcasecmp(user->u_name, name) &&
 		    !utf8_strcasecmp(user->u_domain, domain)) {
-			mutex_enter(&user->u_mutex);
-			if (user->u_state == SMB_USER_STATE_LOGGED_IN) {
-				user->u_refcnt++;
-				mutex_exit(&user->u_mutex);
+			if (smb_user_hold(user))
 				break;
-			}
-			mutex_exit(&user->u_mutex);
 		}
 		user = smb_llist_next(ulist, user);
 	}
@@ -1084,6 +1079,64 @@
 }
 
 /*
+ * Copy the session workstation/client name to buf.  If the workstation
+ * is an empty string (which it will be on TCP connections), use the
+ * client IP address.
+ */
+void
+smb_session_getclient(smb_session_t *sn, char *buf, size_t buflen)
+{
+	char		ipbuf[INET6_ADDRSTRLEN];
+	smb_inaddr_t	*ipaddr;
+
+	ASSERT(sn);
+	ASSERT(buf);
+	ASSERT(buflen);
+
+	*buf = '\0';
+
+	if (sn->workstation[0] != '\0') {
+		(void) strlcpy(buf, sn->workstation, buflen);
+		return;
+	}
+
+	ipaddr = &sn->ipaddr;
+	if (smb_inet_ntop(ipaddr, ipbuf, SMB_IPSTRLEN(ipaddr->a_family)))
+		(void) strlcpy(buf, ipbuf, buflen);
+}
+
+/*
+ * Check whether or not the specified client name is the client of this
+ * session.  The name may be in UNC format (\\CLIENT).
+ *
+ * A workstation/client name is setup on NBT connections as part of the
+ * NetBIOS session request but that isn't available on TCP connections.
+ * If the session doesn't have a client name we typically return the
+ * client IP address as the workstation name on MSRPC requests.  So we
+ * check for the IP address here in addition to the workstation name.
+ */
+boolean_t
+smb_session_isclient(smb_session_t *sn, const char *client)
+{
+	char		buf[INET6_ADDRSTRLEN];
+	smb_inaddr_t	*ipaddr;
+
+	client += strspn(client, "\\");
+
+	if (utf8_strcasecmp(client, sn->workstation) == 0)
+		return (B_TRUE);
+
+	ipaddr = &sn->ipaddr;
+	if (smb_inet_ntop(ipaddr, buf, SMB_IPSTRLEN(ipaddr->a_family)) == NULL)
+		return (B_FALSE);
+
+	if (utf8_strcasecmp(client, buf) == 0)
+		return (B_TRUE);
+
+	return (B_FALSE);
+}
+
+/*
  * smb_request_alloc
  *
  * Allocate an smb_request_t structure from the kmem_cache.  Partially
@@ -1171,7 +1224,7 @@
 void
 dump_smb_inaddr(smb_inaddr_t *ipaddr)
 {
-char ipstr[INET6_ADDRSTRLEN];
+	char ipstr[INET6_ADDRSTRLEN];
 
 	if (smb_inet_ntop(ipaddr, ipstr, SMB_IPSTRLEN(ipaddr->a_family)))
 		cmn_err(CE_WARN, "error ipstr=%s", ipstr);
--- a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c	Fri Jul 17 17:54:42 2009 -0700
@@ -519,8 +519,12 @@
 	if (count != 0)
 		smb_odir_save_cookie(od, 0, cookie);
 
-	/* if eos not already detected, check if more entries */
-	if (!*eos)
+	/*
+	 * If all retrieved entries have been successfully encoded
+	 * and eos has not already been detected, check if there are
+	 * any more entries. eos will be set if there are no more.
+	 */
+	if ((rc == 0) && (!*eos))
 		(void) smb_odir_read_fileinfo(sr, od, &fileinfo, eos);
 
 	return (count);
--- a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c	Fri Jul 17 17:54:42 2009 -0700
@@ -52,6 +52,8 @@
 #include <smbsrv/smb_fsops.h>
 
 uint32_t smb_pad_align(uint32_t offset, uint32_t align);
+void smb_encode_smb_datetimes(smb_request_t *, smb_xa_t *, smb_attr_t *);
+void smb_encode_nt_times(smb_request_t *, smb_xa_t *, smb_attr_t *);
 extern int smb_query_all_info_filename(smb_tree_t *, smb_node_t *,
     char *, size_t);
 
@@ -80,11 +82,10 @@
 	u_offset_t	datasz = 0, allocsz = 0;
 	smb_attr_t	*ap = NULL;
 	char		*namep = NULL;
-	char		*filename = NULL, *alt_nm_ptr = NULL;
+	char		*filename = NULL;
 	int		filename_len = 0;
 	smb_node_t	*node = NULL; /* only set for SMB_FTYPE_DISK files */
-	smb_node_t	*dir_snode = NULL;
-	timestruc_t	*creation_time = NULL;
+	smb_node_t	*dnode = NULL;
 	unsigned char	delete_on_close = 0;
 	unsigned char	is_dir = 0;
 	char		*filebuf = NULL;
@@ -133,7 +134,6 @@
 		}
 
 		dattr = ap->sa_dosattr;
-		creation_time = &ap->sa_crtime;
 
 		if (smb_node_is_dir(node)) {
 			is_dir = 1;
@@ -144,31 +144,55 @@
 			allocsz = ap->sa_vattr.va_nblocks * DEV_BSIZE;
 		}
 
-		dir_snode = node->dir_snode;
+		dnode = node->n_dnode;
 		delete_on_close =
 		    (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0;
+
+		/*
+		 * The number of links reported should be the number of
+		 * non-deleted links. Thus if delete_on_close is set,
+		 * decrement the link count.
+		 */
+		if (delete_on_close && ap->sa_vattr.va_nlink > 0)
+			--(ap->sa_vattr.va_nlink);
+
 		}
 		break;
 
 	case SMB_FTYPE_MESG_PIPE:
 		{
-		/*
-		 * The pipe is only valid for SMB_FTYPE_MESG_PIPE files.
-		 */
+		/* The pipe is only valid for SMB_FTYPE_MESG_PIPE files */
 		namep = sr->fid_ofile->f_pipe->p_name;
 		filename = namep;
 		filename_len = smb_ascii_or_unicode_strlen(sr, filename);
 
 		ap = &pipe_attr;
-		creation_time = (timestruc_t *)&ap->sa_vattr.va_ctime;
+		ap->sa_vattr.va_nlink = 1;
 		dattr = FILE_ATTRIBUTE_NORMAL;
 		datasz = allocsz = 0;
 
-		delete_on_close = 0;
+		delete_on_close = 1;
 		is_dir = 0;
+
+		/* some levels are not valid for pipes */
+		switch (infolev) {
+		case SMB_QUERY_FILE_ALT_NAME_INFO:
+		case SMB_FILE_ALT_NAME_INFORMATION:
+		case SMB_QUERY_FILE_STREAM_INFO:
+		case SMB_FILE_STREAM_INFORMATION:
+		case SMB_QUERY_FILE_COMPRESSION_INFO:
+		case SMB_FILE_COMPRESSION_INFORMATION:
+		case SMB_FILE_ATTR_TAG_INFORMATION:
+			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
+			    ERRDOS, ERROR_INVALID_PARAMETER);
+			return (SDRC_ERROR);
+		case SMB_INFO_QUERY_ALL_EAS:
+			smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
+			    ERRDOS, ERROR_ACCESS_DENIED);
+			return (SDRC_ERROR);
+		}
 		}
 		break;
-
 	default:
 		smbsr_error(sr, 0, ERRDOS, ERRbadfile);
 		return (SDRC_ERROR);
@@ -188,16 +212,14 @@
 		if (allocsz > UINT_MAX)
 			allocsz = UINT_MAX;
 
+		/* unlike other levels attributes should be 0 here for pipes */
+		if (sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE)
+			dattr = 0;
+
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
-		(void) smb_mbc_encodef(&xa->rep_data_mb,
-		    ((sr->session->native_os == NATIVE_OS_WIN95)
-		    ? "YYYllw" : "yyyllw"),
-		    smb_gmt2local(sr, creation_time->tv_sec),
-		    smb_gmt2local(sr, ap->sa_vattr.va_atime.tv_sec),
-		    smb_gmt2local(sr, ap->sa_vattr.va_mtime.tv_sec),
-		    (uint32_t)datasz,
-		    (uint32_t)allocsz,
-		    dattr);
+		smb_encode_smb_datetimes(sr, xa, ap);
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "llw",
+		    (uint32_t)datasz, (uint32_t)allocsz, dattr);
 		break;
 
 	case SMB_INFO_QUERY_EA_SIZE:
@@ -206,20 +228,18 @@
 		if (allocsz > UINT_MAX)
 			allocsz = UINT_MAX;
 
+		/* unlike other levels attributes should be 0 here for pipes */
+		if (sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE)
+			dattr = 0;
+
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
-		(void) smb_mbc_encodef(&xa->rep_data_mb,
-		    ((sr->session->native_os == NATIVE_OS_WIN95)
-		    ? "YYYllwl" : "yyyllwl"),
-		    smb_gmt2local(sr, creation_time->tv_sec),
-		    smb_gmt2local(sr, ap->sa_vattr.va_atime.tv_sec),
-		    smb_gmt2local(sr, ap->sa_vattr.va_mtime.tv_sec),
-		    (uint32_t)datasz,
-		    (uint32_t)allocsz,
-		    dattr, 0);
+		smb_encode_smb_datetimes(sr, xa, ap);
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "llwl",
+		    (uint32_t)datasz, (uint32_t)allocsz, dattr, 0);
 		break;
 
+	case SMB_INFO_QUERY_ALL_EAS:
 	case SMB_INFO_QUERY_EAS_FROM_LIST:
-	case SMB_INFO_QUERY_ALL_EAS:
 	case SMB_FILE_EA_INFORMATION:
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
 		(void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0);
@@ -236,12 +256,8 @@
 		 * Similar change in smb_trans2_query_path_information.c.
 		 */
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
-		(void) smb_mbc_encodef(&xa->rep_data_mb, "TTTTw6.",
-		    creation_time,
-		    &ap->sa_vattr.va_atime,
-		    &ap->sa_vattr.va_mtime,
-		    &ap->sa_vattr.va_ctime,
-		    dattr);
+		smb_encode_nt_times(sr, xa, ap);
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "w6.", dattr);
 		break;
 
 	case SMB_QUERY_FILE_STANDARD_INFO:
@@ -294,19 +310,28 @@
 
 	case SMB_QUERY_FILE_ALL_INFO:
 	case SMB_FILE_ALL_INFORMATION:
+		if (sr->fid_ofile->f_ftype == SMB_FTYPE_DISK) {
+			rc = smb_query_all_info_filename(sr->tid_tree, node,
+			    filebuf, MAXPATHLEN);
+			if (rc != 0) {
+				smbsr_errno(sr, rc);
+				kmem_free(filebuf, MAXPATHLEN+1);
+				return (SDRC_ERROR);
+			}
+			filename = filebuf;
+		} else {
+			/* If the leading \ is missing, add it. */
+			if (*namep != '\\') {
+				(void) snprintf(filebuf, MAXNAMELEN,
+				    "\\%s", namep);
+				filename = filebuf;
+			}
+		}
+		filename_len = smb_ascii_or_unicode_strlen(sr, filename);
+
 		/*
-		 * The reply of this information level on the
-		 * wire doesn't match with protocol specification.
-		 * This is what spec. needs: "TTTTwqqlbbqllqqll"
-		 * But this is actually is sent on the wire:
-		 * "TTTTw6.qqlbb2.l"
-		 * So, there is a 6-byte pad between Attributes and
-		 * AllocationSize. Also there is a 2-byte pad After
-		 * Directory field. Between Directory and FileNameLength
-		 * there is just 4 bytes that it seems is AlignmentRequirement.
-		 * There are 6 other fields between Directory and
-		 * AlignmentRequirement in spec. that aren't sent
-		 * on the wire.
+		 * There is a 6-byte pad between Attributes and AllocationSize,
+		 * and a 2-byte pad after the Directory field.
 		 */
 		if (node) {
 			rc = smb_query_all_info_filename(sr->tid_tree, node,
@@ -322,11 +347,8 @@
 		}
 
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
-		(void) smb_mbc_encodef(&xa->rep_data_mb, "TTTTw6.qqlbb2.l",
-		    creation_time,
-		    &ap->sa_vattr.va_atime,
-		    &ap->sa_vattr.va_mtime,
-		    &ap->sa_vattr.va_ctime,
+		smb_encode_nt_times(sr, xa, ap);
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "w6.qqlbb2.l",
 		    dattr,
 		    (uint64_t)allocsz,
 		    (uint64_t)datasz,
@@ -342,43 +364,41 @@
 	case SMB_QUERY_FILE_ALT_NAME_INFO:
 	case SMB_FILE_ALT_NAME_INFORMATION:
 		/*
-		 * Conform to the rule used by Windows NT/2003 servers.
-		 * Shortname is created only if either the filename or
-		 * extension portion of a file is made up of mixed case.
-		 *
-		 * If the shortname is generated, it will be returned as
-		 * the alternative name.  Otherwise, convert the original
-		 * name to all upper-case and return it as the alternative
-		 * name.
+		 * If the shortname is generated by smb_mangle_name()
+		 * it will be returned as the alternative name.
+		 * Otherwise, convert the original name to  upper-case
+		 * and return it as the alternative name.
 		 */
 		(void) smb_mangle_name(ap->sa_vattr.va_nodeid,
 		    filename, short_name, name83, 0);
-		alt_nm_ptr = (*short_name == 0) ?
-		    utf8_strupr(filename) : short_name;
+		if (*short_name == 0) {
+			(void) strlcpy(short_name, filename, SMB_SHORTNAMELEN);
+			(void) utf8_strupr(short_name);
+		}
+
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
 		(void) smb_mbc_encodef(&xa->rep_data_mb, "%lU", sr,
-		    mts_wcequiv_strlen(alt_nm_ptr), alt_nm_ptr);
+		    mts_wcequiv_strlen(short_name), short_name);
 		break;
 
 	case SMB_QUERY_FILE_STREAM_INFO:
 	case SMB_FILE_STREAM_INFORMATION:
 		{
 		struct smb_node *node = sr->fid_ofile->f_node;
-		if (dir_snode == NULL) {
+		if (dnode == NULL) {
 			kmem_free(filebuf, MAXPATHLEN+1);
 			smbsr_error(sr, 0, ERRDOS, ERRbadfile);
 			return (SDRC_ERROR);
 		}
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
 		if (SMB_IS_STREAM(node)) {
-			ASSERT(node->unnamed_stream_node);
-			ASSERT(node->unnamed_stream_node->n_magic ==
-			    SMB_NODE_MAGIC);
-			ASSERT(node->unnamed_stream_node->n_state !=
+			ASSERT(node->n_unode);
+			ASSERT(node->n_unode->n_magic == SMB_NODE_MAGIC);
+			ASSERT(node->n_unode->n_state !=
 			    SMB_NODE_STATE_DESTROYING);
 
 			(void) smb_encode_stream_info(sr, xa,
-			    node->unnamed_stream_node, ap);
+			    node->n_unode, ap);
 		} else {
 			(void) smb_encode_stream_info(sr, xa, node, ap);
 		}
@@ -572,3 +592,45 @@
 
 	return (pad);
 }
+
+/*
+ * smb_encode_smb_datetimes
+ *
+ * Encode timestamps in the SMB_DATE / SMB_TIME format.
+ */
+void
+smb_encode_smb_datetimes(smb_request_t *sr, smb_xa_t *xa, smb_attr_t *attr)
+{
+	if ((sr->fid_ofile) &&
+	    (sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE)) {
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "lll", 0, 0, 0);
+		return;
+	}
+
+	(void) smb_mbc_encodef(&xa->rep_data_mb,
+	    ((sr->session->native_os == NATIVE_OS_WIN95) ? "YYY" : "yyy"),
+	    smb_gmt2local(sr, attr->sa_crtime.tv_sec),
+	    smb_gmt2local(sr, attr->sa_vattr.va_atime.tv_sec),
+	    smb_gmt2local(sr, attr->sa_vattr.va_mtime.tv_sec));
+}
+
+/*
+ * smb_encode_nt_times
+ *
+ * Encode timestamps in LARGE INTEGER format NT Times.
+ */
+void
+smb_encode_nt_times(smb_request_t *sr, smb_xa_t *xa, smb_attr_t *attr)
+{
+	if ((sr->fid_ofile) &&
+	    (sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE)) {
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "qqqq", 0, 0, 0, 0);
+		return;
+	}
+
+	(void) smb_mbc_encodef(&xa->rep_data_mb, "TTTT",
+	    &attr->sa_crtime,
+	    &attr->sa_vattr.va_atime,
+	    &attr->sa_vattr.va_mtime,
+	    &attr->sa_vattr.va_ctime);
+}
--- a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c	Fri Jul 17 17:54:42 2009 -0700
@@ -322,6 +322,8 @@
 #include <smbsrv/smb_fsops.h>
 
 int smb_query_all_info_filename(smb_tree_t *, smb_node_t *, char *, size_t);
+extern void smb_encode_smb_datetimes(smb_request_t *, smb_xa_t *, smb_attr_t *);
+extern void smb_encode_nt_times(smb_request_t *, smb_xa_t *, smb_attr_t *);
 
 /*
  * Function: int smb_com_trans2_query_path_information(struct smb_request *)
@@ -340,11 +342,12 @@
 	char			short_name[SMB_SHORTNAMELEN];
 	char			name83[SMB_SHORTNAMELEN];
 	unsigned char		is_dir;
+	unsigned char		delete_on_close;
 	int			len;
 
 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
-		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
-		    ERROR_ACCESS_DENIED);
+		smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST, ERRDOS,
+		    ERROR_INVALID_FUNCTION);
 		return (SDRC_ERROR);
 	}
 
@@ -422,6 +425,18 @@
 		allocsz = ap->sa_vattr.va_nblocks * DEV_BSIZE;
 	}
 
+	delete_on_close =
+	    (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0;
+
+	/*
+	 * The number of links reported should be the number of
+	 * non-deleted links. Thus if delete_on_close is set,
+	 * decrement the link count.
+	 */
+	if (delete_on_close && ap->sa_vattr.va_nlink > 0)
+		--(ap->sa_vattr.va_nlink);
+
+
 
 	switch (infolev) {
 	case SMB_INFO_STANDARD:
@@ -431,15 +446,9 @@
 			allocsz = UINT_MAX;
 
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
-		(void) smb_mbc_encodef(&xa->rep_data_mb,
-		    ((sr->session->native_os == NATIVE_OS_WIN95)
-		    ? "YYYllw" : "yyyllw"),
-		    smb_gmt2local(sr, ap->sa_crtime.tv_sec),
-		    smb_gmt2local(sr, ap->sa_vattr.va_atime.tv_sec),
-		    smb_gmt2local(sr, ap->sa_vattr.va_mtime.tv_sec),
-		    (uint32_t)datasz,
-		    (uint32_t)allocsz,
-		    dattr);
+		smb_encode_smb_datetimes(sr, xa, ap);
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "llw",
+		    (uint32_t)datasz, (uint32_t)allocsz, dattr);
 		break;
 
 	case SMB_INFO_QUERY_EA_SIZE:
@@ -449,15 +458,9 @@
 			allocsz = UINT_MAX;
 
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
-		(void) smb_mbc_encodef(&xa->rep_data_mb,
-		    ((sr->session->native_os == NATIVE_OS_WIN95)
-		    ? "YYYllwl" : "yyyllwl"),
-		    smb_gmt2local(sr, ap->sa_crtime.tv_sec),
-		    smb_gmt2local(sr, ap->sa_vattr.va_atime.tv_sec),
-		    smb_gmt2local(sr, ap->sa_vattr.va_mtime.tv_sec),
-		    (uint32_t)datasz,
-		    (uint32_t)allocsz,
-		    dattr, 0);
+		smb_encode_smb_datetimes(sr, xa, ap);
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "llwl",
+		    (uint32_t)datasz, (uint32_t)allocsz, dattr, 0);
 		break;
 
 	case SMB_INFO_QUERY_EAS_FROM_LIST:
@@ -477,12 +480,8 @@
 		 * Similar change in smb_trans2_query_file_information.c.
 		 */
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
-		(void) smb_mbc_encodef(&xa->rep_data_mb, "TTTTw6.",
-		    &ap->sa_crtime,
-		    &ap->sa_vattr.va_atime,
-		    &ap->sa_vattr.va_mtime,
-		    &ap->sa_vattr.va_ctime,
-		    dattr);
+		smb_encode_nt_times(sr, xa, ap);
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "w6.", dattr);
 		break;
 
 	case SMB_QUERY_FILE_STANDARD_INFO:
@@ -496,7 +495,7 @@
 		    (uint64_t)allocsz,
 		    (uint64_t)datasz,
 		    ap->sa_vattr.va_nlink,
-		    (node && (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0),
+		    delete_on_close,
 		    (char)(ap->sa_vattr.va_type == VDIR));
 		break;
 
@@ -520,19 +519,18 @@
 
 	case SMB_QUERY_FILE_ALL_INFO:
 	case SMB_FILE_ALL_INFORMATION:
+		rc = smb_query_all_info_filename(sr->tid_tree, node,
+		    name, MAXPATHLEN);
+		if (rc != 0) {
+			smbsr_errno(sr, rc);
+			smb_node_release(node);
+			kmem_free(name, MAXPATHLEN);
+			return (SDRC_ERROR);
+		}
+
 		/*
-		 * The reply of this information level on the
-		 * wire doesn't match with protocol specification.
-		 * This is what spec. needs: "TTTTwqqlbbqllqqll"
-		 * But this is actually is sent on the wire:
-		 * "TTTTw6.qqlbb2.l"
-		 * So, there is a 6-byte pad between Attributes and
-		 * AllocationSize. Also there is a 2-byte pad After
-		 * Directory field. Between Directory and FileNameLength
-		 * there is just 4 bytes that it seems is AlignmentRequirement.
-		 * There are 6 other fields between Directory and
-		 * AlignmentRequirement in spec. that aren't sent
-		 * on the wire.
+		 * There is a 6-byte pad between Attributes and AllocationSize,
+		 * and a 2-byte pad after the Directory field.
 		 */
 		rc = smb_query_all_info_filename(sr->tid_tree, node,
 		    name, MAXPATHLEN);
@@ -544,11 +542,8 @@
 		}
 
 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
-		(void) smb_mbc_encodef(&xa->rep_data_mb, "TTTTw6.qqlbb2.l",
-		    &ap->sa_crtime,
-		    &ap->sa_vattr.va_atime,
-		    &ap->sa_vattr.va_mtime,
-		    &ap->sa_vattr.va_ctime,
+		smb_encode_nt_times(sr, xa, ap);
+		(void) smb_mbc_encodef(&xa->rep_data_mb, "w6.qqlbb2.l",
 		    dattr,
 		    (uint64_t)allocsz,
 		    (uint64_t)datasz,
@@ -564,14 +559,10 @@
 	case SMB_QUERY_FILE_ALT_NAME_INFO:
 	case SMB_FILE_ALT_NAME_INFORMATION:
 		/*
-		 * Conform to the rule used by Windows NT/2003 servers.
-		 * Shortname is created only if either the filename or
-		 * extension portion of a file is made up of mixed case.
-		 *
-		 * If the shortname is generated, it will be returned as
-		 * the alternative name.  Otherwise, convert the original
-		 * name to all upper-case and return it as the alternative
-		 * name.
+		 * If the shortname is generated by smb_mangle_name()
+		 * it will be returned as the alternative name.
+		 * Otherwise, convert the original name to  upper-case
+		 * and return it as the alternative name.
 		 */
 		(void) smb_mangle_name(ap->sa_vattr.va_nodeid,
 		    name, short_name, name83, 0);
@@ -656,7 +647,7 @@
 	buflen -= len;
 
 	if (SMB_IS_STREAM(node))
-		vp = node->unnamed_stream_node->vp;
+		vp = node->n_unode->vp;
 	else
 		vp = node->vp;
 
--- a/usr/src/uts/common/fs/smbsrv/smb_tree.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c	Fri Jul 17 17:54:42 2009 -0700
@@ -193,8 +193,13 @@
 static void smb_tree_get_flags(const smb_share_t *, vfs_t *, smb_tree_t *);
 static void smb_tree_log(smb_request_t *, const char *, const char *, ...);
 static void smb_tree_close_odirs(smb_tree_t *, uint16_t);
+static smb_ofile_t *smb_tree_get_ofile(smb_tree_t *, smb_ofile_t *);
 static smb_odir_t *smb_tree_get_odir(smb_tree_t *, smb_odir_t *);
 static void smb_tree_set_execsub_info(smb_tree_t *, smb_execsub_info_t *);
+static int smb_tree_enum_private(smb_tree_t *, smb_svcenum_t *);
+static int smb_tree_netinfo_encode(smb_tree_t *, uint8_t *, size_t, uint32_t *);
+static void smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *);
+static void smb_tree_netinfo_fini(smb_netconnectinfo_t *);
 
 /*
  * Extract the share name and share type and connect as appropriate.
@@ -362,8 +367,69 @@
 	return ((tree->t_flags & flags) == flags);
 }
 
+/*
+ * If the enumeration request is for tree data, handle the request
+ * here.  Otherwise, pass it on to the ofiles.
+ *
+ * This function should be called with a hold on the tree.
+ */
+int
+smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum)
+{
+	smb_ofile_t	*of;
+	smb_ofile_t	*next;
+	int		rc;
+
+	ASSERT(tree);
+	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
+
+	if (svcenum->se_type == SMB_SVCENUM_TYPE_TREE)
+		return (smb_tree_enum_private(tree, svcenum));
+
+	of = smb_tree_get_ofile(tree, NULL);
+	while (of) {
+		ASSERT(of->f_tree == tree);
+
+		rc = smb_ofile_enum(of, svcenum);
+		if (rc != 0) {
+			smb_ofile_release(of);
+			break;
+		}
+
+		next = smb_tree_get_ofile(tree, of);
+		smb_ofile_release(of);
+		of = next;
+	}
+
+	return (rc);
+}
+
+/*
+ * Close a file by its unique id.
+ */
+int
+smb_tree_fclose(smb_tree_t *tree, uint32_t uniqid)
+{
+	smb_ofile_t	*of;
+
+	ASSERT(tree);
+	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
+
+	if ((of = smb_ofile_lookup_by_uniqid(tree, uniqid)) == NULL)
+		return (ENOENT);
+
+	if (smb_ofile_disallow_fclose(of)) {
+		smb_ofile_release(of);
+		return (EACCES);
+	}
+
+	smb_ofile_close(of, 0);
+	smb_ofile_release(of);
+	return (0);
+}
 
 /* *************************** Static Functions ***************************** */
+
 #define	SHARES_DIR	".zfs/shares/"
 static void
 smb_tree_acl_access(cred_t *cred, const char *sharename, vnode_t *pathvp,
@@ -434,7 +500,7 @@
 smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
 {
 	smb_user_t		*user = sr->uid_user;
-	smb_node_t		*dir_snode = NULL;
+	smb_node_t		*dnode = NULL;
 	smb_node_t		*snode = NULL;
 	char			last_component[MAXNAMELEN];
 	smb_tree_t		*tree;
@@ -529,15 +595,15 @@
 	/*
 	 * Check that the shared directory exists.
 	 */
-	rc = smb_pathname_reduce(sr, u_cred, si->shr_path, 0, 0, &dir_snode,
+	rc = smb_pathname_reduce(sr, u_cred, si->shr_path, 0, 0, &dnode,
 	    last_component);
 
 	if (rc == 0) {
 		rc = smb_fsop_lookup(sr, u_cred, SMB_FOLLOW_LINKS,
-		    sr->sr_server->si_root_smb_node, dir_snode, last_component,
+		    sr->sr_server->si_root_smb_node, dnode, last_component,
 		    &snode);
 
-		smb_node_release(dir_snode);
+		smb_node_release(dnode);
 	}
 
 	if (rc) {
@@ -704,6 +770,7 @@
 	tree->t_state = SMB_TREE_STATE_CONNECTED;
 	tree->t_magic = SMB_TREE_MAGIC;
 	tree->t_access = access;
+	tree->t_connect_time = gethrestime_sec();
 
 	/* if FS is readonly, enforce that here */
 	if (tree->t_flags & SMB_TREE_READONLY)
@@ -1080,6 +1147,44 @@
 }
 
 /*
+ * Get the next open ofile in the list.  A reference is taken on
+ * the ofile, which can be released later with smb_ofile_release().
+ *
+ * If the specified ofile is NULL, search from the beginning of the
+ * list.  Otherwise, the search starts just after that ofile.
+ *
+ * Returns NULL if there are no open files in the list.
+ */
+static smb_ofile_t *
+smb_tree_get_ofile(smb_tree_t *tree, smb_ofile_t *of)
+{
+	smb_llist_t *ofile_list;
+
+	ASSERT(tree);
+	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
+
+	ofile_list = &tree->t_ofile_list;
+	smb_llist_enter(ofile_list, RW_READER);
+
+	if (of) {
+		ASSERT(of->f_magic == SMB_OFILE_MAGIC);
+		of = smb_llist_next(ofile_list, of);
+	} else {
+		of = smb_llist_head(ofile_list);
+	}
+
+	while (of) {
+		if (smb_ofile_hold(of))
+			break;
+
+		of = smb_llist_next(ofile_list, of);
+	}
+
+	smb_llist_exit(ofile_list);
+	return (of);
+}
+
+/*
  * smb_tree_get_odir
  *
  * Find the next odir in the tree's list of odirs, and obtain a
@@ -1157,3 +1262,96 @@
 		subs->e_cli_netbiosname = tree->t_session->workstation;
 		subs->e_uid = tree->t_user->u_cred->cr_uid;
 }
+
+/*
+ * Private function to support smb_tree_enum.
+ */
+static int
+smb_tree_enum_private(smb_tree_t *tree, smb_svcenum_t *svcenum)
+{
+	uint8_t *pb;
+	uint_t nbytes;
+	int rc;
+
+	if (svcenum->se_nskip > 0) {
+		svcenum->se_nskip--;
+		return (0);
+	}
+
+	if (svcenum->se_nitems >= svcenum->se_nlimit) {
+		svcenum->se_nitems = svcenum->se_nlimit;
+		return (0);
+	}
+
+	pb = &svcenum->se_buf[svcenum->se_bused];
+	rc = smb_tree_netinfo_encode(tree, pb, svcenum->se_bavail, &nbytes);
+	if (rc == 0) {
+		svcenum->se_bavail -= nbytes;
+		svcenum->se_bused += nbytes;
+		svcenum->se_nitems++;
+	}
+
+	return (rc);
+}
+
+/*
+ * Encode connection information into a buffer: connection information
+ * needed in user space to support RPC requests.
+ */
+static int
+smb_tree_netinfo_encode(smb_tree_t *tree, uint8_t *buf, size_t buflen,
+    uint32_t *nbytes)
+{
+	smb_netconnectinfo_t	info;
+	int			rc;
+
+	smb_tree_netinfo_init(tree, &info);
+	rc = smb_netconnectinfo_encode(&info, buf, buflen, nbytes);
+	smb_tree_netinfo_fini(&info);
+
+	return (rc);
+}
+
+/*
+ * Note: ci_numusers should be the number of users connected to
+ * the share rather than the number of references on the tree but
+ * we don't have a mechanism to track users/share in smbsrv yet.
+ */
+static void
+smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *info)
+{
+	smb_user_t	*user;
+
+	ASSERT(tree);
+
+	info->ci_id = tree->t_tid;
+	info->ci_type = tree->t_res_type;
+	info->ci_numopens = tree->t_open_files;
+	info->ci_numusers = tree->t_refcnt;
+	info->ci_time = gethrestime_sec() - tree->t_connect_time;
+
+	info->ci_sharelen = strlen(tree->t_sharename) + 1;
+	info->ci_share = smb_kstrdup(tree->t_sharename, info->ci_sharelen);
+
+	user = tree->t_user;
+	ASSERT(user);
+
+	info->ci_namelen = user->u_domain_len + user->u_name_len + 2;
+	info->ci_username = kmem_alloc(info->ci_namelen, KM_SLEEP);
+	(void) snprintf(info->ci_username, info->ci_namelen, "%s\\%s",
+	    user->u_domain, user->u_name);
+}
+
+static void
+smb_tree_netinfo_fini(smb_netconnectinfo_t *info)
+{
+	if (info == NULL)
+		return;
+
+	if (info->ci_username)
+		kmem_free(info->ci_username, info->ci_namelen);
+	if (info->ci_share)
+		kmem_free(info->ci_share, info->ci_sharelen);
+
+	bzero(info, sizeof (smb_netconnectinfo_t));
+}
--- a/usr/src/uts/common/fs/smbsrv/smb_user.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/fs/smbsrv/smb_user.c	Fri Jul 17 17:54:42 2009 -0700
@@ -171,6 +171,8 @@
 
 static smb_sid_t *smb_admins_sid = NULL;
 
+static boolean_t smb_user_is_logged_in(smb_user_t *);
+static int smb_user_enum_private(smb_user_t *, smb_svcenum_t *);
 static void smb_user_delete(smb_user_t *user);
 static smb_tree_t *smb_user_get_tree(smb_llist_t *, smb_tree_t *);
 
@@ -287,7 +289,8 @@
 /*
  * smb_user_logoff
  *
- *
+ * Change the user state and disconnect trees.
+ * The user list must not be entered or modified here.
  */
 void
 smb_user_logoff(
@@ -376,6 +379,27 @@
 }
 
 /*
+ * Take a reference on a user.
+ */
+boolean_t
+smb_user_hold(smb_user_t *user)
+{
+	ASSERT(user);
+	ASSERT(user->u_magic == SMB_USER_MAGIC);
+
+	mutex_enter(&user->u_mutex);
+
+	if (smb_user_is_logged_in(user)) {
+		user->u_refcnt++;
+		mutex_exit(&user->u_mutex);
+		return (B_TRUE);
+	}
+
+	mutex_exit(&user->u_mutex);
+	return (B_FALSE);
+}
+
+/*
  * smb_user_release
  *
  *
@@ -434,29 +458,10 @@
 		ASSERT(user->u_magic == SMB_USER_MAGIC);
 		ASSERT(user->u_session == session);
 		if (user->u_uid == uid) {
-			mutex_enter(&user->u_mutex);
-			switch (user->u_state) {
-
-			case SMB_USER_STATE_LOGGED_IN:
-				/* The user exists and is still logged in. */
-				user->u_refcnt++;
-				mutex_exit(&user->u_mutex);
+			if (smb_user_hold(user)) {
 				smb_llist_exit(&session->s_user_list);
 				return (user);
-
-			case SMB_USER_STATE_LOGGING_OFF:
-			case SMB_USER_STATE_LOGGED_OFF:
-				/*
-				 * The user exists but has logged off or is in
-				 * the process of logging off.
-				 */
-				mutex_exit(&user->u_mutex);
-				smb_llist_exit(&session->s_user_list);
-				return (NULL);
-
-			default:
-				ASSERT(0);
-				mutex_exit(&user->u_mutex);
+			} else {
 				smb_llist_exit(&session->s_user_list);
 				return (NULL);
 			}
@@ -499,17 +504,11 @@
 	while (next) {
 		ASSERT(next->u_magic == SMB_USER_MAGIC);
 		ASSERT(next->u_session == session);
-		mutex_enter(&next->u_mutex);
-		if (next->u_state == SMB_USER_STATE_LOGGED_IN) {
-			next->u_refcnt++;
-			mutex_exit(&next->u_mutex);
+
+		if (smb_user_hold(next))
 			break;
-		} else {
-			ASSERT((next->u_state == SMB_USER_STATE_LOGGING_OFF) ||
-			    (next->u_state == SMB_USER_STATE_LOGGED_OFF));
-			mutex_exit(&next->u_mutex);
-			next = smb_llist_next(lst, next);
-		}
+
+		next = smb_llist_next(lst, next);
 	}
 	smb_llist_exit(lst);
 
@@ -712,6 +711,40 @@
 }
 
 /*
+ * Close a file by its unique id.
+ */
+int
+smb_user_fclose(smb_user_t *user, uint32_t uniqid)
+{
+	smb_llist_t	*tree_list;
+	smb_tree_t	*tree;
+	int		rc = ENOENT;
+
+	ASSERT(user);
+	ASSERT(user->u_magic == SMB_USER_MAGIC);
+
+	tree_list = &user->u_tree_list;
+	ASSERT(tree_list);
+
+	smb_llist_enter(tree_list, RW_READER);
+	tree = smb_llist_head(tree_list);
+
+	while ((tree != NULL) && (rc == ENOENT)) {
+		ASSERT(tree->t_user == user);
+
+		if (smb_tree_hold(tree)) {
+			rc = smb_tree_fclose(tree, uniqid);
+			smb_tree_release(tree);
+		}
+
+		tree = smb_llist_next(tree_list, tree);
+	}
+
+	smb_llist_exit(tree_list);
+	return (rc);
+}
+
+/*
  * Determine whether or not the user is an administrator.
  * Members of the administrators group have administrative rights.
  */
@@ -734,9 +767,99 @@
 	return (B_FALSE);
 }
 
+/*
+ * This function should be called with a hold on the user.
+ */
+boolean_t
+smb_user_namecmp(smb_user_t *user, const char *name)
+{
+	char		*fq_name;
+	boolean_t	match;
+
+	if (utf8_strcasecmp(name, user->u_name) == 0)
+		return (B_TRUE);
+
+	fq_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+
+	(void) snprintf(fq_name, MAXNAMELEN, "%s\\%s",
+	    user->u_domain, user->u_name);
+
+	match = (utf8_strcasecmp(name, fq_name) == 0);
+	if (!match) {
+		(void) snprintf(fq_name, MAXNAMELEN, "%s@%s",
+		    user->u_name, user->u_domain);
+
+		match = (utf8_strcasecmp(name, fq_name) == 0);
+	}
+
+	kmem_free(fq_name, MAXNAMELEN);
+	return (match);
+}
+
+/*
+ * If the enumeration request is for user data, handle the request
+ * here.  Otherwise, pass it on to the trees.
+ *
+ * This function should be called with a hold on the user.
+ */
+int
+smb_user_enum(smb_user_t *user, smb_svcenum_t *svcenum)
+{
+	smb_tree_t	*tree;
+	smb_tree_t	*next;
+	int		rc;
+
+	ASSERT(user);
+	ASSERT(user->u_magic == SMB_USER_MAGIC);
+
+	if (svcenum->se_type == SMB_SVCENUM_TYPE_USER)
+		return (smb_user_enum_private(user, svcenum));
+
+	tree = smb_user_get_tree(&user->u_tree_list, NULL);
+	while (tree) {
+		ASSERT(tree->t_user == user);
+
+		rc = smb_tree_enum(tree, svcenum);
+		if (rc != 0) {
+			smb_tree_release(tree);
+			break;
+		}
+
+		next = smb_user_get_tree(&user->u_tree_list, tree);
+		smb_tree_release(tree);
+		tree = next;
+	}
+
+	return (rc);
+}
+
 /* *************************** Static Functions ***************************** */
 
 /*
+ * Determine whether or not a user is logged in.
+ * Typically, a reference can only be taken on a logged-in user.
+ *
+ * This is a private function and must be called with the user
+ * mutex held.
+ */
+static boolean_t
+smb_user_is_logged_in(smb_user_t *user)
+{
+	switch (user->u_state) {
+	case SMB_USER_STATE_LOGGED_IN:
+		return (B_TRUE);
+
+	case SMB_USER_STATE_LOGGING_OFF:
+	case SMB_USER_STATE_LOGGED_OFF:
+		return (B_FALSE);
+
+	default:
+		ASSERT(0);
+		return (B_FALSE);
+	}
+}
+
+/*
  * smb_user_delete
  */
 static void
@@ -821,3 +944,104 @@
 {
 	return ((user->u_privcred)? user->u_privcred : user->u_cred);
 }
+
+/*
+ * Private function to support smb_user_enum.
+ */
+static int
+smb_user_enum_private(smb_user_t *user, smb_svcenum_t *svcenum)
+{
+	uint8_t *pb;
+	uint_t nbytes;
+	int rc;
+
+	if (svcenum->se_nskip > 0) {
+		svcenum->se_nskip--;
+		return (0);
+	}
+
+	if (svcenum->se_nitems >= svcenum->se_nlimit) {
+		svcenum->se_nitems = svcenum->se_nlimit;
+		return (0);
+	}
+
+	pb = &svcenum->se_buf[svcenum->se_bused];
+	rc = smb_user_netinfo_encode(user, pb, svcenum->se_bavail, &nbytes);
+	if (rc == 0) {
+		svcenum->se_bavail -= nbytes;
+		svcenum->se_bused += nbytes;
+		svcenum->se_nitems++;
+	}
+
+	return (rc);
+}
+
+/*
+ * Encode the NetInfo for a user into a buffer.  NetInfo contains
+ * information that is often needed in user space to support RPC
+ * requests.
+ */
+int
+smb_user_netinfo_encode(smb_user_t *user, uint8_t *buf, size_t buflen,
+    uint32_t *nbytes)
+{
+	smb_netuserinfo_t	info;
+	int			rc;
+
+	smb_user_netinfo_init(user, &info);
+	rc = smb_netuserinfo_encode(&info, buf, buflen, nbytes);
+	smb_user_netinfo_fini(&info);
+
+	return (rc);
+}
+
+void
+smb_user_netinfo_init(smb_user_t *user, smb_netuserinfo_t *info)
+{
+	smb_session_t	*session;
+	char		*buf;
+
+	ASSERT(user);
+	ASSERT(user->u_domain);
+	ASSERT(user->u_name);
+
+	session = user->u_session;
+	ASSERT(session);
+	ASSERT(session->workstation);
+
+	info->ui_session_id = session->s_kid;
+	info->ui_native_os = session->native_os;
+	info->ui_ipaddr = session->ipaddr;
+	info->ui_numopens = session->s_file_cnt;
+	info->ui_uid = user->u_uid;
+	info->ui_logon_time = user->u_logon_time;
+	info->ui_flags = user->u_flags;
+
+	info->ui_domain_len = user->u_domain_len;
+	info->ui_domain = smb_kstrdup(user->u_domain, info->ui_domain_len);
+
+	info->ui_account_len = user->u_name_len;
+	info->ui_account = smb_kstrdup(user->u_name, info->ui_account_len);
+
+	buf = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+	smb_session_getclient(session, buf, MAXNAMELEN);
+	info->ui_workstation_len = strlen(buf) + 1;
+	info->ui_workstation = smb_kstrdup(buf, info->ui_workstation_len);
+	kmem_free(buf, MAXNAMELEN);
+}
+
+void
+smb_user_netinfo_fini(smb_netuserinfo_t *info)
+{
+	if (info == NULL)
+		return;
+
+	if (info->ui_domain)
+		kmem_free(info->ui_domain, info->ui_domain_len);
+	if (info->ui_account)
+		kmem_free(info->ui_account, info->ui_account_len);
+	if (info->ui_workstation)
+		kmem_free(info->ui_workstation, info->ui_workstation_len);
+
+	bzero(info, sizeof (smb_netuserinfo_t));
+}
--- a/usr/src/uts/common/rpc/xdr.c	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/rpc/xdr.c	Fri Jul 17 17:54:42 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -307,6 +307,29 @@
 }
 
 /*
+ * XDR an unsigned char
+ */
+bool_t
+xdr_u_char(XDR *xdrs, uchar_t *cp)
+{
+	int i;
+
+	switch (xdrs->x_op) {
+	case XDR_ENCODE:
+		i = (*cp);
+		return (XDR_PUTINT32(xdrs, &i));
+	case XDR_DECODE:
+		if (!XDR_GETINT32(xdrs, &i))
+			return (FALSE);
+		*cp = (uchar_t)i;
+		return (TRUE);
+	case XDR_FREE:
+		return (TRUE);
+	}
+	return (FALSE);
+}
+
+/*
  * XDR booleans
  *
  * PSARC 2003/523 Contract Private Interface
@@ -608,6 +631,32 @@
 }
 
 /*
+ * xdr_vector():
+ *
+ * XDR a fixed length array. Unlike variable-length arrays, the storage
+ * of fixed length arrays is static and unfreeable.
+ * > basep: base of the array
+ * > size: size of the array
+ * > elemsize: size of each element
+ * > xdr_elem: routine to XDR each element
+ */
+bool_t
+xdr_vector(XDR *xdrs, char *basep, const uint_t nelem,
+	const uint_t elemsize, const xdrproc_t xdr_elem)
+{
+	uint_t i;
+	char *elptr;
+
+	elptr = basep;
+	for (i = 0; i < nelem; i++) {
+		if (!(*xdr_elem)(xdrs, elptr, LASTUNSIGNED))
+			return (FALSE);
+		elptr += elemsize;
+	}
+	return (TRUE);
+}
+
+/*
  * Wrapper for xdr_string that can be called directly from
  * routines like clnt_call
  */
--- a/usr/src/uts/common/rpc/xdr.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/rpc/xdr.h	Fri Jul 17 17:54:42 2009 -0700
@@ -18,7 +18,7 @@
  *
  * CDDL HEADER END
  *
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -420,6 +420,8 @@
 extern bool_t	xdr_string(XDR *, char **, const uint_t);
 extern bool_t	xdr_union(XDR *, enum_t *, char *,
 		    const struct xdr_discrim *, const xdrproc_t);
+extern bool_t	xdr_vector(XDR *, char *, const uint_t, const uint_t,
+    const xdrproc_t);
 extern unsigned int  xdr_sizeof(xdrproc_t, void *);
 
 extern bool_t   xdr_hyper(XDR *, longlong_t *);
@@ -428,6 +430,7 @@
 extern bool_t   xdr_u_longlong_t(XDR *, u_longlong_t *);
 
 extern bool_t	xdr_char(XDR *, char *);
+extern bool_t	xdr_u_char(XDR *, uchar_t *);
 extern bool_t	xdr_wrapstring(XDR *, char **);
 extern bool_t	xdr_reference(XDR *, caddr_t *, uint_t, const xdrproc_t);
 extern bool_t	xdr_pointer(XDR *, char **, uint_t, const xdrproc_t);
@@ -446,9 +449,6 @@
 #endif
 
 #ifndef _KERNEL
-extern bool_t	xdr_u_char(XDR *, uchar_t *);
-extern bool_t	xdr_vector(XDR *, char *, const uint_t, const uint_t, const
-xdrproc_t);
 extern bool_t	xdr_float(XDR *, float *);
 extern bool_t	xdr_double(XDR *, double *);
 extern bool_t	xdr_quadruple(XDR *, long double *);
@@ -468,12 +468,14 @@
 extern bool_t	xdr_opaque();
 extern bool_t	xdr_string();
 extern bool_t	xdr_union();
+extern bool_t	xdr_vector();
 
 extern bool_t   xdr_hyper();
 extern bool_t   xdr_longlong_t();
 extern bool_t   xdr_u_hyper();
 extern bool_t   xdr_u_longlong_t();
 extern bool_t	xdr_char();
+extern bool_t	xdr_u_char();
 extern bool_t	xdr_reference();
 extern bool_t	xdr_pointer();
 extern void	xdr_free();
@@ -492,8 +494,6 @@
 #endif
 
 #ifndef _KERNEL
-extern bool_t	xdr_u_char();
-extern bool_t	xdr_vector();
 extern bool_t	xdr_float();
 extern bool_t	xdr_double();
 extern bool_t   xdr_quadruple();
--- a/usr/src/uts/common/rpcsvc/idmap_prot.x	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/rpcsvc/idmap_prot.x	Fri Jul 17 17:54:42 2009 -0700
@@ -19,13 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-
 /* opaque type to support non-ASCII strings */
 typedef	string	idmap_utf8str<>;
+typedef	idmap_utf8str	idmap_utf8str_list<>;
 
 /* Return status */
 typedef int idmap_retcode;
@@ -270,6 +270,76 @@
 };
 #endif
 
+/*
+ * Represents an error from the directory lookup service.
+ *
+ * code is an ASCII string that is a key for the error.  It is not
+ * localized.
+ *
+ * fmt is a format string with %n markers for where to include
+ * params[n-1].  It should be, but NEEDSWORK is not localized to
+ * the caller's locale.
+ *
+ * params is a list of parameters for the error - e.g. the name that
+ * encountered a failure, the server that reported the failure, et cetera.
+ * The values are to be used both as marked in fmt and for machine
+ * interpretation of the error.
+ */
+struct directory_error_rpc {
+	idmap_utf8str	code;
+	idmap_utf8str	fmt;
+	idmap_utf8str	params<>;
+};
+
+/*
+ * One value of a multivalued attribute.
+ */
+typedef opaque			directory_value_rpc<>;
+
+/*
+ * The value of an attribute, if found.  Note that this is a list
+ * of directory_value_rpc objects, to support multivalued attributes.
+ */
+union directory_values_rpc switch (bool found) {
+	case TRUE:
+		directory_value_rpc values<>;
+	case FALSE:
+		void;
+};
+
+/*
+ * The status of the lookup for any particular identifier.
+ */
+enum directory_lookup_status_rpc {
+	DIRECTORY_NOT_FOUND = 0,
+	DIRECTORY_FOUND = 1,
+	DIRECTORY_ERROR = 2
+};
+
+/*
+ * This is the data returned for a particular identifier, either a
+ * list of attribute values or an error.
+ */
+union directory_entry_rpc switch (directory_lookup_status_rpc status) {
+	case DIRECTORY_NOT_FOUND:
+		void;
+	case DIRECTORY_FOUND:
+		directory_values_rpc attrs<>;
+	case DIRECTORY_ERROR:
+		directory_error_rpc err;
+};
+
+/*
+ * This is the result from a request, either a list of the entries for
+ * the identifiers specified, or an error.
+ */
+union directory_results_rpc switch (bool failed) {
+	case TRUE:
+		directory_error_rpc	err;
+	case FALSE:
+		directory_entry_rpc	entries<>;
+};
+
 program IDMAP_PROG {
 	version IDMAP_V1 {
 		void
@@ -302,6 +372,27 @@
 		idmap_prop_res
 		IDMAP_GET_PROP(idmap_prop_type) = 6;
 #endif
+		/*
+		 * Retrieve directory information about a list of users
+		 * or groups by name or SID.
+		 *
+		 * ids is a list of user names, group names, or SIDs.
+		 *
+		 * types is a list of types of the ids in the id list.
+		 * If the type list is shorter than the id list, the last
+		 * type listed applies to all of the ids from that point.
+		 * The defined types are:
+		 *     'n' - name (could be user or group)
+		 *     'u' - user
+		 *     'g' - group
+		 *     's' - SID
+		 *
+		 * attrs is a list of attribute names to retrieve.
+		 */
+		directory_results_rpc DIRECTORY_GET_COMMON(
+			idmap_utf8str_list ids,
+			idmap_utf8str types,
+			idmap_utf8str_list attrs) = 7;
 
 	} = 1;
 } = 100172;
--- a/usr/src/uts/common/smbsrv/ndl/srvsvc.ndl	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/smbsrv/ndl/srvsvc.ndl	Fri Jul 17 17:54:42 2009 -0700
@@ -403,6 +403,7 @@
   SWITCH(switch_value)
 	union mslm_NetConnectInfoResUnion ru;
 };
+typedef struct mslm_NetConnectInfo srvsvc_NetConnectInfo_t;
 
 OPERATION(SRVSVC_OPNUM_NetConnectEnum)
 struct mslm_NetConnectEnum {
@@ -993,22 +994,19 @@
  * 
  * servername
  * [in] Pointer to a string that specifies the DNS or NetBIOS name
- * of the remote server on which the function is to execute. If this
- * parameter is NULL, the local computer is used.
- * Windows NT 4.0 and earlier: This string must begin with \\.
+ * of the server.  Servers should not validate this parameter.
+ * This parameter may be NULL and on Windows NT 4.0 and earlier it
+ * should begin with \\.
  *
  * UncClientName
- * [in] Pointer to a string that specifies the computer name of the
- * client to disconnect. If UncClientName is NULL, then all the sessions
- * of the user identified by the username parameter will be deleted on
- * the server specified by servername. For more information, see
- * NetSessionEnum.
+ * [in] Pointer to a string that specifies the name of the client
+ * to disconnect. If UncClientName is NULL, all sessions associated
+ * with the specified user will be disconnected.
  *
  * username
  * [in] Pointer to a string that specifies the name of the user whose
- * session is to be terminated. If this parameter is NULL, all users'
- * sessions from the client specified by the UncClientName parameter
- * are to be terminated.
+ * session is to be terminated. If username is NULL, all sessions
+ * from the specified client will be disconnected.
  *
  * Remarks
  * Windows 95/98/Me: You must specify the session key in the sReserved
@@ -1041,8 +1039,11 @@
  */
 
 /* for svX_platform */
-#define SV_PLATFORM_ID_OS2 400
-#define SV_PLATFORM_ID_NT  500
+#define	SV_PLATFORM_ID_DOS		300
+#define	SV_PLATFORM_ID_OS2		400
+#define	SV_PLATFORM_ID_NT		500
+#define	SV_PLATFORM_ID_OSF		600
+#define	SV_PLATFORM_ID_VMS		700
 
 /* Bit-mapped values for svX_type fields */
 #define SV_TYPE_WORKSTATION         0x00000001
--- a/usr/src/uts/common/smbsrv/smb_ioctl.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/smbsrv/smb_ioctl.h	Fri Jul 17 17:54:42 2009 -0700
@@ -47,8 +47,10 @@
 #define	SMB_IOC_GMTOFF		_IOW(SMB_IOC_BASE, 7, int)
 #define	SMB_IOC_SHARE		_IOW(SMB_IOC_BASE, 8, int)
 #define	SMB_IOC_UNSHARE		_IOW(SMB_IOC_BASE, 9, int)
-#define	SMB_IOC_USER_NUMBER	_IOW(SMB_IOC_BASE, 10, int)
-#define	SMB_IOC_USER_LIST	_IOW(SMB_IOC_BASE, 11, int)
+#define	SMB_IOC_NUMOPEN		_IOW(SMB_IOC_BASE, 10, int)
+#define	SMB_IOC_SVCENUM		_IOW(SMB_IOC_BASE, 11, int)
+#define	SMB_IOC_FILE_CLOSE	_IOW(SMB_IOC_BASE, 12, int)
+#define	SMB_IOC_SESSION_CLOSE	_IOW(SMB_IOC_BASE, 13, int)
 
 typedef struct smb_ioc_header {
 	uint32_t	version;
@@ -80,18 +82,55 @@
 	int		udoor;
 } smb_ioc_start_t;
 
-typedef	struct smb_ioc_usernum {
+typedef	struct smb_ioc_opennum {
 	smb_ioc_header_t hdr;
-	uint32_t	num;
-} smb_ioc_usernum_t;
+	uint32_t	open_users;
+	uint32_t	open_trees;
+	uint32_t	open_files;
+	uint32_t	qualtype;
+	char		qualifier[MAXNAMELEN];
+} smb_ioc_opennum_t;
+
+/*
+ * For enumeration, user and session are synonymous, as are
+ * connection and tree.
+ */
+#define	SMB_SVCENUM_TYPE_USER	0x55534552	/* 'USER' */
+#define	SMB_SVCENUM_TYPE_TREE	0x54524545	/* 'TREE' */
+#define	SMB_SVCENUM_TYPE_FILE	0x46494C45	/* 'FILE' */
+#define	SMB_SVCENUM_TYPE_SHARE	0x53484152	/* 'SHAR' */
 
-typedef	struct smb_ioc_ulist {
+typedef struct smb_svcenum {
+	uint32_t	se_type;	/* object type to enumerate */
+	uint32_t	se_level;	/* level of detail being requested */
+	uint32_t	se_prefmaxlen;	/* client max size buffer preference */
+	uint32_t	se_resume;	/* client resume handle */
+	uint32_t	se_bavail;	/* remaining buffer space in bytes */
+	uint32_t	se_bused;	/* consumed buffer space in bytes */
+	uint32_t	se_ntotal;	/* total number of objects */
+	uint32_t	se_nlimit;	/* max number of objects to return */
+	uint32_t	se_nitems;	/* number of objects in buf */
+	uint32_t	se_nskip;	/* number of objects to skip */
+	uint32_t	se_status;	/* enumeration status */
+	uint32_t	se_buflen;	/* length of the buffer in bytes */
+	uint8_t		se_buf[1];	/* buffer to hold enumeration data */
+} smb_svcenum_t;
+
+typedef	struct smb_ioc_svcenum {
 	smb_ioc_header_t hdr;
-	uint32_t	cookie;
-	uint32_t	num;
-	uint32_t	data_len;
-	uint8_t		data[1];
-} smb_ioc_ulist_t;
+	smb_svcenum_t	svcenum;
+} smb_ioc_svcenum_t;
+
+typedef struct smb_ioc_session {
+	smb_ioc_header_t hdr;
+	char		client[MAXNAMELEN];
+	char		username[MAXNAMELEN];
+} smb_ioc_session_t;
+
+typedef	struct smb_ioc_fileid {
+	smb_ioc_header_t hdr;
+	uint32_t	uniqid;
+} smb_ioc_fileid_t;
 
 typedef struct smb_ioc_cfg {
 	smb_ioc_header_t hdr;
@@ -117,8 +156,10 @@
 	smb_ioc_cfg_t		ioc_cfg;
 	smb_ioc_start_t		ioc_start;
 	smb_ioc_listen_t	ioc_listen;
-	smb_ioc_usernum_t	ioc_unum;
-	smb_ioc_ulist_t		ioc_ulist;
+	smb_ioc_opennum_t	ioc_opennum;
+	smb_ioc_svcenum_t	ioc_svcenum;
+	smb_ioc_session_t	ioc_session;
+	smb_ioc_fileid_t	ioc_fileid;
 	smb_ioc_share_t		ioc_share;
 } smb_ioc_t;
 
--- a/usr/src/uts/common/smbsrv/smb_kproto.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h	Fri Jul 17 17:54:42 2009 -0700
@@ -198,6 +198,7 @@
 	((op)->create_disposition != FILE_SUPERSEDE) &&		\
 	((op)->create_disposition != FILE_OVERWRITE))		\
 
+uint32_t smb_lock_get_lock_count(smb_node_t *);
 uint32_t smb_unlock_range(smb_request_t *, smb_node_t *,
     uint64_t, uint64_t);
 uint32_t smb_lock_range(smb_request_t *, uint64_t, uint64_t, uint32_t,
@@ -313,8 +314,6 @@
 void smb_opipe_door_fini(void);
 int smb_opipe_door_open(int);
 void smb_opipe_door_close(void);
-void smb_user_context_init(smb_user_t *, smb_opipe_context_t *);
-void smb_user_context_fini(smb_opipe_context_t *);
 
 /*
  * SMB server functions (file smb_server.c)
@@ -329,13 +328,14 @@
 int smb_server_tcp_listen(smb_ioc_listen_t *);
 int smb_server_nbt_receive(void);
 int smb_server_tcp_receive(void);
-uint32_t smb_server_get_user_count(void);
 uint32_t smb_server_get_session_count(void);
 int smb_server_share_export(smb_ioc_share_t *);
 int smb_server_share_unexport(smb_ioc_share_t *);
 int smb_server_set_gmtoff(smb_ioc_gmt_t *);
-int smb_server_user_number(smb_ioc_usernum_t *);
-int smb_server_user_list(smb_ioc_ulist_t *);
+int smb_server_numopen(smb_ioc_opennum_t *);
+int smb_server_enum(smb_ioc_svcenum_t *);
+int smb_server_session_close(smb_ioc_session_t *);
+int smb_server_file_close(smb_ioc_fileid_t *);
 
 void smb_server_reconnection_check(smb_server_t *, smb_session_t *);
 void smb_server_get_cfg(smb_server_t *, smb_kmod_cfg_t *);
@@ -474,6 +474,8 @@
 void smb_session_list_terminate(smb_session_list_t *, smb_session_t *);
 void smb_session_list_signal(smb_session_list_t *);
 smb_user_t *smb_session_dup_user(smb_session_t *, char *, char *);
+void smb_session_getclient(smb_session_t *, char *, size_t);
+boolean_t smb_session_isclient(smb_session_t *, const char *);
 void smb_session_correct_keep_alive_values(smb_session_list_t *, uint32_t);
 void smb_session_oplock_break(smb_session_t *, smb_ofile_t *);
 int smb_session_send(smb_session_t *, uint8_t type, mbuf_chain_t *);
@@ -489,16 +491,20 @@
  * ofile functions (file smb_ofile.c)
  */
 smb_ofile_t *smb_ofile_lookup_by_fid(smb_tree_t *, uint16_t);
+smb_ofile_t *smb_ofile_lookup_by_uniqid(smb_tree_t *, uint32_t);
+boolean_t smb_ofile_disallow_fclose(smb_ofile_t *);
 smb_ofile_t *smb_ofile_open(smb_tree_t *, smb_node_t *, uint16_t,
     open_param_t *, uint16_t, uint32_t, smb_error_t *);
 void smb_ofile_close(smb_ofile_t *, uint32_t);
 uint32_t smb_ofile_access(smb_ofile_t *, cred_t *, uint32_t);
 int smb_ofile_seek(smb_ofile_t *, ushort_t, int32_t, uint32_t *);
+boolean_t smb_ofile_hold(smb_ofile_t *);
 void smb_ofile_release(smb_ofile_t *);
 void smb_ofile_close_all(smb_tree_t *);
 void smb_ofile_close_all_by_pid(smb_tree_t *, uint16_t);
 void smb_ofile_set_flags(smb_ofile_t *, uint32_t);
 boolean_t smb_ofile_is_open(smb_ofile_t *);
+int smb_ofile_enum(smb_ofile_t *, smb_svcenum_t *);
 uint32_t smb_ofile_open_check(smb_ofile_t *, cred_t *, uint32_t, uint32_t);
 uint32_t smb_ofile_rename_check(smb_ofile_t *);
 uint32_t smb_ofile_delete_check(smb_ofile_t *);
@@ -554,12 +560,19 @@
 smb_tree_t *smb_user_lookup_share(smb_user_t *, const char *, smb_tree_t *);
 smb_tree_t *smb_user_lookup_volume(smb_user_t *, const char *, smb_tree_t *);
 boolean_t smb_user_is_admin(smb_user_t *);
+boolean_t smb_user_namecmp(smb_user_t *, const char *);
+int smb_user_enum(smb_user_t *, smb_svcenum_t *);
 void smb_user_close_pid(smb_user_t *, uint16_t);
 void smb_user_disconnect_trees(smb_user_t *user);
 void smb_user_disconnect_share(smb_user_t *, const char *);
+int smb_user_fclose(smb_user_t *, uint32_t);
+boolean_t smb_user_hold(smb_user_t *);
 void smb_user_release(smb_user_t *);
 cred_t *smb_user_getcred(smb_user_t *);
 cred_t *smb_user_getprivcred(smb_user_t *);
+void smb_user_netinfo_init(smb_user_t *, smb_netuserinfo_t *);
+void smb_user_netinfo_fini(smb_netuserinfo_t *);
+int smb_user_netinfo_encode(smb_user_t *, uint8_t *, size_t, uint32_t *);
 
 /*
  * SMB tree functions (file smb_tree.c)
@@ -568,6 +581,8 @@
 void smb_tree_disconnect(smb_tree_t *, boolean_t);
 void smb_tree_close_pid(smb_tree_t *, uint16_t);
 boolean_t smb_tree_has_feature(smb_tree_t *, uint_t);
+int smb_tree_enum(smb_tree_t *, smb_svcenum_t *);
+int smb_tree_fclose(smb_tree_t *, uint32_t);
 boolean_t smb_tree_hold(smb_tree_t *);
 void smb_tree_release(smb_tree_t *);
 smb_odir_t *smb_tree_lookup_odir(smb_tree_t *, uint16_t);
--- a/usr/src/uts/common/smbsrv/smb_ktypes.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/smbsrv/smb_ktypes.h	Fri Jul 17 17:54:42 2009 -0700
@@ -507,10 +507,9 @@
 	volatile int		flags;	/* FILE_NOTIFY_CHANGE_* */
 	volatile int		waiting_event; /* # of clients requesting FCN */
 	smb_times_t		n_timestamps; /* cached timestamps */
-	unsigned int		what;
 	smb_oplock_t		n_oplock;
-	struct smb_node		*dir_snode; /* Directory of node */
-	struct smb_node		*unnamed_stream_node; /* set in stream nodes */
+	struct smb_node		*n_dnode; /* directory node */
+	struct smb_node		*n_unode; /* unnamed stream node */
 	/* Credentials for delayed delete */
 	cred_t			*delete_on_close_cred;
 	uint32_t		n_delete_on_close_flags;
@@ -873,6 +872,8 @@
 	acl_type_t		t_acltype;
 	uint32_t		t_access;
 	uint32_t		t_shr_flags;
+	time_t			t_connect_time;
+	volatile uint32_t	t_open_files;
 } smb_tree_t;
 
 #define	SMB_TREE_VFS(tree)	((tree)->t_snode->vp->v_vfsp)
@@ -942,7 +943,7 @@
 	char *p_name;
 	uint32_t p_busy;
 	smb_opipe_hdr_t p_hdr;
-	smb_opipe_context_t p_context;
+	smb_netuserinfo_t p_user;
 	uint8_t *p_doorbuf;
 	uint8_t *p_data;
 } smb_opipe_t;
@@ -1656,7 +1657,7 @@
 	char name[MAXNAMELEN];
 } smb_trans2_setinfo_t;
 
-#define	SMB_IS_STREAM(node) ((node)->unnamed_stream_node)
+#define	SMB_IS_STREAM(node) ((node)->n_unode)
 
 typedef struct smb_tsd {
 	void (*proc)();
--- a/usr/src/uts/common/smbsrv/smb_xdr.h	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/smbsrv/smb_xdr.h	Fri Jul 17 17:54:42 2009 -0700
@@ -33,6 +33,7 @@
 #include <rpc/xdr.h>
 #include <sys/param.h>
 #include <smbsrv/smbinfo.h>
+#include <smbsrv/smb_ioctl.h>
 
 typedef struct smb_dr_kshare {
 	int32_t k_op;
@@ -46,10 +47,6 @@
 #define	xdr_int16_t	xdr_short
 #define	xdr_uint16_t	xdr_u_short
 
-extern bool_t xdr_u_char(XDR *xdrs, uchar_t *cp);
-extern bool_t xdr_vector(XDR *xdrs, char *basep, uint_t nelem,
-    uint_t elemsize, xdrproc_t xdr_elem);
-
 smb_dr_kshare_t *smb_share_mkabsolute(uint8_t *buf, uint32_t len);
 #else
 uint8_t *smb_kshare_mkselfrel(smb_dr_kshare_t *kshare, uint32_t *len);
@@ -91,25 +88,68 @@
 	uint32_t oh_status;
 } smb_opipe_hdr_t;
 
-typedef struct smb_opipe_context {
-	uint64_t oc_session_id;
-	uint16_t oc_uid;
-	uint16_t oc_domain_len;
-	char *oc_domain;
-	uint16_t oc_account_len;
-	char *oc_account;
-	uint16_t oc_workstation_len;
-	char *oc_workstation;
-	smb_inaddr_t oc_ipaddr;
-	int32_t oc_native_os;
-	int64_t oc_logon_time;
-	uint32_t oc_flags;
-} smb_opipe_context_t;
+typedef struct smb_netuserinfo {
+	uint64_t	ui_session_id;
+	uint16_t	ui_uid;
+	uint16_t	ui_domain_len;
+	char		*ui_domain;
+	uint16_t	ui_account_len;
+	char		*ui_account;
+	uint16_t	ui_workstation_len;
+	char		*ui_workstation;
+	smb_inaddr_t	ui_ipaddr;
+	int32_t		ui_native_os;
+	int64_t		ui_logon_time;
+	uint32_t	ui_numopens;
+	uint32_t	ui_flags;
+} smb_netuserinfo_t;
+
+typedef struct smb_opennum {
+	uint32_t	open_users;
+	uint32_t	open_trees;
+	uint32_t	open_files;
+	uint32_t	qualtype;
+	char		qualifier[MAXNAMELEN];
+} smb_opennum_t;
 
-typedef struct smb_ulist {
-	uint32_t ul_cnt;
-	smb_opipe_context_t *ul_users;
-} smb_ulist_t;
+typedef struct smb_netconnectinfo {
+	uint32_t	ci_id;
+	uint32_t	ci_type;
+	uint32_t	ci_numopens;
+	uint32_t	ci_numusers;
+	uint32_t	ci_time;
+	uint32_t	ci_namelen;
+	uint32_t	ci_sharelen;
+	char		*ci_username;
+	char		*ci_share;
+} smb_netconnectinfo_t;
+
+typedef struct smb_netfileinfo {
+	uint16_t	fi_fid;
+	uint32_t	fi_uniqid;
+	uint32_t	fi_permissions;
+	uint32_t	fi_numlocks;
+	uint32_t	fi_pathlen;
+	uint32_t	fi_namelen;
+	char		*fi_path;
+	char		*fi_username;
+} smb_netfileinfo_t;
+
+typedef struct smb_netsvcitem {
+	list_node_t	nsi_lnd;
+	union {
+		smb_netuserinfo_t	nsi_user;
+		smb_netconnectinfo_t	nsi_tree;
+		smb_netfileinfo_t	nsi_ofile;
+	} nsi_un;
+} smb_netsvcitem_t;
+
+typedef struct smb_netsvc {
+	list_t			ns_list;
+	smb_netsvcitem_t	*ns_items;
+	smb_ioc_svcenum_t	*ns_ioc;
+	uint32_t		ns_ioclen;
+} smb_netsvc_t;
 
 /* xdr routines for common door arguments/results */
 extern bool_t xdr_smb_dr_string_t(XDR *, smb_dr_string_t *);
@@ -120,11 +160,18 @@
 int smb_opipe_hdr_encode(smb_opipe_hdr_t *, uint8_t *, uint32_t);
 int smb_opipe_hdr_decode(smb_opipe_hdr_t *, uint8_t *, uint32_t);
 bool_t smb_opipe_hdr_xdr(XDR *xdrs, smb_opipe_hdr_t *objp);
-int smb_opipe_context_encode(smb_opipe_context_t *, uint8_t *, uint32_t,
+int smb_netuserinfo_encode(smb_netuserinfo_t *, uint8_t *, uint32_t, uint_t *);
+int smb_netuserinfo_decode(smb_netuserinfo_t *, uint8_t *, uint32_t, uint_t *);
+bool_t smb_netuserinfo_xdr(XDR *, smb_netuserinfo_t *);
+int smb_netconnectinfo_encode(smb_netconnectinfo_t *, uint8_t *, uint32_t,
     uint_t *);
-int smb_opipe_context_decode(smb_opipe_context_t *, uint8_t *, uint32_t,
+int smb_netconnectinfo_decode(smb_netconnectinfo_t *, uint8_t *, uint32_t,
     uint_t *);
-bool_t smb_opipe_context_xdr(XDR *, smb_opipe_context_t *);
+bool_t smb_netconnectinfo_xdr(XDR *, smb_netconnectinfo_t *);
+int smb_netfileinfo_encode(smb_netfileinfo_t *, uint8_t *, uint32_t, uint_t *);
+int smb_netfileinfo_decode(smb_netfileinfo_t *, uint8_t *, uint32_t, uint_t *);
+bool_t smb_netfileinfo_xdr(XDR *, smb_netfileinfo_t *);
+
 /*
  * VSS Door Structures
  */
--- a/usr/src/uts/common/sys/lvm/meta_basic.x	Fri Jul 17 16:57:52 2009 -0600
+++ b/usr/src/uts/common/sys/lvm/meta_basic.x	Fri Jul 17 17:54:42 2009 -0700
@@ -20,7 +20,7 @@
 % */
 %
 %/*
-% * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+% * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 % * Use is subject to license terms.
 % */
 %
@@ -177,42 +177,6 @@
 %		return (FALSE);
 %	return (TRUE);
 %}
-%
-#ifdef	_KERNEL
-%
-%#define	LASTUNSIGNED	((u_int)0-1)
-%
-%/*
-% * xdr_vector():
-% *
-% * XDR a fixed length array. Unlike variable-length arrays,
-% * the storage of fixed length arrays is static and unfreeable.
-% * > basep: base of the array
-% * > size: size of the array
-% * > elemsize: size of each element
-% * > xdr_elem: routine to XDR each element
-% */
-%bool_t
-%xdr_vector(xdrs, basep, nelem, elemsize, xdr_elem)
-%	XDR *xdrs;
-%	char *basep;
-%	u_int nelem;
-%	u_int elemsize;
-%	xdrproc_t xdr_elem;
-%{
-%	u_int i;
-%	char *elptr;
-%
-%	elptr = basep;
-%	for (i = 0; i < nelem; i++) {
-%		if (! (*xdr_elem)(xdrs, elptr, LASTUNSIGNED)) {
-%			return (FALSE);
-%		}
-%		elptr += elemsize;
-%	}
-%	return (TRUE);
-%}
-#endif /* _KERNEL */
 #endif	/* RPC_XDR */
 
 #ifdef RPC_HDR
@@ -351,11 +315,6 @@
 %extern	bool_t	xdr_minor_t(XDR *xdrs, minor_t *objp);
 %extern	bool_t	xdr_timeval(XDR *xdrs, struct timeval *objp);
 %extern	bool_t	xdr_clnt_stat(XDR *xdrs, enum clnt_stat *objp);
-#ifdef _KERNEL
-%extern bool_t	xdr_vector(XDR *xdrs, char *basep,
-%			u_int nelem, u_int elemsize,
-%			xdrproc_t xdr_elem);
-#endif	/* _KERNEL */
 %#else /* K&R C */
 #ifndef _KERNEL
 %extern	bool_t	xdr_uint_t();
@@ -374,9 +333,5 @@
 %extern	bool_t	xdr_minor_t();
 %extern	bool_t	xdr_timeval();
 %extern	bool_t	xdr_clnt_stat();
-%
-#ifdef _KERNEL
-%extern bool_t	xdr_vector();
-#endif	/* _KERNEL */
 %#endif /* K&R C */
 #endif	/* RPC_HDR */