summaryrefslogtreecommitdiff
path: root/usr/src/lib/smbsrv
diff options
context:
space:
mode:
authoramw <none@none>2007-10-25 16:34:29 -0700
committeramw <none@none>2007-10-25 16:34:29 -0700
commitda6c28aaf62fa55f0fdb8004aa40f88f23bf53f0 (patch)
tree65be91fb78a6a66183197595333f2e8aafb4640a /usr/src/lib/smbsrv
parente845e33dd0d1aea22db7edaa8c7d43955d24609b (diff)
downloadillumos-gate-da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0.tar.gz
PSARC/2007/218 caller_context_t in all VOPs
PSARC/2007/227 VFS Feature Registration and ACL on Create PSARC/2007/244 ZFS Case-insensitive support PSARC/2007/315 Extensible Attribute Interfaces PSARC/2007/394 ls(1) new command line options '-/' and '-%': CIFS system attributes support PSARC/2007/403 Modified Access Checks for CIFS PSARC/2007/410 Add system attribute support to chmod(1) PSARC/2007/432 CIFS system attributes support for cp(1), pack(1), unpack(1), compress(1) and uncompress(1) PSARC/2007/444 Rescind SETTABLE Attribute PSARC/2007/459 CIFS system attributes support for cpio(1), pax(1), tar(1) PSARC/2007/546 Update utilities to match CIFS system attributes changes. PSARC/2007/560 ZFS sharesmb property 4890717 want append-only files 6417428 Case-insensitive file system name lookup to support CIFS 6417435 DOS attributes and additional timestamps to support for CIFS 6417442 File system quarantined and modified attributes to support an integrated Anti-Virus service 6417453 FS boolean property for rejecting/allowing invalid UTF-8 sequences in file names 6473733 RFE: Need support for open-deny modes 6473755 RFE: Need ability to reconcile oplock and delegation conflicts 6494624 sharemgr needs to support CIFS shares better 6546705 All vnode operations need to pass caller_context_t 6546706 Need VOP_SETATTR/VOP_GETATTR to support new, optional attributes 6546893 Solaris system attribute support 6550962 ZFS ACL inheritance needs to be enhanced to support Automatic Inheritance 6553589 RFE: VFS Feature Registration facility 6553770 RFE: ZFS support for ACL-on-CREATE (PSARC 2007/227) 6565581 ls(1) should support file system attributes proposed in PSARC/2007/315 6566784 NTFS streams are not copied along with the files. 6576205 cp(1), pack(1) and compress(1) should support file system attributes proposed in PSARC/2007/315 6578875 RFE: kernel interfaces for nbmand need improvement 6578883 RFE: VOP_SHRLOCK needs additional access types 6578885 chmod(1) should support file system attributes proposed in PSARC/2007/315 6578886 RFE: disallow nbmand state to change on remount 6583349 ACL parser needs to support audit/alarm ACE types 6590347 tar(1) should support filesystem attributes proposed in PSARC/2007/315 6597357 *tar* xv@ doesn't show the hidden directory even though it is restored 6597360 *tar* should re-init xattr info if openat() fails during extraction of and extended attribute 6597368 *tar* cannot restore hard linked extended attributes 6597374 *tar* doesn't display "x " when hard linked attributes are restored 6597375 *tar* extended attribute header off by one 6614861 *cpio* incorrectly archives extended system attributes with -@ 6614896 *pax* incorrectly archives extended system attributes with -@ 6615225 *tar* incorrectly archives extended system attributes with -@ 6617183 CIFS Service - PSARC 2006/715
Diffstat (limited to 'usr/src/lib/smbsrv')
-rw-r--r--usr/src/lib/smbsrv/Makefile43
-rw-r--r--usr/src/lib/smbsrv/Makefile.lib50
-rw-r--r--usr/src/lib/smbsrv/Makefile.smbsrv65
-rw-r--r--usr/src/lib/smbsrv/Makefile.subdirs42
-rw-r--r--usr/src/lib/smbsrv/Makefile.targ42
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/Makefile.com57
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/amd64/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h41
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/llib-lmlrpc31
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers57
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/mlndo.c534
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/mlndr.c1965
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/mlrpc_client.c369
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/mlrpc_encdec.c349
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/mlrpc_heap.c236
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/mlrpc_server.c836
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/common/mlrpc_svc.c286
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/i386/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/sparc/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libmlrpc/sparcv9/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/Makefile.com86
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/amd64/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h237
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/llib-lmlsvc31
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/lmshare.c1233
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/lsalib.c637
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/lsar_lookup.c875
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/lsar_open.c298
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers97
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c306
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_dssetup.c168
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_handle.c179
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c65
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_logr.c574
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c1385
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c202
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_sam.c1463
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_spoolss.c139
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_srvsvc.c1684
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_svcctl.c485
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c752
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_winreg.c454
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_wkssvc.c142
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netdfs.c519
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c502
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c584
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/samlib.c512
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/samr_lookup.c565
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/samr_open.c684
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/secdb.c932
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c413
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/smb_share_util.c106
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/srvsvc_client.c554
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/i386/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/sparc/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/sparcv9/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libsmb/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmb/Makefile.com86
-rw-r--r--usr/src/lib/smbsrv/libsmb/amd64/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/libsmb.h778
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/llib-lsmb31
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/mapfile-vers325
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_api_door_calls.c864
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_auth.c706
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_cfg.c1090
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_crypt.c204
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_ctxbuf.c120
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_domain.c394
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_door_client.c411
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_door_encdec.c313
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c148
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_downcalls.c195
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_group_door_encdec.c337
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_group_xdr.c158
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_ht.c639
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_idmap.c382
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_info.c382
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_mac.c207
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_privilege.c361
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c529
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c1035
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_util.c336
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_wins.c210
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_wksids.c350
-rw-r--r--usr/src/lib/smbsrv/libsmb/i386/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmb/sparc/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmb/sparcv9/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libsmbns/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmbns/Makefile.com62
-rw-r--r--usr/src/lib/smbsrv/libsmbns/amd64/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/libsmbns.h184
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/llib-lsmbns31
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/mapfile-vers81
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c2304
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_ads.h87
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_browser.c1410
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_browser.h190
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c1894
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.h205
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c543
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h64
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c242
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.c300
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.h1617
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_cache.c776
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_datagram.c1061
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_name.c4738
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_netlogon.c643
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_nicconfig.c1163
-rw-r--r--usr/src/lib/smbsrv/libsmbns/i386/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmbns/sparc/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmbns/sparcv9/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/Makefile.com53
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/amd64/Makefile31
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/libsmbrdr.h97
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/llib-lsmbrdr31
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/mapfile-vers63
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr.h241
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.c382
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.h85
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_lib.c592
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c710
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netbios.c457
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netuse.c458
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_read_andx.c210
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_rpcpipe.c518
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_session.c889
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_transact.c254
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/i386/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/sparc/Makefile30
-rw-r--r--usr/src/lib/smbsrv/libsmbrdr/sparcv9/Makefile31
134 files changed, 55852 insertions, 0 deletions
diff --git a/usr/src/lib/smbsrv/Makefile b/usr/src/lib/smbsrv/Makefile
new file mode 100644
index 0000000000..a57e621ffe
--- /dev/null
+++ b/usr/src/lib/smbsrv/Makefile
@@ -0,0 +1,43 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.lib
+
+SUBDIRS = \
+ libmlsvc \
+ libmlrpc \
+ libsmb \
+ libsmbns \
+ libsmbrdr
+
+libmlrpc: libsmb
+libsmbrdr: libsmb
+libsmbns: libsmb
+libmlsvc: libsmb libmlrpc libsmbrdr libsmbns
+
+include ./Makefile.subdirs
diff --git a/usr/src/lib/smbsrv/Makefile.lib b/usr/src/lib/smbsrv/Makefile.lib
new file mode 100644
index 0000000000..abd5776bc9
--- /dev/null
+++ b/usr/src/lib/smbsrv/Makefile.lib
@@ -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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# Common Makefile definitions for smbsrv.
+#
+
+# We reset the Makefile.lib macros ROOTLIBDIR to refer to usr/lib/smbsrv
+# We also install the userland library header files under /usr/include/smbsrv
+ROOTSMBHDRDIR= $(ROOTHDRDIR)/smbsrv
+ROOTSMBHDRS= $(HDRS:%=$(ROOTSMBHDRDIR)/%)
+
+ROOTLIBDIR = $(ROOT)/usr/lib/smbsrv
+
+SRCDIR= ../common
+NDLDIR= $(ROOT)/usr/include/smbsrv/ndl
+LIBS= $(DYNLIB) $(LINTLIB)
+C99MODE = -xc99=%all
+C99LMODE = -Xc99=%all
+CPPFLAGS += -I$(SRCDIR) -I.
+DYNFLAGS += -R/usr/lib/smbsrv
+DYNFLAGS64 += -R/usr/lib/smbsrv/$(MACH64)
+LDLIBS32 += -L$(ROOT)/usr/lib/smbsrv
+LDLIBS64 += -L$(ROOT)/usr/lib/smbsrv/$(MACH64)
+$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
+
+CLEANFILES += $(OBJECTS:%_ndr.o=%_ndr.c)
diff --git a/usr/src/lib/smbsrv/Makefile.smbsrv b/usr/src/lib/smbsrv/Makefile.smbsrv
new file mode 100644
index 0000000000..16d6ddfed1
--- /dev/null
+++ b/usr/src/lib/smbsrv/Makefile.smbsrv
@@ -0,0 +1,65 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# Toplevel Makefile included by each subdirectory. Responsible for the 'check'
+# and 'install_h' targets, as well as descending into the architecture directory
+# to actually build the library.
+#
+
+include ../../Makefile.lib
+include ../Makefile.lib
+
+SUBDIRS= $(MACH)
+#$(BUILD64)SUBDIRS += $(MACH64)
+
+HDRDIR= common
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber lint: $(SUBDIRS)
+
+install: install_h $(SUBDIRS)
+
+check: $(CHECKHDRS)
+
+install_h: $(ROOTSMBHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; VERSION='$(VERSION)' $(MAKE) $(TARGET)
+
+FRC:
+
+$(ROOTSMBHDRDIR)/%: common/%
+ $(INS.file)
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/smbsrv/Makefile.subdirs b/usr/src/lib/smbsrv/Makefile.subdirs
new file mode 100644
index 0000000000..928a4e2167
--- /dev/null
+++ b/usr/src/lib/smbsrv/Makefile.subdirs
@@ -0,0 +1,42 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+all := TARGET = all
+check := TARGET = check
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+install_h := TARGET = install_h
+lint := TARGET = lint
+
+all check clean clobber install install_h lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; VERSION='$(VERSION)' $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/smbsrv/Makefile.targ b/usr/src/lib/smbsrv/Makefile.targ
new file mode 100644
index 0000000000..92a05ef243
--- /dev/null
+++ b/usr/src/lib/smbsrv/Makefile.targ
@@ -0,0 +1,42 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# Common targets for smbsrv Makefiles
+#
+
+%_ndr.c: $(NDLDIR)/%.ndl
+ $(NDRGEN) -Y $(CC) $<
+
+pics/%.o: $(SRC)/common/smbsrv/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
diff --git a/usr/src/lib/smbsrv/libmlrpc/Makefile b/usr/src/lib/smbsrv/libmlrpc/Makefile
new file mode 100644
index 0000000000..c5a61203cd
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+HDRS= libmlrpc.h
+
+include ../Makefile.smbsrv
diff --git a/usr/src/lib/smbsrv/libmlrpc/Makefile.com b/usr/src/lib/smbsrv/libmlrpc/Makefile.com
new file mode 100644
index 0000000000..86c7672546
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/Makefile.com
@@ -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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY = libmlrpc.a
+VERS = .1
+
+OBJS_COMMON = \
+ mlndo.o \
+ mlndr.o \
+ mlrpc_client.o \
+ mlrpc_encdec.o \
+ mlrpc_heap.o \
+ mlrpc_server.o \
+ mlrpc_svc.o
+
+NDLLIST = rpcpdu
+
+OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED) $(NDLLIST:%=%_ndr.o)
+
+include ../../../Makefile.lib
+include ../../Makefile.lib
+
+INCS += -I$(SRC)/common/smbsrv
+
+LDLIBS += -lsmb -lc
+
+CPPFLAGS += $(INCS) -D_REENTRANT
+
+SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
+ $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c)
+
+include ../../Makefile.targ
+include ../../../Makefile.targ
diff --git a/usr/src/lib/smbsrv/libmlrpc/amd64/Makefile b/usr/src/lib/smbsrv/libmlrpc/amd64/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h b/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h
new file mode 100644
index 0000000000..9a20054d8f
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h
@@ -0,0 +1,41 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBMLRPC_H
+#define _LIBMLRPC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBMLRPC_H */
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/llib-lmlrpc b/usr/src/lib/smbsrv/libmlrpc/common/llib-lmlrpc
new file mode 100644
index 0000000000..1621e3c2f6
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/llib-lmlrpc
@@ -0,0 +1,31 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <smbsrv/libmlrpc.h>
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers
new file mode 100644
index 0000000000..6858b4cea0
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers
@@ -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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SUNWprivate {
+ global:
+ mlndr_inner;
+ mlndr_params;
+ mlndr_topmost;
+ mlnds_destruct;
+ mlnds_initialize;
+ mlrpc_binding_pool_initialize;
+ mlrpc_c_bind;
+ mlrpc_c_call;
+ mlrpc_c_free_heap;
+ mlrpc_heap_avail;
+ mlrpc_heap_create;
+ mlrpc_heap_destroy;
+ mlrpc_heap_malloc;
+ mlrpc_heap_mkvcs;
+ mlrpc_heap_strsave;
+ mlrpc_heap_used;
+ mlrpc_register_service;
+ mlsvc_lookup_context;
+ mlsvc_rpc_process;
+ mlsvc_rpc_release;
+ ndt__char;
+ ndt_s_wchar;
+ ndt__uchar;
+ ndt__ulong;
+ ndt__ushort;
+ local:
+ *;
+};
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlndo.c b/usr/src/lib/smbsrv/libmlrpc/common/mlndo.c
new file mode 100644
index 0000000000..c469223c4d
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mlndo.c
@@ -0,0 +1,534 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * MLRPC server-side NDR stream (PDU) operations. Stream operations
+ * should return TRUE (non-zero) on success or FALSE (zero or a null
+ * pointer) on failure. When an operation returns FALSE, including
+ * mlndo_malloc() returning NULL, it should set the mlnds->error to
+ * indicate what went wrong.
+ *
+ * When available, the relevant ndr_reference is passed to the
+ * operation but keep in mind that it may be a null pointer.
+ *
+ * Functions mlndo_get_pdu(), mlndo_put_pdu(), and mlndo_pad_pdu()
+ * must never grow the PDU data. A request for out-of-bounds data is
+ * an error. The swap_bytes flag is 1 if NDR knows that the byte-
+ * order in the PDU is different from the local system.
+ */
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <assert.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/mlrpc.h>
+#include <smbsrv/ndr.h>
+#include <smbsrv/ntstatus.h>
+
+#define NDOBUFSZ 128
+
+#define NDR_PDU_BLOCK_SIZE (4*1024)
+#define NDR_PDU_BLOCK_MASK (NDR_PDU_BLOCK_SIZE - 1)
+#define NDR_PDU_ALIGN(N) \
+ (((N) + NDR_PDU_BLOCK_SIZE) & ~NDR_PDU_BLOCK_MASK)
+#define NDR_PDU_MAX_SIZE (64*1024*1024)
+
+static char *mlndo_malloc(struct mlndr_stream *, unsigned,
+ struct ndr_reference *);
+static int mlndo_free(struct mlndr_stream *, char *, struct ndr_reference *);
+static int mlndo_grow_pdu(struct mlndr_stream *, unsigned long,
+ struct ndr_reference *);
+static int mlndo_pad_pdu(struct mlndr_stream *, unsigned long, unsigned long,
+ struct ndr_reference *);
+static int mlndo_get_pdu(struct mlndr_stream *, unsigned long, unsigned long,
+ char *, int, struct ndr_reference *);
+static int mlndo_put_pdu(struct mlndr_stream *, unsigned long, unsigned long,
+ char *, int, struct ndr_reference *);
+static void mlndo_tattle(struct mlndr_stream *, char *, struct ndr_reference *);
+static void mlndo_tattle_error(struct mlndr_stream *, struct ndr_reference *);
+static int mlndo_reset(struct mlndr_stream *);
+static void mlndo_destruct(struct mlndr_stream *);
+static void mlndo_hexfmt(uint8_t *, int, int, char *, int);
+
+/*
+ * The mlndr stream operations table.
+ */
+static struct mlndr_stream_ops mlnds_ops = {
+ mlndo_malloc,
+ mlndo_free,
+ mlndo_grow_pdu,
+ mlndo_pad_pdu,
+ mlndo_get_pdu,
+ mlndo_put_pdu,
+ mlndo_tattle,
+ mlndo_tattle_error,
+ mlndo_reset,
+ mlndo_destruct
+};
+
+/*
+ * mlnds_bswap
+ *
+ * Copies len bytes from src to dst such that dst contains the bytes
+ * from src in reverse order.
+ *
+ * We expect to be dealing with bytes, words, dwords etc. So the
+ * length must be non-zero and a power of 2.
+ */
+void
+mlnds_bswap(void *srcbuf, void *dstbuf, size_t len)
+{
+ uint8_t *src = (uint8_t *)srcbuf;
+ uint8_t *dst = (uint8_t *)dstbuf;
+
+ if ((len != 0) && ((len & (len - 1)) == 0)) {
+ src += len;
+
+ while (len--)
+ *dst++ = *(--src);
+ }
+}
+
+/*
+ * mlnds_initialize
+ *
+ * Initialize a stream. Sets up the PDU parameters and assigns the stream
+ * operations and the reference to the heap. An external heap is provided
+ * to the stream, rather than each stream creating its own heap.
+ */
+int
+mlnds_initialize(struct mlndr_stream *mlnds, unsigned pdu_size_hint,
+ int composite_op, mlrpc_heap_t *heap)
+{
+ unsigned size;
+
+ assert(mlnds);
+ assert(heap);
+
+ bzero(mlnds, sizeof (*mlnds));
+
+ if (pdu_size_hint > NDR_PDU_MAX_SIZE)
+ return (0);
+
+ size = (pdu_size_hint == 0) ? NDR_PDU_BLOCK_SIZE : pdu_size_hint;
+ mlnds->pdu_base_addr = malloc(size);
+ assert(mlnds->pdu_base_addr);
+
+ mlnds->pdu_max_size = size;
+ mlnds->pdu_size = 0;
+ mlnds->pdu_base_offset = (unsigned long)mlnds->pdu_base_addr;
+
+ mlnds->mlndo = &mlnds_ops;
+ mlnds->heap = (struct mlrpc_heap *)heap;
+
+ mlnds->m_op = composite_op & 0x0F;
+ mlnds->dir = composite_op & 0xF0;
+
+ mlnds->outer_queue_tailp = &mlnds->outer_queue_head;
+ return (1);
+}
+
+/*
+ * mlnds_destruct
+ *
+ * Destroy a stream. This is an external interface to provide access to
+ * the stream's destruct operation.
+ */
+void
+mlnds_destruct(struct mlndr_stream *mlnds)
+{
+ MLNDS_DESTRUCT(mlnds);
+}
+
+/*
+ * mlndo_malloc
+ *
+ * Allocate memory from the stream heap.
+ */
+/*ARGSUSED*/
+static char *
+mlndo_malloc(struct mlndr_stream *mlnds, unsigned len,
+ struct ndr_reference *ref)
+{
+ return (mlrpc_heap_malloc((mlrpc_heap_t *)mlnds->heap, len));
+}
+
+/*
+ * mlndo_free
+ *
+ * Always succeeds: cannot free individual stream allocations.
+ */
+/*ARGSUSED*/
+static int
+mlndo_free(struct mlndr_stream *mlnds, char *p, struct ndr_reference *ref)
+{
+ return (1);
+}
+
+/*
+ * mlndo_grow_pdu
+ *
+ * This is the only place that should change the size of the PDU. If the
+ * desired offset is beyond the current PDU size, we realloc the PDU
+ * buffer to accommodate the request. For efficiency, the PDU is always
+ * extended to a NDR_PDU_BLOCK_SIZE boundary. Requests to grow the PDU
+ * beyond NDR_PDU_MAX_SIZE are rejected.
+ *
+ * Returns 1 to indicate success. Otherwise 0 to indicate failure.
+ */
+static int
+mlndo_grow_pdu(struct mlndr_stream *mlnds, unsigned long want_end_offset,
+ struct ndr_reference *ref)
+{
+ unsigned char *pdu_addr;
+ unsigned pdu_max_size;
+
+ mlndo_printf(mlnds, ref, "grow %d", want_end_offset);
+
+ pdu_max_size = mlnds->pdu_max_size;
+
+ if (want_end_offset > pdu_max_size) {
+ pdu_max_size = NDR_PDU_ALIGN(want_end_offset);
+
+ if (pdu_max_size >= NDR_PDU_MAX_SIZE)
+ return (0);
+
+ pdu_addr = realloc(mlnds->pdu_base_addr, pdu_max_size);
+ if (pdu_addr == 0)
+ return (0);
+
+ mlnds->pdu_max_size = pdu_max_size;
+ mlnds->pdu_base_addr = pdu_addr;
+ mlnds->pdu_base_offset = (unsigned long)pdu_addr;
+ }
+
+ mlnds->pdu_size = want_end_offset;
+ return (1);
+}
+
+static int
+mlndo_pad_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset,
+ unsigned long n_bytes, struct ndr_reference *ref)
+{
+ unsigned char *data;
+
+ data = (unsigned char *)mlnds->pdu_base_offset;
+ data += pdu_offset;
+
+ mlndo_printf(mlnds, ref, "pad %d@%-3d", n_bytes, pdu_offset);
+
+ bzero(data, n_bytes);
+ return (1);
+}
+
+/*
+ * mlndo_get_pdu
+ *
+ * The swap flag is 1 if NDR knows that the byte-order in the PDU
+ * is different from the local system.
+ *
+ * Returns 1 on success or 0 to indicate failure.
+ */
+static int
+mlndo_get_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset,
+ unsigned long n_bytes, char *buf, int swap_bytes,
+ struct ndr_reference *ref)
+{
+ unsigned char *data;
+ char hexbuf[NDOBUFSZ];
+
+ data = (unsigned char *)mlnds->pdu_base_offset;
+ data += pdu_offset;
+
+ mlndo_hexfmt(data, n_bytes, swap_bytes, hexbuf, NDOBUFSZ);
+
+ mlndo_printf(mlnds, ref, "get %d@%-3d = %s",
+ n_bytes, pdu_offset, hexbuf);
+
+ if (!swap_bytes)
+ bcopy(data, buf, n_bytes);
+ else
+ mlnds_bswap(data, (unsigned char *)buf, n_bytes);
+
+ return (1);
+}
+
+/*
+ * mlndo_put_pdu
+ *
+ * This is a receiver makes right protocol. So we do not need
+ * to be concerned about the byte-order of an outgoing PDU.
+ */
+/*ARGSUSED*/
+static int
+mlndo_put_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset,
+ unsigned long n_bytes, char *buf, int swap_bytes,
+ struct ndr_reference *ref)
+{
+ unsigned char *data;
+ char hexbuf[NDOBUFSZ];
+
+ data = (unsigned char *)mlnds->pdu_base_offset;
+ data += pdu_offset;
+
+ mlndo_hexfmt((uint8_t *)buf, n_bytes, 0, hexbuf, NDOBUFSZ);
+
+ mlndo_printf(mlnds, ref, "put %d@%-3d = %s",
+ n_bytes, pdu_offset, hexbuf);
+
+ bcopy(buf, data, n_bytes);
+ return (1);
+}
+
+static void
+mlndo_tattle(struct mlndr_stream *mlnds, char *what,
+ struct ndr_reference *ref)
+{
+ mlndo_printf(mlnds, ref, what);
+}
+
+static void
+mlndo_tattle_error(struct mlndr_stream *mlnds, struct ndr_reference *ref)
+{
+ unsigned char *data;
+ char hexbuf[NDOBUFSZ];
+
+ data = (unsigned char *)mlnds->pdu_base_offset;
+ if (ref)
+ data += ref->pdu_offset;
+ else
+ data += mlnds->pdu_scan_offset;
+
+ mlndo_hexfmt(data, 16, 0, hexbuf, NDOBUFSZ);
+
+ mlndo_printf(mlnds, ref, "ERROR=%d REF=%d OFFSET=%d SIZE=%d/%d",
+ mlnds->error, mlnds->error_ref, mlnds->pdu_scan_offset,
+ mlnds->pdu_size, mlnds->pdu_max_size);
+ mlndo_printf(mlnds, ref, " %s", hexbuf);
+}
+
+/*
+ * mlndo_reset
+ *
+ * Reset a stream: zap the outer_queue. We don't need to tamper
+ * with the stream heap: it's handled externally to the stream.
+ */
+static int
+mlndo_reset(struct mlndr_stream *mlnds)
+{
+ mlndo_printf(mlnds, 0, "reset");
+
+ mlnds->pdu_size = 0;
+ mlnds->pdu_scan_offset = 0;
+ mlnds->outer_queue_head = 0;
+ mlnds->outer_current = 0;
+ mlnds->outer_queue_tailp = &mlnds->outer_queue_head;
+
+ return (1);
+}
+
+/*
+ * mlndo_destruct
+ *
+ * Destruct a stream: zap the outer_queue. Currently, this operation isn't
+ * required because the heap management (creation/destruction) is external
+ * to the stream but it provides a place-holder for stream cleanup.
+ */
+static void
+mlndo_destruct(struct mlndr_stream *mlnds)
+{
+ mlndo_printf(mlnds, 0, "destruct");
+
+ if (mlnds->pdu_base_addr != 0) {
+ free(mlnds->pdu_base_addr);
+ mlnds->pdu_base_addr = 0;
+ mlnds->pdu_base_offset = 0;
+ }
+
+ mlnds->outer_queue_head = 0;
+ mlnds->outer_current = 0;
+ mlnds->outer_queue_tailp = &mlnds->outer_queue_head;
+}
+
+/*
+ * Printf style formatting for NDR operations.
+ */
+void
+mlndo_printf(struct mlndr_stream *mlnds, struct ndr_reference *ref,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char buf[NDOBUFSZ];
+
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, NDOBUFSZ, fmt, ap);
+ va_end(ap);
+
+ if (mlnds)
+ mlndo_fmt(mlnds, ref, buf);
+ else
+ mlndo_trace(buf);
+}
+
+/*
+ * Main output formatter for NDR operations.
+ *
+ * UI 03 ... rpc_vers get 1@0 = 5 {05}
+ * UI 03 ... rpc_vers_minor get 1@1 = 0 {00}
+ *
+ * U Marshalling flag (M=marshal, U=unmarshal)
+ * I Direction flag (I=in, O=out)
+ * ... Field name
+ * get PDU operation (get or put)
+ * 1@0 Bytes @ offset (i.e. 1 byte at offset 0)
+ * {05} Value
+ */
+void
+mlndo_fmt(struct mlndr_stream *mlnds, struct ndr_reference *ref, char *note)
+{
+ struct ndr_reference *p;
+ int indent;
+ char ref_name[NDOBUFSZ];
+ char buf[NDOBUFSZ];
+ int m_op_c = '?', dir_c = '?';
+
+ switch (mlnds->m_op) {
+ case 0: m_op_c = '-'; break;
+ case NDR_M_OP_MARSHALL: m_op_c = 'M'; break;
+ case NDR_M_OP_UNMARSHALL: m_op_c = 'U'; break;
+ default: m_op_c = '?'; break;
+ }
+
+ switch (mlnds->dir) {
+ case 0: dir_c = '-'; break;
+ case NDR_DIR_IN: dir_c = 'I'; break;
+ case NDR_DIR_OUT: dir_c = 'O'; break;
+ default: dir_c = '?'; break;
+ }
+
+ for (indent = 0, p = ref; p; p = p->enclosing)
+ indent++;
+
+ if (ref && ref->name) {
+ if (*ref->name == '[' && ref->enclosing) {
+ indent--;
+ (void) snprintf(ref_name, NDOBUFSZ, "%s%s",
+ ref->enclosing->name, ref->name);
+ } else {
+ (void) strlcpy(ref_name, ref->name, NDOBUFSZ);
+ }
+ } else {
+ (void) strlcpy(ref_name, "----", NDOBUFSZ);
+ }
+
+ (void) snprintf(buf, NDOBUFSZ, "%c%c %02d %-.*s %-*s %s",
+ m_op_c, dir_c, indent, indent,
+ "....+....+....+....+....+....",
+ 20 - indent, ref_name, note);
+
+ mlndo_trace(buf);
+}
+
+/*ARGSUSED*/
+void
+mlndo_trace(const char *s)
+{
+ /*
+ * Temporary fbt for dtrace until user space sdt enabled.
+ */
+}
+
+/*
+ * Format data as hex bytes (limit is 10 bytes):
+ *
+ * 1188689424 {10 f6 d9 46}
+ *
+ * If the input data is greater than 10 bytes, an ellipsis will
+ * be inserted before the closing brace.
+ */
+static void
+mlndo_hexfmt(uint8_t *data, int size, int swap_bytes, char *buf, int len)
+{
+ char *p = buf;
+ int interp = 1;
+ uint32_t c;
+ int n;
+ int i;
+
+ n = (size > 10) ? 10 : size;
+ if (n > len-1)
+ n = len-1;
+
+ switch (size) {
+ case 1:
+ c = *(uint8_t *)data;
+ break;
+ case 2:
+ if (swap_bytes == 0) /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ c = *(uint16_t *)data;
+ else
+ c = (data[0] << 8) | data[1];
+ break;
+ case 4:
+ if (swap_bytes == 0) { /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ c = *(uint32_t *)data;
+ } else {
+ c = (data[0] << 24) | (data[1] << 16)
+ | (data[2] << 8) | data[3];
+ }
+ break;
+ default:
+ c = 0;
+ interp = 0;
+ break;
+ }
+
+ if (interp)
+ p += sprintf(p, "%4u {", c);
+ else
+ p += sprintf(p, " {");
+
+ p += sprintf(p, "%02x", data[0]);
+ for (i = 1; i < n; i++)
+ p += sprintf(p, " %02x", data[i]);
+ if (size > 10)
+ p += sprintf(p, " ...}");
+ else
+ p += sprintf(p, "}");
+
+ /*
+ * Show c if it's a printable character or wide-char.
+ */
+ if (size < 4 && isprint((uint8_t)c))
+ (void) sprintf(p, " %c", (uint8_t)c);
+}
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlndr.c b/usr/src/lib/smbsrv/libmlrpc/common/mlndr.c
new file mode 100644
index 0000000000..6d13d459b3
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mlndr.c
@@ -0,0 +1,1965 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Network Data Representation (NDR) is a compatible subset of the DCE RPC
+ * and MSRPC NDR. NDR is used to move parameters consisting of
+ * complicated trees of data constructs between an RPC client and server.
+ */
+
+#include <strings.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/string.h>
+#include <smbsrv/ndr.h>
+
+#define NDR_STRING_MAX 256
+
+#define NDR_IS_UNION(T) \
+ (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_UNION)
+#define NDR_IS_STRING(T) \
+ (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_STRING)
+
+extern struct ndr_typeinfo ndt_s_wchar;
+
+/*
+ * The following synopsis describes the terms TOP-MOST, OUTER and INNER.
+ *
+ * Each parameter (call arguments and return values) is a TOP-MOST item.
+ * A TOP-MOST item consists of one or more OUTER items. An OUTER item
+ * consists of one or more INNER items. There are important differences
+ * between each kind, which, primarily, have to do with the allocation
+ * of memory to contain data structures and the order of processing.
+ *
+ * This is most easily demonstrated with a short example.
+ * Consider these structures:
+ *
+ * struct top_param {
+ * long level;
+ * struct list * head;
+ * long count;
+ * };
+ *
+ * struct list {
+ * struct list * next;
+ * char * str; // a string
+ * };
+ *
+ * Now, consider an instance tree like this:
+ *
+ * +---------+ +-------+ +-------+
+ * |top_param| +--->|list #1| +--->|list #2|
+ * +---------+ | +-------+ | +-------+
+ * | level | | | next ----+ | next --->(NULL)
+ * | head ----+ | str -->"foo" | str -->"bar"
+ * | count | | flag | | flag |
+ * +---------+ +-------+ +-------+
+ *
+ * The DCE(MS)/RPC Stub Data encoding for the tree is the following.
+ * The vertical bars (|) indicate OUTER construct boundaries.
+ *
+ * +-----+----------------------+----------------------+-----+-----+-----+
+ * |level|#1.next #1.str #1.flag|#2.next #2.str #2.flag|"bar"|"foo"|count|
+ * +-----+----------------------+----------------------+-----+-----+-----+
+ * level |<----------------------- head -------------------------->|count
+ * TOP TOP TOP
+ *
+ * Here's what to notice:
+ *
+ * - The members of the TOP-MOST construct are scattered through the Stub
+ * Data in the order they occur. This example shows a TOP-MOST construct
+ * consisting of atomic types (pointers and integers). A construct
+ * (struct) within the TOP-MOST construct would be contiguous and not
+ * scattered.
+ *
+ * - The members of OUTER constructs are contiguous, which allows for
+ * non-copied relocated (fixed-up) data structures at the packet's
+ * destination. We don't do fix-ups here. The pointers within the
+ * OUTER constructs are processed depth-first in the order that they
+ * occur. If they were processed breadth first, the sequence would
+ * be #1,"foo",#2,"bar". This is tricky because OUTER constructs may
+ * be variable length, and pointers are often encountered before the
+ * size(s) is known.
+ *
+ * - The INNER constructs are simply the members of an OUTER construct.
+ *
+ * For comparison, consider how ONC RPC would handle the same tree of
+ * data. ONC requires very little buffering, while DCE requires enough
+ * buffer space for the entire message. ONC does atom-by-atom depth-first
+ * (de)serialization and copy, while DCE allows for constructs to be
+ * "fixed-up" (relocated) in place at the destination. The packet data
+ * for the same tree processed by ONC RPC would look like this:
+ *
+ * +---------------------------------------------------------------------+
+ * |level #1.next #2.next #2.str "bar" #2.flag #1.str "foo" #1.flag count|
+ * +---------------------------------------------------------------------+
+ * TOP #1 #2 #2 bar #2 #1 foo #1 TOP
+ *
+ * More details about each TOP-MOST, OUTER, and INNER constructs appear
+ * throughout this source file near where such constructs are processed.
+ *
+ * NDR_REFERENCE
+ *
+ * The primary object for NDR is the struct ndr_reference.
+ *
+ * An ndr_reference indicates the local datum (i.e. native "C" data
+ * format), and the element within the Stub Data (contained within the
+ * RPC PDU (protocol data unit). An ndr_reference also indicates,
+ * largely as a debugging aid, something about the type of the
+ * element/datum, and the enclosing construct for the element. The
+ * ndr_reference's are typically allocated on the stack as locals,
+ * and the chain of ndr_reference.enclosing references is in reverse
+ * order of the call graph.
+ *
+ * The ndr_reference.datum is a pointer to the local memory that
+ * contains/receives the value. The ndr_reference.pdu_offset indicates
+ * where in the Stub Data the value is to be stored/retrieved.
+ *
+ * The ndr_reference also contains various parameters to the NDR
+ * process, such as ndr_reference.size_is, which indicates the size
+ * of variable length data, or ndr_reference.switch_is, which
+ * indicates the arm of a union to use.
+ *
+ * QUEUE OF OUTER REFERENCES
+ *
+ * Some OUTER constructs are variable size. Sometimes (often) we don't
+ * know the size of the OUTER construct until after pointers have been
+ * encountered. Hence, we can not begin processing the referent of the
+ * pointer until after the referring OUTER construct is completely
+ * processed, i.e. we don't know where to find/put the referent in the
+ * Stub Data until we know the size of all its predecessors.
+ *
+ * This is managed using the queue of OUTER references. The queue is
+ * anchored in mlndr_stream.outer_queue_head. At any time,
+ * mlndr_stream.outer_queue_tailp indicates where to put the
+ * ndr_reference for the next encountered pointer.
+ *
+ * Refer to the example above as we illustrate the queue here. In these
+ * illustrations, the queue entries are not the data structures themselves.
+ * Rather, they are ndr_reference entries which **refer** to the data
+ * structures in both the PDU and local memory.
+ *
+ * During some point in the processing, the queue looks like this:
+ *
+ * outer_current -------v
+ * outer_queue_head --> list#1 --0
+ * outer_queue_tailp ---------&
+ *
+ * When the pointer #1.next is encountered, and entry is added to the
+ * queue,
+ *
+ * outer_current -------v
+ * outer_queue_head --> list#1 --> list#2 --0
+ * outer_queue_tailp --------------------&
+ *
+ * and the members of #1 continue to be processed, which encounters
+ * #1.str:
+ *
+ * outer_current -------v
+ * outer_queue_head --> list#1 --> list#2 --> "foo" --0
+ * outer_queue_tailp ------------------------------&
+ *
+ * Upon the completion of list#1, the processing continues by moving
+ * to mlndr_stream.outer_current->next, and the tail is set to this
+ * outer member:
+ *
+ * outer_current ------------------v
+ * outer_queue_head --> list#1 --> list#2 --> "foo" --0
+ * outer_queue_tailp --------------------&
+ *
+ * Space for list#2 is allocated, either in the Stub Data or of local
+ * memory. When #2.next is encountered, it is found to be the null
+ * pointer and no reference is added to the queue. When #2.str is
+ * encountered, it is found to be valid, and a reference is added:
+ *
+ * outer_current ------------------v
+ * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0
+ * outer_queue_tailp ------------------------------&
+ *
+ * Processing continues in a similar fashion with the string "bar",
+ * which is variable-length. At this point, memory for "bar" may be
+ * malloc()ed during NDR_M_OP_UNMARSHALL:
+ *
+ * outer_current -----------------------------v
+ * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0
+ * outer_queue_tailp ------------------------------&
+ *
+ * And finishes on string "foo". Notice that because "bar" is a
+ * variable length string, and we don't know the PDU offset for "foo"
+ * until we reach this point.
+ *
+ * When the queue is drained (current->next==0), processing continues
+ * with the next TOP-MOST member.
+ *
+ * The queue of OUTER constructs manages the variable-length semantics
+ * of OUTER constructs and satisfies the depth-first requirement.
+ * We allow the queue to linger until the entire TOP-MOST structure is
+ * processed as an aid to debugging.
+ */
+
+static struct ndr_reference *mlndr_enter_outer_queue(struct ndr_reference *);
+extern int mlndr__ulong(struct ndr_reference *);
+
+/*
+ * TOP-MOST ELEMENTS
+ *
+ * This is fundamentally the first OUTER construct of the parameter,
+ * possibly followed by more OUTER constructs due to pointers. The
+ * datum (local memory) for TOP-MOST constructs (structs) is allocated
+ * by the caller of NDR.
+ *
+ * After the element is transferred, the outer_queue is drained.
+ *
+ * All we have to do is add an entry to the outer_queue for this
+ * top-most member, and commence the outer_queue processing.
+ */
+int
+mlndo_process(struct mlndr_stream *mlnds, struct ndr_typeinfo *ti,
+ char *datum)
+{
+ struct ndr_reference myref;
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.datum = datum;
+ myref.name = "PROCESS";
+ myref.ti = ti;
+
+ return (mlndr_topmost(&myref));
+}
+
+int
+mlndo_operation(struct mlndr_stream *mlnds, struct ndr_typeinfo *ti,
+ int opnum, char *datum)
+{
+ struct ndr_reference myref;
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.datum = datum;
+ myref.name = "OPERATION";
+ myref.ti = ti;
+ myref.inner_flags = NDR_F_SWITCH_IS;
+ myref.switch_is = opnum;
+
+ if (ti->type_flags != NDR_F_INTERFACE) {
+ NDR_SET_ERROR(&myref, NDR_ERR_NOT_AN_INTERFACE);
+ return (0);
+ }
+
+ return ((*ti->ndr_func)(&myref));
+}
+
+int
+mlndr_params(struct ndr_reference *params_ref)
+{
+ struct ndr_typeinfo *ti = params_ref->ti;
+
+ if (ti->type_flags == NDR_F_OPERATION)
+ return (*ti->ndr_func) (params_ref);
+ else
+ return (mlndr_topmost(params_ref));
+}
+
+int
+mlndr_topmost(struct ndr_reference *top_ref)
+{
+ struct mlndr_stream *mlnds;
+ struct ndr_typeinfo *ti;
+ struct ndr_reference *outer_ref = 0;
+ int is_varlen;
+ int is_string;
+ int error;
+ int rc;
+ unsigned n_fixed;
+ int params;
+
+ assert(top_ref);
+ assert(top_ref->stream);
+ assert(top_ref->ti);
+
+ mlnds = top_ref->stream;
+ ti = top_ref->ti;
+
+ is_varlen = ti->pdu_size_variable_part;
+ is_string = NDR_IS_STRING(ti);
+
+ assert(mlnds->outer_queue_tailp && !*mlnds->outer_queue_tailp);
+ assert(!mlnds->outer_current);
+
+ params = top_ref->inner_flags & NDR_F_PARAMS_MASK;
+
+ switch (params) {
+ case NDR_F_NONE:
+ case NDR_F_SWITCH_IS:
+ if (is_string || is_varlen) {
+ error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+ }
+ n_fixed = ti->pdu_size_fixed_part;
+ break;
+
+ case NDR_F_SIZE_IS:
+ error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+
+ case NDR_F_DIMENSION_IS:
+ if (is_varlen) {
+ error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+ }
+ n_fixed = ti->pdu_size_fixed_part * top_ref->dimension_is;
+ break;
+
+ case NDR_F_IS_POINTER:
+ case NDR_F_IS_POINTER+NDR_F_SIZE_IS:
+ n_fixed = 4;
+ break;
+
+ case NDR_F_IS_REFERENCE:
+ case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS:
+ n_fixed = 0;
+ break;
+
+ default:
+ error = NDR_ERR_OUTER_PARAMS_BAD;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+ }
+
+ outer_ref = mlndr_enter_outer_queue(top_ref);
+ if (!outer_ref)
+ return (0); /* error already set */
+
+ /*
+ * Hand-craft the first OUTER construct and directly call
+ * mlndr_inner(). Then, run the outer_queue. We do this
+ * because mlndr_outer() wants to malloc() memory for
+ * the construct, and we already have the memory.
+ */
+
+ /* move the flags, etc, around again, undoes enter_outer_queue() */
+ outer_ref->inner_flags = top_ref->inner_flags;
+ outer_ref->outer_flags = 0;
+ outer_ref->datum = top_ref->datum;
+
+ /* All outer constructs start on a mod4 (longword) boundary */
+ if (!mlndr_outer_align(outer_ref))
+ return (0); /* error already set */
+
+ /* Regardless of what it is, this is where it starts */
+ outer_ref->pdu_offset = mlnds->pdu_scan_offset;
+
+ rc = mlndr_outer_grow(outer_ref, n_fixed);
+ if (!rc)
+ return (0); /* error already set */
+
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_fixed;
+
+ /* set-up outer_current, as though run_outer_queue() was doing it */
+ mlnds->outer_current = outer_ref;
+ mlnds->outer_queue_tailp = &mlnds->outer_current->next;
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+
+ /* do the topmost member */
+ rc = mlndr_inner(outer_ref);
+ if (!rc)
+ return (0); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+
+ /* advance, as though run_outer_queue() was doing it */
+ mlnds->outer_current = mlnds->outer_current->next;
+ return (mlndr_run_outer_queue(mlnds));
+}
+
+static struct ndr_reference *
+mlndr_enter_outer_queue(struct ndr_reference *arg_ref)
+{
+ struct mlndr_stream *mlnds = arg_ref->stream;
+ struct ndr_reference *outer_ref;
+
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ outer_ref = (struct ndr_reference *)
+ MLNDS_MALLOC(mlnds, sizeof (*outer_ref), arg_ref);
+ if (!outer_ref) {
+ NDR_SET_ERROR(arg_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+
+ *outer_ref = *arg_ref;
+
+ /* move advice in inner_flags to outer_flags */
+ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+ outer_ref->inner_flags = 0;
+ outer_ref->enclosing = mlnds->outer_current;
+ outer_ref->backptr = 0;
+ outer_ref->datum = 0;
+
+ assert(mlnds->outer_queue_tailp);
+
+ outer_ref->next = *mlnds->outer_queue_tailp;
+ *mlnds->outer_queue_tailp = outer_ref;
+ mlnds->outer_queue_tailp = &outer_ref->next;
+ return (outer_ref);
+}
+
+int
+mlndr_run_outer_queue(struct mlndr_stream *mlnds)
+{
+ while (mlnds->outer_current) {
+ mlnds->outer_queue_tailp = &mlnds->outer_current->next;
+
+ if (!mlndr_outer(mlnds->outer_current))
+ return (0);
+
+ mlnds->outer_current = mlnds->outer_current->next;
+ }
+
+ return (1);
+}
+
+/*
+ * OUTER CONSTRUCTS
+ *
+ * OUTER constructs are where the real work is, which stems from the
+ * variable-length potential.
+ *
+ * DCE(MS)/RPC VARIABLE LENGTH -- CONFORMANT, VARYING, VARYING/CONFORMANT
+ *
+ * DCE(MS)/RPC provides for three forms of variable length: CONFORMANT,
+ * VARYING, and VARYING/CONFORMANT.
+ *
+ * What makes this so tough is that the variable-length array may be well
+ * encapsulated within the outer construct. Further, because DCE(MS)/RPC
+ * tries to keep the constructs contiguous in the data stream, the sizing
+ * information precedes the entire OUTER construct. The sizing information
+ * must be used at the appropriate time, which can be after many, many,
+ * many fixed-length elements. During IDL type analysis, we know in
+ * advance constructs that encapsulate variable-length constructs. So,
+ * we know when we have a sizing header and when we don't. The actual
+ * semantics of the header are largely deferred.
+ *
+ * Currently, VARYING constructs are not implemented but they are described
+ * here in case they have to be implemented in the future. Similarly,
+ * DCE(MS)/RPC provides for multi-dimensional arrays, which are currently
+ * not implemented. Only one-dimensional, variable-length arrays are
+ * supported.
+ *
+ * CONFORMANT CONSTRUCTS -- VARIABLE LENGTH ARRAYS START THE SHOW
+ *
+ * All variable-length values are arrays. These arrays may be embedded
+ * well within another construct. However, a variable-length construct
+ * may ONLY appear as the last member of an enclosing construct. Example:
+ *
+ * struct credentials {
+ * ulong uid, gid;
+ * ulong n_gids;
+ * [size_is(n_gids)]
+ * ulong gids[*]; // variable-length.
+ * };
+ *
+ * CONFORMANT constructs have a dynamic size in local memory and in the
+ * PDU. The CONFORMANT quality is indicated by the [size_is()] advice.
+ * CONFORMANT constructs have the following header:
+ *
+ * struct conformant_header {
+ * ulong size_is;
+ * };
+ *
+ * (Multi-dimensional CONFORMANT arrays have a similar header for each
+ * dimension - not implemented).
+ *
+ * Example CONFORMANT construct:
+ *
+ * struct user {
+ * char * name;
+ * struct credentials cred; // see above
+ * };
+ *
+ * Consider the data tree:
+ *
+ * +--------+
+ * | user |
+ * +--------+
+ * | name ----> "fred" (the string is a different OUTER)
+ * | uid |
+ * | gid |
+ * | n_gids | for example, 3
+ * | gids[0]|
+ * | gids[1]|
+ * | gids[2]|
+ * +--------+
+ *
+ * The OUTER construct in the Stub Data would be:
+ *
+ * +---+---------+---------------------------------------------+
+ * |pad|size_is=3 name uid gid n_gids=3 gids[0] gids[1] gids[2]|
+ * +---+---------+---------------------------------------------+
+ * szing hdr|user |<-------------- user.cred ------------>|
+ * |<--- fixed-size ---->|<----- conformant ---->|
+ *
+ * The ndr_typeinfo for struct user will have:
+ * pdu_fixed_size_part = 16 four long words (name uid gid n_gids)
+ * pdu_variable_size_part = 4 per element, sizeof gids[0]
+ *
+ * VARYING CONSTRUCTS -- NOT IMPLEMENTED
+ *
+ * VARYING constructs have the following header:
+ *
+ * struct varying_header {
+ * ulong first_is;
+ * ulong length_is;
+ * };
+ *
+ * This indicates which interval of an array is significant.
+ * Non-intersecting elements of the array are undefined and usually
+ * zero-filled. The first_is parameter for C arrays is always 0 for
+ * the first element.
+ *
+ * N.B. Constructs may contain one CONFORMANT element, which is always
+ * last, but may contain many VARYING elements, which can be anywhere.
+ *
+ * VARYING CONFORMANT constructs have the sizing headers arranged like
+ * this:
+ *
+ * struct conformant_header all_conformant[N_CONFORMANT_DIM];
+ * struct varying_header all_varying[N_VARYING_ELEMS_AND_DIMS];
+ *
+ * The sizing header is immediately followed by the values for the
+ * construct. Again, we don't support more than one dimension and
+ * we don't support VARYING constructs at this time.
+ *
+ * A good example of a VARYING/CONFORMANT data structure is the UNIX
+ * directory entry:
+ *
+ * struct dirent {
+ * ushort reclen;
+ * ushort namlen;
+ * ulong inum;
+ * [size_is(reclen-8) length_is(namlen+1)] // -(2+2+4), +1 for NUL
+ * uchar name[*];
+ * };
+ *
+ *
+ * STRINGS ARE A SPECIAL CASE
+ *
+ * Strings are handled specially. MS/RPC uses VARYING/CONFORMANT structures
+ * for strings. This is a simple one-dimensional variable-length array,
+ * typically with its last element all zeroes. We handle strings with the
+ * header:
+ *
+ * struct string_header {
+ * ulong size_is;
+ * ulong first_is; // always 0
+ * ulong length_is; // always same as size_is
+ * };
+ *
+ * If general support for VARYING and VARYING/CONFORMANT mechanisms is
+ * implemented, we probably won't need the strings special case.
+ */
+int
+mlndr_outer(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ int error = NDR_ERR_OUTER_PARAMS_BAD;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ NDR_TATTLE(outer_ref, "--OUTER--");
+
+ /* All outer constructs start on a mod4 (longword) boundary */
+ if (!mlndr_outer_align(outer_ref))
+ return (0); /* error already set */
+
+ /* Regardless of what it is, this is where it starts */
+ outer_ref->pdu_offset = mlnds->pdu_scan_offset;
+
+ if (is_union) {
+ error = NDR_ERR_OUTER_UNION_ILLEGAL;
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+ }
+
+ switch (params) {
+ case NDR_F_NONE:
+ if (is_string)
+ return (mlndr_outer_string(outer_ref));
+ if (is_varlen)
+ return (mlndr_outer_conformant_construct(outer_ref));
+
+ return (mlndr_outer_fixed(outer_ref));
+ break;
+
+ case NDR_F_SIZE_IS:
+ case NDR_F_DIMENSION_IS:
+ if (is_varlen) {
+ error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
+ break;
+ }
+
+ if (params == NDR_F_SIZE_IS)
+ return (mlndr_outer_conformant_array(outer_ref));
+ else
+ return (mlndr_outer_fixed_array(outer_ref));
+ break;
+
+ default:
+ error = NDR_ERR_OUTER_PARAMS_BAD;
+ break;
+ }
+
+ /*
+ * If we get here, something is wrong. Most likely,
+ * the params flags do not match.
+ */
+ NDR_SET_ERROR(outer_ref, error);
+ return (0);
+}
+
+int
+mlndr_outer_fixed(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(!is_varlen && !is_string && !is_union);
+ assert(params == NDR_F_NONE);
+
+ /* no header for this */
+ n_hdr = 0;
+
+ /* fixed part -- exactly one of these */
+ n_fixed = ti->pdu_size_fixed_part;
+ assert(n_fixed > 0);
+
+ /* variable part -- exactly none of these */
+ n_variable = 0;
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ valp = outer_ref->datum;
+ assert(valp);
+ if (outer_ref->backptr) {
+ assert(valp == *outer_ref->backptr);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "FIXED-VALUE";
+ myref.outer_flags = NDR_F_NONE;
+ myref.inner_flags = NDR_F_NONE;
+
+ myref.pdu_offset = outer_ref->pdu_offset;
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
+
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+}
+
+int
+mlndr_outer_fixed_array(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(!is_varlen && !is_string && !is_union);
+ assert(params == NDR_F_DIMENSION_IS);
+
+ /* no header for this */
+ n_hdr = 0;
+
+ /* fixed part -- exactly dimension_is of these */
+ n_fixed = ti->pdu_size_fixed_part * outer_ref->dimension_is;
+ assert(n_fixed > 0);
+
+ /* variable part -- exactly none of these */
+ n_variable = 0;
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ valp = outer_ref->datum;
+ assert(valp);
+ if (outer_ref->backptr) {
+ assert(valp == *outer_ref->backptr);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "FIXED-ARRAY";
+ myref.outer_flags = NDR_F_NONE;
+ myref.inner_flags = NDR_F_DIMENSION_IS;
+ myref.dimension_is = outer_ref->dimension_is;
+
+ myref.pdu_offset = outer_ref->pdu_offset;
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
+
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+}
+
+int
+mlndr_outer_conformant_array(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ unsigned long size_is;
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(!is_varlen && !is_string && !is_union);
+ assert(params == NDR_F_SIZE_IS);
+
+ /* conformant header for this */
+ n_hdr = 4;
+
+ /* fixed part -- exactly none of these */
+ n_fixed = 0;
+
+ /* variable part -- exactly size_of of these */
+ /* notice that it is the **fixed** size of the ti */
+ n_variable = ti->pdu_size_fixed_part * outer_ref->size_is;
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ size_is = outer_ref->size_is;
+ rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+
+ valp = outer_ref->datum;
+ assert(valp);
+ if (outer_ref->backptr) {
+ assert(valp == *outer_ref->backptr);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+
+ if (size_is != outer_ref->size_is) {
+ NDR_SET_ERROR(outer_ref,
+ NDR_ERR_SIZE_IS_MISMATCH_PDU);
+ return (0);
+ }
+
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "CONFORMANT-ARRAY";
+ myref.outer_flags = NDR_F_NONE;
+ myref.inner_flags = NDR_F_SIZE_IS;
+ myref.size_is = outer_ref->size_is;
+
+ myref.inner_flags = NDR_F_DIMENSION_IS; /* convenient */
+ myref.dimension_is = outer_ref->size_is; /* convenient */
+
+ myref.pdu_offset = outer_ref->pdu_offset + 4;
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
+
+ outer_ref->type_flags = NDR_F_NONE;
+ outer_ref->inner_flags = NDR_F_NONE;
+
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+}
+
+int
+mlndr_outer_conformant_construct(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ unsigned long size_is;
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(is_varlen && !is_string && !is_union);
+ assert(params == NDR_F_NONE);
+
+ /* conformant header for this */
+ n_hdr = 4;
+
+ /* fixed part -- exactly one of these */
+ n_fixed = ti->pdu_size_fixed_part;
+
+ /* variable part -- exactly size_of of these */
+ n_variable = 0; /* 0 for the moment */
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ /* For the moment, grow enough for the fixed-size part */
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ /*
+ * We don't know the size yet. We have to wait for
+ * it. Proceed with the fixed-size part, and await
+ * the call to mlndr_size_is().
+ */
+ size_is = 0;
+ rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+
+ valp = outer_ref->datum;
+ assert(valp);
+ if (outer_ref->backptr) {
+ assert(valp == *outer_ref->backptr);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ /*
+ * We know the size of the variable part because
+ * of the CONFORMANT header. We will verify
+ * the header against the [size_is(X)] advice
+ * later when mlndr_size_is() is called.
+ */
+ rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+
+ /* recalculate metrics */
+ n_variable = size_is * ti->pdu_size_variable_part;
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ outer_ref->size_is = size_is; /* verified later */
+
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "CONFORMANT-CONSTRUCT";
+ myref.outer_flags = NDR_F_NONE;
+ myref.inner_flags = NDR_F_NONE;
+ myref.size_is = outer_ref->size_is;
+
+ myref.pdu_offset = outer_ref->pdu_offset + 4;
+ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
+
+ outer_ref->type_flags = NDR_F_SIZE_IS; /* indicate pending */
+ outer_ref->inner_flags = NDR_F_NONE; /* indicate pending */
+
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+
+ if (outer_ref->inner_flags != NDR_F_SIZE_IS) {
+ NDR_SET_ERROR(&myref, NDR_ERR_SIZE_IS_MISMATCH_AFTER);
+ return (0);
+ }
+
+ return (1);
+}
+
+int
+mlndr_size_is(struct ndr_reference *ref)
+{
+ struct mlndr_stream *mlnds = ref->stream;
+ struct ndr_reference *outer_ref = mlnds->outer_current;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ unsigned long size_is;
+ int rc;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_pdu_total;
+
+ assert(ref->inner_flags & NDR_F_SIZE_IS);
+ size_is = ref->size_is;
+
+ if (outer_ref->type_flags != NDR_F_SIZE_IS) {
+ NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_UNEXPECTED);
+ return (0);
+ }
+
+ if (outer_ref->inner_flags & NDR_F_SIZE_IS) {
+ NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_DUPLICATED);
+ return (0);
+ }
+
+ /* repeat metrics, see mlndr_conformant_construct() above */
+ n_hdr = 4;
+ n_fixed = ti->pdu_size_fixed_part;
+ n_variable = size_is * ti->pdu_size_variable_part;
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ /*
+ * We have to set the sizing header and extend
+ * the size of the PDU (already done).
+ */
+ rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is);
+ if (!rc)
+ return (0); /* error already set */
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ /*
+ * Allocation done during mlndr_conformant_construct().
+ * All we are doing here is verifying that the
+ * intended size (ref->size_is) matches the sizing header.
+ */
+ if (size_is != outer_ref->size_is) {
+ NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_MISMATCH_PDU);
+ return (0);
+ }
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ outer_ref->inner_flags |= NDR_F_SIZE_IS;
+ outer_ref->size_is = ref->size_is;
+ return (1);
+}
+
+int
+mlndr_outer_string(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ struct ndr_typeinfo *ti = outer_ref->ti;
+ struct ndr_reference myref;
+ char *valp = NULL;
+ unsigned is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int is_string = NDR_IS_STRING(ti);
+ int rc;
+ unsigned n_zeroes;
+ unsigned ix;
+ unsigned long size_is;
+ unsigned long first_is;
+ unsigned long length_is;
+ unsigned n_hdr;
+ unsigned n_fixed;
+ unsigned n_variable;
+ unsigned n_alloc;
+ unsigned n_pdu_total;
+ int params;
+
+ params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
+
+ assert(is_varlen && is_string && !is_union);
+ assert(params == NDR_F_NONE);
+
+ /* string header for this: size_is first_is length_is */
+ n_hdr = 12;
+
+ /* fixed part -- exactly none of these */
+ n_fixed = 0;
+
+ if (!mlndr_outer_grow(outer_ref, n_hdr))
+ return (0); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ valp = outer_ref->datum;
+ assert(valp);
+
+ if (outer_ref->backptr)
+ assert(valp == *outer_ref->backptr);
+
+ if (ti == &ndt_s_wchar) {
+ /*
+ * size_is is the number of characters in the string,
+ * including the null. We assume valp is UTF-8 encoded.
+ * We can use mts_wcequiv_strlen for ASCII, extended
+ * ASCII or Unicode (UCS-2).
+ */
+ size_is = (mts_wcequiv_strlen(valp) /
+ sizeof (mts_wchar_t)) + 1;
+
+ if (size_is > NDR_STRING_MAX) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
+ return (0);
+ }
+ } else {
+ valp = outer_ref->datum;
+ n_zeroes = 0;
+ for (ix = 0; ix < 1024; ix++) {
+ if (valp[ix] == 0) {
+ n_zeroes++;
+ if (n_zeroes >= is_varlen &&
+ ix % is_varlen == 0) {
+ break;
+ }
+ } else {
+ n_zeroes = 0;
+ }
+ }
+ if (ix >= 1024) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
+ return (0);
+ }
+ size_is = ix+1;
+ }
+
+ first_is = 0;
+ length_is = size_is;
+
+ if (!mlndr_outer_poke_sizing(outer_ref, 0, &size_is) ||
+ !mlndr_outer_poke_sizing(outer_ref, 4, &first_is) ||
+ !mlndr_outer_poke_sizing(outer_ref, 8, &length_is))
+ return (0); /* error already set */
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ if (!mlndr_outer_peek_sizing(outer_ref, 0, &size_is) ||
+ !mlndr_outer_peek_sizing(outer_ref, 4, &first_is) ||
+ !mlndr_outer_peek_sizing(outer_ref, 8, &length_is))
+ return (0); /* error already set */
+
+ /*
+ * In addition to the first_is check, we used to check that
+ * size_is or size_is-1 was equal to length_is but Windows95
+ * doesn't conform to this "rule" (see variable part below).
+ * The srvmgr tool for Windows95 sent the following values
+ * for a path string:
+ *
+ * size_is = 261 (0x105)
+ * first_is = 0
+ * length_is = 53 (0x35)
+ *
+ * The length_is was correct (for the given path) but the
+ * size_is was the maximum path length rather than being
+ * related to length_is.
+ */
+ if (first_is != 0) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING);
+ return (0);
+ }
+
+ if (ti == &ndt_s_wchar) {
+ /*
+ * Decoding Unicode to UTF-8; we need to allow
+ * for the maximum possible char size. It would
+ * be nice to use mbequiv_strlen but the string
+ * may not be null terminated.
+ */
+ n_alloc = (size_is + 1) * MTS_MB_CHAR_MAX;
+ } else {
+ n_alloc = (size_is + 1) * is_varlen;
+ }
+
+ valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref);
+ if (!valp) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
+ return (0);
+ }
+
+ bzero(valp, (size_is+1) * is_varlen);
+
+ if (outer_ref->backptr)
+ *outer_ref->backptr = valp;
+ outer_ref->datum = valp;
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ /*
+ * Variable part - exactly length_is of these.
+ *
+ * Usually, length_is is same as size_is and includes nul.
+ * Some protocols use length_is = size_is-1, and length_is does
+ * not include the nul (which is more consistent with DCE spec).
+ * If the length_is is 0, there is no data following the
+ * sizing header, regardless of size_is.
+ */
+ n_variable = length_is * is_varlen;
+
+ /* sum them up to determine the PDU space required */
+ n_pdu_total = n_hdr + n_fixed + n_variable;
+
+ /* similar sum to determine how much local memory is required */
+ n_alloc = n_fixed + n_variable;
+
+ rc = mlndr_outer_grow(outer_ref, n_pdu_total);
+ if (!rc)
+ return (rc); /* error already set */
+
+ if (length_is > 0) {
+ bzero(&myref, sizeof (myref));
+ myref.stream = mlnds;
+ myref.enclosing = outer_ref;
+ myref.ti = outer_ref->ti;
+ myref.datum = outer_ref->datum;
+ myref.name = "OUTER-STRING";
+ myref.outer_flags = NDR_F_IS_STRING;
+ myref.inner_flags = NDR_F_NONE;
+
+ /*
+ * Set up strlen_is so that we know what to
+ * expect later (see mlndr_s_wchar).
+ */
+ myref.strlen_is = length_is;
+ }
+
+ myref.pdu_offset = outer_ref->pdu_offset + 12;
+
+ /*
+ * Don't try to decode empty strings.
+ */
+ if ((size_is == 0) && (first_is == 0) && (length_is == 0)) {
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+ }
+
+ if ((size_is != 0) && (length_is != 0)) {
+ rc = mlndr_inner(&myref);
+ if (!rc)
+ return (rc); /* error already set */
+ }
+
+ mlnds->pdu_scan_offset = outer_ref->pdu_end_offset;
+ return (1);
+}
+
+int
+mlndr_outer_peek_sizing(struct ndr_reference *outer_ref, unsigned offset,
+ unsigned long *sizing_p)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ unsigned long pdu_offset;
+ int rc;
+
+ pdu_offset = outer_ref->pdu_offset + offset;
+
+ if (pdu_offset < mlnds->outer_current->pdu_offset ||
+ pdu_offset > mlnds->outer_current->pdu_end_offset ||
+ pdu_offset+4 > mlnds->outer_current->pdu_end_offset) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
+ return (0);
+ }
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
+ return (0);
+
+ case NDR_M_OP_UNMARSHALL:
+ rc = MLNDS_GET_PDU(mlnds, pdu_offset, 4, (char *)sizing_p,
+ mlnds->swap, outer_ref);
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ return (rc);
+}
+
+int
+mlndr_outer_poke_sizing(struct ndr_reference *outer_ref, unsigned offset,
+ unsigned long *sizing_p)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ unsigned long pdu_offset;
+ int rc;
+
+ pdu_offset = outer_ref->pdu_offset + offset;
+
+ if (pdu_offset < mlnds->outer_current->pdu_offset ||
+ pdu_offset > mlnds->outer_current->pdu_end_offset ||
+ pdu_offset+4 > mlnds->outer_current->pdu_end_offset) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
+ return (0);
+ }
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ rc = MLNDS_PUT_PDU(mlnds, pdu_offset, 4, (char *)sizing_p,
+ mlnds->swap, outer_ref);
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
+ return (0);
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ return (rc);
+}
+
+/*
+ * All OUTER constructs begin on a mod4 (dword) boundary - except
+ * for the ones that don't: some MSRPC calls appear to use word or
+ * packed alignment. Strings appear to be dword aligned.
+ */
+int
+mlndr_outer_align(struct ndr_reference *outer_ref)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ int rc;
+ unsigned n_pad;
+ unsigned align;
+
+ if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) {
+ align = outer_ref->ti->alignment;
+ n_pad = ((align + 1) - mlnds->pdu_scan_offset) & align;
+ } else {
+ n_pad = (4 - mlnds->pdu_scan_offset) & 3;
+ }
+
+ if (n_pad == 0)
+ return (1); /* already aligned, often the case */
+
+ if (!mlndr_outer_grow(outer_ref, n_pad))
+ return (0); /* error already set */
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ rc = MLNDS_PAD_PDU(mlnds,
+ mlnds->pdu_scan_offset, n_pad, outer_ref);
+ if (!rc) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED);
+ return (0);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ break;
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ mlnds->pdu_scan_offset += n_pad;
+ return (1);
+}
+
+int
+mlndr_outer_grow(struct ndr_reference *outer_ref, unsigned n_total)
+{
+ struct mlndr_stream *mlnds = outer_ref->stream;
+ unsigned long pdu_want_size;
+ int rc, is_ok = 0;
+
+ pdu_want_size = mlnds->pdu_scan_offset + n_total;
+
+ if (pdu_want_size <= mlnds->pdu_max_size) {
+ is_ok = 1;
+ }
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ if (is_ok)
+ break;
+ rc = MLNDS_GROW_PDU(mlnds, pdu_want_size, outer_ref);
+ if (!rc) {
+ NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED);
+ return (0);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ if (is_ok)
+ break;
+ NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW);
+ return (0);
+
+ default:
+ NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ if (mlnds->pdu_size < pdu_want_size)
+ mlnds->pdu_size = pdu_want_size;
+
+ outer_ref->pdu_end_offset = pdu_want_size;
+ return (1);
+}
+
+/*
+ * INNER ELEMENTS
+ *
+ * The local datum (arg_ref->datum) already exists, there is no need to
+ * malloc() it. The datum should point at a member of a structure.
+ *
+ * For the most part, mlndr_inner() and its helpers are just a sanity
+ * check. The underlying ti->ndr_func() could be called immediately
+ * for non-pointer elements. For the sake of robustness, we detect
+ * run-time errors here. Most of the situations this protects against
+ * have already been checked by the IDL compiler. This is also a
+ * common point for processing of all data, and so is a convenient
+ * place to work from for debugging.
+ */
+int
+mlndr_inner(struct ndr_reference *arg_ref)
+{
+ struct ndr_typeinfo *ti = arg_ref->ti;
+ int is_varlen = ti->pdu_size_variable_part;
+ int is_union = NDR_IS_UNION(ti);
+ int error = NDR_ERR_INNER_PARAMS_BAD;
+ int params;
+
+ params = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+
+ switch (params) {
+ case NDR_F_NONE:
+ if (is_union) {
+ error = NDR_ERR_SWITCH_VALUE_MISSING;
+ break;
+ }
+ return (*ti->ndr_func)(arg_ref);
+ break;
+
+ case NDR_F_SIZE_IS:
+ case NDR_F_DIMENSION_IS:
+ case NDR_F_IS_POINTER+NDR_F_SIZE_IS: /* pointer to something */
+ case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */
+ if (is_varlen) {
+ error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
+ break;
+ }
+ if (is_union) {
+ error = NDR_ERR_ARRAY_UNION_ILLEGAL;
+ break;
+ }
+ if (params & NDR_F_IS_POINTER)
+ return (mlndr_inner_pointer(arg_ref));
+ else if (params & NDR_F_IS_REFERENCE)
+ return (mlndr_inner_reference(arg_ref));
+ else
+ return (mlndr_inner_array(arg_ref));
+ break;
+
+ case NDR_F_IS_POINTER: /* type is pointer to one something */
+ if (is_union) {
+ error = NDR_ERR_ARRAY_UNION_ILLEGAL;
+ break;
+ }
+ return (mlndr_inner_pointer(arg_ref));
+ break;
+
+ case NDR_F_IS_REFERENCE: /* type is pointer to one something */
+ if (is_union) {
+ error = NDR_ERR_ARRAY_UNION_ILLEGAL;
+ break;
+ }
+ return (mlndr_inner_reference(arg_ref));
+ break;
+
+ case NDR_F_SWITCH_IS:
+ if (!is_union) {
+ error = NDR_ERR_SWITCH_VALUE_ILLEGAL;
+ break;
+ }
+ return (*ti->ndr_func)(arg_ref);
+ break;
+
+ default:
+ error = NDR_ERR_INNER_PARAMS_BAD;
+ break;
+ }
+
+ /*
+ * If we get here, something is wrong. Most likely,
+ * the params flags do not match
+ */
+ NDR_SET_ERROR(arg_ref, error);
+ return (0);
+}
+
+int
+mlndr_inner_pointer(struct ndr_reference *arg_ref)
+{
+ struct mlndr_stream *mlnds = arg_ref->stream;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ char **valpp = (char **)arg_ref->datum;
+ struct ndr_reference *outer_ref;
+
+ if (!mlndr__ulong(arg_ref))
+ return (0); /* error */
+ if (!*valpp)
+ return (1); /* NULL pointer */
+
+ outer_ref = mlndr_enter_outer_queue(arg_ref);
+ if (!outer_ref)
+ return (0); /* error already set */
+
+ /* move advice in inner_flags to outer_flags sans pointer */
+ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+ outer_ref->outer_flags &= ~NDR_F_IS_POINTER;
+#ifdef NDR_INNER_NOT_YET
+ outer_ref->outer_flags |= NDR_F_BACKPTR;
+ if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
+ outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
+ }
+#endif /* NDR_INNER_NOT_YET */
+
+ outer_ref->backptr = valpp;
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ outer_ref->datum = *valpp;
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ /*
+ * This is probably wrong if the application allocated
+ * memory in advance. Indicate no value for now.
+ * ONC RPC handles this case.
+ */
+ *valpp = 0;
+ outer_ref->datum = 0;
+ break;
+ }
+
+ return (1); /* pointer dereference scheduled */
+}
+
+int
+mlndr_inner_reference(struct ndr_reference *arg_ref)
+{
+ struct mlndr_stream *mlnds = arg_ref->stream;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ char **valpp = (char **)arg_ref->datum;
+ struct ndr_reference *outer_ref;
+
+ outer_ref = mlndr_enter_outer_queue(arg_ref);
+ if (!outer_ref)
+ return (0); /* error already set */
+
+ /* move advice in inner_flags to outer_flags sans pointer */
+ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+ outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE;
+#ifdef NDR_INNER_REF_NOT_YET
+ outer_ref->outer_flags |= NDR_F_BACKPTR;
+ if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
+ outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
+ }
+#endif /* NDR_INNER_REF_NOT_YET */
+
+ outer_ref->backptr = valpp;
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ outer_ref->datum = *valpp;
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ /*
+ * This is probably wrong if the application allocated
+ * memory in advance. Indicate no value for now.
+ * ONC RPC handles this case.
+ */
+ *valpp = 0;
+ outer_ref->datum = 0;
+ break;
+ }
+
+ return (1); /* pointer dereference scheduled */
+}
+
+int
+mlndr_inner_array(struct ndr_reference *encl_ref)
+{
+ struct ndr_typeinfo *ti = encl_ref->ti;
+ struct ndr_reference myref;
+ unsigned long pdu_offset = encl_ref->pdu_offset;
+ unsigned long n_elem;
+ unsigned long i;
+ char name[30];
+
+ if (encl_ref->inner_flags & NDR_F_SIZE_IS) {
+ /* now is the time to check/set size */
+ if (!mlndr_size_is(encl_ref))
+ return (0); /* error already set */
+ n_elem = encl_ref->size_is;
+ } else {
+ assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS);
+ n_elem = encl_ref->dimension_is;
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.enclosing = encl_ref;
+ myref.stream = encl_ref->stream;
+ myref.packed_alignment = 0;
+ myref.ti = ti;
+ myref.inner_flags = NDR_F_NONE;
+
+ for (i = 0; i < n_elem; i++) {
+ (void) sprintf(name, "[%lu]", i);
+ myref.name = name;
+ myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part;
+ myref.datum = encl_ref->datum + i * ti->c_size_fixed_part;
+
+ if (!mlndr_inner(&myref))
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * BASIC TYPES
+ */
+#define MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
+ extern int mlndr_##TYPE(struct ndr_reference *encl_ref); \
+ struct ndr_typeinfo ndt_##TYPE = { \
+ 1, /* NDR version */ \
+ (SIZE)-1, /* alignment */ \
+ NDR_F_NONE, /* flags */ \
+ mlndr_##TYPE, /* ndr_func */ \
+ SIZE, /* pdu_size_fixed_part */ \
+ 0, /* pdu_size_variable_part */ \
+ SIZE, /* c_size_fixed_part */ \
+ 0, /* c_size_variable_part */ \
+ }; \
+ int mlndr_##TYPE(struct ndr_reference *ref) { \
+ return (mlndr_basic_integer(ref, SIZE)); \
+}
+
+#define MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \
+ extern int mlndr_s##TYPE(struct ndr_reference *encl_ref); \
+ struct ndr_typeinfo ndt_s##TYPE = { \
+ 1, /* NDR version */ \
+ (SIZE)-1, /* alignment */ \
+ NDR_F_STRING, /* flags */ \
+ mlndr_s##TYPE, /* ndr_func */ \
+ 0, /* pdu_size_fixed_part */ \
+ SIZE, /* pdu_size_variable_part */ \
+ 0, /* c_size_fixed_part */ \
+ SIZE, /* c_size_variable_part */ \
+ }; \
+ int mlndr_s##TYPE(struct ndr_reference *ref) { \
+ return (mlndr_string_basic_integer(ref, &ndt_##TYPE)); \
+}
+
+#define MAKE_BASIC_TYPE(TYPE, SIZE) \
+ MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
+ MAKE_BASIC_TYPE_STRING(TYPE, SIZE)
+
+extern int
+mlndr_basic_integer(struct ndr_reference *ref, unsigned size);
+
+extern int
+mlndr_string_basic_integer(struct ndr_reference *encl_ref,
+ struct ndr_typeinfo *type_under);
+
+
+MAKE_BASIC_TYPE(_char, 1)
+MAKE_BASIC_TYPE(_uchar, 1)
+MAKE_BASIC_TYPE(_short, 2)
+MAKE_BASIC_TYPE(_ushort, 2)
+MAKE_BASIC_TYPE(_long, 4)
+MAKE_BASIC_TYPE(_ulong, 4)
+
+MAKE_BASIC_TYPE_BASE(_wchar, 2)
+
+int
+mlndr_basic_integer(struct ndr_reference *ref, unsigned size)
+{
+ struct mlndr_stream *mlnds = ref->stream;
+ char *valp = (char *)ref->datum;
+ int rc;
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ rc = MLNDS_PUT_PDU(mlnds, ref->pdu_offset, size,
+ valp, mlnds->swap, ref);
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ rc = MLNDS_GET_PDU(mlnds, ref->pdu_offset, size,
+ valp, mlnds->swap, ref);
+ break;
+
+ default:
+ NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ return (rc);
+}
+
+int
+mlndr_string_basic_integer(struct ndr_reference *encl_ref,
+ struct ndr_typeinfo *type_under)
+{
+ unsigned long pdu_offset = encl_ref->pdu_offset;
+ unsigned size = type_under->pdu_size_fixed_part;
+ char *valp;
+ struct ndr_reference myref;
+ unsigned long i;
+ long sense = 0;
+ char name[30];
+
+ assert(size != 0);
+
+ bzero(&myref, sizeof (myref));
+ myref.enclosing = encl_ref;
+ myref.stream = encl_ref->stream;
+ myref.packed_alignment = 0;
+ myref.ti = type_under;
+ myref.inner_flags = NDR_F_NONE;
+ myref.name = name;
+
+ for (i = 0; i < NDR_STRING_MAX; i++) {
+ (void) sprintf(name, "[%lu]", i);
+ myref.pdu_offset = pdu_offset + i * size;
+ valp = encl_ref->datum + i * size;
+ myref.datum = valp;
+
+ if (!mlndr_inner(&myref))
+ return (0);
+
+ switch (size) {
+ case 1: sense = *valp; break;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ case 2: sense = *(short *)valp; break;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ case 4: sense = *(long *)valp; break;
+ }
+
+ if (!sense)
+ break;
+ }
+
+ return (1);
+}
+
+
+extern int mlndr_s_wchar(struct ndr_reference *encl_ref);
+struct ndr_typeinfo ndt_s_wchar = {
+ 1, /* NDR version */
+ 2-1, /* alignment */
+ NDR_F_STRING, /* flags */
+ mlndr_s_wchar, /* ndr_func */
+ 0, /* pdu_size_fixed_part */
+ 2, /* pdu_size_variable_part */
+ 0, /* c_size_fixed_part */
+ 1, /* c_size_variable_part */
+};
+
+
+/*
+ * Hand coded wchar function because all strings are transported
+ * as wide characters. During NDR_M_OP_MARSHALL, we convert from
+ * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we
+ * convert from wide characters to multi-byte.
+ *
+ * It appeared that NT would sometimes leave a spurious character
+ * in the data stream before the null wide_char, which would get
+ * included in the string decode because we processed until the
+ * null character. It now looks like NT does not always terminate
+ * RPC Unicode strings and the terminating null is a side effect
+ * of field alignment. So now we rely on the strlen_is (set up in
+ * mlndr_outer_string) of the enclosing reference. This may or may
+ * not include the null but it doesn't matter, the algorithm will
+ * get it right.
+ */
+int
+mlndr_s_wchar(struct ndr_reference *encl_ref)
+{
+ struct mlndr_stream *mlnds = encl_ref->stream;
+ unsigned short wide_char;
+ char *valp;
+ struct ndr_reference myref;
+ unsigned long i;
+ char name[30];
+ int count;
+ int char_count = 0;
+
+ if (mlnds->m_op == NDR_M_OP_UNMARSHALL) {
+ /*
+ * To avoid problems with zero length strings
+ * we can just null terminate here and be done.
+ */
+ if (encl_ref->strlen_is == 0) {
+ encl_ref->datum[0] = '\0';
+ return (1);
+ }
+ }
+
+ bzero(&myref, sizeof (myref));
+ myref.enclosing = encl_ref;
+ myref.stream = encl_ref->stream;
+ myref.packed_alignment = 0;
+ myref.ti = &ndt__wchar;
+ myref.inner_flags = NDR_F_NONE;
+ myref.datum = (char *)&wide_char;
+ myref.name = name;
+ myref.pdu_offset = encl_ref->pdu_offset;
+
+ valp = encl_ref->datum;
+ count = 0;
+
+ for (i = 0; i < NDR_STRING_MAX; i++) {
+ (void) sprintf(name, "[%lu]", i);
+
+ if (mlnds->m_op == NDR_M_OP_MARSHALL) {
+ count = mts_mbtowc((mts_wchar_t *)&wide_char, valp,
+ MTS_MB_CHAR_MAX);
+ if (count < 0) {
+ return (0);
+ } else if (count == 0) {
+ /*
+ * If the input char is 0, mts_mbtowc
+ * returns 0 without setting wide_char.
+ * I'm not sure if this is the correct
+ * behaviour for mts_mbtowc but for
+ * now we need to set wide_char to 0
+ * and assume a count of 1.
+ */
+ wide_char = *valp;
+ count = 1;
+ }
+ }
+
+ if (!mlndr_inner(&myref))
+ return (0);
+
+ if (mlnds->m_op == NDR_M_OP_UNMARSHALL) {
+ count = mts_wctomb(valp, wide_char);
+
+ if ((++char_count) == encl_ref->strlen_is) {
+ valp += count;
+ *valp = '\0';
+ break;
+ }
+ }
+
+ if (!wide_char)
+ break;
+
+ myref.pdu_offset += sizeof (wide_char);
+ valp += count;
+ }
+
+ return (1);
+}
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_client.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_client.c
new file mode 100644
index 0000000000..9bce9469ea
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_client.c
@@ -0,0 +1,369 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/errno.h>
+#include <string.h>
+#include <strings.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ndr.h>
+#include <smbsrv/mlrpc.h>
+
+#define MLRPC_IS_LAST_FRAG(F) ((F) & MLRPC_PFC_LAST_FRAG)
+#define MLRPC_DEFAULT_FRAGSZ 8192
+
+static void mlrpc_c_init_hdr(struct mlrpc_client *, struct mlrpc_xaction *);
+static int mlrpc_c_get_frags(struct mlrpc_client *, struct mlrpc_xaction *);
+static void mlrpc_c_remove_hdr(struct mlndr_stream *, int *);
+
+int
+mlrpc_c_bind(struct mlrpc_client *mcli, char *service_name,
+ struct mlrpc_binding **ret_binding_p)
+{
+ struct mlrpc_service *msvc;
+ struct mlrpc_binding *mbind;
+ struct mlrpc_xaction mxa;
+ mlrpcconn_bind_hdr_t *bhdr;
+ mlrpc_p_cont_elem_t *pce;
+ mlrpcconn_bind_ack_hdr_t *bahdr;
+ mlrpc_p_result_t *pre;
+ int rc;
+
+ bzero(&mxa, sizeof (mxa));
+
+ msvc = mlrpc_find_service_by_name(service_name);
+ if (msvc == NULL)
+ return (MLRPC_DRC_FAULT_API_SERVICE_INVALID);
+
+ mxa.binding_list = mcli->binding_list;
+ if ((mbind = mlrpc_new_binding(&mxa)) == NULL)
+ return (MLRPC_DRC_FAULT_API_BIND_NO_SLOTS);
+
+ mlrpc_c_init_hdr(mcli, &mxa);
+
+ bhdr = &mxa.send_hdr.bind_hdr;
+ bhdr->common_hdr.ptype = MLRPC_PTYPE_BIND;
+ bhdr->common_hdr.frag_length = sizeof (*bhdr);
+ bhdr->max_xmit_frag = MLRPC_DEFAULT_FRAGSZ;
+ bhdr->max_recv_frag = MLRPC_DEFAULT_FRAGSZ;
+ bhdr->assoc_group_id = 0;
+ bhdr->p_context_elem.n_context_elem = 1;
+
+ /* Assign presentation context id */
+ pce = &bhdr->p_context_elem.p_cont_elem[0];
+ pce->p_cont_id = mcli->next_p_cont_id++;
+ pce->n_transfer_syn = 1;
+
+ /* Set up UUIDs and versions from the service */
+ pce->abstract_syntax.if_version = msvc->abstract_syntax_version;
+ rc = mlrpc_str_to_uuid(msvc->abstract_syntax_uuid,
+ &pce->abstract_syntax.if_uuid);
+ if (!rc)
+ return (MLRPC_DRC_FAULT_API_SERVICE_INVALID);
+
+ pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version;
+ rc = mlrpc_str_to_uuid(msvc->transfer_syntax_uuid,
+ &pce->transfer_syntaxes[0].if_uuid);
+ if (!rc)
+ return (MLRPC_DRC_FAULT_API_SERVICE_INVALID);
+
+ /* Format and exchange the PDU */
+
+ rc = (*mcli->xa_init)(mcli, &mxa, 0);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ return (rc);
+
+ rc = mlrpc_encode_pdu_hdr(&mxa);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ rc = (*mcli->xa_exchange)(mcli, &mxa);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ rc = mlrpc_decode_pdu_hdr(&mxa);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ /* done with buffers */
+ (*mcli->xa_destruct)(mcli, &mxa);
+
+ bahdr = &mxa.recv_hdr.bind_ack_hdr;
+
+ if (mxa.ptype != MLRPC_PTYPE_BIND_ACK)
+ return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED);
+
+ if (bahdr->p_result_list.n_results != 1)
+ return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED);
+
+ pre = &bahdr->p_result_list.p_results[0];
+
+ if (pre->result != MLRPC_PCDR_ACCEPTANCE)
+ return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED);
+
+ mbind->p_cont_id = pce->p_cont_id;
+ mbind->which_side = MLRPC_BIND_SIDE_CLIENT;
+ mbind->context = mcli;
+ mbind->service = msvc;
+ mbind->instance_specific = 0;
+
+ *ret_binding_p = mbind;
+ return (MLRPC_DRC_OK);
+
+fault_exit:
+ (*mcli->xa_destruct)(mcli, &mxa);
+ return (rc);
+}
+
+int
+mlrpc_c_call(struct mlrpc_binding *mbind, int opnum, void *params,
+ mlrpc_heapref_t *heapref)
+{
+ struct mlrpc_client *mcli = mbind->context;
+ struct mlrpc_service *msvc = mbind->service;
+ struct mlrpc_xaction mxa;
+ mlrpcconn_request_hdr_t *reqhdr;
+ mlrpcconn_common_header_t *rsphdr;
+ unsigned long recv_pdu_scan_offset;
+ int rc;
+
+ if (mlrpc_find_stub_in_svc(msvc, opnum) == NULL)
+ return (MLRPC_DRC_FAULT_API_OPNUM_INVALID);
+
+ bzero(&mxa, sizeof (mxa));
+ mxa.ptype = MLRPC_PTYPE_REQUEST;
+ mxa.opnum = opnum;
+ mxa.binding = mbind;
+
+ mlrpc_c_init_hdr(mcli, &mxa);
+
+ reqhdr = &mxa.send_hdr.request_hdr;
+ reqhdr->common_hdr.ptype = MLRPC_PTYPE_REQUEST;
+ reqhdr->p_cont_id = mbind->p_cont_id;
+ reqhdr->opnum = opnum;
+
+ rc = (*mcli->xa_init)(mcli, &mxa, heapref->heap);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ return (rc);
+
+ /* Reserve room for hdr */
+ mxa.send_mlnds.pdu_scan_offset = sizeof (*reqhdr);
+
+ rc = mlrpc_encode_call(&mxa, params);
+ if (!MLRPC_DRC_IS_OK(rc))
+ goto fault_exit;
+
+ mxa.send_mlnds.pdu_scan_offset = 0;
+
+ /*
+ * Now we have the PDU size, we need to set up the
+ * frag_length and calculate the alloc_hint.
+ */
+ mxa.send_hdr.common_hdr.frag_length = mxa.send_mlnds.pdu_size;
+ reqhdr->alloc_hint = mxa.send_mlnds.pdu_size -
+ sizeof (mlrpcconn_request_hdr_t);
+
+ rc = mlrpc_encode_pdu_hdr(&mxa);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ rc = (*mcli->xa_exchange)(mcli, &mxa);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ rc = mlrpc_decode_pdu_hdr(&mxa);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ if (mxa.ptype != MLRPC_PTYPE_RESPONSE) {
+ rc = MLRPC_DRC_FAULT_RECEIVED_MALFORMED;
+ goto fault_exit;
+ }
+
+ rsphdr = &mxa.recv_hdr.common_hdr;
+
+ if (!MLRPC_IS_LAST_FRAG(rsphdr->pfc_flags)) {
+ /*
+ * This is a multi-fragment response.
+ * Preserve the current scan offset while getting
+ * fragments so that we can continue afterward
+ * as if we had received the entire response as
+ * a single PDU.
+ */
+ recv_pdu_scan_offset = mxa.recv_mlnds.pdu_scan_offset;
+
+ if (mlrpc_c_get_frags(mcli, &mxa) < 0) {
+ rc = MLRPC_DRC_FAULT_RECEIVED_MALFORMED;
+ goto fault_exit;
+ }
+
+ mxa.recv_mlnds.pdu_scan_offset = recv_pdu_scan_offset;
+ }
+
+ rc = mlrpc_decode_return(&mxa, params);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ rc = (*mcli->xa_preserve)(mcli, &mxa, heapref);
+ if (MLRPC_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ (*mcli->xa_destruct)(mcli, &mxa);
+ return (MLRPC_DRC_OK);
+
+fault_exit:
+ (*mcli->xa_destruct)(mcli, &mxa);
+ return (rc);
+}
+
+int
+mlrpc_c_free_heap(struct mlrpc_binding *mbind, mlrpc_heapref_t *heapref)
+{
+ struct mlrpc_client *mcli = mbind->context;
+
+ (*mcli->xa_release)(mcli, heapref);
+ return (0);
+}
+
+static void
+mlrpc_c_init_hdr(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+
+ hdr->rpc_vers = 5;
+ hdr->rpc_vers_minor = 0;
+ hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG;
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII;
+#ifndef _BIG_ENDIAN
+ hdr->packed_drep.intg_char_rep |= MLRPC_REPLAB_INTG_LITTLE_ENDIAN;
+#endif
+ /* hdr->frag_length */
+ hdr->auth_length = 0;
+ hdr->call_id = mcli->next_call_id++;
+}
+
+/*
+ * mlrpc_c_remove_hdr
+ *
+ * Remove an RPC fragment header from the received data stream.
+ *
+ * Original RPC receive buffer:
+ * |- frag1 -| |-frag M(partial)-|
+ * +==================+=============+----+=================+
+ * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN |
+ * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) |
+ * +-----+------------+-------------+ +-----+-----------+
+ * | hdr | data | data | .. | hdr | data |
+ * +=====+============+=============+----+=====+===========+
+ * <------
+ * ^ ^ ^
+ * | | |
+ * base_offset hdr data
+ *
+ * |-------------------------------------|-----------------|
+ * offset len
+ *
+ * RPC receive buffer (after this call):
+ * +==================+=============+----+===========+
+ * | SmbTransact Rsp1 | SmbTransact | | SmbReadX |
+ * | (with RPC hdr) | Rsp2 | .. | RspN |
+ * +-----+------------+-------------+ +-----------+
+ * | hdr | data | data | .. | data |
+ * +=====+============+=============+----+===========+
+ */
+static void
+mlrpc_c_remove_hdr(struct mlndr_stream *mlnds, int *nbytes)
+{
+ char *hdr;
+ char *data;
+
+ hdr = (char *)mlnds->pdu_base_offset + mlnds->pdu_scan_offset;
+ data = hdr + MLRPC_RSP_HDR_SIZE;
+ *nbytes -= MLRPC_RSP_HDR_SIZE;
+
+ bcopy(data, hdr, *nbytes);
+ mlnds->pdu_size -= MLRPC_RSP_HDR_SIZE;
+}
+
+/*
+ * mlrpc_c_get_frags
+ *
+ * A DCE RPC message that is larger than a single fragment is transmitted
+ * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for
+ * both Windows 2000 and 2003.
+ *
+ * Collect RPC fragments and append them to the receive stream buffer.
+ * Each received fragment has a header, which we need to remove as we
+ * build the full RPC PDU.
+ *
+ * The xa_read() calls will translate to SmbReadX requests. Note that
+ * there is no correspondence between SmbReadX buffering and DCE RPC
+ * fragment alignment.
+ *
+ * Return -1 on error. Otherwise, return the total data count of the
+ * complete RPC response upon success.
+ */
+static int
+mlrpc_c_get_frags(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa)
+{
+ struct mlndr_stream *mlnds = &mxa->recv_mlnds;
+ mlrpcconn_common_header_t hdr;
+ int frag_rcvd;
+ int frag_size;
+ int last_frag;
+ int nbytes;
+
+ /*
+ * The scan offest will be used to locate the frag header.
+ */
+ mlnds->pdu_scan_offset = mlnds->pdu_base_offset + mlnds->pdu_size;
+
+ do {
+ frag_rcvd = 0;
+
+ do {
+ if ((nbytes = (*mcli->xa_read)(mcli, mxa)) < 0)
+ return (-1);
+
+ if (frag_rcvd == 0) {
+ mlrpc_decode_frag_hdr(mlnds, &hdr);
+
+ last_frag = MLRPC_IS_LAST_FRAG(hdr.pfc_flags);
+ frag_size = hdr.frag_length
+ - MLRPC_RSP_HDR_SIZE;
+
+ mlrpc_c_remove_hdr(mlnds, &nbytes);
+ mlnds->pdu_scan_offset += frag_size;
+ }
+
+ frag_rcvd += nbytes;
+
+ } while (frag_rcvd < frag_size);
+ } while (!last_frag);
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_encdec.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_encdec.c
new file mode 100644
index 0000000000..884683dfe4
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_encdec.c
@@ -0,0 +1,349 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <strings.h>
+#include <sys/param.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ndr.h>
+#include <smbsrv/mlrpc.h>
+
+#ifdef _BIG_ENDIAN
+static const int mlrpc_native_byte_order = MLRPC_REPLAB_INTG_BIG_ENDIAN;
+#else
+static const int mlrpc_native_byte_order = MLRPC_REPLAB_INTG_LITTLE_ENDIAN;
+#endif
+
+int
+mlrpc_encode_decode_common(struct mlrpc_xaction *mxa, int mode, unsigned opnum,
+ struct ndr_typeinfo *ti, void *datum)
+{
+ struct mlndr_stream *mlnds;
+ int m_op = NDR_MODE_TO_M_OP(mode);
+ int rc;
+
+ if (m_op == NDR_M_OP_MARSHALL)
+ mlnds = &mxa->send_mlnds;
+ else
+ mlnds = &mxa->recv_mlnds;
+
+ /*
+ * Make sure that mlnds is in the correct mode
+ */
+ if (!NDR_MODE_MATCH(mlnds, mode))
+ return (MLRPC_DRC_FAULT_MODE_MISMATCH);
+
+ /*
+ * Perform the (un)marshalling
+ */
+ if (mlndo_operation(mlnds, ti, opnum, datum))
+ return (MLRPC_DRC_OK);
+
+ switch (mlnds->error) {
+ case NDR_ERR_MALLOC_FAILED:
+ rc = MLRPC_DRC_FAULT_OUT_OF_MEMORY;
+ break;
+
+ case NDR_ERR_SWITCH_VALUE_INVALID:
+ rc = MLRPC_DRC_FAULT_PARAM_0_INVALID;
+ break;
+
+ case NDR_ERR_UNDERFLOW:
+ rc = MLRPC_DRC_FAULT_RECEIVED_RUNT;
+ break;
+
+ case NDR_ERR_GROW_FAILED:
+ rc = MLRPC_DRC_FAULT_ENCODE_TOO_BIG;
+ break;
+
+ default:
+ if (m_op == NDR_M_OP_MARSHALL)
+ rc = MLRPC_DRC_FAULT_ENCODE_FAILED;
+ else
+ rc = MLRPC_DRC_FAULT_DECODE_FAILED;
+ break;
+ }
+
+ return (rc);
+}
+
+int
+mlrpc_decode_call(struct mlrpc_xaction *mxa, void *params)
+{
+ int rc;
+
+ rc = mlrpc_encode_decode_common(mxa, NDR_MODE_CALL_RECV,
+ mxa->opnum, mxa->binding->service->interface_ti, params);
+
+ return (rc + MLRPC_PTYPE_REQUEST);
+}
+
+int
+mlrpc_encode_return(struct mlrpc_xaction *mxa, void *params)
+{
+ int rc;
+
+ rc = mlrpc_encode_decode_common(mxa, NDR_MODE_RETURN_SEND,
+ mxa->opnum, mxa->binding->service->interface_ti, params);
+
+ return (rc + MLRPC_PTYPE_RESPONSE);
+}
+
+int
+mlrpc_encode_call(struct mlrpc_xaction *mxa, void *params)
+{
+ int rc;
+
+ rc = mlrpc_encode_decode_common(mxa, NDR_MODE_CALL_SEND,
+ mxa->opnum, mxa->binding->service->interface_ti, params);
+
+ return (rc + MLRPC_PTYPE_REQUEST);
+}
+
+int
+mlrpc_decode_return(struct mlrpc_xaction *mxa, void *params)
+{
+ int rc;
+
+ rc = mlrpc_encode_decode_common(mxa, NDR_MODE_RETURN_RECV,
+ mxa->opnum, mxa->binding->service->interface_ti, params);
+
+ return (rc + MLRPC_PTYPE_RESPONSE);
+}
+
+int
+mlrpc_decode_pdu_hdr(struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *hdr = &mxa->recv_hdr.common_hdr;
+ struct mlndr_stream *mlnds = &mxa->recv_mlnds;
+ int ptype;
+ int rc;
+ int charset;
+ int byte_order;
+
+ if (mlnds->m_op != NDR_M_OP_UNMARSHALL)
+ return (MLRPC_DRC_FAULT_MODE_MISMATCH + 0xFF);
+
+ /*
+ * All PDU headers are at least this big
+ */
+ rc = MLNDS_GROW_PDU(mlnds, sizeof (mlrpcconn_common_header_t), 0);
+ if (!rc)
+ return (MLRPC_DRC_FAULT_RECEIVED_RUNT + 0xFF);
+
+ /*
+ * Peek at the first eight bytes to figure out what we're doing.
+ */
+ rc = MLNDS_GET_PDU(mlnds, 0, 8, (char *)hdr, 0, 0);
+ if (!rc)
+ return (MLRPC_DRC_FAULT_DECODE_FAILED + 0xFF);
+
+ /*
+ * Verify the protocol version.
+ */
+ if ((hdr->rpc_vers != 5) || (hdr->rpc_vers_minor != 0))
+ return (MLRPC_DRC_FAULT_DECODE_FAILED + 0xFF);
+
+ /*
+ * Check for ASCII as the character set. This is an ASCII
+ * versus EBCDIC option and has nothing to do with Unicode.
+ */
+ charset = hdr->packed_drep.intg_char_rep & MLRPC_REPLAB_CHAR_MASK;
+ if (charset != MLRPC_REPLAB_CHAR_ASCII)
+ return (MLRPC_DRC_FAULT_DECODE_FAILED + 0xFF);
+
+ /*
+ * Set the byte swap flag if the PDU byte-order
+ * is different from the local byte-order.
+ */
+ byte_order = hdr->packed_drep.intg_char_rep & MLRPC_REPLAB_INTG_MASK;
+ mlnds->swap = (byte_order != mlrpc_native_byte_order) ? 1 : 0;
+
+ ptype = hdr->ptype;
+ if (ptype == MLRPC_PTYPE_REQUEST &&
+ (hdr->pfc_flags & MLRPC_PFC_OBJECT_UUID) != 0) {
+ ptype = MLRPC_PTYPE_REQUEST_WITH; /* fake for sizing */
+ }
+
+ mxa->ptype = hdr->ptype;
+
+ rc = mlrpc_encode_decode_common(mxa,
+ NDR_M_OP_AND_DIR_TO_MODE(mlnds->m_op, mlnds->dir),
+ ptype, &TYPEINFO(mlrpcconn_hdr), hdr);
+
+ return (rc + 0xFF);
+}
+
+/*
+ * Decode an RPC fragment header. Use mlrpc_decode_pdu_hdr() to process
+ * the first fragment header then this function to process additional
+ * fragment headers.
+ */
+void
+mlrpc_decode_frag_hdr(struct mlndr_stream *mlnds,
+ mlrpcconn_common_header_t *hdr)
+{
+ mlrpcconn_common_header_t *tmp;
+ uint8_t *pdu;
+ int byte_order;
+
+ pdu = (uint8_t *)mlnds->pdu_base_offset + mlnds->pdu_scan_offset;
+ bcopy(pdu, hdr, MLRPC_RSP_HDR_SIZE);
+
+ /*
+ * Swap non-byte fields if the PDU byte-order
+ * is different from the local byte-order.
+ */
+ byte_order = hdr->packed_drep.intg_char_rep & MLRPC_REPLAB_INTG_MASK;
+
+ if (byte_order != mlrpc_native_byte_order) {
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ tmp = (mlrpcconn_common_header_t *)pdu;
+
+ mlnds_bswap(&tmp->frag_length, &hdr->frag_length,
+ sizeof (WORD));
+ mlnds_bswap(&tmp->auth_length, &hdr->auth_length,
+ sizeof (WORD));
+ mlnds_bswap(&tmp->call_id, &hdr->call_id, sizeof (DWORD));
+ }
+}
+
+int
+mlrpc_encode_pdu_hdr(struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+ struct mlndr_stream *mlnds = &mxa->send_mlnds;
+ int ptype;
+ int rc;
+
+ if (mlnds->m_op != NDR_M_OP_MARSHALL)
+ return (MLRPC_DRC_FAULT_MODE_MISMATCH + 0xFF);
+
+ ptype = hdr->ptype;
+ if (ptype == MLRPC_PTYPE_REQUEST &&
+ (hdr->pfc_flags & MLRPC_PFC_OBJECT_UUID) != 0) {
+ ptype = MLRPC_PTYPE_REQUEST_WITH; /* fake for sizing */
+ }
+
+ rc = mlrpc_encode_decode_common(mxa,
+ NDR_M_OP_AND_DIR_TO_MODE(mlnds->m_op, mlnds->dir),
+ ptype, &TYPEINFO(mlrpcconn_hdr), hdr);
+
+ return (rc + 0xFF);
+}
+
+/*
+ * This is a hand-coded derivative of the automatically generated
+ * (un)marshalling routine for bind_ack headers. bind_ack headers
+ * have an interior conformant array, which is inconsistent with
+ * IDL/NDR rules.
+ */
+extern struct ndr_typeinfo ndt__uchar;
+extern struct ndr_typeinfo ndt__ushort;
+extern struct ndr_typeinfo ndt__ulong;
+
+int mlndr__mlrpcconn_bind_ack_hdr(struct ndr_reference *encl_ref);
+struct ndr_typeinfo ndt__mlrpcconn_bind_ack_hdr = {
+ 1, /* NDR version */
+ 3, /* alignment */
+ NDR_F_STRUCT, /* flags */
+ mlndr__mlrpcconn_bind_ack_hdr, /* ndr_func */
+ 68, /* pdu_size_fixed_part */
+ 0, /* pdu_size_variable_part */
+ 68, /* c_size_fixed_part */
+ 0, /* c_size_variable_part */
+};
+
+/*
+ * [_no_reorder]
+ */
+int
+mlndr__mlrpcconn_bind_ack_hdr(struct ndr_reference *encl_ref)
+{
+ struct mlndr_stream *mlnds = encl_ref->stream;
+ struct mlrpcconn_bind_ack_hdr *val = /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ (struct mlrpcconn_bind_ack_hdr *)encl_ref->datum;
+ struct ndr_reference myref;
+ unsigned long offset;
+
+ bzero(&myref, sizeof (myref));
+ myref.enclosing = encl_ref;
+ myref.stream = encl_ref->stream;
+ myref.packed_alignment = 0;
+
+ /* do all members in order */
+ NDR_MEMBER(_mlrpcconn_common_header, common_hdr, 0UL);
+ NDR_MEMBER(_ushort, max_xmit_frag, 16UL);
+ NDR_MEMBER(_ushort, max_recv_frag, 18UL);
+ NDR_MEMBER(_ulong, assoc_group_id, 20UL);
+
+ /* port any is the conformant culprit */
+ offset = 24UL;
+
+ switch (mlnds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ val->sec_addr.length =
+ strlen((char *)val->sec_addr.port_spec) + 1;
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ break;
+
+ default:
+ NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ NDR_MEMBER(_ushort, sec_addr.length, offset);
+ NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec,
+ offset+2UL, val->sec_addr.length);
+
+ offset += 2;
+ offset += val->sec_addr.length;
+ offset += (4 - offset) & 3;
+
+ NDR_MEMBER(_mlrpc_p_result_list, p_result_list, offset);
+ return (1);
+}
+
+unsigned
+mlrpc_bind_ack_hdr_size(struct mlrpcconn_bind_ack_hdr *bahdr)
+{
+ unsigned offset;
+ unsigned length;
+
+ /* port any is the conformant culprit */
+ offset = 24UL;
+
+ length = strlen((char *)bahdr->sec_addr.port_spec) + 1;
+
+ offset += 2;
+ offset += length;
+ offset += (4 - offset) & 3;
+ offset += sizeof (bahdr->p_result_list);
+ return (offset);
+}
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_heap.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_heap.c
new file mode 100644
index 0000000000..97a16ea010
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_heap.c
@@ -0,0 +1,236 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * MLRPC heap management. The heap is used for temporary storage by
+ * both the client and server side library routines. In order to
+ * support the different requirements of the various RPCs, the heap
+ * can grow dynamically if required. We start with a single block
+ * and perform sub-allocations from it. If an RPC requires more space
+ * we will continue to add it a block at a time. This means that we
+ * don't hog lots of memory on every call to support the few times
+ * that we actually need a lot heap space.
+ *
+ * Note that there is no individual free function. Once space has been
+ * allocated, it remains allocated until the heap is destroyed. This
+ * shouldn't be an issue because the heap is being filled with data to
+ * be marshalled or unmarshalled and we need it all to be there until
+ * the point that the entire heap is no longer required.
+ */
+
+#include <sys/errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/uio.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/mlrpc.h>
+
+/*
+ * Allocate a heap structure and the first heap block. For many RPC
+ * operations this will be the only time we need to malloc memory
+ * in this instance of the heap. The only point of note here is that
+ * we put the heap management data in the first block to avoid a
+ * second malloc. Make sure that sizeof(mlrpc_heap_t) is smaller
+ * than MLRPC_HEAP_BLKSZ.
+ *
+ * Note that the heap management data is at the start of the first block.
+ *
+ * Returns a pointer to the newly created heap, which is used like an
+ * opaque handle with the rest of the heap management interface..
+ */
+mlrpc_heap_t *
+mlrpc_heap_create(void)
+{
+ mlrpc_heap_t *heap;
+ char *base;
+
+ if ((base = (char *)malloc(MLRPC_HEAP_BLKSZ)) == NULL)
+ return (NULL);
+
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ heap = (mlrpc_heap_t *)base;
+ bzero(heap, sizeof (mlrpc_heap_t));
+
+ heap->iovcnt = MLRPC_HEAP_MAXIOV;
+ heap->iov = heap->iovec;
+ heap->iov->iov_base = base;
+ heap->iov->iov_len = sizeof (mlrpc_heap_t);
+ heap->top = base + MLRPC_HEAP_BLKSZ;
+ heap->next = base + sizeof (mlrpc_heap_t);
+
+ return (heap);
+}
+
+/*
+ * Deallocate all of the memory associated with a heap. This is the
+ * only way to deallocate heap memory, it isn't possible to free the
+ * space obtained by individual malloc calls.
+ *
+ * Note that the first block contains the heap management data, which
+ * is deleted last.
+ */
+void
+mlrpc_heap_destroy(mlrpc_heap_t *heap)
+{
+ int i;
+ char *p;
+
+ if (heap) {
+ for (i = 1; i < MLRPC_HEAP_MAXIOV; ++i) {
+ if ((p = heap->iovec[i].iov_base) != NULL)
+ free(p);
+ }
+
+ free(heap);
+ }
+}
+
+/*
+ * Allocate space in the specified heap. All requests are padded, if
+ * required, to ensure dword alignment. If the current iov will be
+ * exceeded, we allocate a new block and setup the next iov. Otherwise
+ * all we have to do is move the next pointer and update the current
+ * iov length.
+ *
+ * On success, a pointer to the allocated (dword aligned) area is
+ * returned. Otherwise a null pointer is returned.
+ */
+void *
+mlrpc_heap_malloc(mlrpc_heap_t *heap, unsigned size)
+{
+ char *p;
+ int align;
+ int incr_size;
+
+ align = (4 - size) & 3;
+ size += align;
+
+ if (heap == NULL || size == 0)
+ return (NULL);
+
+ p = heap->next;
+
+ if (p + size > heap->top) {
+ if ((heap->iovcnt == 0) || ((--heap->iovcnt) == 0))
+ return (NULL);
+
+ incr_size = (size < MLRPC_HEAP_BLKSZ) ? MLRPC_HEAP_BLKSZ : size;
+
+ if ((p = (char *)malloc(incr_size)) == NULL)
+ return (NULL);
+
+ ++heap->iov;
+ heap->iov->iov_base = p;
+ heap->iov->iov_len = 0;
+ heap->top = p + incr_size;
+ }
+
+ heap->next = p + size;
+ heap->iov->iov_len += size;
+ return ((void *)p);
+}
+
+/*
+ * Convenience function to do heap strdup.
+ */
+void *
+mlrpc_heap_strsave(mlrpc_heap_t *heap, char *s)
+{
+ int len;
+ void *p;
+
+ if (s == NULL)
+ return (NULL);
+
+ /*
+ * We don't need to clutter the heap with empty strings.
+ */
+ if ((len = strlen(s)) == 0)
+ return ("");
+
+ if ((p = mlrpc_heap_malloc(heap, len+1)) != NULL)
+ (void) strcpy((char *)p, s);
+
+ return (p);
+}
+
+/*
+ * Our regular string marshalling always creates null terminated strings
+ * but some Windows clients and servers are pedantic about the string
+ * formats they will accept and require non-null terminated strings.
+ * This function can be used to build a wide-char, non-null terminated
+ * string in the heap as a varying/conformant array. We need to do the
+ * wide-char conversion here because the marshalling code won't be
+ * aware that this is really a string.
+ */
+void
+mlrpc_heap_mkvcs(mlrpc_heap_t *heap, char *s, mlrpc_vcbuf_t *vcs)
+{
+ int mlen;
+
+ vcs->wclen = mts_wcequiv_strlen(s);
+ vcs->wcsize = vcs->wclen;
+
+ mlen = sizeof (struct mlrpc_vcb) + vcs->wcsize + sizeof (mts_wchar_t);
+
+ vcs->vcb = (struct mlrpc_vcb *)mlrpc_heap_malloc(heap, mlen);
+
+ if (vcs->vcb) {
+ vcs->vcb->vc_first_is = 0;
+ vcs->vcb->vc_length_is = vcs->wclen / sizeof (mts_wchar_t);
+ (void) mts_mbstowcs((mts_wchar_t *)vcs->vcb->buffer, s,
+ vcs->vcb->vc_length_is);
+ }
+}
+
+int
+mlrpc_heap_used(mlrpc_heap_t *heap)
+{
+ int used = 0;
+ int i;
+
+ for (i = 0; i < MLRPC_HEAP_MAXIOV; ++i)
+ used += heap->iovec[i].iov_len;
+
+ return (used);
+}
+
+int
+mlrpc_heap_avail(mlrpc_heap_t *heap)
+{
+ int avail;
+ int count;
+
+ count = (heap->iovcnt == 0) ? 0 : (heap->iovcnt - 1);
+
+ avail = count * MLRPC_HEAP_BLKSZ;
+ avail += (heap->top - heap->next);
+
+ return (avail);
+}
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_server.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_server.c
new file mode 100644
index 0000000000..a5bf41a617
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_server.c
@@ -0,0 +1,836 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Server side RPC handler.
+ */
+
+#include <thread.h>
+#include <synch.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <time.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/mlsvc.h>
+#include <smbsrv/ndr.h>
+#include <smbsrv/mlrpc.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/smb_winpipe.h>
+
+/*
+ * Fragment size (5680: NT style).
+ */
+#define MLRPC_FRAG_SZ 5680
+static unsigned long mlrpc_frag_size = MLRPC_FRAG_SZ;
+
+/*
+ * Context table.
+ */
+#define CTXT_TABLE_ENTRIES 128
+static struct mlsvc_rpc_context context_table[CTXT_TABLE_ENTRIES];
+static mutex_t mlsvc_context_lock;
+
+static int mlrpc_s_process(struct mlrpc_xaction *);
+static int mlrpc_s_bind(struct mlrpc_xaction *);
+static int mlrpc_s_request(struct mlrpc_xaction *);
+static int mlrpc_generic_call_stub(struct mlrpc_xaction *);
+static void mlrpc_reply_prepare_hdr(struct mlrpc_xaction *);
+static int mlrpc_s_alter_context(struct mlrpc_xaction *);
+static void mlrpc_reply_bind_ack(struct mlrpc_xaction *);
+static void mlrpc_reply_fault(struct mlrpc_xaction *, unsigned long);
+static int mlrpc_build_reply(struct mlrpc_xaction *);
+
+/*
+ * The is the RPC service server-side entry point. All MSRPC encoded
+ * messages should be passed through here. We use the same context
+ * structure as the client side but we don't need to set up the client
+ * side info.
+ */
+int
+mlsvc_rpc_process(smb_pipe_t *inpipe, smb_pipe_t **outpipe,
+ smb_dr_user_ctx_t *user_ctx)
+{
+ struct mlsvc_rpc_context *context;
+ struct mlrpc_xaction *mxa;
+ struct mlndr_stream *recv_mlnds;
+ struct mlndr_stream *send_mlnds;
+ unsigned char *pdu_base_addr;
+ int datalen;
+
+ if (inpipe == NULL || user_ctx == NULL)
+ return (-1);
+
+ context = mlsvc_lookup_context(inpipe->sp_pipeid);
+ if (context == NULL)
+ return (-1);
+
+ context->user_ctx = user_ctx;
+
+ mxa = (struct mlrpc_xaction *)malloc(sizeof (struct mlrpc_xaction));
+ if (mxa == NULL)
+ return (-1);
+
+ bzero(mxa, sizeof (struct mlrpc_xaction));
+ mxa->context = context;
+ mxa->binding_list = context->binding;
+
+ if ((mxa->heap = mlrpc_heap_create()) == NULL) {
+ free(mxa);
+ return (-1);
+ }
+
+ recv_mlnds = &mxa->recv_mlnds;
+
+ (void) mlnds_initialize(recv_mlnds, inpipe->sp_datalen,
+ NDR_MODE_CALL_RECV, mxa->heap);
+
+ bcopy(inpipe->sp_data, recv_mlnds->pdu_base_addr, inpipe->sp_datalen);
+
+ send_mlnds = &mxa->send_mlnds;
+ (void) mlnds_initialize(send_mlnds, 0,
+ NDR_MODE_RETURN_SEND, mxa->heap);
+
+ (void) mlrpc_s_process(mxa);
+
+ /*
+ * copy into outpipe
+ */
+ datalen = send_mlnds->pdu_size_with_rpc_hdrs;
+ *outpipe = calloc(1, sizeof (smb_pipe_t) + datalen);
+ (*outpipe)->sp_datalen = datalen;
+
+ /*
+ * Different pointers for single frag vs multi frag responses.
+ */
+ if (send_mlnds->pdu_base_addr_with_rpc_hdrs)
+ pdu_base_addr = send_mlnds->pdu_base_addr_with_rpc_hdrs;
+ else
+ pdu_base_addr = send_mlnds->pdu_base_addr;
+
+ bcopy((char *)pdu_base_addr, (*outpipe)->sp_data, datalen);
+ mlnds_destruct(&mxa->recv_mlnds);
+ mlnds_destruct(&mxa->send_mlnds);
+ mlrpc_heap_destroy(mxa->heap);
+ free(mxa);
+ return (datalen);
+}
+
+/*
+ * Lookup the context for pipeid. If one exists, return a pointer to it.
+ * Otherwise attempt to allocate a new context and return it. If the
+ * context table is full, return a null pointer.
+ */
+struct mlsvc_rpc_context *
+mlsvc_lookup_context(int fid)
+{
+ struct mlsvc_rpc_context *context;
+ struct mlsvc_rpc_context *available = NULL;
+ int i;
+
+ (void) mutex_lock(&mlsvc_context_lock);
+
+ for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) {
+ context = &context_table[i];
+
+ if (available == NULL && context->fid == 0) {
+ available = context;
+ continue;
+ }
+
+ if (context->fid == fid) {
+ (void) mutex_unlock(&mlsvc_context_lock);
+ return (context);
+ }
+ }
+
+ if (available) {
+ bzero(available, sizeof (struct mlsvc_rpc_context));
+ available->fid = fid;
+
+ mlrpc_binding_pool_initialize(&available->binding,
+ available->binding_pool, CTXT_N_BINDING_POOL);
+ }
+
+ (void) mutex_unlock(&mlsvc_context_lock);
+ return (available);
+}
+
+/*
+ * This function should be called to release the context associated
+ * with a fid when the client performs a close file.
+ */
+void
+mlsvc_rpc_release(int fid)
+{
+ struct mlsvc_rpc_context *context;
+ int i;
+
+ (void) mutex_lock(&mlsvc_context_lock);
+
+ for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) {
+ context = &context_table[i];
+
+ if (context->fid == fid) {
+ bzero(context, sizeof (struct mlsvc_rpc_context));
+ break;
+ }
+ }
+
+ (void) mutex_unlock(&mlsvc_context_lock);
+}
+
+/*
+ * This is the entry point for all server-side RPC processing.
+ * It is assumed that the PDU has already been received.
+ */
+static int
+mlrpc_s_process(struct mlrpc_xaction *mxa)
+{
+ int rc;
+
+ rc = mlrpc_decode_pdu_hdr(mxa);
+ if (!MLRPC_DRC_IS_OK(rc))
+ return (-1);
+
+ (void) mlrpc_reply_prepare_hdr(mxa);
+
+ switch (mxa->ptype) {
+ case MLRPC_PTYPE_BIND:
+ rc = mlrpc_s_bind(mxa);
+ break;
+
+ case MLRPC_PTYPE_REQUEST:
+ rc = mlrpc_s_request(mxa);
+ break;
+
+ case MLRPC_PTYPE_ALTER_CONTEXT:
+ rc = mlrpc_s_alter_context(mxa);
+ break;
+
+ default:
+ rc = MLRPC_DRC_FAULT_RPCHDR_PTYPE_INVALID;
+ break;
+ }
+
+ if (MLRPC_DRC_IS_FAULT(rc))
+ mlrpc_reply_fault(mxa, rc);
+
+ (void) mlrpc_build_reply(mxa);
+ return (rc);
+}
+
+/*
+ * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple
+ * p_results[] not supported.
+ */
+static int
+mlrpc_s_bind(struct mlrpc_xaction *mxa)
+{
+ mlrpc_p_cont_list_t *cont_list;
+ mlrpc_p_result_list_t *result_list;
+ mlrpc_p_result_t *result;
+ unsigned p_cont_id;
+ struct mlrpc_binding *mbind;
+ mlrpc_uuid_t *as_uuid;
+ mlrpc_uuid_t *ts_uuid;
+ char as_buf[64];
+ char ts_buf[64];
+ int as_vers;
+ int ts_vers;
+ struct mlndr_stream *send_mlnds;
+ struct mlrpc_service *msvc;
+ int rc;
+ mlrpc_port_any_t *sec_addr;
+
+ /* acquire targets */
+ cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
+ result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
+ result = &result_list->p_results[0];
+
+ /*
+ * Set up temporary secondary address port.
+ * We will correct this later (below).
+ */
+ send_mlnds = &mxa->send_mlnds;
+ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
+ sec_addr->length = 13;
+ (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs");
+
+ result_list->n_results = 1;
+ result_list->reserved = 0;
+ result_list->reserved2 = 0;
+ result->result = MLRPC_PCDR_ACCEPTANCE;
+ result->reason = 0;
+ bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
+
+ /* sanity check */
+ if (cont_list->n_context_elem != 1 ||
+ cont_list->p_cont_elem[0].n_transfer_syn != 1) {
+ mlndo_trace("mlrpc_s_bind: warning: multiple p_cont_elem");
+ }
+
+ p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
+
+ if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL) {
+ /*
+ * Duplicate p_cont_id.
+ * Send a bind_ack with a better error.
+ */
+ mlndo_trace("mlrpc_s_bind: duplicate binding");
+ return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY);
+ }
+
+ if ((mbind = mlrpc_new_binding(mxa)) == NULL) {
+ /*
+ * No free binding slot
+ */
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED;
+ mlndo_trace("mlrpc_s_bind: no resources");
+ return (MLRPC_DRC_OK);
+ }
+
+ as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
+ as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
+
+ ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
+ ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
+
+ msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers);
+ if (!msvc) {
+ mlrpc_uuid_to_str(as_uuid, as_buf);
+ mlrpc_uuid_to_str(ts_uuid, ts_buf);
+
+ mlndo_printf(send_mlnds, 0, "mlrpc_s_bind: unknown service");
+ mlndo_printf(send_mlnds, 0, "abs=%s v%d, xfer=%s v%d",
+ as_buf, as_vers, ts_buf, ts_vers);
+
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
+ return (MLRPC_DRC_OK);
+ }
+
+ /*
+ * We can now use the correct secondary address port.
+ */
+ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
+ sec_addr->length = strlen(msvc->sec_addr_port) + 1;
+ (void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port,
+ MLRPC_PORT_ANY_MAX_PORT_SPEC);
+
+ mbind->p_cont_id = p_cont_id;
+ mbind->which_side = MLRPC_BIND_SIDE_SERVER;
+ /* mbind->context set by app */
+ mbind->service = msvc;
+ mbind->instance_specific = 0;
+
+ mxa->binding = mbind;
+
+ if (msvc->bind_req) {
+ /*
+ * Call the service-specific bind() handler. If
+ * this fails, we shouild send a specific error
+ * on the bind ack.
+ */
+ rc = (msvc->bind_req)(mxa);
+ if (MLRPC_DRC_IS_FAULT(rc)) {
+ mbind->service = 0; /* free binding slot */
+ mbind->which_side = 0;
+ mbind->p_cont_id = 0;
+ mbind->instance_specific = 0;
+ return (rc);
+ }
+ }
+
+ result->transfer_syntax =
+ cont_list->p_cont_elem[0].transfer_syntaxes[0];
+
+ /*
+ * Special rejection of Windows 2000 DSSETUP interface.
+ * This interface was introduced in Windows 2000 but has
+ * been subsequently deprecated due to problems.
+ */
+ if (strcmp(msvc->name, "DSSETUP") == 0) {
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
+ }
+
+ return (MLRPC_DRC_BINDING_MADE);
+}
+
+/*
+ * mlrpc_s_alter_context
+ *
+ * The alter context request is used to request additional presentation
+ * context for another interface and/or version. It's very similar to a
+ * bind request.
+ *
+ * We don't fully support multiple contexts so, for now, we reject this
+ * request. Windows 2000 clients attempt to use an alternate LSA context
+ * when ACLs are modified.
+ */
+static int
+mlrpc_s_alter_context(struct mlrpc_xaction *mxa)
+{
+ mlrpc_p_result_list_t *result_list;
+ mlrpc_p_result_t *result;
+ mlrpc_p_cont_list_t *cont_list;
+ struct mlrpc_binding *mbind;
+ struct mlrpc_service *msvc;
+ unsigned p_cont_id;
+ mlrpc_uuid_t *as_uuid;
+ mlrpc_uuid_t *ts_uuid;
+ int as_vers;
+ int ts_vers;
+ mlrpc_port_any_t *sec_addr;
+
+ result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
+ result_list->n_results = 1;
+ result_list->reserved = 0;
+ result_list->reserved2 = 0;
+
+ result = &result_list->p_results[0];
+ result->result = MLRPC_PCDR_ACCEPTANCE;
+ result->reason = 0;
+ bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
+
+ if (mxa != NULL) {
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
+ return (MLRPC_DRC_OK);
+ }
+
+ cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
+ p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
+
+ if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL)
+ return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY);
+
+ if ((mbind = mlrpc_new_binding(mxa)) == NULL) {
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED;
+ return (MLRPC_DRC_OK);
+ }
+
+ as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
+ as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
+
+ ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
+ ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
+
+ msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers);
+ if (msvc == 0) {
+ result->result = MLRPC_PCDR_PROVIDER_REJECTION;
+ result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
+ return (MLRPC_DRC_OK);
+ }
+
+ mbind->p_cont_id = p_cont_id;
+ mbind->which_side = MLRPC_BIND_SIDE_SERVER;
+ /* mbind->context set by app */
+ mbind->service = msvc;
+ mbind->instance_specific = 0;
+ mxa->binding = mbind;
+
+ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
+ sec_addr->length = 0;
+ bzero(sec_addr->port_spec, MLRPC_PORT_ANY_MAX_PORT_SPEC);
+
+ result->transfer_syntax =
+ cont_list->p_cont_elem[0].transfer_syntaxes[0];
+
+ return (MLRPC_DRC_BINDING_MADE);
+}
+
+static int
+mlrpc_s_request(struct mlrpc_xaction *mxa)
+{
+ struct mlrpc_binding *mbind;
+ struct mlrpc_service *msvc;
+ unsigned p_cont_id;
+ int rc;
+
+ mxa->opnum = mxa->recv_hdr.request_hdr.opnum;
+ p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id;
+
+ if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) == NULL)
+ return (MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID);
+
+ mxa->binding = mbind;
+ msvc = mbind->service;
+
+ /*
+ * Make room for the response hdr.
+ */
+ mxa->send_mlnds.pdu_scan_offset = MLRPC_RSP_HDR_SIZE;
+
+ if (msvc->call_stub)
+ rc = (*msvc->call_stub)(mxa);
+ else
+ rc = mlrpc_generic_call_stub(mxa);
+
+ if (MLRPC_DRC_IS_FAULT(rc)) {
+ mlndo_printf(0, 0, "%s[0x%02x]: 0x%04x",
+ msvc->name, mxa->opnum, rc);
+ }
+
+ return (rc);
+}
+
+/*
+ * The transaction and the two mlnds streams use the same heap, which
+ * should already exist at this point. The heap will also be available
+ * to the stub.
+ */
+static int
+mlrpc_generic_call_stub(struct mlrpc_xaction *mxa)
+{
+ struct mlrpc_binding *mbind = mxa->binding;
+ struct mlrpc_service *msvc = mbind->service;
+ struct ndr_typeinfo *intf_ti = msvc->interface_ti;
+ struct mlrpc_stub_table *ste;
+ int opnum = mxa->opnum;
+ unsigned p_len = intf_ti->c_size_fixed_part;
+ char *param;
+ int rc;
+
+ if (mxa->heap == NULL) {
+ mlndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum);
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+ }
+
+ if ((ste = mlrpc_find_stub_in_svc(msvc, opnum)) == NULL) {
+ mlndo_printf(0, 0, "%s[0x%02x]: invalid opnum",
+ msvc->name, opnum);
+ return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID);
+ }
+
+ if ((param = mlrpc_heap_malloc(mxa->heap, p_len)) == NULL)
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+
+ bzero(param, p_len);
+
+ rc = mlrpc_decode_call(mxa, param);
+ if (!MLRPC_DRC_IS_OK(rc))
+ return (rc);
+
+ rc = (*ste->func)(param, mxa);
+ if (rc == MLRPC_DRC_OK)
+ rc = mlrpc_encode_return(mxa, param);
+
+ return (rc);
+}
+
+/*
+ * We can perform some initial setup of the response header here.
+ * We also need to cache some of the information from the bind
+ * negotiation for use during subsequent RPC calls.
+ */
+static void
+mlrpc_reply_prepare_hdr(struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
+ mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+
+ hdr->rpc_vers = 5;
+ hdr->rpc_vers_minor = 0;
+ hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG;
+ hdr->packed_drep = rhdr->packed_drep;
+ hdr->frag_length = 0;
+ hdr->auth_length = 0;
+ hdr->call_id = rhdr->call_id;
+#ifdef _BIG_ENDIAN
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
+ | MLRPC_REPLAB_INTG_BIG_ENDIAN;
+#else
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
+ | MLRPC_REPLAB_INTG_LITTLE_ENDIAN;
+#endif
+
+ switch (mxa->ptype) {
+ case MLRPC_PTYPE_BIND:
+ hdr->ptype = MLRPC_PTYPE_BIND_ACK;
+ mxa->send_hdr.bind_ack_hdr.max_xmit_frag =
+ mxa->recv_hdr.bind_hdr.max_xmit_frag;
+ mxa->send_hdr.bind_ack_hdr.max_recv_frag =
+ mxa->recv_hdr.bind_hdr.max_recv_frag;
+ mxa->send_hdr.bind_ack_hdr.assoc_group_id =
+ mxa->recv_hdr.bind_hdr.assoc_group_id;
+
+ if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0)
+ mxa->send_hdr.bind_ack_hdr.assoc_group_id = time(0);
+
+ /*
+ * Save the maximum fragment sizes
+ * for use with subsequent requests.
+ */
+ mxa->context->max_xmit_frag =
+ mxa->recv_hdr.bind_hdr.max_xmit_frag;
+
+ mxa->context->max_recv_frag =
+ mxa->recv_hdr.bind_hdr.max_recv_frag;
+
+ break;
+
+ case MLRPC_PTYPE_REQUEST:
+ hdr->ptype = MLRPC_PTYPE_RESPONSE;
+ /* mxa->send_hdr.response_hdr.alloc_hint */
+ mxa->send_hdr.response_hdr.p_cont_id =
+ mxa->recv_hdr.request_hdr.p_cont_id;
+ mxa->send_hdr.response_hdr.cancel_count = 0;
+ mxa->send_hdr.response_hdr.reserved = 0;
+ break;
+
+ case MLRPC_PTYPE_ALTER_CONTEXT:
+ hdr->ptype = MLRPC_PTYPE_ALTER_CONTEXT_RESP;
+ /*
+ * The max_xmit_frag, max_recv_frag
+ * and assoc_group_id are ignored.
+ */
+ break;
+
+ default:
+ hdr->ptype = 0xFF;
+ }
+}
+
+/*
+ * Finish and encode the bind acknowledge (MLRPC_PTYPE_BIND_ACK) header.
+ * The frag_length is different from a regular RPC response.
+ */
+static void
+mlrpc_reply_bind_ack(struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *hdr;
+ mlrpcconn_bind_ack_hdr_t *bahdr;
+
+ hdr = &mxa->send_hdr.common_hdr;
+ bahdr = &mxa->send_hdr.bind_ack_hdr;
+ hdr->frag_length = mlrpc_bind_ack_hdr_size(bahdr);
+}
+
+/*
+ * Signal an RPC fault. The stream is reset and we overwrite whatever
+ * was in the response header with the fault information.
+ */
+static void
+mlrpc_reply_fault(struct mlrpc_xaction *mxa, unsigned long drc)
+{
+ mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
+ mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+ struct mlndr_stream *mlnds = &mxa->send_mlnds;
+ unsigned long fault_status;
+
+ MLNDS_RESET(mlnds);
+
+ hdr->rpc_vers = 5;
+ hdr->rpc_vers_minor = 0;
+ hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG;
+ hdr->packed_drep = rhdr->packed_drep;
+ hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr);
+ hdr->auth_length = 0;
+ hdr->call_id = rhdr->call_id;
+#ifdef _BIG_ENDIAN
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
+ | MLRPC_REPLAB_INTG_BIG_ENDIAN;
+#else
+ hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII
+ | MLRPC_REPLAB_INTG_LITTLE_ENDIAN;
+#endif
+
+ switch (drc & MLRPC_DRC_MASK_SPECIFIER) {
+ case MLRPC_DRC_FAULT_OUT_OF_MEMORY:
+ case MLRPC_DRC_FAULT_ENCODE_TOO_BIG:
+ fault_status = MLRPC_FAULT_NCA_OUT_ARGS_TOO_BIG;
+ break;
+
+ case MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID:
+ fault_status = MLRPC_FAULT_NCA_INVALID_PRES_CONTEXT_ID;
+ break;
+
+ case MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID:
+ fault_status = MLRPC_FAULT_NCA_OP_RNG_ERROR;
+ break;
+
+ case MLRPC_DRC_FAULT_DECODE_FAILED:
+ case MLRPC_DRC_FAULT_ENCODE_FAILED:
+ fault_status = MLRPC_FAULT_NCA_PROTO_ERROR;
+ break;
+
+ default:
+ fault_status = MLRPC_FAULT_NCA_UNSPEC_REJECT;
+ break;
+ }
+
+ mxa->send_hdr.fault_hdr.common_hdr.ptype = MLRPC_PTYPE_FAULT;
+ mxa->send_hdr.fault_hdr.status = fault_status;
+ mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length;
+}
+
+static int
+mlrpc_build_reply(struct mlrpc_xaction *mxa)
+{
+ mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+ struct mlndr_stream *mlnds = &mxa->send_mlnds;
+ unsigned long pdu_size;
+ unsigned long frag_size;
+ unsigned long pdu_data_size;
+ unsigned long frag_data_size;
+ uint32_t rem_dlen;
+ uint32_t save_rem_dlen;
+ uint32_t bytesoff;
+ uint32_t cnt;
+ uint32_t obytes;
+ uint32_t num_ext_frags;
+ uint16_t last_frag = 0;
+ uchar_t *frag_startp;
+ mlrpcconn_common_header_t *rpc_hdr;
+
+ hdr = &mxa->send_hdr.common_hdr;
+
+ frag_size = mlrpc_frag_size;
+ pdu_size = mlnds->pdu_size;
+
+ if (pdu_size <= frag_size) {
+ /*
+ * Single fragment response. The PDU size may be zero
+ * here (i.e. bind or fault response). So don't make
+ * any assumptions about it until after the header is
+ * encoded.
+ */
+ switch (hdr->ptype) {
+ case MLRPC_PTYPE_BIND_ACK:
+ mlrpc_reply_bind_ack(mxa);
+ break;
+
+ case MLRPC_PTYPE_FAULT:
+ /* already setup */
+ break;
+
+ case MLRPC_PTYPE_RESPONSE:
+ hdr->frag_length = pdu_size;
+ mxa->send_hdr.response_hdr.alloc_hint =
+ hdr->frag_length;
+ break;
+
+ default:
+ hdr->frag_length = pdu_size;
+ break;
+ }
+
+ mlnds->pdu_scan_offset = 0;
+ (void) mlrpc_encode_pdu_hdr(mxa);
+
+ mlnds->pdu_size_with_rpc_hdrs = mlnds->pdu_size;
+ mlnds->pdu_base_addr_with_rpc_hdrs = 0;
+ return (0);
+ }
+
+ /*
+ * Multiple fragment response.
+ */
+ hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG;
+ hdr->frag_length = frag_size;
+ mxa->send_hdr.response_hdr.alloc_hint = pdu_size - MLRPC_RSP_HDR_SIZE;
+ mlnds->pdu_scan_offset = 0;
+
+ (void) mlrpc_encode_pdu_hdr(mxa);
+
+ /*
+ * We need to update the 24-byte header in subsequent fragments.
+ *
+ * pdu_data_size: total data remaining to be handled
+ * frag_size: total fragment size including header
+ * frag_data_size: data in fragment
+ * (i.e. frag_size - MLRPC_RSP_HDR_SIZE)
+ */
+ pdu_data_size = pdu_size - MLRPC_RSP_HDR_SIZE;
+ frag_data_size = frag_size - MLRPC_RSP_HDR_SIZE;
+
+ num_ext_frags = pdu_data_size / frag_data_size;
+ /*
+ * if the outpipe is bigger than a frag_size, we need
+ * to stretch the pipe and insert an RPC header at each
+ * frag boundary. This outpipe gets chunked out in xdrlen
+ * sizes for each trans request
+ */
+ mlnds->pdu_base_addr_with_rpc_hdrs
+ = malloc(pdu_size + (num_ext_frags * MLRPC_RSP_HDR_SIZE));
+ mlnds->pdu_size_with_rpc_hdrs =
+ mlnds->pdu_size + (num_ext_frags * MLRPC_RSP_HDR_SIZE);
+
+ /*
+ * Start stretching loop.
+ */
+ bcopy(mlnds->pdu_base_addr,
+ mlnds->pdu_base_addr_with_rpc_hdrs, frag_size);
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ rpc_hdr = (mlrpcconn_common_header_t *)
+ mlnds->pdu_base_addr_with_rpc_hdrs;
+ rpc_hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG;
+ rem_dlen = pdu_data_size - frag_size;
+ bytesoff = frag_size;
+ cnt = 1;
+ while (num_ext_frags--) {
+ /* first copy the RPC header to the front of the frag */
+ bcopy(mlnds->pdu_base_addr, mlnds->pdu_base_addr_with_rpc_hdrs +
+ (cnt * frag_size), MLRPC_RSP_HDR_SIZE);
+
+ /* then copy the data portion of the frag */
+ save_rem_dlen = rem_dlen;
+ if (rem_dlen >= (frag_size - MLRPC_RSP_HDR_SIZE)) {
+ rem_dlen = rem_dlen - frag_size + MLRPC_RSP_HDR_SIZE;
+ obytes = frag_size - MLRPC_RSP_HDR_SIZE;
+ } else {
+ last_frag = 1; /* this is the last one */
+ obytes = rem_dlen;
+ }
+
+ frag_startp = mlnds->pdu_base_addr_with_rpc_hdrs +
+ (cnt * frag_size);
+ bcopy(mlnds->pdu_base_addr + bytesoff,
+ frag_startp + MLRPC_RSP_HDR_SIZE, obytes);
+
+ /* set the FRAG FLAGS in the frag header spot */
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ rpc_hdr = (mlrpcconn_common_header_t *)frag_startp;
+ if (last_frag) {
+ rpc_hdr->frag_length = save_rem_dlen;
+ rpc_hdr->pfc_flags = MLRPC_PFC_LAST_FRAG;
+ } else {
+ rpc_hdr->pfc_flags = 0;
+ }
+
+ bytesoff += (frag_size - MLRPC_RSP_HDR_SIZE);
+ cnt++;
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_svc.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_svc.c
new file mode 100644
index 0000000000..771e54b7e4
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_svc.c
@@ -0,0 +1,286 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ndr.h>
+#include <smbsrv/mlrpc.h>
+
+#define NDL_MAX_SERVICES 32
+static struct mlrpc_service *mlrpc_services[NDL_MAX_SERVICES];
+
+struct mlrpc_stub_table *
+mlrpc_find_stub_in_svc(struct mlrpc_service *msvc, int opnum)
+{
+ struct mlrpc_stub_table *ste;
+
+ for (ste = msvc->stub_table; ste->func; ste++) {
+ if (ste->opnum == opnum)
+ return (ste);
+ }
+
+ return (NULL);
+}
+
+struct mlrpc_service *
+mlrpc_find_service_by_name(const char *name)
+{
+ struct mlrpc_service *msvc;
+ int i;
+
+ for (i = 0; i < NDL_MAX_SERVICES; i++) {
+ if ((msvc = mlrpc_services[i]) == NULL)
+ continue;
+
+ if (strcasecmp(name, msvc->name) != 0)
+ continue;
+
+ mlndo_printf(0, 0, "%s %s", msvc->name, msvc->desc);
+ return (msvc);
+ }
+
+ return (NULL);
+}
+
+struct mlrpc_service *
+mlrpc_find_service_by_uuids(mlrpc_uuid_t *as_uuid, int as_vers,
+ mlrpc_uuid_t *ts_uuid, int ts_vers)
+{
+ struct mlrpc_service *msvc;
+ char abstract_syntax[128];
+ char transfer_syntax[128];
+ int i;
+
+ if (as_uuid)
+ mlrpc_uuid_to_str(as_uuid, abstract_syntax);
+
+ if (ts_uuid)
+ mlrpc_uuid_to_str(ts_uuid, transfer_syntax);
+
+ for (i = 0; i < NDL_MAX_SERVICES; i++) {
+ if ((msvc = mlrpc_services[i]) == NULL)
+ continue;
+
+ if (as_uuid) {
+ if (msvc->abstract_syntax_uuid == 0)
+ continue;
+
+ if (msvc->abstract_syntax_version != as_vers)
+ continue;
+
+ if (strcasecmp(abstract_syntax,
+ msvc->abstract_syntax_uuid))
+ continue;
+ }
+
+ if (ts_uuid) {
+ if (msvc->transfer_syntax_uuid == 0)
+ continue;
+
+ if (msvc->transfer_syntax_version != ts_vers)
+ continue;
+
+ if (strcasecmp(transfer_syntax,
+ msvc->transfer_syntax_uuid))
+ continue;
+ }
+
+ mlndo_printf(0, 0, "%s %s", msvc->name, msvc->desc);
+ return (msvc);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Register a service.
+ *
+ * Returns:
+ * 0 Success
+ * -1 Duplicate service
+ * -2 Duplicate name
+ * -3 Table overflow
+ */
+int
+mlrpc_register_service(struct mlrpc_service *msvc)
+{
+ struct mlrpc_service *p;
+ int free_slot = -1;
+ int i;
+
+ for (i = 0; i < NDL_MAX_SERVICES; i++) {
+ if ((p = mlrpc_services[i]) == NULL) {
+ if (free_slot < 0)
+ free_slot = i;
+ continue;
+ }
+
+ if (p == msvc)
+ return (-1);
+
+ if (strcasecmp(p->name, msvc->name) == 0)
+ return (-2);
+ }
+
+ if (free_slot < 0)
+ return (-3);
+
+ mlrpc_services[free_slot] = msvc;
+ return (0);
+}
+
+void
+mlrpc_unregister_service(struct mlrpc_service *msvc)
+{
+ int i;
+
+ for (i = 0; i < NDL_MAX_SERVICES; i++) {
+ if (mlrpc_services[i] == msvc)
+ mlrpc_services[i] = NULL;
+ }
+}
+
+int
+mlrpc_list_services(char *buffer, int bufsize)
+{
+ struct mlrpc_service *msvc;
+ smb_ctxbuf_t ctx;
+ int i;
+
+ (void) smb_ctxbuf_init(&ctx, (uint8_t *)buffer, bufsize);
+
+ for (i = 0; i < NDL_MAX_SERVICES; i++) {
+ if ((msvc = mlrpc_services[i]) != 0) {
+ (void) smb_ctxbuf_printf(&ctx, "%-16s %s\n",
+ msvc->name, msvc->desc);
+ }
+ }
+
+ return (smb_ctxbuf_len(&ctx));
+}
+
+void
+mlrpc_uuid_to_str(mlrpc_uuid_t *uuid, char *str)
+{
+ (void) sprintf(str, "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
+ uuid->data1, uuid->data2, uuid->data3,
+ uuid->data4[0], uuid->data4[1],
+ uuid->data4[2], uuid->data4[3],
+ uuid->data4[4], uuid->data4[5],
+ uuid->data4[6], uuid->data4[7]);
+}
+
+int
+mlrpc_str_to_uuid(char *str, mlrpc_uuid_t *uuid)
+{
+ char *p = str;
+ char *q;
+ char buf[4];
+ int i;
+
+ uuid->data1 = strtoul(p, &p, 16);
+ if (*p != '-')
+ return (0);
+ p++;
+
+ uuid->data2 = strtol(p, &p, 16);
+ if (*p != '-')
+ return (0);
+ p++;
+
+ uuid->data3 = strtol(p, &p, 16);
+ if (*p != '-')
+ return (0);
+ p++;
+
+ for (i = 0; i < 8; i++) {
+ if (p[0] == 0 || p[1] == 0)
+ return (0);
+
+ buf[0] = *p++;
+ buf[1] = *p++;
+ buf[2] = 0;
+ uuid->data4[i] = strtol(buf, &q, 16);
+ if (*q != 0)
+ return (0);
+ }
+
+ if (*p != 0)
+ return (0);
+
+ return (1);
+}
+
+void
+mlrpc_binding_pool_initialize(struct mlrpc_binding **headpp,
+ struct mlrpc_binding pool[], unsigned n_pool)
+{
+ struct mlrpc_binding *head = NULL;
+ int ix;
+
+ for (ix = n_pool - 1; ix >= 0; ix--) {
+ pool[ix].next = head;
+ pool[ix].service = NULL;
+ pool[ix].p_cont_id = 0xffff;
+ pool[ix].instance_specific = 0;
+ head = &pool[ix];
+ }
+
+ *headpp = head;
+}
+
+struct mlrpc_binding *
+mlrpc_find_binding(struct mlrpc_xaction *mxa, mlrpc_p_context_id_t p_cont_id)
+{
+ struct mlrpc_binding *mbind;
+
+ for (mbind = mxa->binding_list; mbind; mbind = mbind->next) {
+ if (mbind->service != NULL &&
+ mbind->which_side == MLRPC_BIND_SIDE_SERVER &&
+ mbind->p_cont_id == p_cont_id)
+ break;
+ }
+
+ return (mbind);
+}
+
+struct mlrpc_binding *
+mlrpc_new_binding(struct mlrpc_xaction *mxa)
+{
+ struct mlrpc_binding *mbind;
+
+ for (mbind = mxa->binding_list; mbind; mbind = mbind->next) {
+ if (mbind->service == NULL)
+ break;
+ }
+
+ return (mbind);
+}
diff --git a/usr/src/lib/smbsrv/libmlrpc/i386/Makefile b/usr/src/lib/smbsrv/libmlrpc/i386/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libmlrpc/sparc/Makefile b/usr/src/lib/smbsrv/libmlrpc/sparc/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libmlrpc/sparcv9/Makefile b/usr/src/lib/smbsrv/libmlrpc/sparcv9/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlrpc/sparcv9/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libmlsvc/Makefile b/usr/src/lib/smbsrv/libmlsvc/Makefile
new file mode 100644
index 0000000000..21f844a2c0
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+HDRS= libmlsvc.h
+
+include ../Makefile.smbsrv
diff --git a/usr/src/lib/smbsrv/libmlsvc/Makefile.com b/usr/src/lib/smbsrv/libmlsvc/Makefile.com
new file mode 100644
index 0000000000..e0c1ee5c4c
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/Makefile.com
@@ -0,0 +1,86 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY = libmlsvc.a
+VERS = .1
+
+OBJS_COMMON = \
+ lsalib.o \
+ lsar_lookup.o \
+ lsar_open.o \
+ mlsvc_client.o \
+ mlsvc_dssetup.o \
+ mlsvc_handle.o \
+ mlsvc_init.o \
+ mlsvc_logr.o \
+ mlsvc_lsa.o \
+ mlsvc_netr.o \
+ mlsvc_sam.o \
+ mlsvc_srvsvc.o \
+ mlsvc_svcctl.o \
+ mlsvc_util.o \
+ mlsvc_winreg.o \
+ mlsvc_wkssvc.o \
+ netdfs.o \
+ netr_auth.o \
+ netr_logon.o \
+ samlib.o \
+ samr_open.o \
+ samr_lookup.o \
+ secdb.o \
+ srvsvc_client.o \
+ lmshare.o \
+ smb_share_util.o \
+ smb_autohome.o
+
+# Automatically generated from .ndl files
+NDLLIST = \
+ dssetup \
+ eventlog \
+ lsarpc \
+ netdfs \
+ netlogon \
+ samrpc \
+ spoolss \
+ srvsvc \
+ svcctl \
+ winreg
+
+OBJECTS= $(OBJS_COMMON) $(NDLLIST:%=%_ndr.o)
+
+include ../../../Makefile.lib
+include ../../Makefile.lib
+
+INCS += -I$(SRC)/common/smbsrv
+
+LDLIBS += -lmlrpc -lsmbrdr -lsmb -lsmbns -lshare -lnsl -lc
+
+SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
+ $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c)
+
+include ../../Makefile.targ
+include ../../../Makefile.targ
diff --git a/usr/src/lib/smbsrv/libmlsvc/amd64/Makefile b/usr/src/lib/smbsrv/libmlsvc/amd64/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
new file mode 100644
index 0000000000..71cc37af48
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
@@ -0,0 +1,237 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBMLSVC_H
+#define _LIBMLSVC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/hash_table.h>
+#include <smbsrv/smb_token.h>
+#include <smbsrv/smb_privilege.h>
+#include <smbsrv/lmshare.h>
+#include <smbsrv/libsmb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int mlsvc_init(void);
+extern int mlsvc_is_local_domain(const char *);
+extern DWORD lsa_query_primary_domain_info(void);
+extern DWORD lsa_query_account_domain_info(void);
+extern DWORD lsa_enum_trusted_domains(void);
+
+extern boolean_t locate_resource_pdc(char *);
+
+#define SMB_AUTOHOME_FILE "smbautohome"
+#define SMB_AUTOHOME_PATH "/etc"
+
+typedef struct smb_autohome {
+ struct smb_autohome *ah_next;
+ uint32_t ah_hits;
+ time_t ah_timestamp;
+ char *ah_name; /* User account name */
+ char *ah_path; /* Home directory path */
+ char *ah_container; /* ADS container distinguished name */
+} smb_autohome_t;
+
+extern int smb_autohome_add(const char *);
+extern int smb_autohome_remove(const char *);
+extern int smb_is_autohome(const lmshare_info_t *);
+extern void smb_autohome_setent(void);
+extern void smb_autohome_endent(void);
+extern smb_autohome_t *smb_autohome_getent(const char *name);
+extern smb_autohome_t *smb_autohome_lookup(const char *name);
+
+/*
+ * Local groups
+ */
+#define NT_GROUP_FMRI_PREFIX "network/smb/group"
+
+typedef enum {
+ RWLOCK_NONE,
+ RWLOCK_WRITER,
+ RWLOCK_READER
+} krwmode_t;
+
+typedef struct nt_group_data {
+ void *data;
+ int size;
+} nt_group_data_t;
+
+/*
+ * IMPORTANT NOTE:
+ * If you change nt_group_member_t, nt_group_members_t, or nt_group_t
+ * structures, you MIGHT have to change following functions accordingly:
+ * nt_group_setfields
+ * nt_group_init_size
+ * nt_group_init
+ */
+typedef struct nt_group_member {
+ uint16_t info_size; /* size of the whole structure */
+ uint16_t sid_name_use; /* type of the specified SID */
+ char *account; /* Pointer to account name of member */
+ nt_sid_t sid; /* Variable length */
+} nt_group_member_t;
+
+typedef struct nt_group_members {
+ uint32_t size; /* in bytes */
+ uint32_t count;
+ nt_group_member_t list[ANY_SIZE_ARRAY];
+} nt_group_members_t;
+
+typedef struct nt_group {
+ time_t age;
+ nt_group_data_t info;
+ /*
+ * following fields point to a contigous block
+ * of memory that is read and written from/to DB
+ */
+ uint32_t *attr;
+ uint16_t *sid_name_use;
+ char *name;
+ char *comment;
+ nt_sid_t *sid;
+ smb_privset_t *privileges;
+ nt_group_members_t *members;
+} nt_group_t;
+
+typedef struct nt_group_iterator {
+ HT_ITERATOR *iterator;
+ int iteration;
+} nt_group_iterator_t;
+
+extern int nt_group_num_groups(void);
+extern uint32_t nt_group_add(char *, char *);
+extern uint32_t nt_group_modify(char *, char *, char *);
+extern uint32_t nt_group_delete(char *);
+extern nt_group_t *nt_group_getinfo(char *, krwmode_t);
+extern void nt_group_putinfo(nt_group_t *);
+
+extern int nt_group_getpriv(nt_group_t *, uint32_t);
+extern uint32_t nt_group_setpriv(nt_group_t *, uint32_t, uint32_t);
+
+/* Member manipulation functions */
+extern int nt_group_is_member(nt_group_t *, nt_sid_t *);
+extern uint32_t nt_group_del_member(nt_group_t *, void *, int);
+extern uint32_t nt_group_add_member(nt_group_t *, nt_sid_t *, uint16_t, char *);
+extern int nt_group_num_members(nt_group_t *);
+
+extern void nt_group_ht_lock(krwmode_t);
+extern void nt_group_ht_unlock(void);
+
+extern nt_group_iterator_t *nt_group_open_iterator(void);
+extern void nt_group_close_iterator(nt_group_iterator_t *);
+extern nt_group_t *nt_group_iterate(nt_group_iterator_t *);
+
+extern int nt_group_cache_size(void);
+
+extern int nt_group_member_list(int offset, nt_group_t *grp,
+ ntgrp_member_list_t *rmembers);
+extern void nt_group_list(int offset, char *pattern, ntgrp_list_t *list);
+
+extern uint32_t sam_init(void);
+
+extern uint32_t nt_group_add_member_byname(char *, char *);
+extern uint32_t nt_group_del_member_byname(nt_group_t *, char *);
+extern void nt_group_add_groupprivs(nt_group_t *, smb_privset_t *);
+
+extern uint32_t nt_groups_member_privs(nt_sid_t *, smb_privset_t *);
+extern int nt_groups_member_ngroups(nt_sid_t *);
+extern uint32_t nt_groups_member_groups(nt_sid_t *, smb_id_t *, int);
+extern nt_group_t *nt_groups_lookup_rid(uint32_t);
+extern int nt_groups_count(int);
+
+/*
+ * source for account name size is MSDN
+ */
+#define NT_GROUP_NAME_CHAR_MAX 32
+#define NT_GROUP_NAME_MAX (NT_GROUP_NAME_CHAR_MAX * 3 + 1)
+#define NT_GROUP_USER_NAME_MAX (NT_GROUP_NAME_CHAR_MAX * 3 + 1)
+#define NT_GROUP_MEMBER_NAME_MAX (NT_GROUP_NAME_CHAR_MAX * 3 + 1)
+#define NT_GROUP_COMMENT_MAX 256
+
+/*
+ * flags for count operation
+ */
+#define NT_GROUP_CNT_BUILTIN 1
+#define NT_GROUP_CNT_LOCAL 2
+#define NT_GROUP_CNT_ALL 3
+
+/*
+ * flag to distinguish between add and modify
+ * operations.
+ */
+#define NT_GROUP_OP_CHANGE 1
+#define NT_GROUP_OP_SYNC 2
+
+/*
+ * specify key type for deleting a member i.e.
+ * whether it's member's name or member's SID.
+ */
+#define NT_GROUP_KEY_SID 1
+#define NT_GROUP_KEY_NAME 2
+
+/* Macro for walking members */
+#define NEXT_MEMBER(m) (nt_group_member_t *)((char *)(m) + (m)->info_size)
+
+/*
+ * When NT requests the security descriptor for a local file that
+ * doesn't already have a one, we generate one on-the-fly. The SD
+ * contains both user and group SIDs. The problem is that we need a
+ * way to distinguish a user SID from a group SID when NT performs a
+ * subsequent SID lookup to obtain the appropriate name to display.
+ * The following macros are used to map to and from an external
+ * representation so that we can tell the difference between UIDs
+ * and GIDs. The local UID/GID is shifted left and the LSB is used
+ * to distinguish the id type before it is inserted into the SID.
+ * We can then use this type identifier during lookup operations.
+ */
+#define SAM_MIN_RID 1000
+#define SAM_RT_ERROR -1
+#define SAM_RT_UNIX_UID 0
+#define SAM_RT_UNIX_GID 1
+#define SAM_RT_NT_UID 2
+#define SAM_RT_NT_GID 3
+#define SAM_RT_MASK 0x3
+#define SAM_RT_EVERYONE 4
+#define SAM_RT_UNKNOWN 5
+
+#define SAM_RID_TYPE(rid) ((rid) & SAM_RT_MASK)
+#define SAM_DECODE_RID(rid) (((rid) - SAM_MIN_RID) >> 2)
+#define SAM_ENCODE_RID(type, id) ((((id) << 2) | type) + SAM_MIN_RID)
+#define SAM_ENCODE_UXUID(id) SAM_ENCODE_RID(SAM_RT_UNIX_UID, id)
+#define SAM_ENCODE_UXGID(id) SAM_ENCODE_RID(SAM_RT_UNIX_GID, id)
+#define SAM_ENCODE_NTUID(id) SAM_ENCODE_RID(SAM_RT_NT_UID, id)
+#define SAM_ENCODE_NTGID(id) SAM_ENCODE_RID(SAM_RT_NT_GID, id)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBMLSVC_H */
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/llib-lmlsvc b/usr/src/lib/smbsrv/libmlsvc/common/llib-lmlsvc
new file mode 100644
index 0000000000..412e13d740
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/llib-lmlsvc
@@ -0,0 +1,31 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <smbsrv/libmlsvc.h>
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lmshare.c b/usr/src/lib/smbsrv/libmlsvc/common/lmshare.c
new file mode 100644
index 0000000000..5fb02b71f4
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/lmshare.c
@@ -0,0 +1,1233 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Lan Manager (SMB/CIFS) share interface implementation. This interface
+ * returns Win32 error codes, usually network error values (lmerr.h).
+ */
+
+#include <errno.h>
+#include <synch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <syslog.h>
+#include <thread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <synch.h>
+#include <pthread.h>
+#include <sys/mnttab.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <ctype.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbns.h>
+
+#include <libshare.h>
+
+#include <smbsrv/lm.h>
+#include <smbsrv/lmshare.h>
+#include <smbsrv/cifs.h>
+
+#include <smbsrv/ctype.h>
+#include <smbsrv/smb_vops.h>
+#include <smbsrv/smb_fsd.h>
+
+#define LMSHR_HASHTAB_SZ 1024
+
+static HT_HANDLE *lmshare_handle = NULL;
+
+static rwlock_t lmshare_lock;
+static pthread_t lmshare_load_thread;
+static void *lmshare_load(void *);
+static DWORD lmshare_create_table();
+static int lmshare_delete_shmgr(struct lmshare_info *);
+static int lmshare_setinfo_shmgr(struct lmshare_info *);
+static DWORD lmshare_set_refcnt(char *share_name, int refcnt);
+
+typedef struct lmshare_ad_item {
+ TAILQ_ENTRY(lmshare_ad_item) next;
+ char name[MAXNAMELEN];
+ char container[MAXPATHLEN];
+ char flag;
+} lmshare_ad_item_t;
+
+typedef struct lmshare_ad_queue {
+ int nentries;
+ TAILQ_HEAD(adqueue, lmshare_ad_item) adlist;
+} lmshare_ad_queue_t;
+
+static lmshare_ad_queue_t ad_queue;
+static int publish_on = 0;
+
+static pthread_t lmshare_publish_thread;
+static mutex_t lmshare_publish_mutex = PTHREAD_MUTEX_INITIALIZER;
+static cond_t lmshare_publish_cv = DEFAULTCV;
+
+static void *lmshare_publisher(void *);
+static void lmshare_stop_publish();
+
+/*
+ * Start loading lmshare information from sharemanager
+ * and create the cache.
+ */
+int
+lmshare_start()
+{
+ int rc;
+
+ rc = pthread_create(&lmshare_publish_thread, NULL,
+ lmshare_publisher, 0);
+ if (rc != 0) {
+ syslog(LOG_ERR, "Failed to start publisher thread, "
+ "share publishing is disabled");
+ }
+
+ rc = pthread_create(&lmshare_load_thread, NULL,
+ lmshare_load, 0);
+ if (rc != 0) {
+ syslog(LOG_ERR, "Failed to start share loading, "
+ "existing shares will not be available");
+ }
+
+ return (rc);
+}
+
+void
+lmshare_stop()
+{
+ lmshare_stop_publish();
+}
+
+/*
+ * Load shares from sharemanager.
+ */
+/*ARGSUSED*/
+static void *
+lmshare_load(void *args)
+{
+ lmshare_info_t si;
+ sa_handle_t handle;
+ sa_group_t group;
+ sa_share_t share;
+ sa_resource_t resource;
+ sa_optionset_t opts;
+ char *gstate, *path, *rname;
+
+ if (lmshare_create_table() != NERR_Success) {
+ syslog(LOG_ERR, "Failed to create share hash table");
+ return (NULL);
+ }
+
+ handle = sa_init(SA_INIT_SHARE_API);
+ if (handle == NULL) {
+ syslog(LOG_ERR, "Failed to load share "
+ "information: no libshare handle");
+ return (NULL);
+ }
+
+ for (group = sa_get_group(handle, NULL);
+ group != NULL; group = sa_get_next_group(group)) {
+ gstate = sa_get_group_attr(group, "state");
+ if ((gstate == NULL) ||
+ (strcasecmp(gstate, "disabled") == 0)) {
+ /* Skip disabled or unknown state group */
+ continue;
+ }
+ /* Check and see if smb protocol is available */
+ if (sa_get_optionset(group, SMB_PROTOCOL_NAME) == NULL)
+ continue;
+ for (share = sa_get_share(group, NULL);
+ share != NULL; share = sa_get_next_share(share)) {
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL) {
+ syslog(LOG_ERR, "Invalid share NO path");
+ continue;
+ }
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource)) {
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL) {
+ syslog(LOG_ERR, "Invalid share "
+ "resource for path: %s", path);
+ continue;
+ }
+ opts = sa_get_derived_optionset(resource,
+ SMB_PROTOCOL_NAME, 1);
+ smb_build_lmshare_info(rname, path, opts, &si);
+ sa_free_derived_optionset(opts);
+ (void) free(rname);
+ if (lmshare_add(&si, 0) != NERR_Success) {
+ syslog(LOG_ERR, "Failed to load "
+ "share %s", si.share_name);
+ }
+ }
+ /* We are done with all shares for same path */
+ (void) free(path);
+ }
+ }
+
+ sa_fini(handle);
+
+ return (NULL);
+}
+
+/*
+ * lmshare_callback
+ *
+ * Call back to free share structures stored
+ * in shares' hash table.
+ */
+static void
+lmshare_callback(HT_ITEM *item)
+{
+ if (item && item->hi_data)
+ (void) free(item->hi_data);
+}
+
+/*
+ * lmshare_create_table
+ *
+ * Create the share hash table.
+ */
+static DWORD
+lmshare_create_table(void)
+{
+ if (lmshare_handle == NULL) {
+ (void) rwlock_init(&lmshare_lock, USYNC_THREAD, 0);
+ (void) rw_wrlock(&lmshare_lock);
+
+ lmshare_handle = ht_create_table(LMSHR_HASHTAB_SZ,
+ MAXNAMELEN, 0);
+ if (lmshare_handle == NULL) {
+ syslog(LOG_ERR, "lmshare_create_table:"
+ " unable to create share table");
+ (void) rw_unlock(&lmshare_lock);
+ return (NERR_InternalError);
+ }
+ (void) ht_register_callback(lmshare_handle, lmshare_callback);
+ (void) rw_unlock(&lmshare_lock);
+ }
+
+ return (NERR_Success);
+}
+
+/*
+ * lmshare_add_adminshare
+ *
+ * add the admin share for the volume when the share database
+ * for that volume is going to be loaded.
+ */
+DWORD
+lmshare_add_adminshare(char *volname, unsigned char drive)
+{
+ struct lmshare_info si;
+ DWORD rc;
+
+ if (drive == 0)
+ return (NERR_InvalidDevice);
+
+ bzero(&si, sizeof (lmshare_info_t));
+ (void) strcpy(si.directory, volname);
+ si.mode = LMSHRM_TRANS;
+ (void) snprintf(si.share_name, sizeof (si.share_name), "%c$", drive);
+ rc = lmshare_add(&si, 0);
+
+ return (rc);
+}
+
+/*
+ * lmshare_num_shares
+ *
+ * Return the total number of shares, which should be the same value
+ * that would be returned from a share enum request.
+ */
+int
+lmshare_num_shares(void)
+{
+ int n_shares;
+
+ n_shares = ht_get_total_items(lmshare_handle);
+
+ /* If we don't store IPC$ in hash table we should do this */
+ n_shares++;
+
+ return (n_shares);
+}
+
+/*
+ * lmshare_open_iterator
+ *
+ * Create and initialize an iterator for traversing hash table.
+ * It gets a mode that can be LMSHR_IM_ALL to iterate through all
+ * the shares stored in table or LMSHR_IM_PRES to iterate through
+ * only presentable shares.
+ *
+ * It also accepts a local IP address. This is used in dual head
+ * systems to only return the shares that belong to the head which
+ * is specified by the 'ipaddr'. If ipaddr is 0 it'll return shares
+ * of both heads.
+ *
+ * On success return pointer to the new iterator.
+ * On failure return (NULL).
+ */
+lmshare_iterator_t *
+lmshare_open_iterator(int mode)
+{
+ lmshare_iterator_t *shi;
+ int sz = sizeof (lmshare_iterator_t) + sizeof (HT_ITERATOR);
+
+ shi = malloc(sz);
+ if (shi != NULL) {
+ bzero(shi, sz);
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ shi->iterator = (HT_ITERATOR *)
+ ((char *)shi + sizeof (lmshare_iterator_t));
+ shi->mode = mode;
+ } else {
+ syslog(LOG_DEBUG, "Failed to create share iterator handle");
+ }
+ return (shi);
+}
+
+/*
+ * lmshare_close_iterator
+ *
+ * Free memory allocated by the given iterator.
+ */
+void
+lmshare_close_iterator(lmshare_iterator_t *shi)
+{
+ (void) free(shi);
+}
+
+/*
+ * lmshare_iterate
+ *
+ * Iterate on the shares in the hash table. The iterator must be opened
+ * before the first iteration. On subsequent calls, the iterator must be
+ * passed unchanged.
+ *
+ * Returns NULL on failure or when all shares are visited, otherwise
+ * returns information of visited share.
+ *
+ * Note that there are some special shares, i.e. IPC$, that must also
+ * be processed.
+ */
+lmshare_info_t *
+lmshare_iterate(lmshare_iterator_t *shi)
+{
+ HT_ITEM *item;
+ lmshare_info_t *si;
+
+ if (lmshare_handle == NULL || shi == NULL)
+ return (NULL);
+
+ if (shi->iteration == 0) {
+ /*
+ * IPC$ is always first.
+ */
+ (void) strcpy(shi->si.share_name, "IPC$");
+ shi->si.mode = LMSHRM_TRANS;
+ shi->si.stype = (int)(STYPE_IPC | STYPE_SPECIAL);
+ shi->iteration = 1;
+ return (&(shi->si));
+ }
+
+ if (shi->iteration == 1) {
+ if ((item = ht_findfirst(
+ lmshare_handle, shi->iterator)) == NULL) {
+ return (NULL);
+ }
+
+ si = (lmshare_info_t *)(item->hi_data);
+ ++shi->iteration;
+
+ if (si->mode & shi->mode) {
+ (void) memcpy(&(shi->si), si,
+ sizeof (lmshare_info_t));
+ return (&(shi->si));
+ }
+ }
+
+ while ((item = ht_findnext(shi->iterator)) != NULL) {
+ si = (lmshare_info_t *)(item->hi_data);
+ ++shi->iteration;
+ if (si->mode & shi->mode) {
+ (void) memcpy(&(shi->si), si, sizeof (lmshare_info_t));
+
+ return (&(shi->si));
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * lmshare_add
+ *
+ * Add a share. This is a wrapper round lmshare_setinfo that checks
+ * whether or not the share already exists. If the share exists, an
+ * error is returned.
+ *
+ * Don't check lmshare_is_dir here: it causes rootfs to recurse.
+ */
+DWORD
+lmshare_add(lmshare_info_t *si, int doshm)
+{
+ DWORD status = NERR_Success;
+
+ if (si == 0 || lmshare_is_valid(si->share_name) == 0)
+ return (NERR_InvalidDevice);
+
+ (void) utf8_strlwr(si->share_name);
+
+ if (lmshare_exists(si->share_name)) {
+ if ((si->mode & LMSHRM_TRANS) == 0)
+ return (NERR_DuplicateShare);
+ }
+
+ if (si->refcnt == 0) {
+ status = lmshare_setinfo(si, doshm);
+ lmshare_do_publish(si, LMSHR_PUBLISH, 1);
+ }
+
+ if ((si->mode & LMSHRM_TRANS) && (status == NERR_Success)) {
+ si->refcnt++;
+ status = lmshare_set_refcnt(si->share_name, si->refcnt);
+ }
+
+ if (status)
+ return (status);
+
+ return (smb_dwncall_share(LMSHR_ADD, si->directory, si->share_name));
+}
+
+/*
+ * lmshare_delete
+ *
+ * Remove a share. Ensure that all SMB trees associated with this share
+ * are disconnected. If the share does not exist, an error is returned.
+ */
+DWORD
+lmshare_delete(char *share_name, int doshm)
+{
+ lmshare_info_t *si;
+ HT_ITEM *item;
+ DWORD status;
+ char path[MAXPATHLEN];
+
+ if (share_name)
+ (void) utf8_strlwr(share_name);
+
+ if (lmshare_is_valid(share_name) == 0 ||
+ lmshare_exists(share_name) == 0) {
+ return (NERR_NetNameNotFound);
+ }
+
+ (void) rw_wrlock(&lmshare_lock);
+ item = ht_find_item(lmshare_handle, share_name);
+
+ if (item == NULL) {
+ (void) rw_unlock(&lmshare_lock);
+ return (NERR_ItemNotFound);
+ }
+
+ si = (lmshare_info_t *)item->hi_data;
+ if (si == NULL) {
+ (void) rw_unlock(&lmshare_lock);
+ return (NERR_InternalError);
+ }
+
+ if ((si->mode & LMSHRM_TRANS) != 0) {
+ si->refcnt--;
+ if (si->refcnt > 0) {
+ status = lmshare_set_refcnt(si->share_name, si->refcnt);
+ (void) rw_unlock(&lmshare_lock);
+ return (status);
+ }
+ }
+
+ if (doshm && (lmshare_delete_shmgr(si) != 0)) {
+ (void) rw_unlock(&lmshare_lock);
+ return (NERR_InternalError);
+ }
+
+ lmshare_do_publish(si, LMSHR_UNPUBLISH, 1);
+
+ /*
+ * Copy the path before the entry is removed from the hash table
+ */
+
+ (void) strlcpy(path, si->directory, MAXPATHLEN);
+
+ /* Delete from hash table */
+
+ (void) ht_remove_item(lmshare_handle, share_name);
+ (void) rw_unlock(&lmshare_lock);
+
+ return (smb_dwncall_share(LMSHR_DELETE, path, share_name));
+}
+
+/*
+ * lmshare_set_refcnt
+ *
+ * sets the autohome refcnt for a share
+ */
+static DWORD
+lmshare_set_refcnt(char *share_name, int refcnt)
+{
+ lmshare_info_t *si;
+ HT_ITEM *item;
+
+ if (share_name) {
+ (void) utf8_strlwr(share_name);
+ }
+ (void) rw_wrlock(&lmshare_lock);
+ item = ht_find_item(lmshare_handle, share_name);
+ if (item == NULL) {
+ (void) rw_unlock(&lmshare_lock);
+ return (NERR_ItemNotFound);
+ }
+
+ si = (lmshare_info_t *)item->hi_data;
+ if (si == NULL) {
+ (void) rw_unlock(&lmshare_lock);
+ return (NERR_InternalError);
+ }
+ si->refcnt = refcnt;
+ (void) rw_unlock(&lmshare_lock);
+ return (NERR_Success);
+}
+
+/*
+ * lmshare_rename
+ *
+ * Rename a share. Check that the current name exists and the new name
+ * doesn't exist. The rename is performed by deleting the current share
+ * definition and creating a new share with the new name.
+ */
+DWORD
+lmshare_rename(char *from_name, char *to_name, int doshm)
+{
+ struct lmshare_info si;
+ DWORD nerr;
+
+ if (lmshare_is_valid(from_name) == 0 ||
+ lmshare_is_valid(to_name) == 0)
+ return (NERR_InvalidDevice);
+
+ (void) utf8_strlwr(from_name);
+ (void) utf8_strlwr(to_name);
+
+ if (lmshare_exists(from_name) == 0)
+ return (NERR_NetNameNotFound);
+
+ if (lmshare_exists(to_name))
+ return (NERR_DuplicateShare);
+
+ if ((nerr = lmshare_getinfo(from_name, &si)) != NERR_Success)
+ return (nerr);
+
+ if ((nerr = lmshare_delete(from_name, doshm)) != NERR_Success)
+ return (nerr);
+
+ (void) strlcpy(si.share_name, to_name, MAXNAMELEN);
+ return (lmshare_add(&si, 1));
+}
+
+/*
+ * lmshare_exists
+ *
+ * Returns 1 if the share exists. Otherwise returns 0.
+ */
+int
+lmshare_exists(char *share_name)
+{
+ if (share_name == 0 || *share_name == 0)
+ return (0);
+
+ if (ht_find_item(lmshare_handle, share_name) == NULL)
+ return (0);
+ else
+ return (1);
+}
+
+/*
+ * lmshare_is_special
+ *
+ * Simple check to determine if share name represents a special share,
+ * i.e. the last character of the name is a '$'. Returns STYPE_SPECIAL
+ * if the name is special. Otherwise returns 0.
+ */
+int
+lmshare_is_special(char *share_name)
+{
+ int len;
+
+ if (share_name == 0)
+ return (0);
+
+ if ((len = strlen(share_name)) == 0)
+ return (0);
+
+ if (share_name[len - 1] == '$')
+ return (STYPE_SPECIAL);
+ else
+ return (0);
+}
+
+
+/*
+ * lmshare_is_restricted
+ *
+ * Check whether or not there is a restriction on a share. Restricted
+ * shares are generally STYPE_SPECIAL, for example, IPC$. All the
+ * administration share names are restricted: C$, D$ etc. Returns 1
+ * if the share is restricted. Otherwise 0 is returned to indicate
+ * that there are no restrictions.
+ */
+int
+lmshare_is_restricted(char *share_name)
+{
+ static char *restricted[] = {
+ "IPC$"
+ };
+
+ int i;
+
+ for (i = 0; i < sizeof (restricted)/sizeof (restricted[0]); i++) {
+ if (strcasecmp(restricted[i], share_name) == 0)
+ return (1);
+ }
+
+ if (lmshare_is_admin(share_name))
+ return (1);
+
+ return (0);
+}
+
+
+/*
+ * lmshare_is_admin
+ *
+ * Check whether or not access to the share should be restricted to
+ * administrators. This is a bit of a hack because what we're doing
+ * is checking for the default admin shares: C$, D$ etc.. There are
+ * other shares that have restrictions: see lmshare_is_restricted().
+ *
+ * Returns 1 if the shares is an admin share. Otherwise 0 is returned
+ * to indicate that there are no restrictions.
+ */
+int
+lmshare_is_admin(char *share_name)
+{
+ if (share_name == 0)
+ return (0);
+
+ if (strlen(share_name) == 2 &&
+ mts_isalpha(share_name[0]) && share_name[1] == '$') {
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * lmshare_is_valid
+ *
+ * Check if any invalid char is present in share name. According to
+ * MSDN article #236388: "Err Msg: The Share Name Contains Invalid
+ * Characters", the list of invalid character is:
+ *
+ * " / \ [ ] : | < > + ; , ? * =
+ *
+ * Also rejects if control characters are embedded.
+ *
+ * If the sharename is valid, return (1). Otherwise return (0).
+ */
+int
+lmshare_is_valid(char *share_name)
+{
+ char *invalid = "\"/\\[]:|<>+;,?*=";
+ char *cp;
+
+ if (share_name == 0)
+ return (0);
+
+ if (strpbrk(share_name, invalid))
+ return (0);
+
+ for (cp = share_name; *cp != '\0'; cp++)
+ if (iscntrl(*cp))
+ return (0);
+
+ return (1);
+}
+
+/*
+ * lmshare_is_dir
+ *
+ * Check to determine if a share object represents a directory.
+ *
+ * Returns 1 if the path leads to a directory. Otherwise returns 0.
+ */
+int
+lmshare_is_dir(char *path)
+{
+ struct stat stat_info;
+
+ if (stat(path, &stat_info) == 0)
+ if (S_ISDIR(stat_info.st_mode))
+ return (1);
+
+ return (0);
+
+}
+
+/*
+ * lmshare_getinfo
+ *
+ * Load the information for the specified share into the supplied share
+ * info structure. If the shared directory does not begin with a /, one
+ * will be inserted as a prefix.
+ */
+DWORD
+lmshare_getinfo(char *share_name, struct lmshare_info *si)
+{
+ int i, endidx;
+ int dirlen;
+ HT_ITEM *item;
+
+ (void) rw_rdlock(&lmshare_lock);
+
+ (void) utf8_strlwr(share_name);
+ if ((item = ht_find_item(lmshare_handle, share_name)) == NULL) {
+ bzero(si, sizeof (lmshare_info_t));
+ (void) rw_unlock(&lmshare_lock);
+ return (NERR_NetNameNotFound);
+ }
+
+ (void) memcpy(si, item->hi_data, sizeof (lmshare_info_t));
+ (void) rw_unlock(&lmshare_lock);
+
+ if (si->directory[0] == '\0')
+ return (NERR_NetNameNotFound);
+
+ if (si->directory[0] != '/') {
+ dirlen = strlen(si->directory) + 1;
+ endidx = (dirlen < MAXPATHLEN-1) ?
+ dirlen : MAXPATHLEN - 2;
+ for (i = endidx; i >= 0; i--)
+ si->directory[i+1] = si->directory[i];
+ si->directory[MAXPATHLEN-1] = '\0';
+ si->directory[0] = '/';
+ }
+
+ return (NERR_Success);
+}
+
+/*
+ * Remove share from sharemanager repository.
+ */
+static int
+lmshare_delete_shmgr(struct lmshare_info *si)
+{
+ sa_handle_t handle;
+ sa_share_t share;
+ sa_resource_t resource;
+
+ handle = sa_init(SA_INIT_SHARE_API);
+ if (handle == NULL) {
+ syslog(LOG_ERR, "Failed to get handle to "
+ "share lib");
+ return (1);
+ }
+ share = sa_find_share(handle, si->directory);
+ if (share == NULL) {
+ syslog(LOG_ERR, "Failed to get share to delete");
+ sa_fini(handle);
+ return (1);
+ }
+ resource = sa_get_share_resource(share, si->share_name);
+ if (resource == NULL) {
+ syslog(LOG_ERR, "Failed to get share resource to delete");
+ sa_fini(handle);
+ return (1);
+ }
+ if (sa_remove_resource(resource) != SA_OK) {
+ syslog(LOG_ERR, "Failed to remove resource");
+ sa_fini(handle);
+ return (1);
+ }
+ sa_fini(handle);
+ return (0);
+}
+
+static int
+lmshare_setinfo_shmgr(struct lmshare_info *si)
+{
+ sa_handle_t handle;
+ sa_share_t share;
+ sa_group_t group;
+ sa_resource_t resource;
+ int share_created = 0;
+ int err;
+
+ /* Add share to sharemanager */
+ handle = sa_init(SA_INIT_SHARE_API);
+ if (handle == NULL) {
+ syslog(LOG_ERR, "Failed to get handle to share lib");
+ return (1);
+ }
+ share = sa_find_share(handle, si->directory);
+ if (share == NULL) {
+ group = smb_get_smb_share_group(handle);
+ if (group == NULL) {
+ sa_fini(handle);
+ return (1);
+ }
+ share = sa_add_share(group, si->directory, 0, &err);
+ if (share == NULL) {
+ sa_fini(handle);
+ return (1);
+ }
+ share_created = 1;
+ }
+ resource = sa_get_share_resource(share, si->share_name);
+ if (resource == NULL) {
+ resource = sa_add_resource(share, si->share_name,
+ SA_SHARE_PERMANENT, &err);
+ if (resource == NULL) {
+ goto failure;
+ }
+ }
+ if (sa_set_resource_attr(resource,
+ "description", si->comment) != SA_OK) {
+ syslog(LOG_ERR, "Falied to set resource "
+ "description in sharemgr");
+ goto failure;
+ }
+ if (sa_set_resource_attr(resource,
+ SHOPT_AD_CONTAINER, si->container) != SA_OK) {
+ syslog(LOG_ERR, "Falied to set ad-container in sharemgr");
+ goto failure;
+ }
+
+ sa_fini(handle);
+ return (0);
+failure:
+ if (share_created && (share != NULL)) {
+ if (sa_remove_share(share) != SA_OK) {
+ syslog(LOG_ERR, "Failed to cleanup share");
+ }
+ }
+ if (resource != NULL) {
+ if (sa_remove_resource(resource) != SA_OK) {
+ syslog(LOG_ERR, "Failed to cleanup share resource");
+ }
+ }
+ sa_fini(handle);
+ return (1);
+}
+
+/*
+ * del_fromhash
+ *
+ * Delete the given share only from hash table
+ */
+static DWORD
+del_fromhash(char *share_name)
+{
+ if (share_name == 0)
+ return (NERR_NetNameNotFound);
+
+ (void) utf8_strlwr(share_name);
+
+ if (lmshare_is_valid(share_name) == 0 ||
+ lmshare_exists(share_name) == 0) {
+ return (NERR_NetNameNotFound);
+ }
+
+ (void) rw_wrlock(&lmshare_lock);
+ (void) ht_remove_item(lmshare_handle, share_name);
+ (void) rw_unlock(&lmshare_lock);
+
+ return (NERR_Success);
+}
+
+/*
+ * lmshare_setinfo
+ *
+ * Adds the specified share into the system hash table
+ * and also store its info in the corresponding disk
+ * structure if it is not a temporary (LMSHRM_TRANS) share.
+ * when the first share is going to be added, create shares
+ * hash table if it is not already created.
+ * If the share already exists, it will be replaced. If the
+ * new share directory name does not begin with a /, one will be
+ * inserted as a prefix.
+ */
+DWORD
+lmshare_setinfo(lmshare_info_t *si, int doshm)
+{
+ int i, endidx;
+ int dirlen;
+ lmshare_info_t *add_si;
+ int res = NERR_Success;
+ lmshare_info_t old_si;
+
+ if (si->directory[0] != '/') {
+ dirlen = strlen(si->directory) + 1;
+ endidx = (dirlen < MAXPATHLEN - 1) ?
+ dirlen : MAXPATHLEN - 2;
+ for (i = endidx; i >= 0; i--)
+ si->directory[i+1] = si->directory[i];
+ si->directory[MAXPATHLEN-1] = '\0';
+ si->directory[0] = '/';
+ }
+
+ /* XXX Do we need to translate the directory here? to real path */
+ if (lmshare_is_dir(si->directory) == 0)
+ return (NERR_UnknownDevDir);
+
+ /*
+ * We should allocate memory for new entry because we
+ * don't know anything about the passed pointer i.e.
+ * it maybe destroyed by caller of this function while
+ * we only store a pointer to the data in hash table.
+ * Hash table doesn't do any allocation for the data that
+ * is being added.
+ */
+ add_si = malloc(sizeof (lmshare_info_t));
+ if (add_si == NULL) {
+ syslog(LOG_ERR, "LmshareSetinfo: resource shortage");
+ return (NERR_NoRoom);
+ }
+
+ (void) memcpy(add_si, si, sizeof (lmshare_info_t));
+
+ /*
+ * If we can't find it, use the new one to get things in sync,
+ * but if there is an existing one, that is the one to
+ * unpublish.
+ */
+ if (lmshare_getinfo(si->share_name, &old_si) != NERR_Success)
+ (void) memcpy(&old_si, si, sizeof (lmshare_info_t));
+
+ if (doshm) {
+ res = lmshare_delete(si->share_name, doshm);
+ if (res != NERR_Success) {
+ free(add_si);
+ syslog(LOG_ERR, "LmshareSetinfo: delete failed", res);
+ return (res);
+ }
+ } else {
+ /* Unpublish old share from AD */
+ if ((si->mode & LMSHRM_TRANS) == 0) {
+ lmshare_do_publish(&old_si, LMSHR_UNPUBLISH, 1);
+ }
+ (void) del_fromhash(si->share_name);
+ }
+ /* if it's not transient it should be permanent */
+ if ((add_si->mode & LMSHRM_TRANS) == 0)
+ add_si->mode |= LMSHRM_PERM;
+
+
+ add_si->stype = STYPE_DISKTREE;
+ add_si->stype |= lmshare_is_special(add_si->share_name);
+
+ (void) rw_wrlock(&lmshare_lock);
+ if (ht_add_item(lmshare_handle, add_si->share_name, add_si) == NULL) {
+ syslog(LOG_ERR, "lmshare_setinfo[%s]: error in adding share",
+ add_si->share_name);
+ (void) rw_unlock(&lmshare_lock);
+ free(add_si);
+ return (NERR_InternalError);
+ }
+ (void) rw_unlock(&lmshare_lock);
+
+ if ((add_si->mode & LMSHRM_TRANS) == 0) {
+ if (doshm && (lmshare_setinfo_shmgr(add_si) != 0)) {
+ syslog(LOG_ERR, "Update share %s in sharemgr failed",
+ add_si->share_name);
+ return (NERR_InternalError);
+ }
+ lmshare_do_publish(add_si, LMSHR_PUBLISH, 1);
+ }
+
+ return (res);
+}
+
+/*
+ * lmshare_decode_type
+ *
+ * Gets a SMB share type as an integer value and return
+ * a string name for it.
+ */
+static char *
+lmshare_decode_type(uint_t stype)
+{
+ switch (stype) {
+ case STYPE_DISKTREE:
+ return ("Disk");
+ case STYPE_PRINTQ:
+ return ("Print Queue");
+ case STYPE_DEVICE:
+ return ("Device");
+ case STYPE_IPC:
+ return ("IPC");
+ case STYPE_DFS:
+ return ("DFS");
+ case STYPE_SPECIAL:
+ return ("Special");
+ default:
+ return ("Unknown");
+ /* NOTREACHED */
+ };
+}
+
+/*
+ * lmshare_loginfo
+ *
+ * Decodes and writes the information of the given
+ * share to the specified file.
+ */
+void
+lmshare_loginfo(FILE *fp, lmshare_info_t *si)
+{
+ (void) fprintf(fp, "\n%s Information:\n", si->share_name);
+ (void) fprintf(fp, "\tFolder: %s\n", si->directory);
+ (void) fprintf(fp, "\tType: %s\n",
+ lmshare_decode_type((uint_t)si->stype));
+ (void) fprintf(fp, "\tComment: %s\n", si->comment);
+
+ (void) fprintf(fp, "\tStatus: %s\n",
+ ((si->mode & LMSHRM_TRANS) ? "Transient" : "Permanent"));
+
+ (void) fprintf(fp, "\tContainer: %s\n", si->container);
+}
+
+DWORD
+lmshare_list(int offset, lmshare_list_t *list)
+{
+ lmshare_iterator_t *iterator;
+ lmshare_info_t *si;
+ int list_idx = 0;
+ int i = 0;
+
+ bzero(list, sizeof (lmshare_list_t));
+ if ((iterator = lmshare_open_iterator(LMSHRM_ALL)) == NULL)
+ return (NERR_InternalError);
+
+ (void) lmshare_iterate(iterator); /* To skip IPC$ */
+
+ while ((si = lmshare_iterate(iterator)) != NULL) {
+ if (lmshare_is_special(si->share_name)) {
+ /*
+ * Don't return restricted shares.
+ */
+ if (lmshare_is_restricted(si->share_name))
+ continue;
+ }
+
+ if (i++ < offset)
+ continue;
+
+ (void) memcpy(&list->smbshr[list_idx], si,
+ sizeof (lmshare_info_t));
+ if (++list_idx == LMSHARES_PER_REQUEST)
+ break;
+ }
+ lmshare_close_iterator(iterator);
+
+ list->no = list_idx;
+
+ return (NERR_Success);
+}
+
+/*
+ * Put the share on publish queue.
+ */
+void
+lmshare_do_publish(lmshare_info_t *si, char flag, int poke)
+{
+ lmshare_ad_item_t *item = NULL;
+
+ if (publish_on == 0)
+ return;
+ if ((si == NULL) || (si->container[0] == '\0'))
+ return;
+ (void) mutex_lock(&lmshare_publish_mutex);
+ item = (lmshare_ad_item_t *)malloc(sizeof (lmshare_ad_item_t));
+ if (item == NULL) {
+ syslog(LOG_ERR, "Failed to allocate share publish item");
+ (void) mutex_unlock(&lmshare_publish_mutex);
+ return;
+ }
+ item->flag = flag;
+ (void) strlcpy(item->name, si->share_name, sizeof (item->name));
+ (void) strlcpy(item->container, si->container,
+ sizeof (item->container));
+ /*LINTED - E_CONSTANT_CONDITION*/
+ TAILQ_INSERT_TAIL(&ad_queue.adlist, item, next);
+ ad_queue.nentries++;
+ if (poke)
+ (void) cond_signal(&lmshare_publish_cv);
+ (void) mutex_unlock(&lmshare_publish_mutex);
+}
+
+void
+lmshare_stop_publish()
+{
+ (void) mutex_lock(&lmshare_publish_mutex);
+ publish_on = 0;
+ (void) cond_signal(&lmshare_publish_cv);
+ (void) mutex_unlock(&lmshare_publish_mutex);
+}
+
+/*
+ * This functions waits to be signaled and once running
+ * will publish/unpublish any items on list.
+ * lmshare_stop_publish when called will exit this thread.
+ */
+/*ARGSUSED*/
+static void *
+lmshare_publisher(void *arg)
+{
+ ADS_HANDLE *ah;
+ lmshare_ad_item_t *item;
+ char hostname[MAXHOSTNAMELEN];
+ char name[MAXNAMELEN];
+ char container[MAXPATHLEN];
+ char flag;
+
+ /*LINTED - E_CONSTANT_CONDITION*/
+ TAILQ_INIT(&ad_queue.adlist);
+ ad_queue.nentries = 0;
+ publish_on = 1;
+ hostname[0] = '\0';
+
+ for (;;) {
+ (void) cond_wait(&lmshare_publish_cv,
+ &lmshare_publish_mutex);
+
+ if (hostname[0] == '\0') {
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0)
+ continue;
+ }
+
+ if (publish_on == 0) {
+ syslog(LOG_DEBUG, "lmshare: publisher exit");
+ if (ad_queue.nentries == 0) {
+ (void) mutex_unlock(&lmshare_publish_mutex);
+ break;
+ }
+ for (item = TAILQ_FIRST(&ad_queue.adlist); item;
+ item = TAILQ_FIRST(&ad_queue.adlist)) {
+ /*LINTED - E_CONSTANT_CONDITION*/
+ TAILQ_REMOVE(&ad_queue.adlist, item, next);
+ (void) free(item);
+ }
+ ad_queue.nentries = 0;
+ (void) mutex_unlock(&lmshare_publish_mutex);
+ break;
+ }
+ if (ad_queue.nentries == 0)
+ continue;
+ ah = ads_open();
+ if (ah == NULL) {
+ /* We mostly have no AD config so just clear the list */
+ for (item = TAILQ_FIRST(&ad_queue.adlist); item;
+ item = TAILQ_FIRST(&ad_queue.adlist)) {
+ /*LINTED - E_CONSTANT_CONDITION*/
+ TAILQ_REMOVE(&ad_queue.adlist, item, next);
+ (void) free(item);
+ }
+ ad_queue.nentries = 0;
+ continue;
+ }
+ TAILQ_FOREACH(item, &ad_queue.adlist, next) {
+ (void) strlcpy(name, item->name, sizeof (name));
+ (void) strlcpy(container, item->container,
+ sizeof (container));
+ flag = item->flag;
+
+ if (flag == LMSHR_UNPUBLISH)
+ (void) ads_remove_share(ah, name, NULL,
+ container, hostname);
+ else
+ (void) ads_publish_share(ah, name, NULL,
+ container, hostname);
+ }
+ for (item = TAILQ_FIRST(&ad_queue.adlist); item;
+ item = TAILQ_FIRST(&ad_queue.adlist)) {
+ /*LINTED - E_CONSTANT_CONDITION*/
+ TAILQ_REMOVE(&ad_queue.adlist, item, next);
+ (void) free(item);
+ }
+ ad_queue.nentries = 0;
+ if (ah != NULL) {
+ ads_close(ah);
+ ah = NULL;
+ }
+ }
+
+ syslog(LOG_DEBUG, "lmshare: Stopping publisher");
+ return (NULL);
+}
+
+/*
+ * lmshare_get_realpath
+ *
+ * Derive the real path of a share from the path provided by a
+ * Windows client application during the share addition.
+ *
+ * For instance, the real path of C:\ is /cvol and the
+ * real path of F:\home is /vol1/home.
+ *
+ * clipath - path provided by the Windows client is in the
+ * format of <drive letter>:\<dir>
+ * realpath - path that will be stored as the directory field of
+ * the lmshare_info_t structure of the share.
+ * maxlen - maximum length fo the realpath buffer
+ *
+ * Return LAN Manager network error code.
+ */
+/*ARGSUSED*/
+DWORD
+lmshare_get_realpath(const char *clipath, char *realpath, int maxlen)
+{
+ /* XXX do this translation */
+ return (NERR_Success);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c
new file mode 100644
index 0000000000..ca98eb8eab
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c
@@ -0,0 +1,637 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module provides the high level interface to the LSA RPC functions.
+ */
+
+#include <strings.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbns.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/lsalib.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/smb_token.h>
+
+static int lsa_list_accounts(mlsvc_handle_t *);
+
+/*
+ * lsa_query_primary_domain_info
+ *
+ * Obtains the primary domain SID and name from the specified server
+ * (domain controller). The information is stored in the NT domain
+ * database by the lower level lsar_query_info_policy call. The caller
+ * should query the database to obtain a reference to the primary
+ * domain information.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+lsa_query_primary_domain_info(void)
+{
+ mlsvc_handle_t domain_handle;
+ DWORD status;
+
+ if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0)
+ return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+
+ status = lsar_query_info_policy(&domain_handle,
+ MSLSA_POLICY_PRIMARY_DOMAIN_INFO);
+
+ (void) lsar_close(&domain_handle);
+ return (status);
+}
+
+/*
+ * lsa_query_account_domain_info
+ *
+ * Obtains the account domain SID and name from the current server
+ * (domain controller). The information is stored in the NT domain
+ * database by the lower level lsar_query_info_policy call. The caller
+ * should query the database to obtain a reference to the account
+ * domain information.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+lsa_query_account_domain_info(void)
+{
+ mlsvc_handle_t domain_handle;
+ DWORD status;
+
+ if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0)
+ return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+
+ status = lsar_query_info_policy(&domain_handle,
+ MSLSA_POLICY_ACCOUNT_DOMAIN_INFO);
+
+ (void) lsar_close(&domain_handle);
+ return (status);
+}
+
+/*
+ * lsa_enum_trusted_domains
+ *
+ * Enumerate the trusted domains in our primary domain. The information
+ * is stored in the NT domain database by the lower level
+ * lsar_enum_trusted_domains call. The caller should query the database
+ * to obtain a reference to the trusted domain information.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+lsa_enum_trusted_domains(void)
+{
+ mlsvc_handle_t domain_handle;
+ DWORD enum_context;
+ DWORD status;
+
+ if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0)
+ return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+
+ enum_context = 0;
+
+ status = lsar_enum_trusted_domains(&domain_handle, &enum_context);
+ if (status == MLSVC_NO_MORE_DATA) {
+ /*
+ * MLSVC_NO_MORE_DATA indicates that we
+ * have all of the available information.
+ */
+ status = NT_STATUS_SUCCESS;
+ }
+
+ (void) lsar_close(&domain_handle);
+ return (status);
+}
+
+/*
+ * lsa_test_lookup
+ *
+ * Test routine for lsa_lookup_name and lsa_lookup_sid.
+ */
+void
+lsa_test_lookup(char *name)
+{
+ smb_userinfo_t *user_info;
+ nt_sid_t *sid;
+ DWORD status;
+ smb_ntdomain_t *di;
+
+ if ((di = smb_getdomaininfo(0)) == 0)
+ return;
+
+ user_info = mlsvc_alloc_user_info();
+
+ if (lsa_lookup_builtin_name(name, user_info) != 0) {
+ status = lsa_lookup_name(di->server, di->domain, name,
+ user_info);
+
+ if (status == 0) {
+ sid = nt_sid_splice(user_info->domain_sid,
+ user_info->rid);
+
+ (void) lsa_lookup_sid(sid, user_info);
+ free(sid);
+ }
+ }
+
+ mlsvc_free_user_info(user_info);
+}
+
+/*
+ * lsa_lookup_builtin_name
+ *
+ * lookup builtin account table to see if account_name is
+ * there. If it is there, set sid_name_use, domain_sid,
+ * domain_name, and rid fields of the passed user_info
+ * structure and return 0. If lookup fails return 1.
+ */
+int
+lsa_lookup_builtin_name(char *account_name, smb_userinfo_t *user_info)
+{
+ char *domain;
+ int res;
+
+ user_info->domain_sid = nt_builtin_lookup_name(account_name,
+ &user_info->sid_name_use);
+
+ if (user_info->domain_sid == 0)
+ return (1);
+
+ res = nt_sid_split(user_info->domain_sid, &user_info->rid);
+ if (res < 0)
+ return (1);
+
+ domain = nt_builtin_lookup_domain(account_name);
+ if (domain) {
+ user_info->domain_name = strdup(domain);
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * lsa_lookup_local_sam
+ *
+ * lookup for the given account name in the local SAM database.
+ * Returns 0 on success. If lookup fails return 1.
+ */
+int
+lsa_lookup_local_sam(char *domain, char *account_name,
+ smb_userinfo_t *user_info)
+{
+ nt_group_t *grp;
+
+ if (*domain == '\0' || *account_name == '\0')
+ return (1);
+
+ grp = nt_group_getinfo(account_name, RWLOCK_READER);
+ if (grp == 0)
+ return (1);
+
+ user_info->sid_name_use = *grp->sid_name_use;
+ user_info->domain_sid = nt_sid_dup(grp->sid);
+ nt_group_putinfo(grp);
+
+ if (user_info->domain_sid == 0)
+ return (1);
+
+ (void) nt_sid_split(user_info->domain_sid, &user_info->rid);
+ user_info->domain_name = strdup(domain);
+
+ if (user_info->domain_name == 0) {
+ free(user_info->domain_sid);
+ user_info->domain_sid = 0;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * lsa_lookup_local
+ *
+ * if given account name has domain part, check to see if
+ * it matches with host name or any of host's primary addresses.
+ * if any match found first lookup in builtin accounts table and
+ * then in local SAM table.
+ *
+ * if account name doesn't have domain part, first do local lookups
+ * if nothing is found return 1. This means that caller function should
+ * do domain lookup.
+ * if any error happened return -1, if name is found return 0.
+ */
+int
+lsa_lookup_local(char *name, smb_userinfo_t *user_info)
+{
+ char hostname[MAXHOSTNAMELEN];
+ int res = 0;
+ int local_lookup = 0;
+ char *tmp;
+ net_cfg_t cfg;
+ uint32_t addr;
+
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0)
+ return (-1);
+
+ tmp = strchr(name, '\\');
+ if (tmp != 0) {
+ *tmp = 0;
+ if (strcasecmp(name, hostname) == 0)
+ local_lookup = 1;
+
+ if (!local_lookup) {
+ addr = inet_addr(name);
+ if (smb_nic_get_byip(addr, &cfg) != NULL) {
+ local_lookup = 1;
+ }
+ }
+
+ if (!local_lookup) {
+ /* do domain lookup */
+ *tmp = '\\';
+ return (1);
+ }
+
+ name = tmp + 1;
+ local_lookup = 1;
+ }
+
+ res = lsa_lookup_builtin_name(name, user_info);
+ if (res != 0)
+ res = lsa_lookup_local_sam(hostname, name, user_info);
+
+ if (res == 0)
+ return (0);
+
+ if (local_lookup)
+ return (-1);
+
+ return (1);
+}
+
+/*
+ * lsa_lookup_name
+ *
+ * Lookup a name on the specified server (domain controller) and obtain
+ * the appropriate SID. The information is returned in the user_info
+ * structure. The caller is responsible for allocating and releasing
+ * this structure. On success sid_name_use will be set to indicate the
+ * type of SID. If the name is the domain name, this function will be
+ * identical to lsa_domain_info. Otherwise the rid and name fields will
+ * also be valid. On failure sid_name_use will be set to SidTypeUnknown.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+int lsa_lookup_name(char *server, char *domain, char *account_name,
+ smb_userinfo_t *user_info)
+{
+ mlsvc_handle_t domain_handle;
+ int rc;
+
+ rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle);
+ if (rc != 0)
+ return (-1);
+
+ rc = lsar_lookup_names(&domain_handle, account_name, user_info);
+
+ (void) lsar_close(&domain_handle);
+ return (rc);
+}
+
+/*
+ * lsa_lookup_name2
+ *
+ * Returns NT status codes.
+ */
+DWORD lsa_lookup_name2(char *server, char *domain, char *account_name,
+ smb_userinfo_t *user_info)
+{
+ mlsvc_handle_t domain_handle;
+ DWORD status;
+ int rc;
+
+ rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle);
+ if (rc != 0)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ status = lsar_lookup_names2(&domain_handle, account_name, user_info);
+ if (status == NT_STATUS_REVISION_MISMATCH) {
+ /*
+ * Not a Windows 2000 domain controller:
+ * use the NT compatible call.
+ */
+ if (lsar_lookup_names(&domain_handle, account_name,
+ user_info) != 0)
+ status = NT_STATUS_NONE_MAPPED;
+ else
+ status = 0;
+ }
+
+ (void) lsar_close(&domain_handle);
+ return (status);
+}
+
+/*
+ * lsa_lookup_sid
+ *
+ * Lookup a SID on the specified server (domain controller) and obtain
+ * the appropriate name. The information is returned in the user_info
+ * structure. The caller is responsible for allocating and releasing
+ * this structure. On success sid_name_use will be set to indicate the
+ * type of SID. On failure sid_name_use will be set to SidTypeUnknown.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+int
+lsa_lookup_sid(nt_sid_t *sid, smb_userinfo_t *user_info)
+{
+ mlsvc_handle_t domain_handle;
+ int rc;
+
+ rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle);
+ if (rc != 0)
+ return (-1);
+
+ rc = lsar_lookup_sids(&domain_handle,
+ (struct mslsa_sid *)sid, user_info);
+
+ (void) lsar_close(&domain_handle);
+ return (rc);
+}
+
+/*
+ * lsa_lookup_sid2
+ *
+ * Returns NT status codes.
+ */
+DWORD
+lsa_lookup_sid2(nt_sid_t *sid, smb_userinfo_t *user_info)
+{
+ mlsvc_handle_t domain_handle;
+ DWORD status;
+ int rc;
+
+ rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle);
+ if (rc != 0)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ status = lsar_lookup_sids2(&domain_handle,
+ (struct mslsa_sid *)sid, user_info);
+
+ if (status == NT_STATUS_REVISION_MISMATCH) {
+ /*
+ * Not a Windows 2000 domain controller:
+ * use the NT compatible call.
+ */
+ if (lsar_lookup_sids(&domain_handle, (struct mslsa_sid *)sid,
+ user_info) != 0)
+ status = NT_STATUS_NONE_MAPPED;
+ else
+ status = 0;
+ }
+
+ (void) lsar_close(&domain_handle);
+ return (status);
+}
+
+/*
+ * lsa_test_lookup2
+ *
+ * Test routine for lsa_lookup_name2 and lsa_lookup_sid2.
+ */
+void
+lsa_test_lookup2(char *name)
+{
+ smb_userinfo_t *user_info;
+ nt_sid_t *sid;
+ DWORD status;
+ smb_ntdomain_t *di;
+
+ if ((di = smb_getdomaininfo(0)) == 0)
+ return;
+
+ user_info = mlsvc_alloc_user_info();
+
+ if (lsa_lookup_builtin_name(name, user_info) != 0) {
+ status = lsa_lookup_name2(di->server, di->domain, name,
+ user_info);
+
+ if (status == 0) {
+ sid = nt_sid_splice(user_info->domain_sid,
+ user_info->rid);
+
+ (void) lsa_lookup_sid2(sid, user_info);
+ free(sid);
+ }
+ }
+
+ mlsvc_free_user_info(user_info);
+}
+
+/*
+ * lsa_lookup_privs
+ *
+ * Request the privileges associated with the specified account. In
+ * order to get the privileges, we first have to lookup the name on
+ * the specified domain controller and obtain the appropriate SID.
+ * The SID can then be used to open the account and obtain the
+ * account privileges. The results from both the name lookup and the
+ * privileges are returned in the user_info structure. The caller is
+ * responsible for allocating and releasing this structure.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+/*ARGSUSED*/
+int
+lsa_lookup_privs(char *server, char *account_name, char *target_name,
+ smb_userinfo_t *user_info)
+{
+ mlsvc_handle_t domain_handle;
+ int rc;
+#if 0
+ mlsvc_handle_t account_handle;
+ struct mslsa_sid *sid;
+
+ lsa_lookup_name(0, 0, target_name, user_info);
+
+ sid = (struct mslsa_sid *)
+ nt_sid_splice(user_info->domain_sid, user_info->rid);
+
+ lsa_lookup_sid(server, account_name, (nt_sid_t *)sid, user_info);
+#endif
+ if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0)
+ return (-1);
+
+ rc = lsa_list_accounts(&domain_handle);
+#if 0
+ rc = lsar_open_account(&domain_handle, sid, &account_handle);
+ if (rc == 0) {
+ (void) lsar_enum_privs_account(&account_handle, user_info);
+ (void) lsar_close(&account_handle);
+ }
+
+ free(sid);
+#endif
+ (void) lsar_close(&domain_handle);
+ return (rc);
+}
+
+/*
+ * lsa_list_privs
+ *
+ * List the privileges supported by the specified server.
+ * This function is only intended for diagnostics.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+lsa_list_privs(char *server, char *domain)
+{
+ static char name[128];
+ static struct ms_luid luid;
+ mlsvc_handle_t domain_handle;
+ int rc;
+ int i;
+
+ rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle);
+ if (rc != 0)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ for (i = 0; i < 30; ++i) {
+ luid.low_part = i;
+ rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128);
+ if (rc != 0)
+ continue;
+
+ (void) lsar_lookup_priv_value(&domain_handle, name, &luid);
+ (void) lsar_lookup_priv_display_name(&domain_handle, name,
+ name, 128);
+ }
+
+ (void) lsar_close(&domain_handle);
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * lsa_test
+ *
+ * LSA test routine: open and close the LSA interface.
+ * TBD: the parameters should be server and domain.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+/*ARGSUSED*/
+int
+lsa_test(char *server, char *account_name)
+{
+ mlsvc_handle_t domain_handle;
+ int rc;
+
+ rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle);
+ if (rc != 0)
+ return (-1);
+
+ if (lsar_close(&domain_handle) != 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * lsa_list_accounts
+ *
+ * This function can be used to list the accounts in the specified
+ * domain. For now the SIDs are just listed in the system log.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+static int
+lsa_list_accounts(mlsvc_handle_t *domain_handle)
+{
+ mlsvc_handle_t account_handle;
+ struct mslsa_EnumAccountBuf accounts;
+ struct mslsa_sid *sid;
+ char *name;
+ WORD sid_name_use;
+ smb_userinfo_t *user_info;
+ DWORD enum_context = 0;
+ int rc;
+ int i;
+
+ user_info = mlsvc_alloc_user_info();
+ bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf));
+
+ do {
+ rc = lsar_enum_accounts(domain_handle, &enum_context,
+ &accounts);
+ if (rc != 0)
+ return (rc);
+
+ for (i = 0; i < accounts.entries_read; ++i) {
+ sid = accounts.info[i].sid;
+
+ name = nt_builtin_lookup_sid((nt_sid_t *)sid,
+ &sid_name_use);
+
+ if (name == 0) {
+ if (lsar_lookup_sids(domain_handle, sid,
+ user_info) == 0) {
+ name = user_info->name;
+ sid_name_use = user_info->sid_name_use;
+ } else {
+ name = "unknown";
+ sid_name_use = SidTypeUnknown;
+ }
+ }
+
+ nt_sid_logf((nt_sid_t *)sid);
+
+ if (lsar_open_account(domain_handle, sid,
+ &account_handle) == 0) {
+ (void) lsar_enum_privs_account(&account_handle,
+ user_info);
+ (void) lsar_close(&account_handle);
+ }
+
+ free(accounts.info[i].sid);
+ mlsvc_release_user_info(user_info);
+ }
+
+ if (accounts.info)
+ free(accounts.info);
+ } while (rc == 0 && accounts.entries_read != 0);
+
+ mlsvc_free_user_info(user_info);
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsar_lookup.c b/usr/src/lib/smbsrv/libmlsvc/common/lsar_lookup.c
new file mode 100644
index 0000000000..5216818cce
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/lsar_lookup.c
@@ -0,0 +1,875 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Local Security Authority RPC (LSARPC) library interface functions for
+ * query, lookup and enumeration calls.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/errno.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ntaccess.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/ntlocale.h>
+#include <smbsrv/lsalib.h>
+#include <smbsrv/string.h>
+#include <smbsrv/mlsvc.h>
+
+/*
+ * The maximum number of bytes we are prepared to deal with in a
+ * response.
+ */
+#define MLSVC_MAX_RESPONSE_LEN 1024
+
+/*
+ * This structure is used when lookuping up names. We only lookup one
+ * name at a time but the structure will allow for more.
+ */
+typedef struct lookup_name_table {
+ DWORD n_entry;
+ mslsa_string_t name[8];
+} lookup_name_table_t;
+
+/*
+ * lsar_query_security_desc
+ *
+ * Don't use this call yet. It is just a place holder for now.
+ */
+int
+lsar_query_security_desc(mlsvc_handle_t *lsa_handle)
+{
+ struct mslsa_QuerySecurityObject arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int rc;
+ int opnum;
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_QuerySecurityObject;
+
+ bzero(&arg, sizeof (struct mslsa_QuerySecurityObject));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * lsar_query_info_policy
+ *
+ * The general purpose of this function is to allow various pieces of
+ * information to be queried on the domain controller. The only
+ * information queries supported are MSLSA_POLICY_PRIMARY_DOMAIN_INFO
+ * and MSLSA_POLICY_ACCOUNT_DOMAIN_INFO.
+ *
+ * On success, the return code will be 0 and the user_info structure
+ * will be set up. The sid_name_use field will be set to SidTypeDomain
+ * indicating that the domain name and domain sid fields are vaild. If
+ * the infoClass returned from the server is not one of the supported
+ * values, the sid_name_use willbe set to SidTypeUnknown. If the RPC
+ * fails, a negative error code will be returned, in which case the
+ * user_info will not have been updated.
+ */
+DWORD
+lsar_query_info_policy(mlsvc_handle_t *lsa_handle, WORD infoClass)
+{
+ struct mslsa_QueryInfoPolicy arg;
+ struct mlsvc_rpc_context *context;
+ struct mslsa_PrimaryDomainInfo *pd_info;
+ struct mslsa_AccountDomainInfo *ad_info;
+ mlrpc_heapref_t heap;
+ nt_domain_t *nt_new_dp;
+ int opnum;
+ DWORD status;
+
+ if (lsa_handle == 0)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_QueryInfoPolicy;
+
+ bzero(&arg, sizeof (struct mslsa_QueryInfoPolicy));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+ arg.info_class = infoClass;
+
+ (void) mlsvc_rpc_init(&heap);
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ status = NT_SC_VALUE(arg.status);
+ } else {
+ switch (infoClass) {
+ case MSLSA_POLICY_PRIMARY_DOMAIN_INFO:
+ pd_info = &arg.info->ru.pd_info;
+
+ nt_domain_flush(NT_DOMAIN_PRIMARY);
+ nt_new_dp = nt_domain_new(NT_DOMAIN_PRIMARY,
+ (char *)pd_info->name.str,
+ (nt_sid_t *)pd_info->sid);
+ (void) nt_domain_add(nt_new_dp);
+ status = NT_STATUS_SUCCESS;
+ break;
+
+ case MSLSA_POLICY_ACCOUNT_DOMAIN_INFO:
+ ad_info = &arg.info->ru.ad_info;
+
+ nt_domain_flush(NT_DOMAIN_ACCOUNT);
+ nt_new_dp = nt_domain_new(NT_DOMAIN_ACCOUNT,
+ (char *)ad_info->name.str,
+ (nt_sid_t *)ad_info->sid);
+ (void) nt_domain_add(nt_new_dp);
+ status = NT_STATUS_SUCCESS;
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+/*
+ * lsar_lookup_names
+ *
+ * Lookup a name and obtain the domain and user rid. The RPC call will
+ * actually support lookup of multiple names but we probably don't
+ * need to do that. On the final system the lookup level should be
+ * level 2 but for now we want to restrict it to level 1 so that we
+ * don't crash the PDC when we get things wrong.
+ *
+ * If the lookup fails, the status will typically be
+ * NT_STATUS_NONE_MAPPED.
+ */
+int
+lsar_lookup_names(mlsvc_handle_t *lsa_handle, char *name,
+ smb_userinfo_t *user_info)
+{
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ int index;
+ struct mslsa_LookupNames arg;
+ size_t length;
+ lookup_name_table_t name_table;
+ struct mslsa_rid_entry *rid_entry;
+ struct mslsa_domain_entry *domain_entry;
+ char *p;
+
+ if (lsa_handle == NULL || name == NULL || user_info == NULL)
+ return (-1);
+
+ bzero(user_info, sizeof (smb_userinfo_t));
+ user_info->sid_name_use = SidTypeUnknown;
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_LookupNames;
+
+ bzero(&arg, sizeof (struct mslsa_LookupNames));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+
+ arg.name_table = (struct mslsa_lup_name_table *)&name_table;
+ name_table.n_entry = 1;
+
+ /*
+ * Windows NT expects the name length to exclude the terminating
+ * wchar null but doesn't care whether the allosize includes or
+ * excludes the null char. Windows 2000 insists that both the
+ * length and the allosize include the wchar null.
+ *
+ * Note: NT returns an error if the mapped_count is non-zero
+ * when the RPC is called.
+ */
+ if (context->server_os == NATIVE_OS_WIN2000) {
+ /*
+ * Windows 2000 doesn't like an LSA lookup for
+ * DOMAIN\Administrator.
+ */
+ if ((p = strchr(name, '\\')) != 0) {
+ ++p;
+
+ if (strcasecmp(p, "administrator") == 0)
+ name = p;
+ }
+
+ length = mts_wcequiv_strlen(name) + sizeof (mts_wchar_t);
+ arg.lookup_level = MSLSA_LOOKUP_LEVEL_1;
+ } else {
+ length = mts_wcequiv_strlen(name);
+ arg.lookup_level = MSLSA_LOOKUP_LEVEL_1;
+ }
+
+ name_table.name[0].length = length;
+ name_table.name[0].allosize = length;
+ name_table.name[0].str = (unsigned char *)name;
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0) {
+ rc = -1;
+ } else if (arg.mapped_count == 0) {
+ rc = -1;
+ } else {
+ rid_entry = &arg.translated_sids.rids[0];
+ user_info->sid_name_use = rid_entry->sid_name_use;
+ user_info->rid = rid_entry->rid;
+ user_info->name = MEM_STRDUP("mlrpc", name);
+
+ if ((index = rid_entry->domain_index) == -1) {
+ user_info->domain_sid = 0;
+ user_info->domain_name = 0;
+ } else {
+ domain_entry =
+ &arg.domain_table->entries[index];
+ user_info->domain_sid = nt_sid_dup(
+ (nt_sid_t *)domain_entry->domain_sid);
+ user_info->domain_name = MEM_STRDUP("mlrpc",
+ (const char *)
+ domain_entry->domain_name.str);
+ }
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * lsar_lookup_sids
+ *
+ * Lookup a sid and obtain the domain sid and user name. The RPC call
+ * will actually support lookup of multiple sids but we probably don't
+ * need to do that. On the final system the lookup level should be
+ * level 2 but for now we want to restrict it to level 1 so that we
+ * don't crash the PDC when we get things wrong.
+ */
+int
+lsar_lookup_sids(mlsvc_handle_t *lsa_handle, struct mslsa_sid *sid,
+ smb_userinfo_t *user_info)
+{
+ struct mslsa_LookupSids arg;
+ struct mslsa_lup_sid_entry sid_entry;
+ struct mslsa_name_entry *name_entry;
+ struct mslsa_domain_entry *domain_entry;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ int index;
+
+ if (lsa_handle == NULL || sid == NULL || user_info == NULL)
+ return (-1);
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_LookupSids;
+
+ bzero(&arg, sizeof (struct mslsa_LookupSids));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+ arg.lookup_level = MSLSA_LOOKUP_LEVEL_2;
+
+ sid_entry.psid = sid;
+ arg.lup_sid_table.n_entry = 1;
+ arg.lup_sid_table.entries = &sid_entry;
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.mapped_count == 0) {
+ user_info->sid_name_use = SidTypeInvalid;
+ rc = 1;
+ } else {
+ name_entry = &arg.name_table.entries[0];
+ user_info->sid_name_use = name_entry->sid_name_use;
+
+ if (user_info->sid_name_use == SidTypeUser ||
+ user_info->sid_name_use == SidTypeGroup ||
+ user_info->sid_name_use == SidTypeAlias) {
+
+ user_info->rid =
+ sid->SubAuthority[sid->SubAuthCount - 1];
+
+ user_info->name = MEM_STRDUP("mlrpc",
+ (const char *)name_entry->name.str);
+ }
+
+ if ((index = name_entry->domain_ix) == -1) {
+ user_info->domain_sid = 0;
+ user_info->domain_name = 0;
+ } else {
+ domain_entry =
+ &arg.domain_table->entries[index];
+
+ user_info->domain_sid = nt_sid_dup(
+ (nt_sid_t *)domain_entry->domain_sid);
+
+ user_info->domain_name = MEM_STRDUP("mlrpc",
+ (const char *)
+ domain_entry->domain_name.str);
+ }
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * lsar_enum_accounts
+ *
+ * Enumerate the list of accounts (i.e. SIDs). Use the handle returned
+ * from lsa_open_policy2. The enum_context is used to support multiple
+ * calls to this enumeration function. It should be set to 0 on the
+ * first call. It will be updated by the domain controller and should
+ * simply be passed unchanged to subsequent calls until there are no
+ * more accounts. A warning status of 0x1A indicates that no more data
+ * is available. The list of accounts will be returned in accounts.
+ * This list is dynamically allocated using malloc, it should be freed
+ * by the caller when it is no longer required.
+ */
+int
+lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context,
+ struct mslsa_EnumAccountBuf *accounts)
+{
+ struct mslsa_EnumerateAccounts arg;
+ struct mslsa_AccountInfo *info;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ DWORD n_entries;
+ DWORD i;
+ int nbytes;
+
+ if (lsa_handle == NULL || enum_context == NULL || accounts == NULL)
+ return (-1);
+
+ accounts->entries_read = 0;
+ accounts->info = 0;
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_EnumerateAccounts;
+
+ bzero(&arg, sizeof (struct mslsa_EnumerateAccounts));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+ arg.enum_context = *enum_context;
+ arg.max_length = MLSVC_MAX_RESPONSE_LEN;
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0) {
+ if ((arg.status & 0x00FFFFFF) == MLSVC_NO_MORE_DATA) {
+ *enum_context = arg.enum_context;
+ } else {
+ mlsvc_rpc_report_status(opnum,
+ (DWORD)arg.status);
+ rc = -1;
+ }
+ } else if (arg.enum_buf->entries_read != 0) {
+ n_entries = arg.enum_buf->entries_read;
+ nbytes = n_entries * sizeof (struct mslsa_AccountInfo);
+
+ info = (struct mslsa_AccountInfo *)MEM_MALLOC("mlrpc",
+ nbytes);
+ if (info == NULL) {
+ mlsvc_rpc_free(context, &heap);
+ return (-1);
+ }
+
+ for (i = 0; i < n_entries; ++i)
+ info[i].sid = (struct mslsa_sid *)nt_sid_dup(
+ (nt_sid_t *)arg.enum_buf->info[i].sid);
+
+ accounts->entries_read = n_entries;
+ accounts->info = info;
+ *enum_context = arg.enum_context;
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * lsar_enum_trusted_domains
+ *
+ * Enumerate the list of trusted domains. Use the handle returned from
+ * lsa_open_policy2. The enum_context is used to support multiple calls
+ * to this enumeration function. It should be set to 0 on the first
+ * call. It will be updated by the domain controller and should simply
+ * be passed unchanged to subsequent calls until there are no more
+ * domains.
+ *
+ * The trusted domains aren't actually returned here. They are added
+ * to the NT domain database. After all of the trusted domains have
+ * been discovered, the database can be interrogated to find all of
+ * the trusted domains.
+ */
+DWORD
+lsar_enum_trusted_domains(mlsvc_handle_t *lsa_handle, DWORD *enum_context)
+{
+ struct mslsa_EnumTrustedDomain arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ nt_domain_t *nt_new_dp;
+ int opnum;
+ DWORD status;
+ DWORD n_entries;
+ DWORD i;
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_EnumTrustedDomain;
+
+ bzero(&arg, sizeof (struct mslsa_EnumTrustedDomain));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+ arg.enum_context = *enum_context;
+ arg.max_length = MLSVC_MAX_RESPONSE_LEN;
+
+ (void) mlsvc_rpc_init(&heap);
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ *enum_context = arg.enum_context;
+ status = NT_SC_VALUE(arg.status);
+
+ /*
+ * status 0x8000001A means NO_MORE_DATA,
+ * which is not an error.
+ */
+ if (status != MLSVC_NO_MORE_DATA)
+ mlsvc_rpc_report_status(opnum, arg.status);
+ } else if (arg.enum_buf->entries_read == 0) {
+ *enum_context = arg.enum_context;
+ status = 0;
+ } else {
+ nt_domain_flush(NT_DOMAIN_TRUSTED);
+ n_entries = arg.enum_buf->entries_read;
+
+ for (i = 0; i < n_entries; ++i) {
+ nt_new_dp = nt_domain_new(
+ NT_DOMAIN_TRUSTED,
+ (char *)arg.enum_buf->info[i].name.str,
+ (nt_sid_t *)arg.enum_buf->info[i].sid);
+
+ (void) nt_domain_add(nt_new_dp);
+ }
+
+ *enum_context = arg.enum_context;
+ status = 0;
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+/*
+ * lsar_enum_privs_account
+ *
+ * Privileges enum? Need an account handle.
+ */
+/*ARGSUSED*/
+int
+lsar_enum_privs_account(mlsvc_handle_t *account_handle,
+ smb_userinfo_t *user_info)
+{
+ struct mslsa_EnumPrivsAccount arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+
+ context = account_handle->context;
+ opnum = LSARPC_OPNUM_EnumPrivsAccount;
+
+ bzero(&arg, sizeof (struct mslsa_EnumPrivsAccount));
+ (void) memcpy(&arg.account_handle, &account_handle->handle,
+ sizeof (mslsa_handle_t));
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if ((rc == 0) && (arg.status != 0)) {
+ mlsvc_rpc_report_status(opnum, (DWORD)arg.status);
+ rc = -1;
+ }
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * lsar_lookup_priv_value
+ *
+ * Map a privilege name to a local unique id (LUID). Privilege names
+ * are consistent across the network. LUIDs are machine specific.
+ * This function provides the means to map a privilege name to the
+ * LUID used by a remote server to represent it. The handle here is
+ * a policy handle.
+ */
+int
+lsar_lookup_priv_value(mlsvc_handle_t *lsa_handle, char *name,
+ struct ms_luid *luid)
+{
+ struct mslsa_LookupPrivValue arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ size_t length;
+
+ if (lsa_handle == NULL || name == NULL || luid == NULL)
+ return (-1);
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_LookupPrivValue;
+
+ bzero(&arg, sizeof (struct mslsa_LookupPrivValue));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+
+ length = mts_wcequiv_strlen(name);
+ if (context->server_os == NATIVE_OS_WIN2000)
+ length += sizeof (mts_wchar_t);
+
+ arg.name.length = length;
+ arg.name.allosize = length;
+ arg.name.str = (unsigned char *)name;
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0)
+ rc = -1;
+ else
+ (void) memcpy(luid, &arg.luid, sizeof (struct ms_luid));
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * lsar_lookup_priv_name
+ *
+ * Map a local unique id (LUID) to a privilege name. Privilege names
+ * are consistent across the network. LUIDs are machine specific.
+ * This function the means to map the LUID used by a remote server to
+ * the appropriate privilege name. The handle here is a policy handle.
+ */
+int
+lsar_lookup_priv_name(mlsvc_handle_t *lsa_handle, struct ms_luid *luid,
+ char *name, int namelen)
+{
+ struct mslsa_LookupPrivName arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+
+ if (lsa_handle == NULL || luid == NULL || name == NULL)
+ return (-1);
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_LookupPrivName;
+
+ bzero(&arg, sizeof (struct mslsa_LookupPrivName));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+ (void) memcpy(&arg.luid, luid, sizeof (struct ms_luid));
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0)
+ rc = -1;
+ else
+ (void) strlcpy(name, (char const *)arg.name->str,
+ namelen);
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * lsar_lookup_priv_display_name
+ *
+ * Map a privilege name to a privilege display name. The input handle
+ * should be an LSA policy handle and the name would normally be one
+ * of the privileges defined in smb_privilege.h
+ *
+ * There's something peculiar about the return status from NT servers,
+ * it's not always present. So for now, I'm ignoring the status in the
+ * RPC response.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+lsar_lookup_priv_display_name(mlsvc_handle_t *lsa_handle, char *name,
+ char *display_name, int display_len)
+{
+ struct mslsa_LookupPrivDisplayName arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ size_t length;
+ DWORD status;
+
+ if (lsa_handle == NULL || name == NULL || display_name == NULL)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_LookupPrivDisplayName;
+
+ bzero(&arg, sizeof (struct mslsa_LookupPrivDisplayName));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+
+ length = mts_wcequiv_strlen(name);
+ arg.name.length = length;
+ arg.name.allosize = length;
+ arg.name.str = (unsigned char *)name;
+
+ arg.client_language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
+ arg.default_language = MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL);
+
+ (void) mlsvc_rpc_init(&heap);
+
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0)
+ status = NT_STATUS_INVALID_PARAMETER;
+#if 0
+ else if (arg.status != 0)
+ status = NT_SC_VALUE(arg.status);
+#endif
+ else {
+ (void) strlcpy(display_name,
+ (char const *)arg.display_name->str, display_len);
+ status = NT_STATUS_SUCCESS;
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+/*
+ * lsar_lookup_sids2
+ */
+DWORD
+lsar_lookup_sids2(mlsvc_handle_t *lsa_handle, struct mslsa_sid *sid,
+ smb_userinfo_t *user_info)
+{
+ struct lsar_lookup_sids2 arg;
+ struct lsar_name_entry2 *name_entry;
+ struct mslsa_lup_sid_entry sid_entry;
+ struct mslsa_domain_entry *domain_entry;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int index;
+ DWORD status;
+
+ if (lsa_handle == NULL || sid == NULL || user_info == NULL)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_LookupSids2;
+
+ if (context->server_os != NATIVE_OS_WIN2000)
+ return (NT_STATUS_REVISION_MISMATCH);
+
+ bzero(&arg, sizeof (struct lsar_lookup_sids2));
+ (void) memcpy(&arg.policy_handle, lsa_handle, sizeof (mslsa_handle_t));
+
+ sid_entry.psid = sid;
+ arg.lup_sid_table.n_entry = 1;
+ arg.lup_sid_table.entries = &sid_entry;
+ arg.lookup_level = MSLSA_LOOKUP_LEVEL_1;
+ arg.requested_count = arg.lup_sid_table.n_entry;
+
+ (void) mlsvc_rpc_init(&heap);
+
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.mapped_count == 0) {
+ user_info->sid_name_use = SidTypeInvalid;
+ status = NT_STATUS_NONE_MAPPED;
+ } else if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ status = NT_SC_VALUE(arg.status);
+ } else {
+ status = 0;
+
+ name_entry = &arg.name_table.entries[0];
+ user_info->sid_name_use = name_entry->sid_name_use;
+
+ if (user_info->sid_name_use == SidTypeUser ||
+ user_info->sid_name_use == SidTypeGroup ||
+ user_info->sid_name_use == SidTypeAlias) {
+
+ user_info->rid =
+ sid->SubAuthority[sid->SubAuthCount - 1];
+
+ user_info->name = MEM_STRDUP("mlrpc",
+ (char const *)name_entry->name.str);
+
+ }
+
+ if ((index = name_entry->domain_ix) == -1) {
+ user_info->domain_sid = 0;
+ user_info->domain_name = 0;
+ } else {
+ domain_entry = &arg.domain_table->entries[index];
+
+ user_info->domain_sid = nt_sid_dup(
+ (nt_sid_t *)domain_entry->domain_sid);
+
+ user_info->domain_name = MEM_STRDUP("mlrpc",
+ (char const *)domain_entry->domain_name.str);
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+
+/*
+ * lsar_lookup_names2
+ *
+ * Windows NT expects the name length to exclude the terminating
+ * wchar null but Windows 2000 insists that both the length and
+ * the allosize include the wchar null. Windows NT doesn't care
+ * whether or not the allosize includes or excludes the null char.
+ *
+ * As a precaution, I set the lookup level to 1 on Windows 2000
+ * until I can do some more testing.
+ *
+ * Note that NT returns an error if the mapped_count is non-zero
+ * when the RPC is called.
+ *
+ * It should be okay to lookup DOMAIN\Administrator in this function.
+ */
+DWORD
+lsar_lookup_names2(mlsvc_handle_t *lsa_handle, char *name,
+ smb_userinfo_t *user_info)
+{
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int index;
+ struct lsar_LookupNames2 arg;
+ size_t length;
+ lookup_name_table_t name_table;
+ struct lsar_rid_entry2 *rid_entry;
+ struct mslsa_domain_entry *domain_entry;
+ DWORD status;
+
+ if (lsa_handle == NULL || name == NULL || user_info == NULL)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ bzero(user_info, sizeof (smb_userinfo_t));
+ user_info->sid_name_use = SidTypeUnknown;
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_LookupNames2;
+
+ if (context->server_os != NATIVE_OS_WIN2000)
+ return (NT_STATUS_REVISION_MISMATCH);
+
+ bzero(&arg, sizeof (struct lsar_LookupNames2));
+ (void) memcpy(&arg.policy_handle, lsa_handle, sizeof (mslsa_handle_t));
+ arg.unknown_sb2 = 0x00000002;
+ arg.lookup_level = MSLSA_LOOKUP_LEVEL_1;
+
+ arg.name_table = (struct mslsa_lup_name_table *)&name_table;
+ name_table.n_entry = 1;
+
+ length = mts_wcequiv_strlen(name) + sizeof (mts_wchar_t);
+ name_table.name[0].length = length;
+ name_table.name[0].allosize = length;
+ name_table.name[0].str = (unsigned char *)name;
+
+ (void) mlsvc_rpc_init(&heap);
+
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ status = NT_SC_VALUE(arg.status);
+ } else if (arg.mapped_count == 0) {
+ user_info->sid_name_use = SidTypeInvalid;
+ status = NT_STATUS_NONE_MAPPED;
+ } else {
+ status = 0;
+
+ rid_entry = &arg.translated_sids.rids[0];
+ user_info->sid_name_use = rid_entry->sid_name_use;
+ user_info->rid = rid_entry->rid;
+ user_info->name = MEM_STRDUP("mlrpc", name);
+
+ if ((index = rid_entry->domain_index) == -1) {
+ user_info->domain_sid = 0;
+ user_info->domain_name = 0;
+ } else {
+ domain_entry = &arg.domain_table->entries[index];
+
+ user_info->domain_sid = nt_sid_dup(
+ (nt_sid_t *)domain_entry->domain_sid);
+
+ user_info->domain_name = MEM_STRDUP("mlrpc",
+ (char const *)domain_entry->domain_name.str);
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+void
+mlsvc_rpc_report_status(int opnum, DWORD status)
+{
+ char *s = "unknown";
+
+ if (status == 0)
+ s = "success";
+ else if (NT_SC_IS_ERROR(status))
+ s = "error";
+ else if (NT_SC_IS_WARNING(status))
+ s = "warning";
+ else if (NT_SC_IS_INFO(status))
+ s = "info";
+
+ smb_tracef("mlrpc[0x%02x]: %s: %s (0x%08x)",
+ opnum, s, xlate_nt_status(status), status);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsar_open.c b/usr/src/lib/smbsrv/libmlsvc/common/lsar_open.c
new file mode 100644
index 0000000000..57b1b62a97
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/lsar_open.c
@@ -0,0 +1,298 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Local Security Authority RPC (LSARPC) library interface functions for
+ * open and close calls.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbrdr.h>
+#include <smbsrv/mlsvc.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/ntaccess.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/lsalib.h>
+
+/*
+ * lsar_open
+ *
+ * This is a wrapper round lsar_open_policy2 to ensure that we connect
+ * using the appropriate session and logon. We default to the resource
+ * domain information if the caller didn't supply a server name and a
+ * domain name.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+int lsar_open(int ipc_mode, char *server, char *domain, char *username,
+ char *password, mlsvc_handle_t *domain_handle)
+{
+ smb_ntdomain_t *di;
+ int remote_os;
+ int remote_lm;
+ int rc;
+
+ if ((di = smb_getdomaininfo(0)) == NULL)
+ return (-1);
+
+ if (server == NULL || domain == NULL) {
+ server = di->server;
+ domain = di->domain;
+ }
+
+ switch (ipc_mode) {
+ case MLSVC_IPC_USER:
+ /*
+ * Use the supplied credentials.
+ */
+ rc = mlsvc_user_logon(server, domain, username, password);
+ break;
+
+ case MLSVC_IPC_ADMIN:
+ /*
+ * Use the resource domain administrator credentials.
+ */
+ server = di->server;
+ domain = di->domain;
+ username = smbrdr_ipc_get_user();
+
+ rc = mlsvc_admin_logon(server, domain);
+ break;
+
+ case MLSVC_IPC_ANON:
+ default:
+ rc = mlsvc_anonymous_logon(server, domain, &username);
+ break;
+ }
+
+ if (rc != 0)
+ return (-1);
+
+ rc = lsar_open_policy2(server, domain, username, domain_handle);
+ if (rc == 0) {
+ if (mlsvc_session_native_values(domain_handle->context->fid,
+ &remote_os, &remote_lm, 0) != 0)
+ remote_os = NATIVE_OS_UNKNOWN;
+
+ domain_handle->context->server_os = remote_os;
+ }
+ return (rc);
+}
+
+
+/*
+ * lsar_open_policy2
+ *
+ * Obtain an LSA policy handle. A policy handle is required to access
+ * LSA resources on a remote server. The server name supplied here does
+ * not need the double backslash prefix; it is added here. Call this
+ * function via lsar_open to ensure that the appropriate connection is
+ * in place.
+ *
+ * I'm not sure if it makes a difference whether we use GENERIC_EXECUTE
+ * or STANDARD_RIGHTS_EXECUTE. For a long time I used the standard bit
+ * and then I added the generic bit while working on privileges because
+ * NT sets that bit. I don't think it matters.
+ *
+ * Returns 0 on success. Otherwise non-zero to indicate a failure.
+ */
+int lsar_open_policy2(char *server, char *domain, char *username,
+ mlsvc_handle_t *lsa_handle)
+{
+ struct mslsa_OpenPolicy2 arg;
+ mlrpc_heapref_t heap;
+ int rc;
+ int opnum;
+ int fid;
+ int remote_os;
+ int remote_lm;
+ int len;
+
+ if (server == NULL || domain == NULL ||
+ username == NULL || lsa_handle == NULL)
+ return (-1);
+
+ fid = mlsvc_open_pipe(server, domain, username, "\\lsarpc");
+ if (fid < 0)
+ return (-1);
+
+ if ((rc = mlsvc_rpc_bind(lsa_handle, fid, "LSARPC")) < 0) {
+ (void) mlsvc_close_pipe(fid);
+ return (rc);
+ }
+
+ opnum = LSARPC_OPNUM_OpenPolicy2;
+ bzero(&arg, sizeof (struct mslsa_OpenPolicy2));
+
+ len = strlen(server) + 4;
+ arg.servername = malloc(len);
+ if (arg.servername == NULL) {
+ (void) mlsvc_close_pipe(fid);
+ free(lsa_handle->context);
+ return (-1);
+ }
+
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+ arg.attributes.length = sizeof (struct mslsa_object_attributes);
+
+ (void) mlsvc_session_native_values(fid, &remote_os, &remote_lm, 0);
+
+ if (remote_os == NATIVE_OS_NT5_0) {
+ arg.desiredAccess = MAXIMUM_ALLOWED;
+ } else {
+ arg.desiredAccess = GENERIC_EXECUTE
+ | STANDARD_RIGHTS_EXECUTE
+ | POLICY_VIEW_LOCAL_INFORMATION
+ | POLICY_LOOKUP_NAMES;
+ }
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(lsa_handle->context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0) {
+ rc = -1;
+ } else {
+ (void) memcpy(&lsa_handle->handle, &arg.domain_handle,
+ sizeof (mslsa_handle_t));
+
+ if (mlsvc_is_null_handle(lsa_handle))
+ rc = -1;
+ }
+ }
+
+ mlsvc_rpc_free(lsa_handle->context, &heap);
+ free(arg.servername);
+
+ if (rc != 0) {
+ (void) mlsvc_close_pipe(fid);
+ free(lsa_handle->context);
+ }
+
+ return (rc);
+}
+
+/*
+ * lsar_open_account
+ *
+ * Obtain an LSA account handle. The lsa_handle must be a valid handle
+ * obtained via lsar_open_policy2. The main thing to remember here is
+ * to set up the context in the lsa_account_handle. I'm not sure what
+ * the requirements are for desired access. Some values require admin
+ * access.
+ *
+ * Returns 0 on success. Otherwise non-zero to indicate a failure.
+ */
+int
+lsar_open_account(mlsvc_handle_t *lsa_handle, struct mslsa_sid *sid,
+ mlsvc_handle_t *lsa_account_handle)
+{
+ struct mslsa_OpenAccount arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int rc;
+ int opnum;
+
+ if (mlsvc_is_null_handle(lsa_handle) ||
+ sid == NULL || lsa_account_handle == NULL)
+ return (-1);
+
+ context = lsa_handle->context;
+ opnum = LSARPC_OPNUM_OpenAccount;
+ bzero(&arg, sizeof (struct mslsa_OpenAccount));
+
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+ arg.sid = sid;
+ arg.access_mask = STANDARD_RIGHTS_REQUIRED
+#if 0
+ | POLICY_VIEW_AUDIT_INFORMATION
+ | POLICY_GET_PRIVATE_INFORMATION
+ | POLICY_TRUST_ADMIN
+#endif
+ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0) {
+ rc = -1;
+ } else {
+ lsa_account_handle->context = context;
+
+ (void) memcpy(&lsa_account_handle->handle,
+ &arg.account_handle, sizeof (mslsa_handle_t));
+
+ if (mlsvc_is_null_handle(lsa_account_handle))
+ rc = -1;
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * lsar_close
+ *
+ * Close the LSA connection associated with the handle. The lsa_handle
+ * must be a valid handle obtained via a call to lsar_open_policy2 or
+ * lsar_open_account. On success the handle will be zeroed out to
+ * ensure that it is not used again. If this is the top level handle
+ * (i.e. the one obtained via lsar_open_policy2) the pipe is closed
+ * and the context is freed.
+ *
+ * Returns 0 on success. Otherwise non-zero to indicate a failure.
+ */
+int
+lsar_close(mlsvc_handle_t *lsa_handle)
+{
+ struct mslsa_CloseHandle arg;
+ mlrpc_heapref_t heap;
+ int rc;
+ int opnum;
+
+ if (mlsvc_is_null_handle(lsa_handle))
+ return (-1);
+
+ opnum = LSARPC_OPNUM_CloseHandle;
+ bzero(&arg, sizeof (struct mslsa_CloseHandle));
+ (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t));
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(lsa_handle->context, opnum, &arg, &heap);
+ mlsvc_rpc_free(lsa_handle->context, &heap);
+
+ if (lsa_handle->context->handle == &lsa_handle->handle) {
+ (void) mlsvc_close_pipe(lsa_handle->context->fid);
+ free(lsa_handle->context);
+ }
+
+ bzero(lsa_handle, sizeof (mlsvc_handle_t));
+ return (rc);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
new file mode 100644
index 0000000000..e05f51b279
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
@@ -0,0 +1,97 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+SUNWprivate {
+ global:
+ lmshare_add;
+ lmshare_close_iterator;
+ lmshare_delete;
+ lmshare_exists;
+ lmshare_getinfo;
+ lmshare_get_realpath;
+ lmshare_is_admin;
+ lmshare_is_dir;
+ lmshare_is_restricted;
+ lmshare_is_special;
+ lmshare_is_valid;
+ lmshare_iterate;
+ lmshare_list;
+ lmshare_num_shares;
+ lmshare_open_iterator;
+ lmshare_rename;
+ lmshare_setinfo;
+ lmshare_start;
+ lmshare_stop;
+ lsa_lookup_name2;
+ lsa_lookup_sid2;
+ lsa_query_primary_domain_info;
+ lsa_query_account_domain_info;
+ lsa_enum_trusted_domains;
+ mlsvc_init;
+ mlsvc_validate_user;
+ mlsvc_is_local_domain;
+ nt_group_add;
+ nt_group_add_groupprivs;
+ nt_group_add_member_byname;
+ nt_group_cache_size;
+ nt_group_close_iterator;
+ nt_group_delete;
+ nt_group_del_member_byname;
+ nt_group_getinfo;
+ nt_group_getpriv;
+ nt_group_ht_lock;
+ nt_group_ht_unlock;
+ nt_group_is_member;
+ nt_group_iterate;
+ nt_group_modify;
+ nt_group_num_groups;
+ nt_group_num_members;
+ nt_group_open_iterator;
+ nt_group_putinfo;
+ nt_groups_count;
+ nt_group_setpriv;
+ nt_groups_lookup_rid;
+ nt_groups_member_groups;
+ nt_groups_member_ngroups;
+ nt_groups_member_privs;
+ nt_group_list;
+ nt_group_member_list;
+ sam_init;
+ smb_logon;
+ smb_token_destroy;
+ smb_build_lmshare_info;
+ smb_get_smb_share_group;
+ smb_autohome_add;
+ smb_autohome_endent;
+ smb_autohome_getent;
+ smb_autohome_lookup;
+ smb_autohome_remove;
+ smb_autohome_setent;
+ smb_is_autohome;
+ local:
+ *;
+};
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
new file mode 100644
index 0000000000..ac8cc3003c
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
@@ -0,0 +1,306 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Context functions to support the RPC interface library.
+ */
+
+#include <sys/errno.h>
+#include <strings.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbrdr.h>
+#include <smbsrv/mlsvc_util.h>
+
+static int mlsvc_xa_init(struct mlrpc_client *, struct mlrpc_xaction *,
+ mlrpc_heap_t *);
+static int mlsvc_xa_exchange(struct mlrpc_client *, struct mlrpc_xaction *);
+static int mlsvc_xa_read(struct mlrpc_client *, struct mlrpc_xaction *);
+static int mlsvc_xa_preserve(struct mlrpc_client *, struct mlrpc_xaction *,
+ mlrpc_heapref_t *);
+static int mlsvc_xa_destruct(struct mlrpc_client *, struct mlrpc_xaction *);
+static void mlsvc_xa_release(struct mlrpc_client *, mlrpc_heapref_t *heapref);
+
+/*
+ * mlsvc_rpc_bind
+ *
+ * This the entry point for all client RPC services. This call must be
+ * made to initialize an RPC context structure and bind to the remote
+ * service before any RPCs can be exchanged with that service. The
+ * descriptor is a wrapper that is used to associate an RPC handle with
+ * the context data for that specific instance of the interface. The
+ * handle is zeroed to ensure that it doesn't look like a valid handle.
+ * The context handle is assigned to point at the RPC handle so that we
+ * know when to free the context. As each handle is initialized it will
+ * include a pointer to this context but only when we close this initial
+ * RPC handle can the context be freed.
+ *
+ * On success, return a pointer to the descriptor. Otherwise return a
+ * null pointer.
+ */
+int
+mlsvc_rpc_bind(mlsvc_handle_t *desc, int fid, char *service)
+{
+ struct mlsvc_rpc_context *context;
+ int rc;
+
+ bzero(&desc->handle, sizeof (ms_handle_t));
+
+ context = malloc(sizeof (struct mlsvc_rpc_context));
+ if ((desc->context = context) == NULL)
+ return (-1);
+
+ bzero(context, sizeof (struct mlsvc_rpc_context));
+ context->cli.context = context;
+
+ mlrpc_binding_pool_initialize(&context->cli.binding_list,
+ context->binding_pool, CTXT_N_BINDING_POOL);
+
+ context->fid = fid;
+ context->handle = &desc->handle;
+ context->cli.xa_init = mlsvc_xa_init;
+ context->cli.xa_exchange = mlsvc_xa_exchange;
+ context->cli.xa_read = mlsvc_xa_read;
+ context->cli.xa_preserve = mlsvc_xa_preserve;
+ context->cli.xa_destruct = mlsvc_xa_destruct;
+ context->cli.xa_release = mlsvc_xa_release;
+
+ rc = mlrpc_c_bind(&context->cli, service, &context->binding);
+ if (MLRPC_DRC_IS_FAULT(rc)) {
+ free(context);
+ desc->context = NULL;
+ return (-1);
+ }
+
+ return (rc);
+}
+
+/*
+ * mlsvc_rpc_init
+ *
+ * This function must be called by client side applications before
+ * calling mlsvc_rpc_call to allocate a heap. The heap must be
+ * destroyed by either calling mlrpc_heap_destroy or mlsvc_rpc_free.
+ * Use mlrpc_heap_destroy if mlsvc_rpc_call has not yet been called.
+ * Otherwise use mlsvc_rpc_free.
+ *
+ * Returns 0 on success. Otherwise returns -1 to indicate an error.
+ */
+int
+mlsvc_rpc_init(mlrpc_heapref_t *heapref)
+{
+ bzero(heapref, sizeof (mlrpc_heapref_t));
+
+ if ((heapref->heap = mlrpc_heap_create()) == NULL)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * mlsvc_rpc_call
+ *
+ * This function should be called by the client RPC interface functions
+ * to make an RPC call. The remote service is identified by the context
+ * handle, which should have been initialized with by mlsvc_rpc_bind.
+ */
+int
+mlsvc_rpc_call(struct mlsvc_rpc_context *context, int opnum, void *params,
+ mlrpc_heapref_t *heapref)
+{
+ return (mlrpc_c_call(context->binding, opnum, params, heapref));
+}
+
+/*
+ * mlsvc_rpc_free
+ *
+ * This function should be called by the client RPC interface functions
+ * to free the heap after an RPC call returns.
+ */
+void
+mlsvc_rpc_free(struct mlsvc_rpc_context *context, mlrpc_heapref_t *heapref)
+{
+ mlrpc_c_free_heap(context->binding, heapref);
+}
+
+/*
+ * The following functions provide the callback interface in the
+ * context handle.
+ */
+/*ARGSUSED*/
+static int
+mlsvc_xa_init(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa,
+ mlrpc_heap_t *heap)
+{
+ struct mlndr_stream *recv_mlnds = &mxa->recv_mlnds;
+ struct mlndr_stream *send_mlnds = &mxa->send_mlnds;
+
+ /*
+ * If the caller hasn't provided a heap, create one here.
+ */
+ if (heap == 0) {
+ if ((heap = mlrpc_heap_create()) == 0)
+ return (-1);
+ }
+
+ mxa->heap = heap;
+
+ mlnds_initialize(send_mlnds, 0, NDR_MODE_CALL_SEND, heap);
+ mlnds_initialize(recv_mlnds, 16 * 1024, NDR_MODE_RETURN_RECV, heap);
+ return (0);
+}
+
+/*
+ * mlsvc_xa_exchange
+ *
+ * This is the entry pointy for an RPC client call exchange with
+ * a server, which will result in an smbrdr SmbTransact request.
+ *
+ * SmbTransact should return the number of bytes received, which
+ * we record as the PDU size, or a negative error code.
+ */
+static int
+mlsvc_xa_exchange(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa)
+{
+ struct mlsvc_rpc_context *context = mcli->context;
+ struct mlndr_stream *recv_mlnds = &mxa->recv_mlnds;
+ struct mlndr_stream *send_mlnds = &mxa->send_mlnds;
+ int rc;
+
+ rc = smbrdr_rpc_transact(context->fid,
+ (char *)send_mlnds->pdu_base_offset, send_mlnds->pdu_size,
+ (char *)recv_mlnds->pdu_base_offset, recv_mlnds->pdu_max_size);
+
+ if (rc < 0)
+ recv_mlnds->pdu_size = 0;
+ else
+ recv_mlnds->pdu_size = rc;
+
+ return (rc);
+}
+
+/*
+ * mlsvc_xa_read
+ *
+ * This entry point will be invoked if the xa-exchange response contained
+ * only the first fragment of a multi-fragment response. The RPC client
+ * code will then make repeated xa-read requests to obtain the remaining
+ * fragments, which will result in smbrdr SmbReadX requests.
+ *
+ * SmbReadX should return the number of bytes received, in which case we
+ * expand the PDU size to include the received data, or a negative error
+ * code.
+ */
+static int
+mlsvc_xa_read(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa)
+{
+ struct mlsvc_rpc_context *context = mcli->context;
+ struct mlndr_stream *mlnds = &mxa->recv_mlnds;
+ int len;
+ int rc;
+
+ if ((len = (mlnds->pdu_max_size - mlnds->pdu_size)) < 0)
+ return (-1);
+
+ rc = smbrdr_rpc_readx(context->fid,
+ (char *)mlnds->pdu_base_offset + mlnds->pdu_size, len);
+
+ if (rc < 0)
+ return (-1);
+
+ mlnds->pdu_size += rc;
+
+ if (mlnds->pdu_size > mlnds->pdu_max_size) {
+ mlnds->pdu_size = mlnds->pdu_max_size;
+ return (-1);
+ }
+
+ return (rc);
+}
+
+/*
+ * mlsvc_xa_preserve
+ *
+ * This function is called to preserve the heap. We save a reference
+ * to the heap and set the mxa heap pointer to null so that the heap
+ * will not be discarded when mlsvc_xa_destruct is called.
+ */
+/*ARGSUSED*/
+static int
+mlsvc_xa_preserve(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa,
+ mlrpc_heapref_t *heapref)
+{
+ heapref->state = MLRPC_HRST_PRESERVED;
+ heapref->heap = mxa->heap;
+ heapref->recv_pdu_buf = (char *)mxa->recv_mlnds.pdu_base_addr;
+ heapref->send_pdu_buf = (char *)mxa->send_mlnds.pdu_base_addr;
+
+ mxa->heap = NULL;
+ return (0);
+}
+
+/*
+ * mlsvc_xa_destruct
+ *
+ * This function is called to dispose of the heap. If the heap has
+ * been preserved via mlsvc_xa_preserve, the mxa heap pointer will
+ * be null and we assume that the heap will be released later via
+ * a call to mlsvc_xa_release. Otherwise we free the memory here.
+ */
+/*ARGSUSED*/
+static int
+mlsvc_xa_destruct(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa)
+{
+ if (mxa->heap) {
+ mlnds_destruct(&mxa->recv_mlnds);
+ mlnds_destruct(&mxa->send_mlnds);
+ mlrpc_heap_destroy(mxa->heap);
+ }
+
+ return (0);
+}
+
+/*
+ * mlsvc_xa_release
+ *
+ * This function is called, via some indirection, as a result of a
+ * call to mlsvc_rpc_free. This is where we free the heap memory
+ * that was preserved during an RPC call.
+ */
+/*ARGSUSED*/
+static void
+mlsvc_xa_release(struct mlrpc_client *mcli, mlrpc_heapref_t *heapref)
+{
+ if (heapref == NULL)
+ return;
+
+ if (heapref->state == MLRPC_HRST_PRESERVED) {
+ free(heapref->recv_pdu_buf);
+ free(heapref->send_pdu_buf);
+ mlrpc_heap_destroy(heapref->heap);
+ }
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_dssetup.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_dssetup.c
new file mode 100644
index 0000000000..a20304afa8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_dssetup.c
@@ -0,0 +1,168 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Active Directory Setup RPC interface used by Windows2000.
+ */
+
+#include <strings.h>
+#include <stdlib.h>
+#include <netdb.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlrpc.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/dssetup.ndl>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/nmpipes.h>
+
+static int dssetup_DsRoleGetPrimaryDomainInfo(void *, struct mlrpc_xaction *);
+
+static mlrpc_stub_table_t dssetup_stub_table[] = {
+ { dssetup_DsRoleGetPrimaryDomainInfo,
+ DSSETUP_OPNUM_DsRoleGetPrimaryDomainInfo },
+ {0}
+};
+
+static mlrpc_service_t dssetup_service = {
+ "DSSETUP", /* name */
+ "Active Directory Setup", /* desc */
+ "\\lsarpc", /* endpoint */
+ PIPE_LSASS, /* sec_addr_port */
+ "3919286a-b10c-11d0-9ba800c04fd92ef5", 0, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(dssetup_interface), /* interface ti */
+ dssetup_stub_table /* stub_table */
+};
+
+/*
+ * dssetup_initialize
+ *
+ * This function registers the DSSETUP interface with the RPC runtime
+ * library. It must be called in order to use either the client side
+ * or the server side functions.
+ */
+void
+dssetup_initialize(void)
+{
+ (void) mlrpc_register_service(&dssetup_service);
+}
+
+/*
+ * Request for primary domain information and status.
+ */
+static int
+dssetup_DsRoleGetPrimaryDomainInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct dssetup_DsRoleGetPrimaryDomainInfo *param = arg;
+ char dns_domain[MAXHOSTNAMELEN];
+ smb_ntdomain_t *di;
+ DWORD status;
+
+ switch (param->level) {
+ case DS_ROLE_BASIC_INFORMATION:
+ break;
+
+ case DS_ROLE_UPGRADE_STATUS:
+ case DS_ROLE_OP_STATUS:
+ default:
+ bzero(param,
+ sizeof (struct dssetup_DsRoleGetPrimaryDomainInfo));
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_LEVEL);
+ return (MLRPC_DRC_OK);
+ }
+
+ di = smb_getdomaininfo(0);
+ (void) smb_getdomainname(dns_domain, MAXHOSTNAMELEN);
+
+ if (di == NULL) {
+ bzero(param,
+ sizeof (struct dssetup_DsRoleGetPrimaryDomainInfo));
+ param->status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ return (MLRPC_DRC_OK);
+ }
+
+ (void) utf8_strlwr(dns_domain);
+
+ param->ru.info1.role = DS_ROLE_MEMBER_SERVER;
+ param->ru.info1.flags = 0;
+ param->ru.info1.nt_domain =
+ (uint8_t *)MLRPC_HEAP_STRSAVE(mxa, di->domain);
+ param->ru.info1.dns_domain =
+ (uint8_t *)MLRPC_HEAP_STRSAVE(mxa, dns_domain);
+ param->ru.info1.forest =
+ (uint8_t *)MLRPC_HEAP_STRSAVE(mxa, dns_domain);
+ bzero(&param->ru.info1.domain_guid, sizeof (mlrpc_uuid_t));
+
+ if (param->ru.info1.nt_domain == NULL ||
+ param->ru.info1.dns_domain == NULL ||
+ param->ru.info1.forest == NULL) {
+ bzero(param,
+ sizeof (struct dssetup_DsRoleGetPrimaryDomainInfo));
+ status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ } else {
+ status = NT_STATUS_SUCCESS;
+ }
+
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+DECL_FIXUP_STRUCT(dssetup_GetPrimaryDomainInfo_ru);
+DECL_FIXUP_STRUCT(dssetup_GetPrimaryDomainInfoRes);
+DECL_FIXUP_STRUCT(dssetup_DsRoleGetPrimaryDomainInfo);
+
+void
+fixup_dssetup_DsRoleGetPrimaryDomainInfo(
+ struct dssetup_DsRoleGetPrimaryDomainInfo *val)
+{
+ unsigned short size1 = 0;
+ unsigned short size2 = 0;
+ unsigned short size3 = 0;
+
+ switch (val->switch_value) {
+ CASE_INFO_ENT(dssetup_DsRolePrimaryDomInfo, 1);
+ CASE_INFO_ENT(dssetup_DsRolePrimaryDomInfo, 2);
+ CASE_INFO_ENT(dssetup_DsRolePrimaryDomInfo, 3);
+
+ default:
+ return;
+ };
+
+ size2 = size1 + (2 * sizeof (DWORD));
+ size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD);
+
+ FIXUP_PDU_SIZE(dssetup_GetPrimaryDomainInfo_ru, size1);
+ FIXUP_PDU_SIZE(dssetup_GetPrimaryDomainInfoRes, size2);
+ FIXUP_PDU_SIZE(dssetup_DsRoleGetPrimaryDomainInfo, size3);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_handle.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_handle.c
new file mode 100644
index 0000000000..a13b7346f5
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_handle.c
@@ -0,0 +1,179 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module provides the handles used in the various server-side
+ * RPC functions. I don't think other systems care about the value in
+ * the handle. It should be treated as an opaque data block. Handles
+ * are issued when a service is opened and obsoleted when it is closed.
+ * We should check incoming RPC requests to ensure that the handle
+ * being used is associated with the particular service being accessed.
+ */
+
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ntsid.h>
+
+/*
+ * Each time a handle is allocated it is added to the global handle
+ * descriptor list because we need some way of identifying the
+ * interface and domain to which the handle was assigned when it is
+ * returned on a subsequent RPC.
+ */
+ms_handle_t mlsvc_handle;
+ms_handle_desc_t *mlsvc_desc_list;
+
+/*
+ * mlsvc_get_handle
+ *
+ * This function returns a handle for use with the server-side RPC
+ * functions. Every time it is called it will increment the handle
+ * value and return a pointer to it. On NT, handle[0] always seems
+ * to be zero and handle[1] increments. The rest seems to be some
+ * sort of unique value so the local domain SID should do.
+ *
+ * The handle is added to the global handle descriptor list with the
+ * designated ifspec and key tag.
+ */
+ms_handle_t *
+mlsvc_get_handle(ms_ifspec_t ifspec, char *key, DWORD discrim)
+{
+ ms_handle_desc_t *desc;
+ nt_sid_t *sid;
+
+ if ((desc = malloc(sizeof (ms_handle_desc_t))) == NULL)
+ assert(desc);
+
+ sid = nt_domain_local_sid();
+ if (mlsvc_handle.handle[1] == 0) {
+ mlsvc_handle.handle[0] = 0;
+ mlsvc_handle.handle[1] = 0;
+ mlsvc_handle.handle[2] = sid->SubAuthority[1];
+ mlsvc_handle.handle[3] = sid->SubAuthority[2];
+ mlsvc_handle.handle[4] = sid->SubAuthority[3];
+ }
+
+ ++mlsvc_handle.handle[1];
+
+ bcopy(&mlsvc_handle, &desc->handle, sizeof (ms_handle_t));
+ desc->ifspec = ifspec;
+ desc->discrim = discrim;
+ desc->next = mlsvc_desc_list;
+ mlsvc_desc_list = desc;
+
+ if (key)
+ (void) strlcpy(desc->key, key, MLSVC_HANDLE_KEY_MAX);
+ else
+ desc->key[0] = '\0';
+
+ return (&mlsvc_handle);
+}
+
+
+/*
+ * mlsvc_put_handle
+ *
+ * Remove a handle from the global handle descriptor list and free the
+ * memory it was using. If the list contained the descriptor, a value
+ * of 0 is returned. Otherwise -1 is returned.
+ */
+int
+mlsvc_put_handle(ms_handle_t *handle)
+{
+ ms_handle_desc_t *desc;
+ ms_handle_desc_t **ppdesc = &mlsvc_desc_list;
+
+ assert(handle);
+
+ while (*ppdesc) {
+ desc = *ppdesc;
+
+ if (bcmp(&desc->handle, handle, sizeof (ms_handle_t)) == 0) {
+ *ppdesc = desc->next;
+ free(desc);
+ return (0);
+ }
+
+ ppdesc = &(*ppdesc)->next;
+ }
+
+ return (-1);
+}
+
+
+/*
+ * mlsvc_validate_handle
+ *
+ * Lookup a handle in the global handle descriptor list. If the handle
+ * is in the list, a pointer to the descriptor is returned. Otherwise
+ * a null pointer is returned.
+ */
+int
+mlsvc_validate_handle(ms_handle_t *handle, char *key)
+{
+ ms_handle_desc_t *desc;
+
+ assert(handle);
+ assert(key);
+
+ if ((desc = mlsvc_lookup_handle(handle)) == 0)
+ return (NULL);
+
+ if (strcmp(desc->key, key))
+ return (NULL);
+
+ return (1);
+}
+
+
+/*
+ * mlsvc_lookup_handle
+ *
+ * Lookup a handle in the global handle descriptor list. If the handle
+ * is in the list, a pointer to the descriptor is returned. Otherwise
+ * a null pointer is returned.
+ */
+ms_handle_desc_t *
+mlsvc_lookup_handle(ms_handle_t *handle)
+{
+ ms_handle_desc_t *desc = mlsvc_desc_list;
+
+ assert(handle);
+
+ while (desc) {
+ if (bcmp(&desc->handle, handle, sizeof (ms_handle_t)) == 0)
+ return (desc);
+
+ desc = desc->next;
+ }
+
+ return (NULL);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c
new file mode 100644
index 0000000000..fc17554949
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c
@@ -0,0 +1,65 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/lsalib.h>
+
+void dssetup_initialize(void);
+void srvsvc_initialize(void);
+void wkssvc_initialize(void);
+void lsarpc_initialize(void);
+void logr_initialize(void);
+void netr_initialize(void);
+void samr_initialize(void);
+void svcctl_initialize(void);
+void winreg_initialize(void);
+
+/*
+ * All mlrpc initialization is invoked from here.
+ * Returns 0 upon success. Otherwise, returns -1.
+ */
+int
+mlsvc_init(void)
+{
+ srvsvc_initialize();
+ wkssvc_initialize();
+ lsarpc_initialize();
+ netr_initialize();
+ dssetup_initialize();
+ samr_initialize();
+ svcctl_initialize();
+ winreg_initialize();
+ logr_initialize();
+
+ (void) sam_init();
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_logr.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_logr.c
new file mode 100644
index 0000000000..14c13a0315
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_logr.c
@@ -0,0 +1,574 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Event Log Service RPC (LOGR) interface definition.
+ */
+
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <strings.h>
+#include <netdb.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/eventlog.ndl>
+
+#define FWD +1
+#define REW -1
+
+/* define the logging structs here - from syslog.h */
+#define NMSGMASK 1023
+#define MAXMSGLEN 223
+#define LOGBUFSIZE (MAXMSGLEN + 1)
+
+#define LOG_PRI(p) ((p) & LOG_PRIMASK)
+
+typedef struct log_entry {
+ struct timeval timestamp; /* time of log entry */
+ int pri; /* message priority */
+ char msg[LOGBUFSIZE]; /* log message text */
+ int thread_id; /* calling function thread ID */
+ char thread_name[12]; /* calling function thread name */
+ unsigned long caller_adr; /* calling function address */
+} log_entry_t;
+
+typedef struct log_info {
+ log_entry_t entry[NMSGMASK+1];
+ int ix;
+ int alarm_on;
+ int alarm_disable;
+ int timestamp_level;
+ int disp_msg_len;
+ int prefix_len;
+} log_info_t;
+
+/*
+ * READ flags for EventLogRead
+ *
+ * EVENTLOG_SEEK_READ
+ * The read operation proceeds from the record specified by the
+ * dwRecordOffset parameter. This flag cannot be used with
+ * EVENTLOG_SEQUENTIAL_READ.
+ *
+ * EVENTLOG_SEQUENTIAL_READ
+ * The read operation proceeds sequentially from the last call to the
+ * ReadEventLog function using this handle. This flag cannot be used
+ * with EVENTLOG_SEEK_READ.
+ *
+ * If the buffer is large enough, more than one record can be read at
+ * the specified seek position; you must specify one of the following
+ * flags to indicate the direction for successive read operations.
+ *
+ * EVENTLOG_FORWARDS_READ
+ * The log is read in chronological order. This flag cannot be used
+ * with EVENTLOG_BACKWARDS_READ.
+ *
+ * EVENTLOG_BACKWARDS_READ
+ * The log is read in reverse chronological order. This flag cannot be
+ * used with EVENTLOG_FORWARDS_READ.
+ */
+#define EVENTLOG_SEQUENTIAL_READ 0x0001
+#define EVENTLOG_SEEK_READ 0x0002
+#define EVENTLOG_FORWARDS_READ 0x0004
+#define EVENTLOG_BACKWARDS_READ 0x0008
+
+/*
+ * The types of events that can be logged.
+ */
+#define EVENTLOG_SUCCESS 0x0000
+#define EVENTLOG_ERROR_TYPE 0x0001
+#define EVENTLOG_WARNING_TYPE 0x0002
+#define EVENTLOG_INFORMATION_TYPE 0x0004
+#define EVENTLOG_AUDIT_SUCCESS 0x0008
+#define EVENTLOG_AUDIT_FAILURE 0x0010
+
+/*
+ * Event Identifiers
+ *
+ * Event identifiers uniquely identify a particular event. Each event
+ * source can define its own numbered events and the description strings
+ * to which they are mapped. Event viewers can present these strings to
+ * the user. They should help the user understand what went wrong and
+ * suggest what actions to take. Direct the description at users solving
+ * their own problems, not at administrators or support technicians.
+ * Make the description clear and concise and avoid culture-specific
+ * phrases.
+ *
+ * The following diagram illustrates the format of an event identifier.
+ *
+ * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +---+-+-+-----------------------+-------------------------------+
+ * |Sev|C|R| Facility | Code |
+ * +---+-+-+-----------------------+-------------------------------+
+ *
+ * Sev
+ * Indicates the severity. This is one of the following values:
+ * 00 - Success
+ * 01 - Informational
+ * 10 - Warning
+ * 11 - Error
+ *
+ * C
+ * Indicates a customer code (1) or a system code (0).
+ * R
+ * Reserved bit.
+ * Facility
+ * Facility code.
+ * Code
+ * Status code for the facility.
+ */
+#define EVENTID_SEVERITY_SUCCESS 0x00000000
+#define EVENTID_SEVERITY_INFO 0x40000000
+#define EVENTID_SEVERITY_WARNING 0x80000000
+#define EVENTID_SEVERITY_ERROR 0xC0000000
+
+#define EVENTID_SYSTEM_CODE 0x00000000
+#define EVENTID_CUSTOMER_CODE 0x20000000
+
+#define MAX_SRCNAME_LEN 20
+#define LOGR_KEY "LogrOpen"
+
+
+static int logr_s_EventLogClose(void *, struct mlrpc_xaction *);
+static int logr_s_EventLogQueryCount(void *, struct mlrpc_xaction *);
+static int logr_s_EventLogGetOldestRec(void *, struct mlrpc_xaction *);
+static int logr_s_EventLogOpen(void *, struct mlrpc_xaction *);
+static int logr_s_EventLogRead(void *, struct mlrpc_xaction *);
+
+static mlrpc_stub_table_t logr_stub_table[] = {
+ { logr_s_EventLogClose, LOGR_OPNUM_EventLogClose },
+ { logr_s_EventLogQueryCount, LOGR_OPNUM_EventLogQueryCount },
+ { logr_s_EventLogGetOldestRec, LOGR_OPNUM_EventLogGetOldestRec },
+ { logr_s_EventLogOpen, LOGR_OPNUM_EventLogOpen },
+ { logr_s_EventLogRead, LOGR_OPNUM_EventLogRead },
+ {0}
+};
+
+static mlrpc_service_t logr_service = {
+ "LOGR", /* name */
+ "Event Log Service", /* desc */
+ "\\eventlog", /* endpoint */
+ PIPE_NTSVCS, /* sec_addr_port */
+ "82273fdc-e32a-18c3-3f78827929dc23ea", 0, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(logr_interface), /* interface ti */
+ logr_stub_table /* stub_table */
+};
+
+typedef struct {
+ DWORD tot_recnum;
+ DWORD last_sentrec;
+ char first_read;
+ struct log_info log;
+} read_data_t;
+
+static char logr_sysname[SYS_NMLN];
+static char hostname[MAXHOSTNAMELEN];
+static mts_wchar_t wcs_hostname[MAXHOSTNAMELEN];
+static int hostname_len = 0;
+static mts_wchar_t wcs_srcname[MAX_SRCNAME_LEN];
+static int srcname_len = 0;
+static int str_offs, sh_len;
+
+/*
+ * logr_initialize
+ *
+ * This function registers the LOGR RPC interface with the RPC runtime
+ * library. It must be called in order to use either the client side
+ * or the server side functions.
+ */
+void
+logr_initialize(void)
+{
+ struct utsname name;
+ char *sysname;
+
+ if (uname(&name) < 0)
+ sysname = "Solaris";
+ else
+ sysname = name.sysname;
+
+ (void) strlcpy(logr_sysname, sysname, SYS_NMLN);
+ (void) mlrpc_register_service(&logr_service);
+}
+
+/*
+ * logr_s_EventLogClose
+ *
+ * This is a request to close the LOGR interface specified by the
+ * handle. Free the handle and its associated resources and zero out
+ * the result handle for the client.
+ */
+/*ARGSUSED*/
+static int
+logr_s_EventLogClose(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct logr_EventLogClose *param = arg;
+ ms_handle_desc_t *desc;
+ read_data_t *data;
+
+ if ((desc = mlsvc_lookup_handle((ms_handle_t *)&param->handle)) == 0) {
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+ return (MLRPC_DRC_OK);
+ }
+
+ data = (read_data_t *)(desc->discrim);
+ free(data);
+ (void) mlsvc_put_handle((ms_handle_t *)&param->handle);
+
+ bzero(&param->result_handle, sizeof (logr_handle_t));
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * logr_s_EventLogOpen
+ *
+ * This is a request to open the event log.
+ *
+ * Return a handle for use with subsequent event log requests.
+ */
+/*ARGSUSED*/
+static int
+logr_s_EventLogOpen(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct logr_EventLogOpen *param = arg;
+ ms_handle_t *handle;
+ int log_enable = 0;
+
+ smb_config_rdlock();
+ log_enable = smb_config_getyorn(SMB_CI_LOGR_ENABLE);
+ smb_config_unlock();
+
+ if (log_enable == 0) {
+ bzero(&param->handle, sizeof (logr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+ }
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_LOGR, LOGR_KEY, 0);
+ bcopy(handle, &param->handle, sizeof (logr_handle_t));
+
+ if (hostname_len == 0) {
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) {
+ bzero(&param->handle, sizeof (logr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+ }
+
+ hostname_len = (strlen(hostname) + 1) * 2;
+ (void) mts_mbstowcs(wcs_hostname, hostname, hostname_len / 2);
+ srcname_len = (strlen(logr_sysname) + 1) * 2;
+ (void) mts_mbstowcs(wcs_srcname, logr_sysname, srcname_len / 2);
+ sh_len = srcname_len + hostname_len;
+ str_offs = 12 * sizeof (DWORD) + 4 * sizeof (WORD) + sh_len;
+ }
+
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * logr_get_snapshot
+ *
+ * Allocate memory and make a copy, as a snapshot, from system log.
+ */
+static read_data_t *
+logr_get_snapshot(void)
+{
+ read_data_t *data = NULL;
+ return (data);
+}
+
+/*
+ * logr_s_EventLogQueryCount
+ *
+ * take a snapshot from system log, assign it to the given handle.
+ * return number of log entries in the snapshot as result of RPC
+ * call.
+ */
+/*ARGSUSED*/
+static int
+logr_s_EventLogQueryCount(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct logr_EventLogQueryCount *param = arg;
+ ms_handle_desc_t *desc;
+ read_data_t *data;
+
+ if ((desc = mlsvc_lookup_handle((ms_handle_t *)&param->handle)) == 0) {
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+ return (MLRPC_DRC_OK);
+ }
+
+ if ((data = logr_get_snapshot()) == NULL) {
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ desc->discrim = (DWORD)data;
+ param->rec_num = data->tot_recnum;
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * logr_s_EventLogGetOldestRec
+ *
+ * Return oldest record number in the snapshot as result of RPC call.
+ */
+/*ARGSUSED*/
+static int
+logr_s_EventLogGetOldestRec(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct logr_EventLogGetOldestRec *param = arg;
+ ms_handle_desc_t *desc;
+ read_data_t *data;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->handle);
+ if (desc == NULL) {
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+ return (MLRPC_DRC_OK);
+ }
+
+ data = (read_data_t *)desc->discrim;
+ param->oldest_rec = data->log.ix - data->tot_recnum;
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * set_event_typeid
+ *
+ * Map the local system log priority to the event type and event ID
+ * for Windows events.
+ */
+void
+set_event_typeid(int le_pri, WORD *etype, DWORD *eid)
+{
+ switch (LOG_PRI(le_pri)) {
+ case LOG_EMERG:
+ case LOG_ALERT:
+ case LOG_CRIT:
+ case LOG_ERR:
+ *eid = EVENTID_SEVERITY_ERROR;
+ *etype = EVENTLOG_ERROR_TYPE;
+ break;
+ case LOG_WARNING:
+ *eid = EVENTID_SEVERITY_WARNING;
+ *etype = EVENTLOG_WARNING_TYPE;
+ break;
+ case LOG_NOTICE:
+ case LOG_INFO:
+ case LOG_DEBUG:
+ *eid = EVENTID_SEVERITY_INFO;
+ *etype = EVENTLOG_INFORMATION_TYPE;
+ break;
+ default:
+ *eid = EVENTID_SEVERITY_SUCCESS;
+ *etype = EVENTLOG_SUCCESS;
+ }
+}
+
+static log_entry_t *
+log_get_entry(struct log_info *linfo, int entno)
+{
+ return (&linfo->entry[entno]);
+}
+
+/*
+ * Fill a Windows event record based on a local system log record.
+ */
+static void
+set_logrec(log_entry_t *le, DWORD recno, logr_record_t *rec)
+{
+ int len;
+
+ rec->Length1 = sizeof (logr_record_t);
+ rec->Reserved = 0x654C664C;
+ rec->RecordNumber = recno;
+ rec->TimeGenerated = le->timestamp.tv_sec;
+ rec->TimeWritten = le->timestamp.tv_sec;
+ set_event_typeid(le->pri, &rec->EventType, &rec->EventID);
+ rec->NumStrings = 1;
+ rec->EventCategory = 0;
+ rec->ReservedFlags = 0;
+ rec->ClosingRecordNumber = 0;
+ rec->StringOffset = str_offs;
+ rec->UserSidLength = 0;
+ rec->UserSidOffset = sizeof (logr_record_t) - sizeof (DWORD);
+ rec->DataLength = 0;
+ rec->DataOffset = sizeof (logr_record_t) - sizeof (DWORD);
+ bzero(rec->info, LOGR_INFOLEN);
+ (void) memcpy(rec->info, wcs_srcname, srcname_len);
+ (void) memcpy(rec->info + srcname_len, wcs_hostname, hostname_len);
+
+ len = (LOGR_INFOLEN - sh_len) / 2;
+
+ if ((strlen(le->msg) + 1) < len)
+ len = strlen(le->msg) + 1;
+
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ (void) mts_mbstowcs((mts_wchar_t *)(rec->info+sh_len), le->msg, len);
+ rec->Length2 = sizeof (logr_record_t);
+}
+
+/*
+ * logr_s_EventLogRead
+ *
+ * Reads a whole number of entries from system log. The function can
+ * read log entries in chronological or reverse chronological order.
+ */
+/*ARGSUSED*/
+static int
+logr_s_EventLogRead(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct logr_EventLogRead *param = arg;
+ ms_handle_desc_t *desc;
+ read_data_t *rdata;
+ log_entry_t *le;
+ DWORD ent_no, ent_num, ent_remain;
+ logr_record_t *rec;
+ BYTE *buf;
+ int dir, ent_per_req;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->handle);
+ if (desc == NULL) {
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+ return (MLRPC_DRC_OK);
+ }
+
+ rdata = (read_data_t *)(desc->discrim);
+ if (rdata == 0) {
+ if ((rdata = logr_get_snapshot()) == NULL) {
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ desc->discrim = (DWORD)rdata;
+ }
+
+ dir = (param->read_flags & EVENTLOG_FORWARDS_READ) ? FWD : REW;
+
+ if (param->read_flags & EVENTLOG_SEEK_READ) {
+ rdata->last_sentrec = param->rec_offset;
+ } else if (rdata->first_read) {
+ /*
+ * set last record number which is read for
+ * the first iteration of sequential read.
+ */
+ rdata->last_sentrec = (dir == FWD)
+ ? (rdata->log.ix - rdata->tot_recnum) : rdata->log.ix;
+ }
+
+ ent_remain = (dir == FWD)
+ ? (rdata->tot_recnum - rdata->last_sentrec) : rdata->last_sentrec;
+
+ /*
+ * function should return as many whole log entries as
+ * will fit in the buffer; it should not return partial
+ * entries, even if there is room in the buffer.
+ */
+ ent_per_req = param->nbytes_to_read / sizeof (logr_record_t);
+ if (ent_remain > ent_per_req)
+ ent_remain = ent_per_req;
+
+ if (ent_remain == 0) {
+ /*
+ * Send this error to Windows client so that it
+ * can figure out that there is no more record
+ * to read.
+ */
+ param->sent_size = 0;
+ param->unknown = 0;
+ param->status = NT_SC_ERROR(NT_STATUS_END_OF_FILE);
+ return (MLRPC_DRC_OK);
+ }
+
+ buf = (param->read_flags & EVENTLOG_SEEK_READ)
+ ? param->ru.rec : param->ru.recs;
+
+ for (ent_num = 0, ent_no = rdata->last_sentrec;
+ ent_num < ent_remain; ent_num++, ent_no += dir) {
+ le = log_get_entry(&rdata->log, ent_no & NMSGMASK);
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ rec = (logr_record_t *)buf;
+ set_logrec(le, ent_no, rec);
+ buf += sizeof (logr_record_t);
+ }
+ rdata->last_sentrec = ent_no;
+ rdata->first_read = 0;
+
+ param->sent_size = sizeof (logr_record_t) * ent_remain;
+ param->unknown = 0;
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Declare extern references.
+ */
+DECL_FIXUP_STRUCT(logr_read_u);
+DECL_FIXUP_STRUCT(logr_read_info);
+DECL_FIXUP_STRUCT(logr_EventLogRead);
+
+/*
+ * Patch the logr_EventLogRead union.
+ * This function is called from mlsvc_logr_ndr.c
+ */
+void
+fixup_logr_EventLogRead(void *xarg)
+{
+ struct logr_EventLogRead *arg = (struct logr_EventLogRead *)xarg;
+ unsigned short size1 = 0;
+ unsigned short size2 = 0;
+ unsigned short size3 = 0;
+ DWORD nbr = arg->nbytes_to_read;
+
+ switch (nbr) {
+ case 0:
+ size1 = 0;
+ break;
+ default:
+ size1 = nbr;
+ break;
+ };
+
+ size2 = size1 + (2 * sizeof (DWORD));
+ size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD);
+
+ FIXUP_PDU_SIZE(logr_read_u, size1);
+ FIXUP_PDU_SIZE(logr_read_info, size2);
+ FIXUP_PDU_SIZE(logr_EventLogRead, size3);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c
new file mode 100644
index 0000000000..6aa4d716fe
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c
@@ -0,0 +1,1385 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * Local Security Authority RPC (LSARPC) server-side interface definition.
+ */
+
+#include <unistd.h>
+#include <strings.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/lsarpc.ndl>
+#include <smbsrv/lsalib.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/ntlocale.h>
+
+struct local_group_table {
+ WORD sid_name_use;
+ WORD domain_ix;
+ char *sid;
+ char *name;
+};
+
+static int lsarpc_s_CloseHandle(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_QuerySecurityObject(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_EnumAccounts(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_EnumTrustedDomain(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_OpenAccount(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_EnumPrivsAccount(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_LookupPrivValue(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_LookupPrivName(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_LookupPrivDisplayName(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_QueryInfoPolicy(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_OpenDomainHandle(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_OpenDomainHandle(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_LookupSids(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_LookupNames(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_GetConnectedUser(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_LookupSids2(void *arg, struct mlrpc_xaction *);
+static int lsarpc_s_LookupNames2(void *arg, struct mlrpc_xaction *);
+
+static int lsarpc_s_PrimaryDomainInfo(struct mslsa_PrimaryDomainInfo *,
+ struct mlrpc_xaction *);
+static int lsarpc_s_AccountDomainInfo(struct mslsa_AccountDomainInfo *,
+ struct mlrpc_xaction *);
+static int lsarpc_s_LookupNtSid(struct mlrpc_xaction *, nt_sid_t *,
+ smb_userinfo_t *, struct mslsa_name_entry *, int);
+static int lsarpc_s_LookupLocalSid(struct mlrpc_xaction *, nt_sid_t *,
+ smb_userinfo_t *, struct mslsa_name_entry *);
+static int lsarpc_s_LookupBuiltinSid(struct mlrpc_xaction *, nt_sid_t *,
+ smb_userinfo_t *, struct mslsa_name_entry *);
+static int lsarpc_s_UnknownSid(struct mlrpc_xaction *, nt_sid_t *,
+ smb_userinfo_t *, struct mslsa_name_entry *);
+static int lsarpc_s_UpdateDomainTable(struct mlrpc_xaction *,
+ smb_userinfo_t *, struct mslsa_domain_table *, DWORD *);
+
+static int lsarpc_w2k_enable;
+
+static mlrpc_stub_table_t lsarpc_stub_table[] = {
+ { lsarpc_s_CloseHandle, LSARPC_OPNUM_CloseHandle },
+ { lsarpc_s_QuerySecurityObject, LSARPC_OPNUM_QuerySecurityObject },
+ { lsarpc_s_EnumAccounts, LSARPC_OPNUM_EnumerateAccounts },
+ { lsarpc_s_EnumTrustedDomain, LSARPC_OPNUM_EnumTrustedDomain },
+ { lsarpc_s_OpenAccount, LSARPC_OPNUM_OpenAccount },
+ { lsarpc_s_EnumPrivsAccount, LSARPC_OPNUM_EnumPrivsAccount },
+ { lsarpc_s_LookupPrivValue, LSARPC_OPNUM_LookupPrivValue },
+ { lsarpc_s_LookupPrivName, LSARPC_OPNUM_LookupPrivName },
+ { lsarpc_s_LookupPrivDisplayName, LSARPC_OPNUM_LookupPrivDisplayName },
+ { lsarpc_s_QueryInfoPolicy, LSARPC_OPNUM_QueryInfoPolicy },
+ { lsarpc_s_OpenDomainHandle, LSARPC_OPNUM_OpenPolicy },
+ { lsarpc_s_OpenDomainHandle, LSARPC_OPNUM_OpenPolicy2 },
+ { lsarpc_s_LookupSids, LSARPC_OPNUM_LookupSids },
+ { lsarpc_s_LookupNames, LSARPC_OPNUM_LookupNames },
+ { lsarpc_s_GetConnectedUser, LSARPC_OPNUM_GetConnectedUser },
+ { lsarpc_s_LookupSids2, LSARPC_OPNUM_LookupSids2 },
+ { lsarpc_s_LookupNames2, LSARPC_OPNUM_LookupNames2 },
+ {0}
+};
+
+static mlrpc_service_t lsarpc_service = {
+ "LSARPC", /* name */
+ "Local Security Authority", /* desc */
+ "\\lsarpc", /* endpoint */
+ PIPE_LSASS, /* sec_addr_port */
+ "12345778-1234-abcd-ef000123456789ab", 0, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(lsarpc_interface), /* interface ti */
+ lsarpc_stub_table /* stub_table */
+};
+
+/*
+ * Windows 2000 interface.
+ */
+static mlrpc_service_t lsarpc_w2k_service = {
+ "LSARPC_W2K", /* name */
+ "Local Security Authority", /* desc */
+ "\\lsarpc", /* endpoint */
+ PIPE_LSASS, /* sec_addr_port */
+ "3919286a-b10c-11d0-9ba800c04fd92ef5", 0, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(lsarpc_interface), /* interface ti */
+ lsarpc_stub_table /* stub_table */
+};
+
+/*
+ * lsarpc_initialize
+ *
+ * This function registers the LSA RPC interface with the RPC runtime
+ * library. It must be called in order to use either the client side
+ * or the server side functions.
+ */
+void
+lsarpc_initialize(void)
+{
+ (void) mlrpc_register_service(&lsarpc_service);
+
+ if (lsarpc_w2k_enable)
+ (void) mlrpc_register_service(&lsarpc_w2k_service);
+}
+
+/*
+ * lsarpc_s_OpenDomainHandle opnum=0x06
+ *
+ * This is a request to open the LSA (OpenPolicy and OpenPolicy2).
+ * The client is looking for an LSA domain handle. Handles appear to
+ * be a 20 byte opaque object with the top 4 bytes all zero. As it is
+ * opaque to the client, we can put anything we like in it. Real handles
+ * do appear to contain a sequence number which is incremented when a
+ * new handle is issued. However, we don't really care about that
+ * (yet). Always return MLRPC_DRC_OK.
+ */
+/*ARGSUSED*/
+static int
+lsarpc_s_OpenDomainHandle(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_OpenPolicy2 *param = arg;
+
+ bzero(&param->domain_handle, sizeof (mslsa_handle_t));
+ (void) strcpy((char *)&param->domain_handle.hand2, "DomainHandle");
+ param->status = 0;
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_CloseHandle opnum=0x00
+ *
+ * This is a request to close the LSA interface specified by the handle.
+ * We don't track handles (yet), so just zero out the handle and return
+ * MLRPC_DRC_OK. Setting the handle to zero appears to be standard
+ * behaviour and someone may rely on it, i.e. we do on the client side.
+ */
+/*ARGSUSED*/
+static int
+lsarpc_s_CloseHandle(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_CloseHandle *param = arg;
+
+ bzero(&param->result_handle, sizeof (param->result_handle));
+ param->status = 0;
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_QuerySecurityObject
+ */
+/*ARGSUSED*/
+static int
+lsarpc_s_QuerySecurityObject(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_QuerySecurityObject *param = arg;
+
+ bzero(param, sizeof (struct mslsa_QuerySecurityObject));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_EnumAccounts
+ *
+ * Enumerate the list of local accounts SIDs. The client should supply
+ * a valid OpenPolicy2 handle. The enum_context is used to support
+ * multiple enumeration calls to obtain the complete list of SIDs.
+ * It should be set to 0 on the first call and passed unchanged on
+ * subsequent calls until there are no more accounts - the server will
+ * return NT_SC_WARNING(MLSVC_NO_MORE_DATA).
+ *
+ * For now just set the status to access-denied. Note that we still have
+ * to provide a valid address for enum_buf because it's a reference and
+ * the marshalling rules require that references must not be null.
+ * The enum_context is used to support multiple
+ */
+static int
+lsarpc_s_EnumAccounts(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_EnumerateAccounts *param = arg;
+ struct mslsa_EnumAccountBuf *enum_buf;
+
+ bzero(param, sizeof (struct mslsa_EnumerateAccounts));
+
+ enum_buf = MLRPC_HEAP_NEW(mxa, struct mslsa_EnumAccountBuf);
+ if (enum_buf == NULL) {
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(enum_buf, sizeof (struct mslsa_EnumAccountBuf));
+ param->enum_buf = enum_buf;
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * lsarpc_s_EnumTrustedDomain
+ *
+ * This is the server side function for handling requests to enumerate
+ * the list of trusted domains: currently held in the NT domain database.
+ * This call requires an OpenPolicy2 handle. The enum_context is used to
+ * support multiple enumeration calls to obtain the complete list.
+ * It should be set to 0 on the first call and passed unchanged on
+ * subsequent calls until there are no more accounts - the server will
+ * return NT_SC_WARNING(MLSVC_NO_MORE_DATA).
+ *
+ * For now just set the status to access-denied. Note that we still have
+ * to provide a valid address for enum_buf because it's a reference and
+ * the marshalling rules require that references must not be null.
+ */
+static int
+lsarpc_s_EnumTrustedDomain(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_EnumTrustedDomain *param = arg;
+ struct mslsa_EnumTrustedDomainBuf *enum_buf;
+
+ bzero(param, sizeof (struct mslsa_EnumTrustedDomain));
+
+ enum_buf = MLRPC_HEAP_NEW(mxa, struct mslsa_EnumTrustedDomainBuf);
+ if (enum_buf == NULL) {
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(enum_buf, sizeof (struct mslsa_EnumTrustedDomainBuf));
+ param->enum_buf = enum_buf;
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * lsarpc_s_OpenAccount
+ *
+ * This is a request to open an account handle. This function hasn't
+ * been tested. It is just a template in case some server somewhere
+ * makes this call. See lsarpc_s_OpenDomainHandle for more information.
+ */
+/*ARGSUSED*/
+static int
+lsarpc_s_OpenAccount(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_OpenAccount *param = arg;
+
+ if (param->handle.hand1 != 0 ||
+ strcmp("DomainHandle", (char *)&param->handle.hand2)) {
+ param->status = NT_SC_ERROR(ERROR_NO_SUCH_DOMAIN);
+ } else {
+ (void) strcpy((char *)&param->account_handle.hand2,
+ "AccountHandle");
+ param->status = 0;
+ }
+
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * lsarpc_s_EnumPrivsAccount
+ *
+ * This is the server side function for handling requests for account
+ * privileges. For now just set the status to not-supported status and
+ * return MLRPC_DRC_OK. Note that we still have to provide a valid
+ * address for enum_buf because it's a reference and the marshalling
+ * rules require that references must not be null.
+ */
+/*ARGSUSED*/
+static int
+lsarpc_s_EnumPrivsAccount(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_EnumPrivsAccount *param = arg;
+
+ bzero(param, sizeof (struct mslsa_EnumPrivsAccount));
+ param->status = NT_SC_ERROR(NT_STATUS_NOT_SUPPORTED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_LookupPrivValue
+ *
+ * Server side function used to map a privilege name to a locally unique
+ * identifier (LUID).
+ */
+/*ARGSUSED*/
+static int
+lsarpc_s_LookupPrivValue(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_LookupPrivValue *param = arg;
+ smb_privinfo_t *pi;
+
+ if ((pi = smb_priv_getbyname((char *)param->name.str)) == NULL) {
+ bzero(param, sizeof (struct mslsa_LookupPrivValue));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE);
+ return (MLRPC_DRC_OK);
+ }
+
+ param->luid.low_part = pi->id;
+ param->luid.high_part = 0;
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_LookupPrivName
+ *
+ * Server side function used to map a locally unique identifier (LUID)
+ * to the appropriate privilege name string.
+ */
+static int
+lsarpc_s_LookupPrivName(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_LookupPrivName *param = arg;
+ smb_privinfo_t *pi;
+ int rc;
+
+ if ((pi = smb_priv_getbyvalue(param->luid.low_part)) == NULL) {
+ bzero(param, sizeof (struct mslsa_LookupPrivName));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE);
+ return (MLRPC_DRC_OK);
+ }
+
+ param->name = MLRPC_HEAP_NEW(mxa, mslsa_string_t);
+ if (param->name == NULL) {
+ bzero(param, sizeof (struct mslsa_LookupPrivName));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ rc = mlsvc_string_save((ms_string_t *)param->name, pi->name, mxa);
+ if (rc == 0) {
+ bzero(param, sizeof (struct mslsa_LookupPrivName));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_LookupPrivDisplayName
+ *
+ * This is the server side function for handling requests for account
+ * privileges. For now just set the status to not-supported status and
+ * return MLRPC_DRC_OK.
+ */
+static int
+lsarpc_s_LookupPrivDisplayName(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_LookupPrivDisplayName *param = arg;
+ smb_privinfo_t *pi;
+ int rc;
+
+ if ((pi = smb_priv_getbyname((char *)param->name.str)) == NULL) {
+ bzero(param, sizeof (struct mslsa_LookupPrivDisplayName));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE);
+ return (MLRPC_DRC_OK);
+ }
+
+ param->display_name = MLRPC_HEAP_NEW(mxa, mslsa_string_t);
+ if (param->display_name == NULL) {
+ bzero(param, sizeof (struct mslsa_LookupPrivDisplayName));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ rc = mlsvc_string_save((ms_string_t *)param->display_name,
+ pi->display_name, mxa);
+
+ if (rc == 0) {
+ bzero(param, sizeof (struct mslsa_LookupPrivDisplayName));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ param->language_ret = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_GetConnectedUser
+ *
+ * This is still guesswork. Netmon doesn't know about this
+ * call and I'm not really sure what it is intended to achieve.
+ * Another packet capture application, Ethereal, calls this RPC as
+ * GetConnectedUser.
+ * We will receive our own hostname in the request and it appears
+ * we should respond with an account name and the domain name of connected
+ * user from the client that makes this call.
+ */
+static int
+lsarpc_s_GetConnectedUser(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_GetConnectedUser *param = arg;
+ smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx;
+ DWORD status = NT_STATUS_SUCCESS;
+ int rc1;
+ int rc2;
+
+ if (user_ctx == NULL) {
+ bzero(param, sizeof (struct mslsa_GetConnectedUser));
+ status = NT_SC_ERROR(NT_STATUS_NO_TOKEN);
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (smb_getdomaininfo(0) == NULL) {
+ bzero(param, sizeof (struct mslsa_GetConnectedUser));
+ status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->owner = MLRPC_HEAP_NEW(mxa, struct mslsa_string_desc);
+ param->domain = MLRPC_HEAP_NEW(mxa, struct mslsa_DomainName);
+ if (param->owner == NULL || param->domain == NULL) {
+ status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->domain->name = MLRPC_HEAP_NEW(mxa, struct mslsa_string_desc);
+ if (param->domain->name == NULL) {
+ status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ rc1 = mlsvc_string_save((ms_string_t *)param->owner,
+ user_ctx->du_account, mxa);
+
+ rc2 = mlsvc_string_save((ms_string_t *)param->domain->name,
+ user_ctx->du_domain, mxa);
+
+ if (rc1 == 0 || rc2 == 0)
+ status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * lsarpc_s_QueryInfoPolicy
+ *
+ * This is the server side function for handling LSA information policy
+ * queries. Currently, we only support primary domain and account
+ * domain queries. This is just a front end to switch on the request
+ * and hand it off to the appropriate function to actually deal with
+ * obtaining and building the response.
+ */
+static int
+lsarpc_s_QueryInfoPolicy(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_QueryInfoPolicy *param = arg;
+ struct mslsa_PolicyInfo *info;
+ int result;
+
+ info = (struct mslsa_PolicyInfo *)MLRPC_HEAP_MALLOC(
+ mxa, sizeof (struct mslsa_PolicyInfo));
+
+ info->switch_value = param->info_class;
+
+ switch (param->info_class) {
+ case MSLSA_POLICY_PRIMARY_DOMAIN_INFO:
+ result = lsarpc_s_PrimaryDomainInfo(&info->ru.pd_info, mxa);
+ break;
+
+ case MSLSA_POLICY_ACCOUNT_DOMAIN_INFO:
+ result = lsarpc_s_AccountDomainInfo(&info->ru.ad_info, mxa);
+ break;
+
+ default:
+ result = (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED);
+ break;
+ }
+
+ param->info = info;
+ param->status = NT_STATUS_SUCCESS;
+ return (result);
+}
+
+
+/*
+ * lsarpc_s_PrimaryDomainInfo
+ *
+ * This is the service side function for handling primary domain policy
+ * queries. This will return the primary domain name and sid. This is
+ * currently a pass through interface so all we do is act as a proxy
+ * between the client and the DC. If there is no session, fake up the
+ * response with default values - useful for share mode.
+ *
+ * If the server name matches the local hostname, we should return
+ * the local domain SID.
+ */
+static int
+lsarpc_s_PrimaryDomainInfo(struct mslsa_PrimaryDomainInfo *pd_info,
+ struct mlrpc_xaction *mxa)
+{
+ int security_mode;
+ smb_ntdomain_t *di;
+ nt_domain_t *ntdp;
+ nt_sid_t *sid;
+ char domain_name[MLSVC_DOMAIN_NAME_MAX];
+ char *name;
+ DWORD status;
+ int rc;
+
+ status = NT_STATUS_SUCCESS;
+
+ security_mode = smb_get_security_mode();
+
+ if (security_mode != SMB_SECMODE_DOMAIN) {
+ rc = smb_gethostname(domain_name, MLSVC_DOMAIN_NAME_MAX, 1);
+ if (rc != 0) {
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+ }
+
+ name = domain_name;
+ sid = nt_sid_dup(nt_domain_local_sid());
+ } else {
+ if ((di = smb_getdomaininfo(0)) == 0) {
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+ }
+
+ ntdp = nt_domain_lookup_name(di->domain);
+ if (ntdp == 0) {
+ (void) lsa_query_primary_domain_info();
+ ntdp = nt_domain_lookup_name(di->domain);
+ }
+
+ if (ntdp == 0) {
+ sid = nt_sid_gen_null_sid();
+ name = di->domain;
+ } else {
+ sid = nt_sid_dup(ntdp->sid);
+ name = ntdp->name;
+ }
+ }
+
+ if (sid == 0) {
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+ }
+
+ if (mlsvc_string_save((ms_string_t *)&pd_info->name, name, mxa) == 0)
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+
+ if ((pd_info->sid = (struct mslsa_sid *)mlsvc_sid_save(sid, mxa)) == 0)
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+
+ free(sid);
+
+ if (status != NT_STATUS_SUCCESS)
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * lsarpc_s_AccountDomainInfo
+ *
+ * This is the service side function for handling account domain policy
+ * queries. This is where we return our local domain information so that
+ * NT knows who to query for information on local names and SIDs. The
+ * domain name is the local hostname.
+ */
+static int
+lsarpc_s_AccountDomainInfo(struct mslsa_AccountDomainInfo *ad_info,
+ struct mlrpc_xaction *mxa)
+{
+ char domain_name[MLSVC_DOMAIN_NAME_MAX];
+ nt_sid_t *domain_sid;
+ int rc;
+
+ if (smb_gethostname(domain_name, MLSVC_DOMAIN_NAME_MAX, 1) != 0)
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+
+ if ((domain_sid = nt_domain_local_sid()) == NULL)
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+
+ rc = mlsvc_string_save((ms_string_t *)&ad_info->name,
+ domain_name, mxa);
+ if (rc == 0)
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+
+ ad_info->sid = (struct mslsa_sid *)mlsvc_sid_save(domain_sid, mxa);
+ if (ad_info->sid == NULL)
+ return (MLRPC_DRC_FAULT_OUT_OF_MEMORY);
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_LookupNames
+ *
+ * This is the service side function for handling name lookup requests.
+ * Currently, we only support lookups of a single name. This is also a
+ * pass through interface so all we do is act as a proxy between the
+ * client and the DC.
+ */
+static int
+lsarpc_s_LookupNames(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_LookupNames *param = arg;
+ struct mslsa_rid_entry *rids;
+ smb_userinfo_t *user_info = 0;
+ struct mslsa_domain_table *domain_table;
+ struct mslsa_domain_entry *domain_entry;
+ char *name = "";
+ DWORD status = NT_STATUS_SUCCESS;
+ int rc = 0;
+
+ if (param->name_table->n_entry != 1)
+ return (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED);
+
+ rids = MLRPC_HEAP_NEW(mxa, struct mslsa_rid_entry);
+ domain_table = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_table);
+ domain_entry = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_entry);
+ user_info = mlsvc_alloc_user_info();
+
+ if (rids == NULL || domain_table == NULL ||
+ domain_entry == NULL || user_info == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto name_lookup_failed;
+ }
+
+ name = (char *)param->name_table->names->str;
+
+ rc = lsa_lookup_local(name, user_info);
+ if (rc < 0) {
+ status = NT_STATUS_NO_SUCH_USER;
+ goto name_lookup_failed;
+ }
+
+ if (rc > 0) {
+ if (lsa_lookup_name(0, 0, name, user_info) != 0) {
+ status = NT_STATUS_NO_SUCH_USER;
+ goto name_lookup_failed;
+ }
+ }
+
+ /*
+ * Set up the rid table.
+ */
+ rids[0].sid_name_use = user_info->sid_name_use;
+ rids[0].rid = user_info->rid;
+ rids[0].domain_index = 0;
+ param->translated_sids.n_entry = 1;
+ param->translated_sids.rids = rids;
+
+ /*
+ * Set up the domain table.
+ */
+ domain_table->entries = domain_entry;
+ domain_table->n_entry = 1;
+ domain_table->max_n_entry = MLSVC_DOMAIN_MAX;
+
+ rc = mlsvc_string_save((ms_string_t *)&domain_entry->domain_name,
+ user_info->domain_name, mxa);
+
+ domain_entry->domain_sid =
+ (struct mslsa_sid *)mlsvc_sid_save(user_info->domain_sid, mxa);
+
+ if (rc == 0 || domain_entry->domain_sid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto name_lookup_failed;
+ }
+
+ param->domain_table = domain_table;
+ param->mapped_count = 1;
+ param->status = 0;
+
+ mlsvc_free_user_info(user_info);
+ return (MLRPC_DRC_OK);
+
+name_lookup_failed:
+ mlsvc_free_user_info(user_info);
+ bzero(param, sizeof (struct mslsa_LookupNames));
+ param->status = NT_SC_ERROR(status);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_LookupSids
+ *
+ * This is the service side function for handling sid lookup requests.
+ * We have to set up both the name table and the domain table in the
+ * response. For each SID, we check for UNIX domain (local lookup) or
+ * NT domain (DC lookup) and call the appropriate lookup function. This
+ * should resolve the SID to a name. Then we need to update the domain
+ * table and make the name entry point at the appropriate domain table
+ * entry.
+ *
+ * On success return 0. Otherwise return an RPC specific error code.
+ */
+static int
+lsarpc_s_LookupSids(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslsa_LookupSids *param = arg;
+ struct mslsa_domain_table *domain_table;
+ struct mslsa_domain_entry *domain_entry;
+ struct mslsa_name_entry *names;
+ smb_userinfo_t *user_info;
+ nt_sid_t *sid;
+ DWORD n_entry;
+ int result;
+ int i;
+
+ user_info = mlsvc_alloc_user_info();
+
+ n_entry = param->lup_sid_table.n_entry;
+ names = MLRPC_HEAP_NEWN(mxa, struct mslsa_name_entry, n_entry);
+ domain_table = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_table);
+ domain_entry = MLRPC_HEAP_NEWN(mxa, struct mslsa_domain_entry,
+ MLSVC_DOMAIN_MAX);
+
+ if (names == NULL || domain_table == NULL ||
+ domain_entry == NULL || user_info == NULL) {
+ bzero(param, sizeof (struct mslsa_LookupSids));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ domain_table->entries = domain_entry;
+ domain_table->n_entry = 0;
+ domain_table->max_n_entry = MLSVC_DOMAIN_MAX;
+
+ for (i = 0; i < n_entry; ++i) {
+ bzero(&names[i], sizeof (struct mslsa_name_entry));
+ sid = (nt_sid_t *)param->lup_sid_table.entries[i].psid;
+
+ if (nt_sid_is_local(sid)) {
+ result = lsarpc_s_LookupLocalSid(mxa, sid, user_info,
+ &names[i]);
+ } else {
+ result = lsarpc_s_LookupBuiltinSid(mxa, sid, user_info,
+ &names[i]);
+
+ if (result != 0)
+ result = lsarpc_s_LookupNtSid(mxa, sid,
+ user_info, &names[i], 1);
+
+ if (result != 0) {
+ result = lsarpc_s_UnknownSid(mxa, sid,
+ user_info, &names[i]);
+ }
+ }
+
+ if (result == -1) {
+ mlsvc_free_user_info(user_info);
+ param->domain_table = 0;
+ param->name_table.n_entry = 0;
+ param->name_table.entries = 0;
+ param->mapped_count = 0;
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_SID);
+ return (MLRPC_DRC_OK);
+ }
+
+ result = lsarpc_s_UpdateDomainTable(mxa, user_info,
+ domain_table, &names[i].domain_ix);
+
+ if (result == -1) {
+ mlsvc_free_user_info(user_info);
+ param->domain_table = 0;
+ param->name_table.n_entry = 0;
+ param->name_table.entries = 0;
+ param->mapped_count = 0;
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_SID);
+ return (MLRPC_DRC_OK);
+ }
+
+ mlsvc_release_user_info(user_info);
+ }
+
+ param->domain_table = domain_table;
+ param->name_table.n_entry = n_entry;
+ param->name_table.entries = names;
+ param->mapped_count = n_entry;
+ param->status = 0;
+
+ mlsvc_free_user_info(user_info);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_LookupLocalSid
+ *
+ * This function handles local domain SID lookup. If the SID matches the
+ * local domain SID, we lookup the local files to map the RID to a name.
+ * We attempt to handle both users and groups. When the SID was supplied
+ * to the client, the ID type should have been encoded in the RID. We
+ * decode the RID and lookup it up in either the passwd file or the
+ * group file as appropriate.
+ *
+ * On success, 0 is returned. Otherwise -1 is returned.
+ */
+static int
+lsarpc_s_LookupLocalSid(struct mlrpc_xaction *mxa, nt_sid_t *sid,
+ smb_userinfo_t *user_info, struct mslsa_name_entry *name)
+{
+ char buffer[MLSVC_DOMAIN_NAME_MAX];
+ char namebuf[MLSVC_DOMAIN_NAME_MAX];
+ nt_sid_t *lds;
+ nt_sid_t *tmp_sid;
+ nt_group_t *grp;
+ struct passwd *pw;
+ struct group *gr;
+ DWORD rid;
+ int unix_id;
+
+ if (smb_gethostname(buffer, MLSVC_DOMAIN_NAME_MAX, 1) != 0)
+ return (-1);
+
+ /*
+ * Only free tmp_sid in error paths. If it is assigned to the
+ * user_info, it will be freed later when that structure is
+ * released.
+ */
+ if ((tmp_sid = nt_sid_dup(sid)) == NULL)
+ return (-1);
+
+ rid = 0;
+ lds = nt_domain_local_sid();
+ user_info->sid_name_use = SidTypeInvalid;
+
+ if (nt_sid_is_equal(lds, tmp_sid)) {
+ user_info->sid_name_use = SidTypeDomain;
+ user_info->name = strdup(buffer);
+ } else {
+ (void) nt_sid_split(tmp_sid, &rid);
+
+ switch (SAM_RID_TYPE(rid)) {
+ case SAM_RT_NT_UID:
+ break;
+
+ case SAM_RT_NT_GID:
+ user_info->sid_name_use = SidTypeAlias;
+ grp = nt_groups_lookup_rid(rid);
+ if (grp)
+ user_info->name = strdup(grp->name);
+ else {
+ (void) snprintf(namebuf, sizeof (namebuf),
+ "%d (no name)", rid);
+ user_info->name = strdup(namebuf);
+ }
+ break;
+
+ case SAM_RT_UNIX_UID:
+ /*
+ * It is always possible that the rid will not
+ * correspond to an entry in the local passwd or group
+ * file. In this case we can return the RID with a
+ * message to indicate the problem, which seems better
+ * than returning an invalid SID error.
+ */
+ unix_id = SAM_DECODE_RID(rid);
+ (void) snprintf(namebuf, sizeof (namebuf),
+ "%d (no name)", unix_id);
+ user_info->sid_name_use = SidTypeUser;
+ pw = getpwuid(unix_id);
+ user_info->name = (pw) ?
+ strdup(pw->pw_name) : strdup(namebuf);
+ break;
+
+ case SAM_RT_UNIX_GID:
+ unix_id = SAM_DECODE_RID(rid);
+ (void) snprintf(namebuf, sizeof (namebuf),
+ "%d (no name)", unix_id);
+ user_info->sid_name_use = SidTypeAlias;
+ gr = getgrgid(unix_id);
+ user_info->name = (gr) ?
+ strdup(gr->gr_name) : strdup(namebuf);
+ break;
+ }
+ }
+
+ if (user_info->sid_name_use == SidTypeInvalid) {
+ free(tmp_sid);
+ return (-1);
+ }
+
+ /*
+ * Set up the rest of user_info.
+ * Don't free tmp_sid after this.
+ */
+ user_info->rid = rid;
+ user_info->domain_name = strdup(buffer);
+ user_info->domain_sid = tmp_sid;
+
+ bzero(name, sizeof (struct mslsa_name_entry));
+ name->sid_name_use = user_info->sid_name_use;
+
+ if (!mlsvc_string_save(
+ (ms_string_t *)&name->name, user_info->name, mxa)) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * lsarpc_s_LookupNtSid
+ *
+ * This function handles NT domain SID lookup on the domain controller.
+ * Most of the work is performed by lsa_lookup_sid. We just have to
+ * update the name data for the response. It is assumed that any SID
+ * passed to this function has already been checked and correctly
+ * identified as an NT domain SID. It shouldn't break anything if you
+ * get it wrong, the domain controller will just reject the SID.
+ *
+ * On success, 0 is returned. Otherwise -1 is returned.
+ */
+static int
+lsarpc_s_LookupNtSid(struct mlrpc_xaction *mxa, nt_sid_t *sid,
+ smb_userinfo_t *user_info, struct mslsa_name_entry *name, int version)
+{
+ char *username;
+ DWORD status;
+
+ if (smb_getdomaininfo(0) == 0) {
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ return (-1);
+ }
+
+ if (version == 2)
+ status = lsa_lookup_sid2(sid, user_info);
+ else
+ status = lsa_lookup_sid(sid, user_info);
+
+ if (status != 0)
+ return (-1);
+
+ switch (user_info->sid_name_use) {
+ case SidTypeDomain:
+ if ((username = user_info->domain_name) == 0)
+ user_info->sid_name_use = SidTypeUnknown;
+ break;
+
+ case SidTypeUser:
+ case SidTypeGroup:
+ case SidTypeAlias:
+ case SidTypeDeletedAccount:
+ case SidTypeWellKnownGroup:
+ if ((username = user_info->name) == 0)
+ user_info->sid_name_use = SidTypeUnknown;
+ break;
+
+ default:
+ return (-1);
+ }
+
+ if (username == 0)
+ username = "unknown";
+ bzero(name, sizeof (struct mslsa_name_entry));
+ name->sid_name_use = user_info->sid_name_use;
+
+ if (!mlsvc_string_save((ms_string_t *)&name->name, username, mxa))
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * lsarpc_s_LookupBuiltinSid
+ *
+ * This function handles predefined local groups and aliases in the NT
+ * AUTHORITY or BUILTIN domains, and some other miscellaneous bits. I
+ * don't think NT cares about the domain field of well-known groups or
+ * aliases but it seems sensible to set it up anyway. If we get a match,
+ * set up the name in the response heap.
+ *
+ * On success, 0 is returned. Otherwise non-zero is returned. A non-zero
+ * return value should not be automatically interpreted as an error. The
+ * caller should attempt to resolve the SID through alternative means.
+ */
+static int
+lsarpc_s_LookupBuiltinSid(struct mlrpc_xaction *mxa, nt_sid_t *sid,
+ smb_userinfo_t *user_info, struct mslsa_name_entry *name)
+{
+ char *np;
+ WORD sid_name_use;
+
+ if ((np = nt_builtin_lookup_sid(sid, &sid_name_use)) == NULL)
+ return (1);
+
+ user_info->sid_name_use = sid_name_use;
+ user_info->name = strdup(np);
+ user_info->domain_sid = nt_sid_dup(sid);
+
+ if (user_info->name == NULL || user_info->domain_sid == NULL) {
+ mlsvc_release_user_info(user_info);
+ return (-1);
+ }
+
+ if (sid_name_use != SidTypeDomain && sid->SubAuthCount != 0)
+ user_info->rid = sid->SubAuthority[sid->SubAuthCount - 1];
+ else
+ user_info->rid = 0;
+
+ if ((np = nt_builtin_lookup_domain(user_info->name)) != NULL)
+ user_info->domain_name = strdup(np);
+ else
+ user_info->domain_name = strdup("UNKNOWN");
+
+ if (user_info->domain_name == NULL) {
+ mlsvc_release_user_info(user_info);
+ return (-1);
+ }
+
+ if (sid_name_use == SidTypeAlias &&
+ user_info->domain_sid->SubAuthCount != 0) {
+ --user_info->domain_sid->SubAuthCount;
+ }
+
+ bzero(name, sizeof (struct mslsa_name_entry));
+ name->sid_name_use = sid_name_use;
+
+ if (sid_name_use == SidTypeUnknown) {
+ mlsvc_release_user_info(user_info);
+ return (1);
+ }
+
+ if (!mlsvc_string_save(
+ (ms_string_t *)&name->name, user_info->name, mxa)) {
+ mlsvc_release_user_info(user_info);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * lsarpc_s_UnknownSid
+ *
+ * This function handles unknown SIDs. By the time this is called we
+ * know that this is not a local SID and that the PDC has no idea to
+ * whom this sid refers. It may be a remnant from a time when the
+ * server was in another domain. All we can do is turn into the SID
+ * into a string and return it in place of a user name.
+ *
+ * On success, 0 is returned. Otherwise -1 is returned.
+ */
+static int
+lsarpc_s_UnknownSid(struct mlrpc_xaction *mxa, nt_sid_t *sid,
+ smb_userinfo_t *user_info, struct mslsa_name_entry *name)
+{
+ char domain_name[MLSVC_DOMAIN_NAME_MAX];
+ char *sidbuf;
+
+ if ((sidbuf = nt_sid_format(sid)) == NULL)
+ return (-1);
+
+ if (smb_gethostname(domain_name, MLSVC_DOMAIN_NAME_MAX, 1) != 0)
+ return (-1);
+
+ (void) utf8_strupr(domain_name);
+ mlsvc_release_user_info(user_info);
+ user_info->sid_name_use = SidTypeUnknown;
+ user_info->name = sidbuf;
+ user_info->domain_name = strdup(domain_name);
+ user_info->domain_sid = nt_sid_dup(nt_domain_local_sid());
+
+ bzero(name, sizeof (struct mslsa_name_entry));
+ name->sid_name_use = user_info->sid_name_use;
+
+ if (!mlsvc_string_save(
+ (ms_string_t *)&name->name, user_info->name, mxa)) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * lsarpc_s_UpdateDomainTable
+ *
+ * This routine is responsible for maintaining the domain table which
+ * will be returned from a SID lookup. Whenever a name is added to the
+ * name table, this function should be called with the corresponding
+ * domain name. If the domain information is not already in the table,
+ * it is added. On success return 0; Otherwise -1 is returned.
+ */
+static int
+lsarpc_s_UpdateDomainTable(struct mlrpc_xaction *mxa,
+ smb_userinfo_t *user_info, struct mslsa_domain_table *domain_table,
+ DWORD *domain_idx)
+{
+ struct mslsa_domain_entry *dentry;
+ DWORD n_entry;
+ DWORD i;
+
+ if (user_info->sid_name_use == SidTypeWellKnownGroup ||
+ user_info->sid_name_use == SidTypeUnknown ||
+ user_info->sid_name_use == SidTypeInvalid) {
+ /*
+ * These types don't need to reference an entry in the
+ * domain table. So return -1.
+ */
+ *domain_idx = (DWORD)-1;
+ return (0);
+ }
+
+ if ((dentry = domain_table->entries) == NULL)
+ return (-1);
+
+ if ((n_entry = domain_table->n_entry) >= MLSVC_DOMAIN_MAX)
+ return (-1);
+
+ for (i = 0; i < n_entry; ++i) {
+ if (nt_sid_is_equal((nt_sid_t *)dentry[i].domain_sid,
+ user_info->domain_sid)) {
+ *domain_idx = i;
+ return (0);
+ }
+ }
+
+ if (i == MLSVC_DOMAIN_MAX)
+ return (-1);
+
+ if (!mlsvc_string_save((ms_string_t *)&dentry[i].domain_name,
+ user_info->domain_name, mxa))
+ return (-1);
+
+ dentry[i].domain_sid =
+ (struct mslsa_sid *)mlsvc_sid_save(user_info->domain_sid, mxa);
+
+ if (dentry[i].domain_sid == NULL)
+ return (-1);
+
+ ++domain_table->n_entry;
+ *domain_idx = i;
+ return (0);
+}
+
+/*
+ * lsarpc_s_LookupSids2
+ *
+ * Other than the use of lsar_lookup_sids2 and lsar_name_entry2, this
+ * is identical to lsarpc_s_LookupSids.
+ */
+static int
+lsarpc_s_LookupSids2(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct lsar_lookup_sids2 *param = arg;
+ struct lsar_name_entry2 *names;
+ struct mslsa_domain_table *domain_table;
+ struct mslsa_domain_entry *domain_entry;
+ smb_userinfo_t *user_info;
+ nt_sid_t *sid;
+ DWORD n_entry;
+ int result;
+ int i;
+
+ user_info = mlsvc_alloc_user_info();
+
+ n_entry = param->lup_sid_table.n_entry;
+ names = MLRPC_HEAP_NEWN(mxa, struct lsar_name_entry2, n_entry);
+ domain_table = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_table);
+ domain_entry = MLRPC_HEAP_NEWN(mxa, struct mslsa_domain_entry,
+ MLSVC_DOMAIN_MAX);
+
+ if (names == NULL || domain_table == NULL ||
+ domain_entry == NULL || user_info == NULL) {
+ bzero(param, sizeof (struct lsar_lookup_sids2));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ domain_table->entries = domain_entry;
+ domain_table->n_entry = 0;
+ domain_table->max_n_entry = MLSVC_DOMAIN_MAX;
+
+ for (i = 0; i < n_entry; ++i) {
+ bzero(&names[i], sizeof (struct lsar_name_entry2));
+ sid = (nt_sid_t *)param->lup_sid_table.entries[i].psid;
+
+ if (nt_sid_is_local(sid)) {
+ result = lsarpc_s_LookupLocalSid(mxa, sid, user_info,
+ (struct mslsa_name_entry *)&names[i]);
+ } else {
+ result = lsarpc_s_LookupBuiltinSid(mxa, sid, user_info,
+ (struct mslsa_name_entry *)&names[i]);
+
+ if (result != 0)
+ result = lsarpc_s_LookupNtSid(mxa, sid,
+ user_info,
+ (struct mslsa_name_entry *)&names[i], 2);
+
+ if (result != 0) {
+ result = lsarpc_s_UnknownSid(mxa, sid,
+ user_info,
+ (struct mslsa_name_entry *)&names[i]);
+ }
+ }
+
+ if (result == -1) {
+ mlsvc_free_user_info(user_info);
+ param->domain_table = 0;
+ param->name_table.n_entry = 0;
+ param->name_table.entries = 0;
+ param->mapped_count = 0;
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_SID);
+ return (MLRPC_DRC_OK);
+ }
+
+ result = lsarpc_s_UpdateDomainTable(mxa, user_info,
+ domain_table, &names[i].domain_ix);
+
+ if (result == -1) {
+ mlsvc_free_user_info(user_info);
+ param->domain_table = 0;
+ param->name_table.n_entry = 0;
+ param->name_table.entries = 0;
+ param->mapped_count = 0;
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_SID);
+
+ return (MLRPC_DRC_OK);
+ }
+
+ mlsvc_release_user_info(user_info);
+ }
+
+ param->domain_table = domain_table;
+ param->name_table.n_entry = n_entry;
+ param->name_table.entries = names;
+ param->mapped_count = n_entry;
+ param->status = 0;
+
+ mlsvc_free_user_info(user_info);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * lsarpc_s_LookupNames2
+ *
+ * Other than the use of lsar_LookupNames2 and lsar_rid_entry2, this
+ * is identical to lsarpc_s_LookupNames.
+ */
+static int
+lsarpc_s_LookupNames2(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct lsar_LookupNames2 *param = arg;
+ struct lsar_rid_entry2 *rids;
+ smb_userinfo_t *user_info = 0;
+ struct mslsa_domain_table *domain_table;
+ struct mslsa_domain_entry *domain_entry;
+ char *name = "";
+ DWORD status = NT_STATUS_SUCCESS;
+ int rc = 0;
+
+ if (param->name_table->n_entry != 1)
+ return (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED);
+
+ rids = MLRPC_HEAP_NEW(mxa, struct lsar_rid_entry2);
+ domain_table = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_table);
+ domain_entry = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_entry);
+ user_info = mlsvc_alloc_user_info();
+
+ if (rids == 0 || domain_table == 0 ||
+ domain_entry == 0 || user_info == 0) {
+ status = NT_STATUS_NO_MEMORY;
+ goto name_lookup2_failed;
+ }
+
+ name = (char *)param->name_table->names->str;
+
+ rc = lsa_lookup_local(name, user_info);
+ if (rc < 0) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto name_lookup2_failed;
+ }
+
+ if (rc > 0) {
+ if (lsa_lookup_name2(0, 0, name, user_info) != 0) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto name_lookup2_failed;
+ }
+ }
+
+ /*
+ * Set up the rid table.
+ */
+ bzero(rids, sizeof (struct lsar_rid_entry2));
+ rids[0].sid_name_use = user_info->sid_name_use;
+ rids[0].rid = user_info->rid;
+ rids[0].domain_index = 0;
+ param->translated_sids.n_entry = 1;
+ param->translated_sids.rids = rids;
+
+ /*
+ * Set up the domain table.
+ */
+ domain_table->entries = domain_entry;
+ domain_table->n_entry = 1;
+ domain_table->max_n_entry = MLSVC_DOMAIN_MAX;
+
+ rc = mlsvc_string_save((ms_string_t *)&domain_entry->domain_name,
+ user_info->domain_name, mxa);
+
+ domain_entry->domain_sid =
+ (struct mslsa_sid *)mlsvc_sid_save(user_info->domain_sid, mxa);
+
+ if (rc == 0 || domain_entry->domain_sid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto name_lookup2_failed;
+ }
+
+ param->domain_table = domain_table;
+ param->mapped_count = 1;
+ param->status = 0;
+
+ mlsvc_free_user_info(user_info);
+ return (MLRPC_DRC_OK);
+
+name_lookup2_failed:
+ mlsvc_free_user_info(user_info);
+ bzero(param, sizeof (struct lsar_LookupNames2));
+ param->status = NT_SC_ERROR(status);
+ return (MLRPC_DRC_OK);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c
new file mode 100644
index 0000000000..4edfcacd51
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c
@@ -0,0 +1,202 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * NetLogon RPC (NETR) interface definition. This module provides
+ * the server side NETR RPC interface and the interface registration
+ * function.
+ */
+
+#include <strings.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/netlogon.ndl>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/netrauth.h>
+
+static int netr_s_ServerReqChallenge(void *, struct mlrpc_xaction *);
+static int netr_s_ServerAuthenticate2(void *, struct mlrpc_xaction *);
+static int netr_s_ServerPasswordSet(void *, struct mlrpc_xaction *);
+static int netr_s_SamLogon(void *, struct mlrpc_xaction *);
+static int netr_s_SamLogoff(void *, struct mlrpc_xaction *);
+
+static mlrpc_stub_table_t netr_stub_table[] = {
+ { netr_s_ServerReqChallenge, NETR_OPNUM_ServerReqChallenge },
+ { netr_s_ServerAuthenticate2, NETR_OPNUM_ServerAuthenticate2 },
+ { netr_s_ServerPasswordSet, NETR_OPNUM_ServerPasswordSet },
+ { netr_s_SamLogon, NETR_OPNUM_SamLogon },
+ { netr_s_SamLogoff, NETR_OPNUM_SamLogoff },
+ {0}
+};
+
+static mlrpc_service_t netr_service = {
+ "NETR", /* name */
+ "NetLogon", /* desc */
+ "\\netlogon", /* endpoint */
+ PIPE_LSASS, /* sec_addr_port */
+ "12345678-1234-abcd-ef0001234567cffb", 1, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(netr_interface), /* interface ti */
+ netr_stub_table /* stub_table */
+};
+
+/*
+ * netr_initialize
+ *
+ * This function registers the NETR RPC interface with the RPC runtime
+ * library. It must be called in order to use either the client side
+ * or the server side functions.
+ */
+void
+netr_initialize(void)
+{
+ (void) mlrpc_register_service(&netr_service);
+}
+
+/*
+ * netr_s_ServerReqChallenge
+ */
+/*ARGSUSED*/
+static int
+netr_s_ServerReqChallenge(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netr_ServerReqChallenge *param = arg;
+
+ bzero(param, sizeof (struct netr_ServerReqChallenge));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * netr_s_ServerAuthenticate2
+ */
+/*ARGSUSED*/
+static int
+netr_s_ServerAuthenticate2(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netr_ServerAuthenticate2 *param = arg;
+
+ bzero(param, sizeof (struct netr_ServerAuthenticate2));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * netr_s_ServerPasswordSet
+ */
+/*ARGSUSED*/
+static int
+netr_s_ServerPasswordSet(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netr_PasswordSet *param = arg;
+
+ bzero(param, sizeof (struct netr_PasswordSet));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * netr_s_SamLogon
+ */
+/*ARGSUSED*/
+static int
+netr_s_SamLogon(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netr_SamLogon *param = arg;
+
+ bzero(param, sizeof (struct netr_SamLogon));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * netr_s_SamLogoff
+ */
+/*ARGSUSED*/
+static int
+netr_s_SamLogoff(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netr_SamLogoff *param = arg;
+
+ bzero(param, sizeof (struct netr_SamLogoff));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Declare extern references.
+ */
+DECL_FIXUP_STRUCT(netr_validation_u);
+DECL_FIXUP_STRUCT(netr_validation_info);
+DECL_FIXUP_STRUCT(netr_SamLogon);
+
+/*
+ * Patch the netr_SamLogon union.
+ * This function is called from mlsvc_netr_ndr.c
+ */
+void
+fixup_netr_SamLogon(struct netr_SamLogon *arg)
+{
+ unsigned short size1 = 0;
+ unsigned short size2 = 0;
+ unsigned short size3 = 0;
+ WORD level = (WORD)arg->validation_level;
+
+ switch (level) {
+ case 3:
+ /*
+ * The netr_validation_u union contains a pointer, which
+ * is a DWORD in NDR. So we need to set size1 to ensure
+ * that we can correctly decode the remaining parameters.
+ */
+ size1 = sizeof (DWORD);
+ break;
+
+ default:
+ /*
+ * If the request is badly formed or the level is invalid,
+ * the server returns NT_STATUS_INVALID_INFO_CLASS. Size1
+ * must be zero to correctly decode the status.
+ */
+ size1 = 0;
+ break;
+ };
+
+ size2 = size1 + (2 * sizeof (DWORD));
+ size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD);
+
+ FIXUP_PDU_SIZE(netr_validation_u, size1);
+ FIXUP_PDU_SIZE(netr_validation_info, size2);
+ FIXUP_PDU_SIZE(netr_SamLogon, size3);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_sam.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_sam.c
new file mode 100644
index 0000000000..3b51c05e71
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_sam.c
@@ -0,0 +1,1463 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Security Accounts Manager RPC (SAMR) interface definition.
+ */
+
+#include <strings.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/samrpc.ndl>
+#include <smbsrv/samlib.h>
+
+/*
+ * The keys associated with the various handles dispensed
+ * by the SAMR server. These keys can be used to validate
+ * client activity. These values are never passed over
+ * the network so security shouldn't be an issue.
+ */
+#define SAMR_CONNECT_KEY "SamrConnect"
+#define SAMR_DOMAIN_KEY "SamrDomain"
+#define SAMR_USER_KEY "SamrUser"
+#define SAMR_GROUP_KEY "SamrGroup"
+#define SAMR_ALIAS_KEY "SamrAlias"
+
+/*
+ * Domain discriminator values. Set the top bit to try
+ * to distinguish these values from user and group ids.
+ */
+#define SAMR_DATABASE_DOMAIN 0x80000001
+#define SAMR_LOCAL_DOMAIN 0x80000002
+#define SAMR_BUILTIN_DOMAIN 0x80000003
+#define SAMR_PRIMARY_DOMAIN 0x80000004
+
+static DWORD samr_s_enum_local_domains(struct samr_EnumLocalDomain *,
+ struct mlrpc_xaction *);
+
+static mlrpc_stub_table_t samr_stub_table[];
+
+static mlrpc_service_t samr_service = {
+ "SAMR", /* name */
+ "Security Accounts Manager", /* desc */
+ "\\samr", /* endpoint */
+ PIPE_LSASS, /* sec_addr_port */
+ "12345778-1234-abcd-ef000123456789ac", 1, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(samr_interface), /* interface ti */
+ samr_stub_table /* stub_table */
+};
+
+/*
+ * samr_initialize
+ *
+ * This function registers the SAM RPC interface with the RPC runtime
+ * library. It must be called in order to use either the client side
+ * or the server side functions.
+ */
+void
+samr_initialize(void)
+{
+ (void) mlrpc_register_service(&samr_service);
+}
+
+/*
+ * samr_s_ConnectAnon
+ *
+ * This is a request to connect to the local SAM database. We don't
+ * support any form of update request and our database doesn't
+ * contain any private information, so there is little point in
+ * doing any access access checking here.
+ *
+ * Return a handle for use with subsequent SAM requests.
+ */
+/*ARGSUSED*/
+static int
+samr_s_ConnectAnon(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_ConnectAnon *param = arg;
+ ms_handle_t *handle;
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_CONNECT_KEY,
+ SAMR_DATABASE_DOMAIN);
+ bcopy(handle, &param->handle, sizeof (samr_handle_t));
+
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_CloseHandle
+ *
+ * This is a request to close the SAM interface specified by the handle.
+ * Free the handle and zero out the result handle for the client.
+ *
+ * We could do some checking here but it probably doesn't matter.
+ */
+/*ARGSUSED*/
+static int
+samr_s_CloseHandle(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_CloseHandle *param = arg;
+
+#ifdef SAMR_S_DEBUG
+ if (mlsvc_lookup_handle((ms_handle_t *)&param->handle) == 0) {
+ bzero(&param->result_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+ return (MLRPC_DRC_OK);
+ }
+#endif /* SAMR_S_DEBUG */
+
+ (void) mlsvc_put_handle((ms_handle_t *)&param->handle);
+ bzero(&param->result_handle, sizeof (samr_handle_t));
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_LookupDomain
+ *
+ * This is a request to map a domain name to a domain SID. We can map
+ * the primary domain name, our local domain name (hostname) and the
+ * builtin domain names to the appropriate SID. Anything else will be
+ * rejected.
+ */
+static int
+samr_s_LookupDomain(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_LookupDomain *param = arg;
+ char resource_domain[MAXHOSTNAMELEN];
+ char *domain_name;
+ char *p;
+ nt_sid_t *sid = NULL;
+
+ if ((domain_name = (char *)param->domain_name.str) == NULL) {
+ bzero(param, sizeof (struct samr_LookupDomain));
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_PARAMETER);
+ return (MLRPC_DRC_OK);
+ }
+
+ smb_config_rdlock();
+ p = smb_config_getstr(SMB_CI_DOMAIN_NAME);
+ (void) strlcpy(resource_domain, p, MAXHOSTNAMELEN);
+ smb_config_unlock();
+
+ if (mlsvc_is_local_domain(domain_name) == 1) {
+ sid = nt_sid_dup(nt_domain_local_sid());
+ } else if (strcasecmp(resource_domain, domain_name) == 0) {
+ /*
+ * We should not be asked to provide
+ * the domain SID for the primary domain.
+ */
+ sid = NULL;
+ } else {
+ sid = nt_builtin_lookup_name(domain_name, 0);
+ }
+
+ if (sid) {
+ param->sid = (struct samr_sid *)mlsvc_sid_save(sid, mxa);
+ free(sid);
+
+ if (param->sid == NULL) {
+ bzero(param, sizeof (struct samr_LookupDomain));
+ param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ param->status = NT_STATUS_SUCCESS;
+ } else {
+ param->sid = NULL;
+ param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_DOMAIN);
+ }
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_EnumLocalDomains
+ *
+ * This is a request for the local domains supported by this server.
+ * All we do here is validate the handle and set the status. The real
+ * work is done in samr_s_enum_local_domains.
+ */
+static int
+samr_s_EnumLocalDomains(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_EnumLocalDomain *param = arg;
+ ms_handle_t *handle;
+ DWORD status;
+
+ handle = (ms_handle_t *)&param->handle;
+
+ if (mlsvc_validate_handle(handle, SAMR_CONNECT_KEY) == 0)
+ status = NT_STATUS_ACCESS_DENIED;
+ else
+ status = samr_s_enum_local_domains(param, mxa);
+
+ if (status == NT_STATUS_SUCCESS) {
+ param->enum_context = param->info->entries_read;
+ param->total_entries = param->info->entries_read;
+ param->status = NT_STATUS_SUCCESS;
+ } else {
+ bzero(param, sizeof (struct samr_EnumLocalDomain));
+ param->status = NT_SC_ERROR(status);
+ }
+
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * samr_s_enum_local_domains
+ *
+ * This function should only be called via samr_s_EnumLocalDomains to
+ * ensure that the appropriate validation is performed. We will answer
+ * queries about two domains: the local domain, synonymous with the
+ * local hostname, and the BUILTIN domain. So we return these two
+ * strings.
+ *
+ * Returns NT status values.
+ */
+static DWORD
+samr_s_enum_local_domains(struct samr_EnumLocalDomain *param,
+ struct mlrpc_xaction *mxa)
+{
+ struct samr_LocalDomainInfo *info;
+ struct samr_LocalDomainEntry *entry;
+ char *hostname;
+
+ hostname = MLRPC_HEAP_MALLOC(mxa, MAXHOSTNAMELEN);
+ if (hostname == NULL)
+ return (NT_STATUS_NO_MEMORY);
+
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0)
+ return (NT_STATUS_NO_MEMORY);
+
+ entry = MLRPC_HEAP_NEWN(mxa, struct samr_LocalDomainEntry, 2);
+ if (entry == NULL)
+ return (NT_STATUS_NO_MEMORY);
+
+ bzero(entry, (sizeof (struct samr_LocalDomainEntry) * 2));
+ (void) mlsvc_string_save((ms_string_t *)&entry[0].name, hostname, mxa);
+ (void) mlsvc_string_save((ms_string_t *)&entry[1].name, "Builtin", mxa);
+
+ info = MLRPC_HEAP_NEW(mxa, struct samr_LocalDomainInfo);
+ if (info == NULL)
+ return (NT_STATUS_NO_MEMORY);
+
+ info->entries_read = 2;
+ info->entry = entry;
+ param->info = info;
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * samr_s_OpenDomain
+ *
+ * This is a request to open a domain within the local SAM database.
+ * The caller must supply a valid handle obtained via a successful
+ * connect. We return a handle to be used to access objects within
+ * this domain.
+ */
+/*ARGSUSED*/
+static int
+samr_s_OpenDomain(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_OpenDomain *param = arg;
+ ms_handle_t *handle = 0;
+ nt_domain_t *domain;
+
+ if (!mlsvc_validate_handle(
+ (ms_handle_t *)&param->handle, SAMR_CONNECT_KEY)) {
+ bzero(&param->domain_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+ }
+
+ domain = nt_domain_lookup_sid((nt_sid_t *)param->sid);
+ if (domain == NULL) {
+ bzero(&param->domain_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ return (MLRPC_DRC_OK);
+ }
+
+ switch (domain->type) {
+ case NT_DOMAIN_BUILTIN:
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR,
+ SAMR_DOMAIN_KEY, SAMR_BUILTIN_DOMAIN);
+
+ bcopy(handle, &param->domain_handle, sizeof (samr_handle_t));
+ param->status = 0;
+ break;
+
+ case NT_DOMAIN_LOCAL:
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR,
+ SAMR_DOMAIN_KEY, SAMR_LOCAL_DOMAIN);
+
+ bcopy(handle, &param->domain_handle, sizeof (samr_handle_t));
+ param->status = 0;
+ break;
+
+ default:
+ bzero(&param->domain_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ }
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_QueryDomainInfo
+ *
+ * The caller should pass a domain handle.
+ *
+ * Windows 95 Server Manager sends requests for levels 6 and 7 when
+ * the services menu item is selected. Level 2 is basically for getting
+ * number of users, groups, and aliases in a domain.
+ * We have no information on what the various information levels mean.
+ */
+static int
+samr_s_QueryDomainInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_QueryDomainInfo *param = arg;
+ ms_handle_desc_t *desc;
+ char *hostname;
+ char *domain_str = "";
+ int rc;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->domain_handle);
+ if (desc == NULL || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) {
+ bzero(param, sizeof (struct samr_QueryDomainInfo));
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+ return (MLRPC_DRC_OK);
+ }
+
+ switch (param->info_level) {
+ case SAMR_QUERY_DOMAIN_INFO_6:
+ param->ru.info6.unknown1 = 0x00000000;
+ param->ru.info6.unknown2 = 0x00147FB0;
+ param->ru.info6.unknown3 = 0x00000000;
+ param->ru.info6.unknown4 = 0x00000000;
+ param->ru.info6.unknown5 = 0x00000000;
+ param->status = NT_STATUS_SUCCESS;
+ break;
+
+ case SAMR_QUERY_DOMAIN_INFO_7:
+ param->ru.info7.unknown1 = 0x00000003;
+ param->status = NT_STATUS_SUCCESS;
+ break;
+
+ case SAMR_QUERY_DOMAIN_INFO_2:
+ if (desc->discrim == SAMR_LOCAL_DOMAIN) {
+ hostname = MLRPC_HEAP_MALLOC(mxa, MAXHOSTNAMELEN);
+ rc = smb_gethostname(hostname, MAXHOSTNAMELEN, 1);
+ if (rc != 0 || hostname == NULL) {
+ bzero(param,
+ sizeof (struct samr_QueryDomainInfo));
+ param->status =
+ NT_SC_ERROR(NT_STATUS_NO_MEMORY);
+ return (MLRPC_DRC_OK);
+ }
+
+ domain_str = hostname;
+ } else {
+ if (desc->discrim == SAMR_BUILTIN_DOMAIN)
+ domain_str = "Builtin";
+ }
+
+ param->ru.info2.unknown1 = 0x00000000;
+ param->ru.info2.unknown2 = 0x80000000;
+
+ (void) mlsvc_string_save((ms_string_t *)&(param->ru.info2.s1),
+ "", mxa);
+
+ (void) mlsvc_string_save(
+ (ms_string_t *)&(param->ru.info2.domain), domain_str, mxa);
+
+ (void) mlsvc_string_save((ms_string_t *)&(param->ru.info2.s2),
+ "", mxa);
+
+ param->ru.info2.sequence_num = 0x0000002B;
+ param->ru.info2.unknown3 = 0x00000000;
+ param->ru.info2.unknown4 = 0x00000001;
+ param->ru.info2.unknown5 = 0x00000003;
+ param->ru.info2.unknown6 = 0x00000001;
+ param->ru.info2.num_users = 0;
+ param->ru.info2.num_groups = 0;
+ param->ru.info2.num_aliases =
+ (desc->discrim == SAMR_BUILTIN_DOMAIN) ?
+ nt_groups_count(NT_GROUP_CNT_BUILTIN) :
+ nt_groups_count(NT_GROUP_CNT_LOCAL);
+
+ param->status = NT_STATUS_SUCCESS;
+ break;
+
+ default:
+ bzero(param, sizeof (struct samr_QueryDomainInfo));
+ return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID);
+ };
+
+ param->address = (DWORD)&param->ru;
+ param->switch_value = param->info_level;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_LookupNames
+ *
+ * The definition for this interface is obviously wrong but I can't
+ * seem to get it to work the way I think it should. It should
+ * support multiple name lookup but I can only get one working for now.
+ */
+static int
+samr_s_LookupNames(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_LookupNames *param = arg;
+ ms_handle_desc_t *desc;
+ struct passwd *pw;
+ struct group *gr;
+ nt_sid_t *sid;
+ nt_group_t *grp;
+ WORD rid_type;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->handle);
+ if (desc == 0 || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) {
+ bzero(param, sizeof (struct samr_LookupNames));
+ param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+ return (MLRPC_DRC_OK);
+ }
+
+ if (param->n_entry != 1) {
+ bzero(param, sizeof (struct samr_LookupNames));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+ }
+
+ if (param->name.str == NULL) {
+ bzero(param, sizeof (struct samr_LookupNames));
+ /*
+ * Windows NT returns NT_STATUS_NONE_MAPPED when the
+ * name is NULL.
+ * Windows 2000 returns STATUS_INVALID_ACCOUNT_NAME.
+ */
+ param->status = NT_SC_ERROR(NT_STATUS_NONE_MAPPED);
+ return (MLRPC_DRC_OK);
+ }
+
+ param->rids.rid = MLRPC_HEAP_NEW(mxa, DWORD);
+ param->rid_types.rid_type = MLRPC_HEAP_NEW(mxa, DWORD);
+
+ if (desc->discrim == SAMR_BUILTIN_DOMAIN) {
+ sid = nt_builtin_lookup_name((char *)param->name.str,
+ &rid_type);
+
+ if (sid != 0) {
+ param->rids.n_entry = 1;
+ (void) nt_sid_get_rid(sid, &param->rids.rid[0]);
+ param->rid_types.n_entry = 1;
+ param->rid_types.rid_type[0] = rid_type;
+ param->status = NT_STATUS_SUCCESS;
+ free(sid);
+ return (MLRPC_DRC_OK);
+ }
+ } else if (desc->discrim == SAMR_LOCAL_DOMAIN) {
+ grp = nt_group_getinfo((char *)param->name.str, RWLOCK_READER);
+
+ if (grp != NULL) {
+ param->rids.n_entry = 1;
+ (void) nt_sid_get_rid(grp->sid, &param->rids.rid[0]);
+ param->rid_types.n_entry = 1;
+ param->rid_types.rid_type[0] = *grp->sid_name_use;
+ param->status = NT_STATUS_SUCCESS;
+ nt_group_putinfo(grp);
+ return (MLRPC_DRC_OK);
+ }
+
+ if ((pw = getpwnam((const char *)param->name.str)) != NULL) {
+ param->rids.n_entry = 1;
+ param->rids.rid[0] = SAM_ENCODE_UXUID(pw->pw_uid);
+ param->rid_types.n_entry = 1;
+ param->rid_types.rid_type[0] = SidTypeUser;
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+ }
+
+ if ((gr = getgrnam((const char *)param->name.str)) != NULL) {
+ param->rids.n_entry = 1;
+ param->rids.rid[0] = SAM_ENCODE_UXGID(gr->gr_gid);
+ param->rid_types.n_entry = 1;
+ param->rid_types.rid_type[0] = SidTypeAlias;
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+ }
+ }
+
+ param->rids.n_entry = 0;
+ param->rid_types.n_entry = 0;
+ param->status = NT_SC_ERROR(NT_STATUS_NONE_MAPPED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_OpenUser
+ *
+ * This is a request to open a user within a specified domain in the
+ * local SAM database. The caller must supply a valid domain handle,
+ * obtained via a successful domain open request. The user is
+ * specified by the rid in the request.
+ */
+/*ARGSUSED*/
+static int
+samr_s_OpenUser(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_OpenUser *param = arg;
+ ms_handle_t *handle;
+
+ if (!mlsvc_validate_handle(
+ (ms_handle_t *)&param->handle, SAMR_DOMAIN_KEY)) {
+ bzero(&param->user_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+ }
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_USER_KEY,
+ param->rid);
+ bcopy(handle, &param->user_handle, sizeof (samr_handle_t));
+
+ /*
+ * Need QueryUserInfo(level 21).
+ */
+ bzero(&param->user_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_DeleteUser
+ *
+ * This is a request to delete a user within a specified domain in the
+ * local SAM database. The caller should supply a valid user handle but
+ * we deny access regardless.
+ */
+/*ARGSUSED*/
+static int
+samr_s_DeleteUser(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_DeleteUser *param = arg;
+
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_QueryUserInfo
+ *
+ * Returns:
+ * NT_STATUS_SUCCESS
+ * NT_STATUS_ACCESS_DENIED
+ * NT_STATUS_INVALID_INFO_CLASS
+ */
+/*ARGSUSED*/
+static int
+samr_s_QueryUserInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_QueryUserInfo *param = arg;
+
+ if (!mlsvc_validate_handle(
+ (ms_handle_t *)&param->user_handle, SAMR_USER_KEY)) {
+ bzero(param, sizeof (struct samr_QueryUserInfo));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param, sizeof (struct samr_QueryUserInfo));
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_QueryUserGroups
+ *
+ * This is a request to obtain a list of groups of which a user is a
+ * member. The user is identified from the handle, which contains an
+ * encoded uid in the discriminator field.
+ *
+ * Get complete list of groups and check for builtin domain.
+ */
+static int
+samr_s_QueryUserGroups(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_QueryUserGroups *param = arg;
+ struct samr_UserGroupInfo *info;
+ ms_handle_desc_t *desc;
+ struct passwd *pw;
+ DWORD uid;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->user_handle);
+ if (desc == 0 || strcmp(desc->key, SAMR_USER_KEY)) {
+ bzero(param, sizeof (struct samr_QueryUserGroups));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+ }
+
+ info = MLRPC_HEAP_NEW(mxa, struct samr_UserGroupInfo);
+ info->groups = MLRPC_HEAP_NEW(mxa, struct samr_UserGroups);
+
+ uid = SAM_DECODE_RID(desc->discrim);
+
+ if ((pw = getpwuid(uid)) != 0) {
+ info->n_entry = 1;
+ info->groups->rid = SAM_ENCODE_UXGID(pw->pw_gid);
+ info->groups->attr = SE_GROUP_MANDATORY
+ | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ param->info = info;
+ param->status = 0;
+ } else {
+ bzero(param, sizeof (struct samr_QueryUserGroups));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ }
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_OpenGroup
+ *
+ * This is a request to open a group within the specified domain in the
+ * local SAM database. The caller must supply a valid domain handle,
+ * obtained via a successful domain open request. The group is
+ * specified by the rid in the request. If this is a local RID it
+ * should already be encoded with type information.
+ *
+ * We return a handle to be used to access information about this group.
+ */
+/*ARGSUSED*/
+static int
+samr_s_OpenGroup(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_OpenGroup *param = arg;
+ ms_handle_t *handle;
+
+ if (!mlsvc_validate_handle(
+ (ms_handle_t *)&param->handle, SAMR_DOMAIN_KEY)) {
+ bzero(&param->group_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+ }
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_GROUP_KEY,
+ param->rid);
+ bcopy(handle, &param->group_handle, sizeof (samr_handle_t));
+
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_Connect
+ *
+ * This is a request to connect to the local SAM database. We don't
+ * support any form of update request and our database doesn't
+ * contain any private information, so there is little point in
+ * doing any access access checking here.
+ *
+ * Return a handle for use with subsequent SAM requests.
+ */
+/*ARGSUSED*/
+static int
+samr_s_Connect(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_Connect *param = arg;
+ ms_handle_t *handle;
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR,
+ SAMR_CONNECT_KEY, SAMR_DATABASE_DOMAIN);
+ bcopy(handle, &param->handle, sizeof (samr_handle_t));
+
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_GetUserPwInfo
+ *
+ * This is a request to get a user's password.
+ */
+/*ARGSUSED*/
+static int
+samr_s_GetUserPwInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_GetUserPwInfo *param = arg;
+ ms_handle_t *handle;
+ DWORD status = 0;
+
+ handle = (ms_handle_t *)&param->user_handle;
+
+ if (!mlsvc_validate_handle(handle, SAMR_USER_KEY))
+ status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+
+ bzero(param, sizeof (struct samr_GetUserPwInfo));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_CreateUser
+ *
+ * This is a request to create a user within a specified domain in the
+ * local SAM database. We always deny access.
+ */
+/*ARGSUSED*/
+static int
+samr_s_CreateUser(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_CreateUser *param = arg;
+
+ bzero(&param->user_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_ChangeUserPasswd
+ */
+/*ARGSUSED*/
+static int
+samr_s_ChangeUserPasswd(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_ChangeUserPasswd *param = arg;
+
+ bzero(param, sizeof (struct samr_ChangeUserPasswd));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_GetDomainPwInfo
+ */
+/*ARGSUSED*/
+static int
+samr_s_GetDomainPwInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_GetDomainPwInfo *param = arg;
+
+ bzero(param, sizeof (struct samr_GetDomainPwInfo));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_SetUserInfo
+ */
+/*ARGSUSED*/
+static int
+samr_s_SetUserInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_SetUserInfo *param = arg;
+
+ bzero(param, sizeof (struct samr_SetUserInfo));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_QueryDispInfo
+ *
+ * This function is supposed to return local users' information.
+ * As we don't support local users, this function dosen't send
+ * back any information.
+ *
+ * I added a peice of code that returns information for Administrator
+ * and Guest builtin users. All information are hard-coded which I get
+ * from packet captures. Currently, this peice of code is opt-out.
+ */
+/*ARGSUSED*/
+static int
+samr_s_QueryDispInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_QueryDispInfo *param = arg;
+ ms_handle_desc_t *desc;
+ DWORD status = 0;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->domain_handle);
+ if (desc == NULL || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0))
+ status = NT_STATUS_INVALID_HANDLE;
+
+#ifdef SAMR_SUPPORT_USER
+ if ((desc->discrim != SAMR_LOCAL_DOMAIN) || (param->start_idx != 0)) {
+ param->total_size = 0;
+ param->returned_size = 0;
+ param->switch_value = 1;
+ param->count = 0;
+ param->users = 0;
+ } else {
+ param->total_size = 328;
+ param->returned_size = 328;
+ param->switch_value = 1;
+ param->count = 2;
+ param->users = (struct user_disp_info *)MLRPC_HEAP_MALLOC(mxa,
+ sizeof (struct user_disp_info));
+
+ param->users->count = 2;
+ param->users->acct[0].index = 1;
+ param->users->acct[0].rid = 500;
+ param->users->acct[0].ctrl = 0x210;
+
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->users->acct[0].name,
+ "Administrator", mxa);
+
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->users->acct[0].fullname,
+ "Built-in account for administering the computer/domain",
+ mxa);
+
+ bzero(&param->users->acct[0].desc, sizeof (samr_string_t));
+
+ param->users->acct[1].index = 2;
+ param->users->acct[1].rid = 501;
+ param->users->acct[1].ctrl = 0x211;
+
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->users->acct[1].name,
+ "Guest", mxa);
+
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->users->acct[1].fullname,
+ "Built-in account for guest access to the computer/domain",
+ mxa);
+
+ bzero(&param->users->acct[1].desc, sizeof (samr_string_t));
+ }
+#else
+ param->total_size = 0;
+ param->returned_size = 0;
+ param->switch_value = 1;
+ param->count = 0;
+ param->users = 0;
+#endif
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_EnumDomainGroups
+ *
+ *
+ * This function is supposed to return local users' information.
+ * As we don't support local users, this function dosen't send
+ * back any information.
+ *
+ * I added a peice of code that returns information for a
+ * domain group as None. All information are hard-coded which I get
+ * from packet captures. Currently, this peice of code is opt-out.
+ */
+/*ARGSUSED*/
+static int
+samr_s_EnumDomainGroups(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_EnumDomainGroups *param = arg;
+ ms_handle_desc_t *desc;
+ DWORD status = NT_STATUS_SUCCESS;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->domain_handle);
+ if (desc == NULL || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0))
+ status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+
+ param->total_size = 0;
+ param->returned_size = 0;
+ param->switch_value = 3;
+ param->count = 0;
+ param->groups = 0;
+ param->status = status;
+ return (MLRPC_DRC_OK);
+
+#ifdef SAMR_SUPPORT_GROUPS
+ if ((desc->discrim != SAMR_LOCAL_DOMAIN) || (param->start_idx != 0)) {
+ param->total_size = 0;
+ param->returned_size = 0;
+ param->switch_value = 3;
+ param->count = 0;
+ param->groups = 0;
+ } else {
+ param->total_size = 64;
+ param->returned_size = 64;
+ param->switch_value = 3;
+ param->count = 1;
+ param->groups = (struct group_disp_info *)MLRPC_HEAP_MALLOC(
+ mxa, sizeof (struct group_disp_info));
+
+ param->groups->count = 1;
+ param->groups->acct[0].index = 1;
+ param->groups->acct[0].rid = 513;
+ param->groups->acct[0].ctrl = 0x7;
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->groups->acct[0].name, "None", mxa);
+
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->groups->acct[0].desc,
+ "Ordinary users", mxa);
+ }
+
+ param->status = NT_STATUS_SUCCESS;
+ return (MLRPC_DRC_OK);
+#endif
+}
+
+/*
+ * samr_s_OpenAlias
+ *
+ * Lookup for requested alias, if it exists return a handle
+ * for that alias. The alias domain sid should match with
+ * the passed domain handle.
+ */
+/*ARGSUSED*/
+static int
+samr_s_OpenAlias(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_OpenAlias *param = arg;
+ ms_handle_desc_t *desc = 0;
+ ms_handle_t *handle;
+ nt_group_t *grp;
+ DWORD status = NT_STATUS_SUCCESS;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->domain_handle);
+ if (desc == 0 || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) {
+ status = NT_STATUS_INVALID_HANDLE;
+ goto open_alias_err;
+ }
+
+ if (param->access_mask != SAMR_ALIAS_ACCESS_GET_INFO) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto open_alias_err;
+ }
+
+ grp = nt_groups_lookup_rid(param->rid);
+ if (grp == 0) {
+ status = NT_STATUS_NO_SUCH_ALIAS;
+ goto open_alias_err;
+ }
+
+ if (((desc->discrim == SAMR_LOCAL_DOMAIN) &&
+ !nt_sid_is_local(grp->sid)) ||
+ ((desc->discrim == SAMR_BUILTIN_DOMAIN) &&
+ !nt_sid_is_builtin(grp->sid))) {
+ status = NT_STATUS_NO_SUCH_ALIAS;
+ goto open_alias_err;
+ }
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_ALIAS_KEY,
+ param->rid);
+ bcopy(handle, &param->alias_handle, sizeof (samr_handle_t));
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+
+open_alias_err:
+ bzero(&param->alias_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(status);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_CreateDomainAlias
+ *
+ * Creates a local group in the security database, which is the
+ * security accounts manager (SAM)
+ * For more information you can look at MSDN page for NetLocalGroupAdd.
+ * This RPC is used by CMC and right now it returns access denied.
+ * The peice of code that creates a local group doesn't get compiled.
+ */
+/*ARGSUSED*/
+static int
+samr_s_CreateDomainAlias(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_CreateDomainAlias *param = arg;
+
+#ifdef SAMR_SUPPORT_ADD_ALIAS
+ DWORD status = NT_STATUS_SUCCESS;
+ ms_handle_desc_t *desc = 0;
+ ms_handle_t *handle;
+ nt_group_t *grp;
+ char *alias_name;
+#endif
+ bzero(&param->alias_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+
+#ifdef SAMR_SUPPORT_ADD_ALIAS
+ alias_name = param->alias_name.str;
+ if (alias_name == 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto create_alias_err;
+ }
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->domain_handle);
+ if (desc == 0 ||
+ (desc->discrim != SAMR_LOCAL_DOMAIN) ||
+ (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) {
+ status = NT_STATUS_INVALID_HANDLE;
+ goto create_alias_err;
+ }
+
+ /*
+ * Check access mask. User should be member of
+ * Administrators or Account Operators local group.
+ */
+ status = nt_group_add(alias_name, 0,
+ NT_GROUP_AF_ADD | NT_GROUP_AF_LOCAL);
+
+ if (status != NT_STATUS_SUCCESS)
+ goto create_alias_err;
+
+ grp = nt_group_getinfo(alias_name, RWLOCK_READER);
+ if (grp == NULL) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto create_alias_err;
+ }
+
+ (void) nt_sid_get_rid(grp->sid, &param->rid);
+ nt_group_putinfo(grp);
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_ALIAS_KEY,
+ param->rid);
+ bcopy(handle, &param->alias_handle, sizeof (samr_handle_t));
+
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+
+create_alias_err:
+ bzero(&param->alias_handle, sizeof (samr_handle_t));
+ param->status = NT_SC_ERROR(status);
+ return (MLRPC_DRC_OK);
+#endif
+}
+
+/*
+ * samr_s_SetAliasInfo
+ *
+ * For more information you can look at MSDN page for NetLocalGroupSetInfo.
+ */
+/*ARGSUSED*/
+static int
+samr_s_SetAliasInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_SetAliasInfo *param = arg;
+ DWORD status = NT_STATUS_SUCCESS;
+
+ if (!mlsvc_validate_handle(
+ (ms_handle_t *)&param->alias_handle, SAMR_ALIAS_KEY)) {
+ status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
+ }
+
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_QueryAliasInfo
+ *
+ * Retrieves information about the specified local group account
+ * by given handle.
+ */
+static int
+samr_s_QueryAliasInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_QueryAliasInfo *param = arg;
+ ms_handle_desc_t *desc;
+ nt_group_t *grp;
+ DWORD status;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->alias_handle);
+ if (desc == NULL || (strcmp(desc->key, SAMR_ALIAS_KEY) != 0)) {
+ status = NT_STATUS_INVALID_HANDLE;
+ goto query_alias_err;
+ }
+
+ grp = nt_groups_lookup_rid(desc->discrim);
+ if (grp == NULL) {
+ status = NT_STATUS_NO_SUCH_ALIAS;
+ goto query_alias_err;
+ }
+
+ switch (param->level) {
+ case SAMR_QUERY_ALIAS_INFO_1:
+ param->ru.info1.level = param->level;
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->ru.info1.name, grp->name, mxa);
+
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->ru.info1.desc, grp->comment, mxa);
+
+ param->ru.info1.unknown = 1;
+ break;
+
+ case SAMR_QUERY_ALIAS_INFO_3:
+ param->ru.info3.level = param->level;
+ (void) mlsvc_string_save(
+ (ms_string_t *)&param->ru.info3.desc, grp->comment, mxa);
+
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ goto query_alias_err;
+ };
+
+ param->address = (DWORD)&param->ru;
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+
+query_alias_err:
+ param->status = NT_SC_ERROR(status);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_DeleteDomainAlias
+ *
+ * Deletes a local group account and all its members from the
+ * security database, which is the security accounts manager (SAM) database.
+ * Only members of the Administrators or Account Operators local group can
+ * execute this function.
+ * For more information you can look at MSDN page for NetLocalGroupSetInfo.
+ *
+ * This RPC is used by CMC and right now it returns access denied.
+ * The peice of code that removes a local group doesn't get compiled.
+ */
+/*ARGSUSED*/
+static int
+samr_s_DeleteDomainAlias(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_DeleteDomainAlias *param = arg;
+
+#ifdef SAMR_SUPPORT_DEL_ALIAS
+ ms_handle_desc_t *desc = 0;
+ nt_group_t *grp;
+ char *alias_name;
+ DWORD status;
+#endif
+
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+ return (MLRPC_DRC_OK);
+
+#ifdef SAMR_SUPPORT_DEL_ALIAS
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->alias_handle);
+ if (desc == 0 || (strcmp(desc->key, SAMR_ALIAS_KEY) != 0)) {
+ status = NT_STATUS_INVALID_HANDLE;
+ goto delete_alias_err;
+ }
+
+ grp = nt_groups_lookup_rid(desc->discrim);
+ if (grp == 0) {
+ status = NT_STATUS_NO_SUCH_ALIAS;
+ goto delete_alias_err;
+ }
+
+ alias_name = strdup(grp->name);
+ if (alias_name == 0) {
+ status = NT_STATUS_NO_MEMORY;
+ goto delete_alias_err;
+ }
+
+ status = nt_group_delete(alias_name);
+ free(alias_name);
+ if (status != NT_STATUS_SUCCESS)
+ goto delete_alias_err;
+
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+
+delete_alias_err:
+ param->status = NT_SC_ERROR(status);
+ return (MLRPC_DRC_OK);
+#endif
+}
+
+/*
+ * samr_s_EnumDomainAliases
+ *
+ * This function sends back a list which contains all local groups' name.
+ */
+static int
+samr_s_EnumDomainAliases(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_EnumDomainAliases *param = arg;
+ ms_handle_desc_t *desc;
+ nt_group_t *grp = NULL;
+ DWORD status;
+ nt_group_iterator_t *gi;
+ nt_sid_t *local_sid;
+ nt_sid_t *builtin_sid;
+ nt_sid_t *sid;
+ DWORD cnt, skip;
+ struct name_rid *info;
+
+ desc = mlsvc_lookup_handle((ms_handle_t *)&param->domain_handle);
+ if (desc == NULL || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) {
+ status = NT_STATUS_INVALID_HANDLE;
+ goto enum_alias_err;
+ }
+
+ local_sid = nt_domain_local_sid();
+ builtin_sid = nt_builtin_lookup_name("BUILTIN", 0);
+
+ if (desc->discrim == SAMR_LOCAL_DOMAIN) {
+ sid = local_sid;
+ } else if (desc->discrim == SAMR_BUILTIN_DOMAIN) {
+ sid = builtin_sid;
+ } else {
+ status = NT_STATUS_INVALID_HANDLE;
+ goto enum_alias_err;
+ }
+
+ cnt = skip = 0;
+ gi = nt_group_open_iterator();
+ nt_group_ht_lock(RWLOCK_READER);
+ while ((grp = nt_group_iterate(gi)) != 0) {
+ if (skip++ < param->resume_handle)
+ continue;
+ if (nt_sid_is_indomain(sid, grp->sid))
+ cnt++;
+ }
+ nt_group_ht_unlock();
+ nt_group_close_iterator(gi);
+
+ param->aliases = (struct aliases_info *)MLRPC_HEAP_MALLOC(mxa,
+ sizeof (struct aliases_info) + (cnt-1) * sizeof (struct name_rid));
+
+ param->aliases->count = cnt;
+ param->aliases->address = cnt;
+ info = param->aliases->info;
+
+ skip = 0;
+ gi = nt_group_open_iterator();
+ nt_group_ht_lock(RWLOCK_READER);
+ while ((grp = nt_group_iterate(gi)) != NULL) {
+ if (skip++ < param->resume_handle)
+ continue;
+ if (nt_sid_is_indomain(sid, grp->sid)) {
+ (void) nt_sid_get_rid(grp->sid, &info->rid);
+ (void) mlsvc_string_save((ms_string_t *)&info->name,
+ grp->name, mxa);
+
+ info++;
+ }
+ }
+ nt_group_ht_unlock();
+ nt_group_close_iterator(gi);
+
+ param->out_resume = cnt;
+ param->entries = cnt;
+ param->status = 0;
+ return (MLRPC_DRC_OK);
+
+enum_alias_err:
+ param->status = NT_SC_ERROR(status);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * samr_s_Connect3
+ *
+ * This is the connect3 form of the connect request. It contains an
+ * extra parameter over samr_Connect. See samr_s_Connect for other
+ * details. NT returns an RPC fault - so we can do the same for now.
+ * Doing it this way should avoid the unsupported opnum error message
+ * appearing in the log.
+ */
+/*ARGSUSED*/
+static int
+samr_s_Connect3(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_Connect3 *param = arg;
+
+ bzero(param, sizeof (struct samr_Connect3));
+ return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID);
+}
+
+
+/*
+ * samr_s_Connect4
+ *
+ * This is the connect4 form of the connect request used by Windows XP.
+ * Returns an RPC fault for now.
+ */
+/*ARGSUSED*/
+static int
+samr_s_Connect4(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct samr_Connect4 *param = arg;
+
+ bzero(param, sizeof (struct samr_Connect4));
+ return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID);
+}
+
+static mlrpc_stub_table_t samr_stub_table[] = {
+ { samr_s_ConnectAnon, SAMR_OPNUM_ConnectAnon },
+ { samr_s_CloseHandle, SAMR_OPNUM_CloseHandle },
+ { samr_s_LookupDomain, SAMR_OPNUM_LookupDomain },
+ { samr_s_EnumLocalDomains, SAMR_OPNUM_EnumLocalDomains },
+ { samr_s_OpenDomain, SAMR_OPNUM_OpenDomain },
+ { samr_s_QueryDomainInfo, SAMR_OPNUM_QueryDomainInfo },
+ { samr_s_LookupNames, SAMR_OPNUM_LookupNames },
+ { samr_s_OpenUser, SAMR_OPNUM_OpenUser },
+ { samr_s_DeleteUser, SAMR_OPNUM_DeleteUser },
+ { samr_s_QueryUserInfo, SAMR_OPNUM_QueryUserInfo },
+ { samr_s_QueryUserGroups, SAMR_OPNUM_QueryUserGroups },
+ { samr_s_OpenGroup, SAMR_OPNUM_OpenGroup },
+ { samr_s_Connect, SAMR_OPNUM_Connect },
+ { samr_s_GetUserPwInfo, SAMR_OPNUM_GetUserPwInfo },
+ { samr_s_CreateUser, SAMR_OPNUM_CreateUser },
+ { samr_s_ChangeUserPasswd, SAMR_OPNUM_ChangeUserPasswd },
+ { samr_s_GetDomainPwInfo, SAMR_OPNUM_GetDomainPwInfo },
+ { samr_s_SetUserInfo, SAMR_OPNUM_SetUserInfo },
+ { samr_s_Connect3, SAMR_OPNUM_Connect3 },
+ { samr_s_Connect4, SAMR_OPNUM_Connect4 },
+ { samr_s_QueryDispInfo, SAMR_OPNUM_QueryDispInfo },
+ { samr_s_OpenAlias, SAMR_OPNUM_OpenAlias },
+ { samr_s_CreateDomainAlias, SAMR_OPNUM_CreateDomainAlias },
+ { samr_s_SetAliasInfo, SAMR_OPNUM_SetAliasInfo },
+ { samr_s_QueryAliasInfo, SAMR_OPNUM_QueryAliasInfo },
+ { samr_s_DeleteDomainAlias, SAMR_OPNUM_DeleteDomainAlias },
+ { samr_s_EnumDomainAliases, SAMR_OPNUM_EnumDomainAliases },
+ { samr_s_EnumDomainGroups, SAMR_OPNUM_EnumDomainGroups },
+ {0}
+};
+
+/*
+ * There is a bug in the way that midl and the marshalling code handles
+ * unions so we need to fix some of the data offsets at runtime. The
+ * following macros and the fixup functions handle the corrections.
+ */
+DECL_FIXUP_STRUCT(samr_QueryDomainInfo_ru);
+DECL_FIXUP_STRUCT(samr_QueryDomainInfoRes);
+DECL_FIXUP_STRUCT(samr_QueryDomainInfo);
+
+DECL_FIXUP_STRUCT(samr_QueryAliasInfo_ru);
+DECL_FIXUP_STRUCT(samr_QueryAliasInfoRes);
+DECL_FIXUP_STRUCT(samr_QueryAliasInfo);
+
+DECL_FIXUP_STRUCT(QueryUserInfo_result_u);
+DECL_FIXUP_STRUCT(QueryUserInfo_result);
+DECL_FIXUP_STRUCT(samr_QueryUserInfo);
+
+void
+fixup_samr_QueryDomainInfo(struct samr_QueryDomainInfo *val)
+{
+ unsigned short size1 = 0;
+ unsigned short size2 = 0;
+ unsigned short size3 = 0;
+
+ switch (val->switch_value) {
+ CASE_INFO_ENT(samr_QueryDomainInfo, 2);
+ CASE_INFO_ENT(samr_QueryDomainInfo, 6);
+ CASE_INFO_ENT(samr_QueryDomainInfo, 7);
+
+ default:
+ return;
+ };
+
+ size2 = size1 + (2 * sizeof (DWORD));
+ size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD);
+
+ FIXUP_PDU_SIZE(samr_QueryDomainInfo_ru, size1);
+ FIXUP_PDU_SIZE(samr_QueryDomainInfoRes, size2);
+ FIXUP_PDU_SIZE(samr_QueryDomainInfo, size3);
+}
+
+void
+fixup_samr_QueryAliasInfo(struct samr_QueryAliasInfo *val)
+{
+ unsigned short size1 = 0;
+ unsigned short size2 = 0;
+ unsigned short size3 = 0;
+
+ switch (val->level) {
+ CASE_INFO_ENT(samr_QueryAliasInfo, 1);
+ CASE_INFO_ENT(samr_QueryAliasInfo, 3);
+
+ default:
+ return;
+ };
+
+ size2 = size1 + (2 * sizeof (DWORD));
+ size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD);
+
+ FIXUP_PDU_SIZE(samr_QueryAliasInfo_ru, size1);
+ FIXUP_PDU_SIZE(samr_QueryAliasInfoRes, size2);
+ FIXUP_PDU_SIZE(samr_QueryAliasInfo, size3);
+}
+
+void
+fixup_samr_QueryUserInfo(struct samr_QueryUserInfo *val)
+{
+ unsigned short size1 = 0;
+ unsigned short size2 = 0;
+ unsigned short size3 = 0;
+
+ switch (val->switch_index) {
+ CASE_INFO_ENT(samr_QueryUserInfo, 1);
+ CASE_INFO_ENT(samr_QueryUserInfo, 6);
+ CASE_INFO_ENT(samr_QueryUserInfo, 7);
+ CASE_INFO_ENT(samr_QueryUserInfo, 8);
+ CASE_INFO_ENT(samr_QueryUserInfo, 9);
+ CASE_INFO_ENT(samr_QueryUserInfo, 16);
+
+ default:
+ return;
+ };
+
+ size2 = size1 + (2 * sizeof (DWORD));
+ size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD);
+
+ FIXUP_PDU_SIZE(QueryUserInfo_result_u, size1);
+ FIXUP_PDU_SIZE(QueryUserInfo_result, size2);
+ FIXUP_PDU_SIZE(samr_QueryUserInfo, size3);
+}
+
+/*
+ * As long as there is only one entry in the union, there is no need
+ * to patch anything.
+ */
+/*ARGSUSED*/
+void
+fixup_samr_QueryGroupInfo(struct samr_QueryGroupInfo *val)
+{
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_spoolss.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_spoolss.c
new file mode 100644
index 0000000000..bac138cf09
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_spoolss.c
@@ -0,0 +1,139 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Printing and Spooling RPC interface definition.
+ * A stub to resolve RPC requests to this service.
+ */
+
+#include <smbsrv/ndl/spoolss.ndl>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/nmpipes.h>
+
+static mlrpc_stub_table_t spoolss_stub_table[];
+
+static mlrpc_service_t spoolss_service = {
+ "SPOOLSS", /* name */
+ "Print Spool Service", /* desc */
+ "\\spoolss", /* endpoint */
+ PIPE_SPOOLSS, /* sec_addr_port */
+ "12345678-1234-abcd-ef000123456789ab", 1, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(spoolss_interface), /* interface ti */
+ spoolss_stub_table /* stub_table */
+};
+
+/*
+ * spoolss_initialize
+ *
+ * This function registers the SPOOLSS RPC interface with the RPC
+ * runtime library. It must be called in order to use either the
+ * client side or the server side functions.
+ */
+void
+spoolss_initialize(void)
+{
+ (void) mlrpc_register_service(&spoolss_service);
+}
+
+/*
+ * spoolss_s_OpenPrinter
+ *
+ * We don't offer print spooling support. It should be okay to
+ * set the status to access denied and return MLRPC_DRC_OK.
+ */
+static int
+spoolss_s_OpenPrinter(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct spoolss_OpenPrinter *param = arg;
+
+ bzero(param, sizeof (struct spoolss_OpenPrinter));
+ param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
+
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * spoolss_s_stub
+ */
+static int
+spoolss_s_stub(void *arg, struct mlrpc_xaction *mxa)
+{
+ return (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED);
+}
+
+static mlrpc_stub_table_t spoolss_stub_table[] = {
+ { spoolss_s_OpenPrinter, SPOOLSS_OPNUM_OpenPrinter },
+ { spoolss_s_stub, SPOOLSS_OPNUM_GetJob },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrinter },
+ { spoolss_s_stub, SPOOLSS_OPNUM_GetPrinterDriver },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrinterDriver },
+ { spoolss_s_stub, SPOOLSS_OPNUM_AddPrintProcessor },
+ { spoolss_s_stub, SPOOLSS_OPNUM_GetPrintProcessorDirectory },
+ { spoolss_s_stub, SPOOLSS_OPNUM_AbortPrinter },
+ { spoolss_s_stub, SPOOLSS_OPNUM_ReadPrinter },
+ { spoolss_s_stub, SPOOLSS_OPNUM_WaitForPrinterChange },
+ { spoolss_s_stub, SPOOLSS_OPNUM_AddForm },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeleteForm },
+ { spoolss_s_stub, SPOOLSS_OPNUM_GetForm },
+ { spoolss_s_stub, SPOOLSS_OPNUM_SetForm },
+ { spoolss_s_stub, SPOOLSS_OPNUM_EnumMonitors },
+ { spoolss_s_stub, SPOOLSS_OPNUM_AddPort },
+ { spoolss_s_stub, SPOOLSS_OPNUM_ConfigurePort },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeletePort },
+ { spoolss_s_stub, SPOOLSS_OPNUM_CreatePrinterIc },
+ { spoolss_s_stub, SPOOLSS_OPNUM_PlayDescriptionPrinterIc },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrinterIc },
+ { spoolss_s_stub, SPOOLSS_OPNUM_AddPrinterConnection },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrinterConnection },
+ { spoolss_s_stub, SPOOLSS_OPNUM_PrinterMessageBox },
+ { spoolss_s_stub, SPOOLSS_OPNUM_AddMonitor },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeleteMonitor },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrintProcessor },
+ { spoolss_s_stub, SPOOLSS_OPNUM_AddPrintProvider },
+ { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrintProvider },
+ { spoolss_s_stub, SPOOLSS_OPNUM_ResetPrinter },
+ { spoolss_s_stub, SPOOLSS_OPNUM_FindFirstChangeNotify },
+ { spoolss_s_stub, SPOOLSS_OPNUM_FindNextChangeNotify },
+ { spoolss_s_stub, SPOOLSS_OPNUM_RouterFindFirstNotify },
+ { spoolss_s_stub, SPOOLSS_OPNUM_ReplyOpenPrinter },
+ { spoolss_s_stub, SPOOLSS_OPNUM_RouterReplyPrinter },
+ { spoolss_s_stub, SPOOLSS_OPNUM_ReplyClosePrinter },
+ { spoolss_s_stub, SPOOLSS_OPNUM_AddPortEx },
+ { spoolss_s_stub, SPOOLSS_OPNUM_RemoteFindFirstChangeNotify },
+ { spoolss_s_stub, SPOOLSS_OPNUM_SpoolerInitialize },
+ { spoolss_s_stub, SPOOLSS_OPNUM_ResetPrinterEx },
+ { spoolss_s_stub, SPOOLSS_OPNUM_RouterRefreshChangeNotify },
+ { spoolss_s_OpenPrinter, SPOOLSS_OPNUM_OpenPrinter2 },
+ {0}
+};
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_srvsvc.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_srvsvc.c
new file mode 100644
index 0000000000..2a9a1f52d0
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_srvsvc.c
@@ -0,0 +1,1684 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Server Service RPC (SRVSVC) server-side interface definition.
+ * The server service provides a remote administration interface.
+ *
+ * This service uses NERR/Win32 error codes rather than NT status
+ * values.
+ */
+
+#include <sys/errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <strings.h>
+#include <time.h>
+#include <tzfile.h>
+#include <time.h>
+#include <thread.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <smbsrv/smb_fsd.h>
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/lmerr.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/cifs.h>
+#include <smbsrv/netrauth.h>
+#include <smbsrv/mlsvc.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/srvsvc.ndl>
+#include <smbsrv/smb_common_door.h>
+
+#define SV_TYPE_SENT_BY_ME (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_NT)
+
+static DWORD mlsvc_NetSessionEnumLevel0(struct mslm_infonres *, DWORD,
+ struct mlrpc_xaction *);
+static DWORD mlsvc_NetSessionEnumLevel1(struct mslm_infonres *, DWORD,
+ struct mlrpc_xaction *);
+static DWORD mlsvc_NetShareEnumLevel0(struct mslm_infonres *, DWORD,
+ struct mlrpc_xaction *, char);
+static DWORD mlsvc_NetShareEnumLevel1(struct mslm_infonres *, DWORD,
+ struct mlrpc_xaction *, char);
+static DWORD mlsvc_NetShareEnumLevel2(struct mslm_infonres *, DWORD,
+ struct mlrpc_xaction *, char);
+static DWORD mlsvc_NetShareEnumLevel502(struct mslm_infonres *, DWORD,
+ struct mlrpc_xaction *, char);
+static DWORD mlsvc_NetShareEnumCommon(struct mlrpc_xaction *, DWORD,
+ int, lmshare_info_t *, void *);
+static int srvsvc_is_poweruser(struct mlrpc_xaction *);
+
+static char empty_string[1];
+
+static mlrpc_stub_table_t srvsvc_stub_table[];
+
+static mlrpc_service_t srvsvc_service = {
+ "SRVSVC", /* name */
+ "Server services", /* desc */
+ "\\srvsvc", /* endpoint */
+ PIPE_NTSVCS, /* sec_addr_port */
+ "4b324fc8-1670-01d3-12785a47bf6ee188", 3, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(srvsvc_interface), /* interface ti */
+ srvsvc_stub_table /* stub_table */
+};
+
+/*
+ * srvsvc_fix_comment
+ *
+ * The parser sometimes has problems with empty strings so we
+ * need to ensure that the comment field has something in it.
+ */
+static inline char *
+srvsvc_fix_comment(char *original, char *alternative)
+{
+ if (original == 0 || strlen(original) == 0)
+ return (alternative);
+
+ return (original);
+}
+
+/*
+ * srvsvc_share_mkpath
+ *
+ * Create the share path required by the share enum calls. This function
+ * creates the path in a MLRPC heap buffer ready for use by the caller.
+ *
+ * Some Windows over-the-wire backup applications do not work unless a
+ * drive letter is present in the share path. We don't care about the
+ * drive letter since the path is fully qualified with the volume name.
+ * We can try using drive B since by default that letter isn't assigned
+ * and even if it conflicts, we should still be okay with the fully
+ * qualified path.
+ *
+ * Windows clients seem to be mostly okay with the forward slash in
+ * share paths but they cannot handle one immediately after the drive
+ * letter, i.e. D:/. For consistency we convert all the slashes in
+ * the path.
+ *
+ * Returns a pointer to a heap buffer containing the share path, which
+ * could be a null pointer if the heap allocation fails.
+ */
+static char *
+srvsvc_share_mkpath(struct mlrpc_xaction *mxa, char *path)
+{
+ char tmpbuf[MAXPATHLEN];
+ char *p;
+
+ if (strlen(path) == 0)
+ return (MLRPC_HEAP_STRSAVE(mxa, path));
+
+ /* strip the volume from the path (/vol1/home -> /home) */
+ p = strchr(path[0] == '/' ? &path[1] : path, '/');
+
+ (void) snprintf(tmpbuf, MAXPATHLEN, "%c:%s", 'B'
+ /* vattr.drive_letter */, p == NULL ? "/": p);
+ (void) strsubst(tmpbuf, '/', '\\');
+
+ return (MLRPC_HEAP_STRSAVE(mxa, tmpbuf));
+}
+
+/*
+ * srvsvc_add_autohome
+ *
+ * Add the autohome share for the user to the shares' list
+ * if autohome is enabled the share is not a permanent share.
+ */
+static int
+srvsvc_add_autohome(struct mlrpc_xaction *mxa, char *username, DWORD i,
+ int level, char *infop)
+{
+ lmshare_info_t si;
+ DWORD status;
+
+ if ((lmshare_getinfo(username, &si) == NERR_Success) &&
+ (si.mode & LMSHRM_TRANS)) {
+ status = mlsvc_NetShareEnumCommon(mxa, i, level, &si,
+ (void *)infop);
+ if (status == ERROR_SUCCESS)
+ i++;
+ }
+
+ return (i);
+}
+
+/*
+ * srvsvc_initialize
+ *
+ * This function registers the SRVSVC RPC interface with the RPC runtime
+ * library. It must be called in order to use either the client side
+ * or the server side functions.
+ */
+void
+srvsvc_initialize(void)
+{
+ (void) mlrpc_register_service(&srvsvc_service);
+}
+
+/*
+ * srvsvc_s_NetConnectEnum
+ *
+ * Under construction. This is just enough to get the interface working.
+ * Current level 0 and level 1 connection info are supported.
+ *
+ * Level 1 request is made by 'srvmgr' (Server Manager)
+ * utility of NT Server part of NT Domain to MLRPC server
+ * while double click of share info icon. These values
+ * are currectly virtual to MLRPC client and does't
+ * reflect the real state of server.
+ */
+static int
+srvsvc_s_NetConnectEnum(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetConnectEnum *param = arg;
+ struct mslm_NetConnectInfoBuf0 *ci0;
+ struct mslm_NetConnectInfoBuf1 *ci1;
+ DWORD status;
+
+ status = ERROR_SUCCESS;
+ switch (param->info.level) {
+ case 0:
+ ci0 = MLRPC_HEAP_NEW(mxa, struct mslm_NetConnectInfoBuf0);
+ if (ci0 == 0) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ ci0->coni0_id = 0x17;
+
+ param->info.ru.info0
+ = MLRPC_HEAP_NEW(mxa, struct mslm_NetConnectInfo0);
+
+ if (param->info.ru.info0 == 0) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ param->info.ru.info0->ci0 = ci0;
+ param->info.ru.info0->entries_read = 1;
+
+ param->total_entries = 1;
+ param->resume_handle = 0;
+ break;
+
+ case 1:
+ ci1 = MLRPC_HEAP_NEW(mxa, struct mslm_NetConnectInfoBuf1);
+ if (ci1 == 0) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ 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 =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, "Administrator");
+
+ ci1->coni1_netname =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, "IPC$");
+
+ param->info.ru.info1 = MLRPC_HEAP_NEW(mxa,
+ struct mslm_NetConnectInfo1);
+
+ if (param->info.ru.info1 == 0) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ param->info.ru.info1->ci1 = ci1;
+ param->info.ru.info1->entries_read = 1;
+
+ param->total_entries = 1;
+ param->resume_handle = 0;
+ break;
+
+ default:
+ status = ERROR_ACCESS_DENIED;
+ break;
+ }
+
+ if (status != ERROR_SUCCESS)
+ bzero(param, sizeof (struct mslm_NetConnectEnum));
+
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * srvsvc_s_NetFileEnum
+ *
+ * Under construction. The values used here are fictional values and
+ * bear no relation to any real values, living or otherwise. I just
+ * made them up to get the interface working.
+ */
+static int
+srvsvc_s_NetFileEnum(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetFileEnum *param = arg;
+ struct mslm_NetFileInfoBuf3 *fi3;
+
+ if (param->info.switch_value != 3) {
+ bzero(param, sizeof (struct mslm_NetFileEnum));
+ param->status = ERROR_INVALID_LEVEL;
+ return (MLRPC_DRC_OK);
+ }
+
+ fi3 = MLRPC_HEAP_NEW(mxa, struct mslm_NetFileInfoBuf3);
+ if (fi3 == 0) {
+ bzero(param, sizeof (struct mslm_NetFileEnum));
+ param->status = ERROR_NOT_ENOUGH_MEMORY;
+ return (MLRPC_DRC_OK);
+ }
+
+ fi3->fi3_id = 0xF5;
+ fi3->fi3_permissions = 0x23;
+ fi3->fi3_num_locks = 0;
+ fi3->fi3_pathname =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, "\\PIPE\\srvsvc");
+
+ fi3->fi3_username =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, "Administrator");
+
+ param->info.ru.info3 = MLRPC_HEAP_NEW(mxa, struct mslm_NetFileInfo3);
+ if (param->info.ru.info3 == 0) {
+ bzero(param, sizeof (struct mslm_NetFileEnum));
+ param->status = ERROR_NOT_ENOUGH_MEMORY;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->info.ru.info3->fi3 = fi3;
+ param->info.ru.info3->entries_read = 1;
+ param->total_entries = 1;
+
+ if (param->resume_handle)
+ *param->resume_handle = 0x5F;
+
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * srvsvc_s_NetFileClose
+ *
+ * Under construction. This is just enough to get the interface working.
+ */
+/*ARGSUSED*/
+static int
+srvsvc_s_NetFileClose(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetFileClose *param = arg;
+
+ bzero(param, sizeof (struct mslm_NetFileClose));
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * srvsvc_s_NetShareGetInfo
+ *
+ * This call is made by Windows2000 to get share information. There are
+ * probably other information levels but these are the only ones I've
+ * seen so far.
+ *
+ * Returns Win32 error codes.
+ */
+static int
+srvsvc_s_NetShareGetInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mlsm_NetShareGetInfo *param = arg;
+ struct mslm_NetShareGetInfo0 *info0;
+ struct mslm_NetShareGetInfo1 *info1;
+ struct mslm_NetShareGetInfo2 *info2;
+ struct mslm_NetShareGetInfo502 *info502;
+ struct mslm_NetShareGetInfo1005 *info1005;
+ struct lmshare_info si;
+ char shr_comment[LMSHR_COMMENT_MAX];
+ DWORD status;
+
+ status = lmshare_getinfo((char *)param->netname, &si);
+ if (status != NERR_Success) {
+ if (strcasecmp((const char *)param->netname, "IPC$") == 0) {
+ /*
+ * Windows clients don't send the \\PIPE path for IPC$.
+ */
+ (void) memset(&si, 0, sizeof (lmshare_info_t));
+ (void) strcpy(si.share_name, "IPC$");
+ (void) strcpy(si.comment, "Remote IPC");
+ si.stype = (int)(STYPE_IPC | STYPE_SPECIAL);
+ } else {
+ bzero(param, sizeof (struct mlsm_NetShareGetInfo));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+ }
+
+ if (si.comment && strlen(si.comment))
+ (void) snprintf(shr_comment, sizeof (shr_comment), "%s %s",
+ si.directory, si.comment);
+ else
+ (void) strcpy(shr_comment, si.directory);
+
+ status = ERROR_SUCCESS;
+
+ switch (param->level) {
+ case 0:
+ info0 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo0);
+ if (info0 == 0) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ info0->shi0_netname
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si.share_name);
+
+ param->result.ru.info0 = info0;
+ break;
+
+ case 1:
+ info1 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo1);
+ if (info1 == 0) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ info1->shi1_netname =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si.share_name);
+ info1->shi1_comment =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment);
+ info1->shi1_type = si.stype;
+ param->result.ru.info1 = info1;
+ break;
+
+ case 2:
+ info2 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo2);
+ if (info2 == 0) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ info2->shi2_netname =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si.share_name);
+ info2->shi2_comment =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment);
+ info2->shi2_path =
+ (unsigned char *)srvsvc_share_mkpath(mxa, si.directory);
+ info2->shi2_passwd = 0;
+ info2->shi2_type = si.stype;
+ info2->shi2_permissions = 0;
+ info2->shi2_max_uses = SHI_USES_UNLIMITED;
+ info2->shi2_current_uses = 0;
+ param->result.ru.info2 = info2;
+ break;
+
+ case 1005:
+ info1005 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo1005);
+ if (info1005 == 0) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ info1005->shi1005_flags = 0;
+ param->result.ru.info1005 = info1005;
+ break;
+
+ case 502:
+ /*
+ * Level 502 provides level 2 information plus a
+ * security descriptor. We don't support security
+ * descriptors on shares yet.
+ */
+ info502 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo502);
+ if (info502 == 0) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ info502->shi502_netname =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si.share_name);
+ info502->shi502_comment =
+ (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment);
+ info502->shi502_path =
+ (unsigned char *)srvsvc_share_mkpath(mxa, si.directory);
+ info502->shi502_passwd = 0;
+ info502->shi502_type = si.stype;
+ info502->shi502_permissions = 0;
+ info502->shi502_max_uses = SHI_USES_UNLIMITED;
+ info502->shi502_current_uses = 0;
+ info502->shi502_reserved = 0;
+ info502->shi502_security_descriptor = 0;
+ param->result.ru.info502 = info502;
+ break;
+
+ default:
+ status = ERROR_ACCESS_DENIED;
+ break;
+ }
+
+ if (status != ERROR_SUCCESS)
+ bzero(param, sizeof (struct mlsm_NetShareGetInfo));
+ else
+ param->result.switch_value = param->level;
+
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * srvsvc_s_NetShareSetInfo
+ *
+ * This call is made by SrvMgr to set share information.
+ * Always returns ERROR_ACCESS_DENIED for now.
+ *
+ * Returns Win32 error codes.
+ */
+static int
+srvsvc_s_NetShareSetInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mlsm_NetShareSetInfo *param = arg;
+
+ (void) memset(param, 0, sizeof (struct mlsm_NetShareSetInfo));
+ param->parm_err_ptr = (DWORD)MLRPC_HEAP_MALLOC(mxa, sizeof (DWORD));
+ param->parm_err = 0;
+
+ smb_config_rdlock();
+ if (smb_config_getyorn(SMB_CI_SRVSVC_SHRSET_ENABLE) != 0)
+ param->status = ERROR_SUCCESS;
+ else
+ param->status = ERROR_ACCESS_DENIED;
+ smb_config_unlock();
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * srvsvc_s_NetSessionEnum
+ *
+ * Level 1 request is made by the 'srvmgr' (Server Manager) utility on
+ * NT Server when the user info icon is selected.
+ *
+ * Return Values
+ * If the function succeeds, the return value is NERR_Success.
+ * If the function fails, the return value can be one of the following
+ * error codes:
+ *
+ * ERROR_ACCESS_DENIED The user does not have access to the requested
+ * information.
+ * ERROR_INVALID_LEVEL The value specified for the level parameter is
+ * invalid.
+ * ERROR_INVALID_PARAMETER The specified parameter is invalid.
+ * ERROR_MORE_DATA More entries are available. Specify a large
+ * enough buffer to receive all entries.
+ * ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available.
+ * NERR_ClientNameNotFound A session does not exist with the computer
+ * name.
+ * NERR_InvalidComputer The computer name is invalid.
+ * NERR_UserNotFound The user name could not be found.
+ */
+static int
+srvsvc_s_NetSessionEnum(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetSessionEnum *param = arg;
+ struct mslm_infonres *infonres;
+ DWORD status;
+ DWORD n_sessions;
+
+ infonres = MLRPC_HEAP_NEW(mxa, struct mslm_infonres);
+ if (infonres == 0) {
+ bzero(param, sizeof (struct mslm_NetSessionEnum));
+ param->status = ERROR_NOT_ENOUGH_MEMORY;
+ return (MLRPC_DRC_OK);
+ }
+
+ infonres->entriesread = 0;
+ infonres->entries = 0;
+ param->result.level = param->level;
+ param->result.bufptr.p = infonres;
+ param->total_entries = 1;
+ param->status = ERROR_SUCCESS;
+
+ n_sessions = (DWORD) smb_dwncall_user_num();
+
+ switch (param->level) {
+ case 0:
+ status = mlsvc_NetSessionEnumLevel0(infonres, n_sessions, mxa);
+ break;
+
+ case 1:
+ status = mlsvc_NetSessionEnumLevel1(infonres, n_sessions, mxa);
+ break;
+
+ default:
+ status = ERROR_INVALID_LEVEL;
+ break;
+ }
+
+ if (status != 0) {
+ bzero(param, sizeof (struct mslm_NetSessionEnum));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->resume_handle = 0;
+ param->total_entries = infonres->entriesread;
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * mlsvc_NetSessionEnumLevel0
+ *
+ * Build the level 0 session information.
+ */
+/*ARGSUSED*/
+static DWORD
+mlsvc_NetSessionEnumLevel0(struct mslm_infonres *infonres, DWORD n_sessions,
+ struct mlrpc_xaction *mxa)
+{
+ struct mslm_SESSION_INFO_0 *info0;
+ smb_dr_ulist_t *ulist;
+ smb_dr_user_ctx_t *user;
+ char *workstation;
+ char ipaddr_buf[INET_ADDRSTRLEN];
+ int i, offset, cnt, total;
+
+ info0 = MLRPC_HEAP_NEWN(mxa, struct mslm_SESSION_INFO_0, n_sessions);
+ if (info0 == 0)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ ulist = malloc(sizeof (smb_dr_ulist_t));
+ if (!ulist)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ for (total = 0, offset = 0;
+ (cnt = smb_dwncall_get_users(offset, ulist)) > 0;
+ offset += cnt) {
+ for (i = 0; i < cnt && total < n_sessions; i++, total++) {
+ user = &ulist->dul_users[i];
+ /*
+ * Ignore local tokens (IP address is zero).
+ */
+ if (user->du_ipaddr == 0) {
+ total--;
+ smb_dr_ulist_free(ulist);
+ ulist = malloc(sizeof (smb_dr_ulist_t));
+ if (!ulist)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ continue;
+ }
+
+ if ((workstation = user->du_workstation) == 0) {
+ (void) inet_ntop(AF_INET,
+ (char *)&user->du_ipaddr, ipaddr_buf,
+ sizeof (ipaddr_buf));
+ workstation = ipaddr_buf;
+ }
+
+ info0[total].sesi0_cname = MLRPC_HEAP_STRSAVE(mxa,
+ workstation);
+ if (info0[total].sesi0_cname == 0) {
+ smb_dr_ulist_free(ulist);
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ }
+ smb_dr_ulist_free(ulist);
+ ulist = malloc(sizeof (smb_dr_ulist_t));
+ if (!ulist)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ }
+
+ infonres->entriesread = total;
+ infonres->entries = info0;
+ return (ERROR_SUCCESS);
+}
+
+
+/*
+ * mlsvc_NetSessionEnumLevel1
+ *
+ * Build the level 1 session information.
+ */
+/*ARGSUSED*/
+static DWORD
+mlsvc_NetSessionEnumLevel1(struct mslm_infonres *infonres, DWORD n_sessions,
+ struct mlrpc_xaction *mxa)
+{
+ struct mslm_SESSION_INFO_1 *info1;
+ smb_dr_ulist_t *ulist;
+ smb_dr_user_ctx_t *user;
+ char *workstation;
+ char *account;
+ char ipaddr_buf[INET_ADDRSTRLEN];
+ int i, offset, cnt, total;
+
+ info1 = MLRPC_HEAP_NEWN(mxa, struct mslm_SESSION_INFO_1, n_sessions);
+ if (info1 == 0)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ ulist = malloc(sizeof (smb_dr_ulist_t));
+ if (!ulist)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ for (total = 0, offset = 0;
+ (cnt = smb_dwncall_get_users(offset, ulist)) > 0;
+ offset += cnt) {
+ for (i = 0; i < cnt && total < n_sessions; i++, total++) {
+ user = &ulist->dul_users[i];
+ /*
+ * Ignore local user_ctxs (IP address is zero).
+ */
+ if (user->du_ipaddr == 0) {
+ total--;
+ smb_dr_ulist_free(ulist);
+ ulist = malloc(sizeof (smb_dr_ulist_t));
+ if (!ulist)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ continue;
+ }
+
+ if ((workstation = user->du_workstation) == 0) {
+ (void) inet_ntop(AF_INET,
+ (char *)&user->du_ipaddr,
+ ipaddr_buf, sizeof (ipaddr_buf));
+ workstation = ipaddr_buf;
+ }
+
+ if ((account = user->du_account) == 0)
+ account = "Unknown";
+
+ info1[total].sesi1_cname = MLRPC_HEAP_STRSAVE(mxa,
+ workstation);
+ info1[total].sesi1_uname = MLRPC_HEAP_STRSAVE(mxa,
+ account);
+
+ if (info1[total].sesi1_cname == 0 ||
+ info1[total].sesi1_uname == 0) {
+ smb_dr_ulist_free(ulist);
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ info1[total].sesi1_nopens = 1;
+ info1[total].sesi1_time = time(0) -
+ user->du_logon_time;
+ info1[total].sesi1_itime = 0;
+ info1[total].sesi1_uflags =
+ (user->du_flags & SMB_ATF_GUEST) ? SESS_GUEST : 0;
+ }
+ smb_dr_ulist_free(ulist);
+ ulist = malloc(sizeof (smb_dr_ulist_t));
+ if (!ulist)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ infonres->entriesread = total;
+ infonres->entries = info1;
+ return (ERROR_SUCCESS);
+}
+
+/*
+ * srvsvc_s_NetSessionDel
+ *
+ * Ends a network session between a server and a workstation.
+ * On NT only members of the Administrators or Account Operators
+ * local groups are permitted to use NetSessionDel.
+ *
+ * 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:
+ *
+ * ERROR_ACCESS_DENIED The user does not have access to the
+ * 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.
+ */
+static int
+srvsvc_s_NetSessionDel(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetSessionDel *param = arg;
+
+ if (srvsvc_is_poweruser(mxa) == 0) {
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * SRVSVC NetServerGetInfo
+ *
+ * IN LPTSTR servername,
+ * IN DWORD level,
+ * OUT union switch(level) {
+ * case 100: mslm_SERVER_INFO_100 *p100;
+ * case 101: mslm_SERVER_INFO_101 *p101;
+ * case 102: mslm_SERVER_INFO_102 *p102;
+ * default: char *nullptr;
+ * } bufptr,
+ * OUT DWORD status
+ */
+static int
+srvsvc_s_NetServerGetInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetServerGetInfo *param = arg;
+ struct mslm_SERVER_INFO_100 *info100;
+ struct mslm_SERVER_INFO_101 *info101;
+ struct mslm_SERVER_INFO_102 *info102;
+ char *sys_comment;
+ char hostname[MAXHOSTNAMELEN];
+
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) {
+netservergetinfo_no_memory:
+ bzero(param, sizeof (struct mslm_NetServerGetInfo));
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ smb_config_rdlock();
+ sys_comment = smb_config_getstr(SMB_CI_SYS_CMNT);
+ sys_comment = srvsvc_fix_comment(sys_comment, " ");
+ smb_config_unlock();
+
+ switch (param->level) {
+ case 100:
+ info100 = MLRPC_HEAP_NEW(mxa, struct mslm_SERVER_INFO_100);
+ if (info100 == 0)
+ goto netservergetinfo_no_memory;
+
+ bzero(info100, sizeof (struct mslm_SERVER_INFO_100));
+ info100->sv100_platform_id = SV_PLATFORM_ID_NT;
+ info100->sv100_name
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, hostname);
+
+ if (info100->sv100_name == 0)
+ goto netservergetinfo_no_memory;
+
+ param->result.bufptr.bufptr100 = info100;
+ break;
+
+ case 101:
+ info101 = MLRPC_HEAP_NEW(mxa, struct mslm_SERVER_INFO_101);
+ if (info101 == 0)
+ goto netservergetinfo_no_memory;
+
+ bzero(info101, sizeof (struct mslm_SERVER_INFO_101));
+ info101->sv101_platform_id = SV_PLATFORM_ID_NT;
+ info101->sv101_version_major = 4;
+ info101->sv101_version_minor = 0;
+ info101->sv101_type = SV_TYPE_SENT_BY_ME;
+ info101->sv101_name
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, hostname);
+
+ info101->sv101_comment
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, sys_comment);
+
+ if (info101->sv101_name == 0 || info101->sv101_comment == 0)
+ goto netservergetinfo_no_memory;
+
+ param->result.bufptr.bufptr101 = info101;
+ break;
+
+ case 102:
+ info102 = MLRPC_HEAP_NEW(mxa, struct mslm_SERVER_INFO_102);
+ if (info102 == 0)
+ goto netservergetinfo_no_memory;
+
+ bzero(info102, sizeof (struct mslm_SERVER_INFO_102));
+ info102->sv102_platform_id = SV_PLATFORM_ID_NT;
+ info102->sv102_version_major = 4;
+ info102->sv102_version_minor = 0;
+ info102->sv102_type = SV_TYPE_SENT_BY_ME;
+ info102->sv102_name
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, hostname);
+
+ info102->sv102_comment
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, sys_comment);
+
+ /*
+ * The following level 102 fields are defaulted to zero
+ * by virtue of the call to bzero above.
+ *
+ * sv102_users
+ * sv102_disc
+ * sv102_hidden
+ * sv102_announce
+ * sv102_anndelta
+ * sv102_licenses
+ * sv102_userpath
+ */
+ if (info102->sv102_name == 0 || info102->sv102_comment == 0)
+ goto netservergetinfo_no_memory;
+
+ param->result.bufptr.bufptr102 = info102;
+ break;
+
+ default:
+ bzero(&param->result,
+ sizeof (struct mslm_NetServerGetInfo_result));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->result.level = param->level;
+ param->status = (ERROR_SUCCESS);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * NetRemoteTOD
+ *
+ * Returns information about the time of day on this server.
+ *
+ * typedef struct _TIME_OF_DAY_INFO {
+ * DWORD tod_elapsedt; // seconds since 00:00:00 January 1 1970 GMT
+ * DWORD tod_msecs; // arbitrary milliseconds (since reset)
+ * DWORD tod_hours; // current hour [0-23]
+ * DWORD tod_mins; // current minute [0-59]
+ * DWORD tod_secs; // current second [0-59]
+ * DWORD tod_hunds; // current hundredth (0.01) second [0-99]
+ * LONG tod_timezone; // time zone of the server
+ * DWORD tod_tinterval; // clock tick time interval
+ * DWORD tod_day; // day of the month [1-31]
+ * DWORD tod_month; // month of the year [1-12]
+ * DWORD tod_year; // current year
+ * DWORD tod_weekday; // day of the week since sunday [0-6]
+ * } TIME_OF_DAY_INFO;
+ *
+ * The time zone of the server is calculated in minutes from Greenwich
+ * Mean Time (GMT). For time zones west of Greenwich, the value is
+ * positive; for time zones east of Greenwich, the value is negative.
+ * A value of -1 indicates that the time zone is undefined.
+ *
+ * The clock tick value represents a resolution of one ten-thousandth
+ * (0.0001) second.
+ */
+static int
+srvsvc_s_NetRemoteTOD(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetRemoteTOD *param = arg;
+ struct mslm_TIME_OF_DAY_INFO *tod;
+ struct timeval time_val;
+ struct tm tm;
+
+ (void) gettimeofday(&time_val, 0);
+ (void) gmtime_r(&time_val.tv_sec, &tm);
+
+ tod = MLRPC_HEAP_NEW(mxa, struct mslm_TIME_OF_DAY_INFO);
+ if (tod == NULL) {
+ bzero(param, sizeof (struct mslm_NetRemoteTOD));
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ tod->tod_elapsedt = time_val.tv_sec;
+ tod->tod_msecs = time_val.tv_usec;
+ tod->tod_hours = tm.tm_hour;
+ tod->tod_mins = tm.tm_min;
+ tod->tod_secs = tm.tm_sec;
+ tod->tod_hunds = 0;
+ tod->tod_tinterval = 1000;
+ tod->tod_day = tm.tm_mday;
+ tod->tod_month = tm.tm_mon+1;
+ tod->tod_year = tm.tm_year+1900;
+ tod->tod_weekday = tm.tm_wday;
+
+ (void) localtime_r(&time_val.tv_sec, &tm);
+
+ param->bufptr = tod;
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * srvsvc_s_NetNameValidate
+ *
+ * Perform name validation.
+ * I've observed that the Computer Management Windows Application
+ * always send this request with type=0x09 and the flags=0 when
+ * attempting to validate a share name.
+ *
+ * The share name is consider invalid if it contains any of the
+ * following character (as mentioned in MSDN article #236388).
+ *
+ * " / \ [ ] : | < > + ; , ? * =
+ *
+ *
+ * For now, if the type is other than 0x09, return access denied.
+ *
+ * Returns Win32 error codes.
+ */
+/*ARGSUSED*/
+static int
+srvsvc_s_NetNameValidate(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetNameValidate *param = arg;
+
+ switch (param->type) {
+ case 0x09:
+ param->status = lmshare_is_valid((char *)param->pathname) ?
+ ERROR_SUCCESS : ERROR_INVALID_NAME;
+ break;
+
+ default:
+ param->status = ERROR_ACCESS_DENIED;
+ break;
+ }
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * srvsvc_s_NetShareAdd
+ *
+ * Add a new share. We support info levels 2 and 502 but ignore the
+ * security descriptor in level 502 requests. Only the administrator,
+ * or a member of the domain administrators group, is allowed to add
+ * 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
+ * from the client's command line.
+ *
+ * Note that we don't support security descriptors on a share. If the
+ * /grant is used, the share will be created but the subsequent attempt
+ * to manipulate the security descriptor (NetShareGetInfo) will fail.
+ * Similarly for the /remove option.
+ *
+ * Returns Win32 error codes.
+ */
+static int
+srvsvc_s_NetShareAdd(void *arg, struct mlrpc_xaction *mxa)
+{
+ static DWORD parm_err = 0;
+ DWORD parm_stat;
+ struct mslm_NetShareAdd *param = arg;
+ smb_dr_user_ctx_t *user_ctx;
+ struct mslm_SHARE_INFO_2 *info2;
+ struct lmshare_info si;
+ char realpath[MAXPATHLEN];
+
+ user_ctx = mxa->context->user_ctx;
+
+ if (srvsvc_is_poweruser(mxa) == 0) {
+ bzero(param, sizeof (struct mslm_NetShareAdd));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+ }
+
+ switch (param->level) {
+ case 2:
+ info2 = param->info.un.info2;
+ break;
+
+ case 502:
+ info2 = (struct mslm_SHARE_INFO_2 *)param->info.un.info502;
+ break;
+
+ default:
+ bzero(param, sizeof (struct mslm_NetShareAdd));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (info2->shi2_netname == 0 || info2->shi2_path == 0) {
+ bzero(param, sizeof (struct mslm_NetShareAdd));
+ param->status = NERR_NetNameNotFound;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (lmshare_is_restricted((char *)info2->shi2_netname)) {
+ bzero(param, sizeof (struct mslm_NetShareAdd));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (info2->shi2_remark == 0)
+ info2->shi2_remark = (unsigned char *)"";
+
+ /*
+ * Derive the real path which will be stored in the
+ * directory field of the lmshare_info_t structure
+ * from the path field in this RPC request.
+ */
+ parm_stat = lmshare_get_realpath((const char *)info2->shi2_path,
+ realpath, MAXPATHLEN);
+
+ if (parm_stat != NERR_Success) {
+ bzero(param, sizeof (struct mslm_NetShareAdd));
+ param->status = parm_stat;
+ param->parm_err
+ = (user_ctx->du_native_os == NATIVE_OS_WIN95) ?
+ 0 : &parm_err;
+ return (MLRPC_DRC_OK);
+ }
+
+ (void) memset(&si, 0, sizeof (lmshare_info_t));
+ (void) strlcpy(si.share_name, (const char *)info2->shi2_netname,
+ MAXNAMELEN);
+
+ (void) strlcpy(si.directory, realpath, MAXPATHLEN);
+ (void) strlcpy(si.comment, (const char *)info2->shi2_remark,
+ LMSHR_COMMENT_MAX);
+
+ si.mode = LMSHRM_PERM;
+
+ param->status = lmshare_add(&si, 1);
+ param->parm_err = (user_ctx->du_native_os == NATIVE_OS_WIN95) ?
+ 0 : &parm_err;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * srvsvc_is_poweruser
+ *
+ * Check whether or not the specified user has power-user privileges,
+ * i.e. is a member of the Domain Admins, Administrators or Power
+ * Users groups. This is typically required for operations such as
+ * adding/deleting shares.
+ *
+ * Returns 1 if the user is a power user, otherwise returns 0.
+ */
+static int
+srvsvc_is_poweruser(struct mlrpc_xaction *mxa)
+{
+ smb_dr_user_ctx_t *user = mxa->context->user_ctx;
+
+ return ((user->du_flags & SMB_ATF_ADMIN) ||
+ (user->du_flags & SMB_ATF_POWERUSER));
+}
+
+/*
+ * srvsvc_s_NetShareEnum
+ *
+ * Request for various levels of information about our shares.
+ * Level 0: just the share names.
+ * Level 1: the share name, the share type and the comment field.
+ * Level 2: everything that we know about the shares.
+ */
+static int
+srvsvc_s_NetShareEnum(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetShareEnum *param = arg;
+ struct mslm_infonres *infonres;
+ DWORD status;
+ DWORD n_shares;
+
+ infonres = MLRPC_HEAP_NEW(mxa, struct mslm_infonres);
+ if (infonres == 0) {
+ bzero(param, sizeof (struct mslm_NetShareEnum));
+ param->status = ERROR_NOT_ENOUGH_MEMORY;
+ return (MLRPC_DRC_OK);
+ }
+
+ infonres->entriesread = 0;
+ infonres->entries = 0;
+ param->result.level = param->level;
+ param->result.bufptr.p = infonres;
+ param->totalentries = 1; /* NT stream hint value: prefmaxlen? */
+ param->status = ERROR_SUCCESS;
+
+ n_shares = lmshare_num_shares();
+
+ switch (param->level) {
+ case 0:
+ status = mlsvc_NetShareEnumLevel0(infonres, n_shares, mxa, 0);
+ break;
+
+ case 1:
+ status = mlsvc_NetShareEnumLevel1(infonres, n_shares, mxa, 0);
+ break;
+
+ case 2:
+ status = mlsvc_NetShareEnumLevel2(infonres, n_shares, mxa, 0);
+ break;
+
+ case 502:
+ status = mlsvc_NetShareEnumLevel502(infonres, n_shares, mxa, 0);
+ break;
+
+ default:
+ status = ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (status != 0) {
+ bzero(param, sizeof (struct mslm_NetShareEnum));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->resume_handle = 0;
+ param->totalentries = infonres->entriesread;
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+
+/*
+ * srvsvc_s_NetShareEnumSticky
+ *
+ * Request for various levels of information about our shares.
+ * Level 0: just the share names.
+ * Level 1: the share name, the share type and the comment field.
+ * Level 2: everything that we know about the shares.
+ *
+ * NetShareEnumSticky is the same as NetShareEnum except that hidden
+ * shares are not returned. This call was apparently added due to a
+ * bug in the NT implementation of NetShareEnum - it didn't process
+ * the resume handle correctly so that attempts to enumerate large
+ * share lists resulted in an infinite loop.
+ */
+static int
+srvsvc_s_NetShareEnumSticky(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetShareEnum *param = arg;
+ struct mslm_infonres *infonres;
+ DWORD resume_handle;
+ DWORD status;
+ DWORD n_shares;
+
+ infonres = MLRPC_HEAP_NEW(mxa, struct mslm_infonres);
+ if (infonres == 0) {
+ bzero(param, sizeof (struct mslm_NetShareEnum));
+ param->status = ERROR_NOT_ENOUGH_MEMORY;
+ return (MLRPC_DRC_OK);
+ }
+
+ infonres->entriesread = 0;
+ infonres->entries = 0;
+ param->result.level = param->level;
+ param->result.bufptr.p = infonres;
+ param->totalentries = 1; /* NT stream hint value: prefmaxlen? */
+ param->status = ERROR_SUCCESS;
+
+ n_shares = lmshare_num_shares();
+
+ if (param->resume_handle)
+ resume_handle = *param->resume_handle;
+ else
+ resume_handle = 0;
+
+ switch (param->level) {
+ case 0:
+ status = mlsvc_NetShareEnumLevel0(infonres, n_shares, mxa, 1);
+ break;
+
+ case 1:
+ status = mlsvc_NetShareEnumLevel1(infonres, n_shares, mxa, 1);
+ break;
+
+ case 2:
+ status = mlsvc_NetShareEnumLevel2(infonres, n_shares, mxa, 1);
+ break;
+
+ case 502:
+ status = mlsvc_NetShareEnumLevel502(infonres, n_shares, mxa, 1);
+ break;
+
+ default:
+ status = ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (status != 0) {
+ bzero(param, sizeof (struct mslm_NetShareEnum));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (param->resume_handle)
+ *param->resume_handle = resume_handle;
+ param->totalentries = infonres->entriesread;
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+
+
+/*
+ * mlsvc_NetShareEnumLevel0
+ *
+ * Build the level 0 share information. The list should have been built
+ * before we got here so all we have to do is copy the share names to
+ * the response heap and setup the infonres values.
+ */
+static DWORD
+mlsvc_NetShareEnumLevel0(struct mslm_infonres *infonres, DWORD n_shares,
+ struct mlrpc_xaction *mxa, char sticky)
+{
+ struct mslm_SHARE_INFO_0 *info0;
+ lmshare_iterator_t *iterator;
+ lmshare_info_t *si;
+ DWORD i;
+ DWORD status;
+ smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx;
+
+ info0 = MLRPC_HEAP_NEWN(mxa, struct mslm_SHARE_INFO_0, n_shares);
+ if (info0 == 0) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ return (status);
+ }
+
+ iterator = lmshare_open_iterator(LMSHRM_ALL);
+ if (iterator == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ return (status);
+ }
+
+ i = 0;
+ while ((si = lmshare_iterate(iterator)) != 0) {
+ if (sticky && (si->stype & STYPE_SPECIAL))
+ continue;
+
+ if (smb_is_autohome(si))
+ continue;
+
+ status = mlsvc_NetShareEnumCommon(mxa, i, 0, si,
+ (void *)info0);
+
+ if (status != ERROR_SUCCESS)
+ break;
+
+ i++;
+ }
+
+ i = srvsvc_add_autohome(mxa, user_ctx->du_account, i, 0, (char *)info0);
+
+ lmshare_close_iterator(iterator);
+
+ infonres->entriesread = i;
+ infonres->entries = info0;
+ return (ERROR_SUCCESS);
+}
+
+
+/*
+ * mlsvc_NetShareEnumLevel1
+ *
+ * Build the level 1 share information. The list should have been built
+ * before we arrived here so all we have to do is copy the share info
+ * to the response heap and setup the infonres values. The only thing
+ * to be aware of here is that there are minor difference between the
+ * various share types.
+ */
+static DWORD
+mlsvc_NetShareEnumLevel1(struct mslm_infonres *infonres, DWORD n_shares,
+ struct mlrpc_xaction *mxa, char sticky)
+{
+ struct mslm_SHARE_INFO_1 *info1;
+ lmshare_iterator_t *iterator;
+ lmshare_info_t *si;
+ DWORD i;
+ smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx;
+
+ info1 = MLRPC_HEAP_NEWN(mxa, struct mslm_SHARE_INFO_1, n_shares);
+ if (info1 == 0)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ iterator = lmshare_open_iterator(LMSHRM_ALL);
+ if (iterator == NULL)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ i = 0;
+ while ((si = lmshare_iterate(iterator)) != 0) {
+ if (sticky && (si->stype & STYPE_SPECIAL))
+ continue;
+
+ if (smb_is_autohome(si))
+ continue;
+
+ if (mlsvc_NetShareEnumCommon(mxa, i, 1, si,
+ (void *)info1) != ERROR_SUCCESS)
+ break;
+ i++;
+ }
+
+ i = srvsvc_add_autohome(mxa, user_ctx->du_account, i, 1, (char *)info1);
+
+ lmshare_close_iterator(iterator);
+
+ infonres->entriesread = i;
+ infonres->entries = info1;
+ return (ERROR_SUCCESS);
+}
+
+/*
+ * mlsvc_NetShareEnumLevel2
+ *
+ * Build the level 2 share information. The list should have been built
+ * before we arrived here so all we have to do is copy the share info
+ * to the response heap and setup the infonres values. The only thing
+ * to be aware of here is that there are minor difference between the
+ * various share types.
+ */
+static DWORD
+mlsvc_NetShareEnumLevel2(struct mslm_infonres *infonres, DWORD n_shares,
+ struct mlrpc_xaction *mxa, char sticky)
+{
+ struct mslm_SHARE_INFO_2 *info2;
+ lmshare_iterator_t *iterator;
+ lmshare_info_t *si;
+ DWORD i;
+ smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx;
+
+ info2 = MLRPC_HEAP_NEWN(mxa, struct mslm_SHARE_INFO_2, n_shares);
+ if (info2 == 0)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ iterator = lmshare_open_iterator(LMSHRM_ALL);
+ if (iterator == NULL)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ i = 0;
+ while ((si = lmshare_iterate(iterator)) != 0) {
+ if (sticky && (si->stype & STYPE_SPECIAL))
+ continue;
+
+ if (smb_is_autohome(si))
+ continue;
+
+ if (mlsvc_NetShareEnumCommon(mxa, i, 2, si,
+ (void *)info2) != ERROR_SUCCESS)
+ break;
+ i++;
+ }
+
+ i = srvsvc_add_autohome(mxa, user_ctx->du_account, i, 2, (char *)info2);
+
+ lmshare_close_iterator(iterator);
+ infonres->entriesread = i;
+ infonres->entries = info2;
+ return (ERROR_SUCCESS);
+}
+
+/*
+ * mlsvc_NetShareEnumLevel502
+ *
+ * Build the level 502 share information. This is the same as level 2
+ * but with a security descriptor in the share structure. We don't
+ * support SD's on shares so we can just set that field to zero. See
+ * mlsvc_NetShareEnumLevel2 for more information.
+ */
+static DWORD
+mlsvc_NetShareEnumLevel502(struct mslm_infonres *infonres, DWORD n_shares,
+ struct mlrpc_xaction *mxa, char sticky)
+{
+ struct mslm_SHARE_INFO_502 *info502;
+ lmshare_iterator_t *iterator;
+ lmshare_info_t *si;
+ DWORD i;
+ smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx;
+
+ info502 = MLRPC_HEAP_NEWN(mxa, struct mslm_SHARE_INFO_502, n_shares);
+
+ if (info502 == 0)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ iterator = lmshare_open_iterator(LMSHRM_ALL);
+ if (iterator == NULL)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ i = 0;
+ while ((si = lmshare_iterate(iterator)) != 0) {
+ if (sticky && (si->stype & STYPE_SPECIAL))
+ continue;
+
+ if (smb_is_autohome(si))
+ continue;
+
+ if (mlsvc_NetShareEnumCommon(
+ mxa, i, 502, si, (void *)info502) != ERROR_SUCCESS)
+ break;
+ i++;
+ }
+
+ i = srvsvc_add_autohome(mxa, user_ctx->du_account, i, 502,
+ (char *)info502);
+
+ lmshare_close_iterator(iterator);
+ infonres->entriesread = i;
+ infonres->entries = info502;
+ return (ERROR_SUCCESS);
+}
+
+/*
+ * mlsvc_NetShareEnumCommon
+ *
+ * Build the levels 0, 1, 2 and 502 share information. This function
+ * is called by the various NetShareEnum levels for each share. If
+ * we cannot build the share data for some reason, we return an error
+ * but the actual value of the error is not important to the caller.
+ * The caller just needs to know not to include this info in the RPC
+ * response.
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_NOT_ENOUGH_MEMORY
+ * ERROR_INVALID_LEVEL
+ */
+static DWORD
+mlsvc_NetShareEnumCommon(struct mlrpc_xaction *mxa, DWORD i, int level,
+ lmshare_info_t *si, void *infop)
+{
+ struct mslm_SHARE_INFO_0 *info0;
+ struct mslm_SHARE_INFO_1 *info1;
+ struct mslm_SHARE_INFO_2 *info2;
+ struct mslm_SHARE_INFO_502 *info502;
+ char shr_comment[LMSHR_COMMENT_MAX];
+
+ if ((si->stype & STYPE_MASK) == STYPE_IPC) {
+ /*
+ * Windows clients don't send the \\PIPE path for IPC$.
+ */
+ si->directory[0] = '\0';
+ (void) strcpy(si->comment, "Remote IPC");
+ }
+
+ if (si->comment && strlen(si->comment))
+ (void) snprintf(shr_comment, sizeof (shr_comment), "%s (%s)",
+ si->directory, si->comment);
+ else
+ (void) strcpy(shr_comment, si->directory);
+
+ switch (level) {
+ case 0:
+ info0 = (struct mslm_SHARE_INFO_0 *)infop;
+ info0[i].shi0_netname
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si->share_name);
+
+ if (info0[i].shi0_netname == 0)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ break;
+
+ case 1:
+ info1 = (struct mslm_SHARE_INFO_1 *)infop;
+ info1[i].shi1_netname
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si->share_name);
+
+ info1[i].shi1_remark
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment);
+
+ info1[i].shi1_type = si->stype;
+
+ if (!info1[i].shi1_netname || !info1[i].shi1_remark)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ break;
+
+ case 2:
+ info2 = (struct mslm_SHARE_INFO_2 *)infop;
+ info2[i].shi2_netname
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si->share_name);
+
+ info2[i].shi2_remark
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment);
+
+ info2[i].shi2_path
+ = (unsigned char *)srvsvc_share_mkpath(mxa, si->directory);
+
+ info2[i].shi2_type = si->stype;
+ info2[i].shi2_permissions = 0;
+ info2[i].shi2_max_uses = SHI_USES_UNLIMITED;
+ info2[i].shi2_current_uses = 0;
+ info2[i].shi2_passwd
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, empty_string);
+
+ if (!info2[i].shi2_netname || !info2[i].shi2_remark ||
+ !info2[i].shi2_passwd || !info2[i].shi2_path)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ break;
+
+ case 502:
+ info502 = (struct mslm_SHARE_INFO_502 *)infop;
+ info502[i].shi502_netname
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si->share_name);
+
+ info502[i].shi502_remark
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment);
+
+ info502[i].shi502_path
+ = (unsigned char *)srvsvc_share_mkpath(mxa, si->directory);
+
+ info502[i].shi502_type = si->stype;
+ info502[i].shi502_permissions = 0;
+ info502[i].shi502_max_uses = SHI_USES_UNLIMITED;
+ info502[i].shi502_current_uses = 0;
+ info502[i].shi502_passwd
+ = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, empty_string);
+
+ info502[i].shi502_reserved = 0;
+ info502[i].shi502_security_descriptor = 0;
+
+ if (!info502[i].shi502_netname || !info502[i].shi502_remark ||
+ !info502[i].shi502_passwd || !info502[i].shi502_path)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+ break;
+
+ default:
+ return (ERROR_INVALID_LEVEL);
+ }
+
+ return (ERROR_SUCCESS);
+}
+
+/*
+ * srvsvc_s_NetShareDel
+ *
+ * Delete a share. Only the administrator, or a member of the domain
+ * administrators group, is 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
+ * from the client's command line.
+ *
+ * Returns Win32 error codes.
+ */
+static int
+srvsvc_s_NetShareDel(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetShareDel *param = arg;
+
+ if (srvsvc_is_poweruser(mxa) == 0 ||
+ lmshare_is_restricted((char *)param->netname)) {
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->status = lmshare_delete((char *)param->netname, 1);
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * srvsvc_s_NetGetFileSecurity
+ *
+ * Get security descriptor of the requested file/folder
+ *
+ * Right now, just returns ERROR_ACCESS_DENIED, because we cannot
+ * get the requested SD here in MLRPC code.
+ */
+/*ARGSUSED*/
+static int
+srvsvc_s_NetGetFileSecurity(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetGetFileSecurity *param = arg;
+
+ param->length = 0;
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * srvsvc_s_NetSetFileSecurity
+ *
+ * Set the given security descriptor for the requested file/folder
+ *
+ * Right now, just returns ERROR_ACCESS_DENIED, because we cannot
+ * set the requested SD here in MLRPC code.
+ */
+/*ARGSUSED*/
+static int
+srvsvc_s_NetSetFileSecurity(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetSetFileSecurity *param = arg;
+
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+static mlrpc_stub_table_t srvsvc_stub_table[] = {
+ { srvsvc_s_NetConnectEnum, SRVSVC_OPNUM_NetConnectEnum },
+ { srvsvc_s_NetFileEnum, SRVSVC_OPNUM_NetFileEnum },
+ { srvsvc_s_NetFileClose, SRVSVC_OPNUM_NetFileClose },
+ { srvsvc_s_NetShareGetInfo, SRVSVC_OPNUM_NetShareGetInfo },
+ { srvsvc_s_NetShareSetInfo, SRVSVC_OPNUM_NetShareSetInfo },
+ { srvsvc_s_NetSessionEnum, SRVSVC_OPNUM_NetSessionEnum },
+ { srvsvc_s_NetSessionDel, SRVSVC_OPNUM_NetSessionDel },
+ { srvsvc_s_NetServerGetInfo, SRVSVC_OPNUM_NetServerGetInfo },
+ { srvsvc_s_NetRemoteTOD, SRVSVC_OPNUM_NetRemoteTOD },
+ { srvsvc_s_NetNameValidate, SRVSVC_OPNUM_NetNameValidate },
+ { srvsvc_s_NetShareAdd, SRVSVC_OPNUM_NetShareAdd },
+ { srvsvc_s_NetShareDel, SRVSVC_OPNUM_NetShareDel },
+ { srvsvc_s_NetShareEnum, SRVSVC_OPNUM_NetShareEnum },
+ { srvsvc_s_NetShareEnumSticky, SRVSVC_OPNUM_NetShareEnumSticky },
+ { srvsvc_s_NetGetFileSecurity, SRVSVC_OPNUM_NetGetFileSecurity },
+ { srvsvc_s_NetSetFileSecurity, SRVSVC_OPNUM_NetSetFileSecurity },
+ {0}
+};
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_svcctl.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_svcctl.c
new file mode 100644
index 0000000000..eca831f899
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_svcctl.c
@@ -0,0 +1,485 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * NT Service Control Services (SVCCTL) RPC interface definition.
+ * This interface provides remote access to add, remove, start and
+ * stop services.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/winsvc.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/svcctl.ndl>
+
+/*
+ * SVCCTL diagnostics flag: set to 1 to enable verbose logging.
+ */
+int svcctl_debug = 0;
+
+/*
+ * The handle keys for the various types of handles returned
+ * by this interface.
+ */
+#define SVCCTL_MANAGER_KEY "svcctlManager"
+#define SVCCTL_SERVICE_KEY "svcctlService"
+
+
+typedef struct {
+ char *svc_name;
+ char *display_name;
+ char *local_name;
+} svc_info_t;
+
+
+/*
+ * The list of service we report to Server Manager. Entries don't
+ * have to be alphabetically arranged here; Server Manager will
+ * sort the list.
+ *
+ * NOTE: The enumeration list is currently built in a fixed-size,
+ * 1024 byte buffer. Be careful not to over-run the buffer.
+ */
+static svc_info_t svc_info[] = {
+ { "Dhcp", "DHCP Client", "dhcpc" },
+ { "EventLog", "EventLog", NULL },
+ { "Netlogon", "Net Logon", NULL },
+ { "WebAdmin", "Web Administration", "httpd" },
+ { "RlgnSvr", "Remote Login", "rlogin" },
+ { "RpcSs", "Remote Procedure Call (RPC) Service", NULL },
+ { "RshSvr", "Remote Shell", "rsh" },
+ { "SshSvr", "Secure Shell", "ssh" },
+ { "TlntSvr", "Telnet", "telnet" },
+ { "Dnscache", "DNS Client", "dns" },
+ { "NisSvr", "Network Information Services", NULL },
+ { "NtLmSsp", "NT LM Security Support Provider", NULL },
+ { "Samss", "Security Accounts Manager", NULL },
+ { "UPS", "Uninterruptible Power Supply", "ups" },
+ { "TftpSvr", "TFTP", "tftp" }
+};
+
+#define SVCCTL_NUM_SVCS (sizeof (svc_info)/sizeof (svc_info[0]))
+
+
+static DWORD svcctl_get_status(const char *);
+static DWORD svcctl_validate_service(char *);
+static DWORD svcctl_validate_handle(char *, ms_handle_t *, char *,
+ struct mlrpc_xaction *);
+static int svcctl_is_admin(struct mlrpc_xaction *);
+
+static int svcctl_s_Close(void *, struct mlrpc_xaction *);
+static int svcctl_s_OpenManager(void *, struct mlrpc_xaction *);
+static int svcctl_s_OpenService(void *, struct mlrpc_xaction *);
+static int svcctl_s_QueryServiceStatus(void *, struct mlrpc_xaction *);
+static int svcctl_s_QueryServiceConfig(void *, struct mlrpc_xaction *);
+static int svcctl_s_EnumServicesStatus(void *, struct mlrpc_xaction *);
+
+static mlrpc_stub_table_t svcctl_stub_table[] = {
+ { svcctl_s_Close, SVCCTL_OPNUM_Close },
+ { svcctl_s_OpenManager, SVCCTL_OPNUM_OpenManager },
+ { svcctl_s_OpenService, SVCCTL_OPNUM_OpenService },
+ { svcctl_s_QueryServiceStatus, SVCCTL_OPNUM_QueryServiceStatus },
+ { svcctl_s_QueryServiceConfig, SVCCTL_OPNUM_QueryServiceConfig },
+ { svcctl_s_EnumServicesStatus, SVCCTL_OPNUM_EnumServicesStatus },
+ {0}
+};
+
+static mlrpc_service_t svcctl_service = {
+ "SVCCTL", /* name */
+ "Service Control Services", /* desc */
+ "\\svcctl", /* endpoint */
+ PIPE_NTSVCS, /* sec_addr_port */
+ "367abb81-9844-35f1-ad3298f038001003", 2, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(svcctl_interface), /* interface ti */
+ svcctl_stub_table /* stub_table */
+};
+
+/*
+ * svcctl_initialize
+ *
+ * This function registers the SVCCTL RPC interface with the RPC runtime
+ * library. It must be called in order to use either the client side
+ * or the server side functions.
+ */
+void
+svcctl_initialize(void)
+{
+ (void) mlrpc_register_service(&svcctl_service);
+}
+
+/*
+ * svcctl_s_Close
+ *
+ * This is a request to close the SVCCTL interface specified by the
+ * handle. Free the handle and zero out the result handle for the
+ * client.
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_INVALID_HANDLE
+ */
+static int
+svcctl_s_Close(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct svcctl_Close *param = arg;
+ smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx;
+ ms_handle_t *handle;
+ DWORD status;
+
+ if (svcctl_debug)
+ smb_token_log(LOG_DEBUG, user_ctx, "(SvcctlClose)");
+
+ handle = (ms_handle_t *)&param->handle;
+ status = svcctl_validate_handle("SvcctlClose", handle, 0, mxa);
+
+ if (status == ERROR_SUCCESS)
+ (void) mlsvc_put_handle((ms_handle_t *)&param->handle);
+
+ bzero(&param->result_handle, sizeof (svcctl_handle_t));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * svcctl_s_OpenManager
+ *
+ * This is a request to open the service control manager. Dependent
+ * on the desired access we either generate a handle to be used on
+ * subsequent requests or deny access.
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_ACCESS_DENIED
+ *
+ * Return a handle for use with subsequent svcctl requests.
+ */
+static int
+svcctl_s_OpenManager(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct svcctl_OpenManager *param = arg;
+ ms_handle_t *handle;
+ int rc;
+
+ rc = svcctl_is_admin(mxa);
+
+ if ((rc == 0) || (param->desired_access & SC_MANAGER_LOCK) != 0) {
+ /*
+ * The user doesn't have Administrator rights
+ * or wants a write lock on the Services DB.
+ */
+ bzero(&param->handle, sizeof (svcctl_handle_t));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+ }
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SVCCTL, SVCCTL_MANAGER_KEY, 0);
+ bcopy(handle, &param->handle, sizeof (svcctl_handle_t));
+
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * svcctl_s_OpenService
+ *
+ * Return a handle for use with subsequent svcctl requests.
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_INVALID_HANDLE
+ * ERROR_SERVICE_DOES_NOT_EXIST
+ */
+static int
+svcctl_s_OpenService(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct svcctl_OpenService *param = arg;
+ ms_handle_t *handle;
+ DWORD status;
+
+ status = svcctl_validate_handle("SvcctlOpenService",
+ (ms_handle_t *)&param->manager_handle, SVCCTL_MANAGER_KEY, mxa);
+
+ if (status != ERROR_SUCCESS) {
+ bzero(&param->service_handle, sizeof (svcctl_handle_t));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ status = svcctl_validate_service((char *)param->service_name);
+ if (status != ERROR_SUCCESS) {
+ bzero(&param->service_handle, sizeof (svcctl_handle_t));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_SVCCTL, SVCCTL_SERVICE_KEY, 0);
+ bcopy(handle, &param->service_handle, sizeof (svcctl_handle_t));
+
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * svcctl_s_QueryServiceStatus
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_INVALID_HANDLE
+ */
+static int
+svcctl_s_QueryServiceStatus(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct svcctl_QueryServiceStatus *param = arg;
+ DWORD status;
+
+ status = svcctl_validate_handle("SvcctlQueryServiceStatus",
+ (ms_handle_t *)&param->service_handle, SVCCTL_SERVICE_KEY, mxa);
+
+ if (status != ERROR_SUCCESS) {
+ bzero(&param, sizeof (struct svcctl_QueryServiceStatus));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->service_status.service_type = SERVICE_WIN32_SHARE_PROCESS;
+ param->service_status.cur_state = SERVICE_RUNNING;
+ param->service_status.ctrl_accepted = 0;
+ param->service_status.w32_exitcode = 0;
+ param->service_status.svc_specified_exitcode = 0;
+ param->service_status.check_point = 0;
+ param->service_status.wait_hint = 0;
+
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * svcctl_s_EnumServicesStatus
+ *
+ * Enumerate the list of services we support. Currently, this list
+ * is built in a fixed-size 1024 byte buffer - be careful not to
+ * over-run the buffer.
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_INVALID_HANDLE
+ */
+static int
+svcctl_s_EnumServicesStatus(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct svcctl_EnumServicesStatus *param = arg;
+ svc_enum_status_t *service_table;
+ svc_enum_status_t *svc;
+ mts_wchar_t *wide_name;
+ char *name;
+ int i, namelen;
+ int offs;
+ DWORD status;
+
+ status = svcctl_validate_handle("SvcctlEnumServicesStatus",
+ (ms_handle_t *)&param->manager_handle, SVCCTL_MANAGER_KEY, mxa);
+
+ if (status != ERROR_SUCCESS) {
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (param->buf_size < 1024) {
+ param->status = ERROR_MORE_DATA;
+ return (MLRPC_DRC_OK);
+ }
+
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ service_table = (svc_enum_status_t *)param->services;
+ offs = SVCCTL_NUM_SVCS * sizeof (svc_enum_status_t);
+
+ for (i = 0; i < SVCCTL_NUM_SVCS; i++) {
+ svc = &service_table[i];
+
+ svc->svc_name = offs;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ wide_name = (mts_wchar_t *)&param->services[offs];
+ name = svc_info[i].svc_name;
+ namelen = strlen(name) + 1;
+ (void) mts_mbstowcs(wide_name, name, namelen);
+
+ offs += namelen * 2;
+
+ svc->display_name = offs;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ wide_name = (mts_wchar_t *)&param->services[offs];
+ name = svc_info[i].display_name;
+ namelen = strlen(name) + 1;
+ (void) mts_mbstowcs(wide_name, name, namelen);
+
+ offs += namelen * 2;
+
+ name = svc_info[i].local_name;
+ if (name)
+ svc->svc_status.cur_state = svcctl_get_status(name);
+ else
+ svc->svc_status.cur_state = SERVICE_RUNNING;
+
+ svc->svc_status.service_type = SERVICE_WIN32_SHARE_PROCESS;
+ svc->svc_status.ctrl_accepted = 0;
+ svc->svc_status.w32_exitcode = 0;
+ svc->svc_status.svc_specified_exitcode = 0;
+ svc->svc_status.check_point = 0;
+ svc->svc_status.wait_hint = 0;
+ }
+
+ param->buf_size = 1024;
+ param->bytes_needed = 0;
+ param->svc_num = SVCCTL_NUM_SVCS;
+ param->resume_handle = 0;
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * svcctl_s_QueryServiceConfig
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_INVALID_HANDLE
+ */
+static int
+svcctl_s_QueryServiceConfig(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct svcctl_QueryServiceConfig *param = arg;
+ DWORD status;
+
+ status = svcctl_validate_handle("SvcctlQueryServiceConfig",
+ (ms_handle_t *)&param->service_handle, SVCCTL_SERVICE_KEY, mxa);
+
+ if (status != ERROR_SUCCESS) {
+ bzero(&param, sizeof (struct svcctl_QueryServiceConfig));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->service_cfg.service_type = SERVICE_WIN32_SHARE_PROCESS;
+ param->service_cfg.start_type = SERVICE_AUTO_START;
+ param->service_cfg.error_control = SERVICE_ERROR_IGNORE;
+ param->service_cfg.binary_pathname = 0;
+ param->service_cfg.loadorder_group = 0;
+ param->service_cfg.tag_id = 0;
+ param->service_cfg.dependencies = 0;
+ param->service_cfg.service_startname = 0;
+ param->service_cfg.display_name = 0;
+
+ param->cfg_bytes = sizeof (svc_config_t);
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Check to see whether or not a service is supported. The check is
+ * case-insensitive to avoid any naming issues due to the different
+ * versions of Windows.
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_SERVICE_DOES_NOT_EXIST
+ */
+static DWORD
+svcctl_validate_service(char *svc_name)
+{
+ int i;
+
+ if (svc_name == NULL)
+ return (ERROR_SERVICE_DOES_NOT_EXIST);
+
+ for (i = 0; i < SVCCTL_NUM_SVCS; i++) {
+ if (strcasecmp(svc_name, svc_info[i].svc_name) == 0)
+ return (ERROR_SUCCESS);
+ }
+
+ return (ERROR_SERVICE_DOES_NOT_EXIST);
+}
+
+/*
+ * Check whether or not the svcctl module allocated this handle.
+ *
+ * Returns:
+ * ERROR_SUCCESS
+ * ERROR_INVALID_HANDLE.
+ */
+/*ARGSUSED*/
+static DWORD
+svcctl_validate_handle(char *name, ms_handle_t *handle, char *key,
+ struct mlrpc_xaction *mxa)
+{
+ int mgr_erc;
+ int svc_erc;
+
+ mgr_erc = mlsvc_validate_handle(handle, SVCCTL_MANAGER_KEY);
+ svc_erc = mlsvc_validate_handle(handle, SVCCTL_SERVICE_KEY);
+
+ if (mgr_erc == 0 && svc_erc == 0)
+ return (ERROR_INVALID_HANDLE);
+
+ return (ERROR_SUCCESS);
+}
+
+/*
+ * Report the service status: SERVICE_PAUSED or SERVICE_RUNNING.
+ */
+/*ARGSUSED*/
+static DWORD
+svcctl_get_status(const char *name)
+{
+ return (SERVICE_RUNNING);
+}
+
+/*
+ * SVCCTL access is restricted to administrators: members of
+ * the Domain Admins or Administrators groups.
+ *
+ * Returns 1 if the user has admin rights. Otherwise returns 0.
+ */
+static int
+svcctl_is_admin(struct mlrpc_xaction *mxa)
+{
+ smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx;
+
+ if (user_ctx == NULL)
+ return (0);
+
+ return (user_ctx->du_flags & SMB_ATF_ADMIN);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
new file mode 100644
index 0000000000..29cd3f058a
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
@@ -0,0 +1,752 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Utility functions to support the RPC interface library.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <strings.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/time.h>
+#include <sys/systm.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbrdr.h>
+#include <smbsrv/libsmbns.h>
+#include <smbsrv/libmlsvc.h>
+
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/lsalib.h>
+#include <smbsrv/samlib.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/mlsvc.h>
+
+extern int netr_open(char *, char *, mlsvc_handle_t *);
+extern int netr_close(mlsvc_handle_t *);
+extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD);
+extern int mlsvc_user_getauth(char *, char *, smb_auth_info_t *);
+
+static int mlsvc_lookup_local_name(char *name, nt_sid_t **sid);
+static int mlsvc_lookup_nt_name(char *name, nt_sid_t **sid);
+static int mlsvc_lookup_nt_sid(nt_sid_t *sid, char *buf, int bufsize);
+
+/*
+ * Compare the supplied domain name with the local hostname.
+ * We need to deal with both server names and fully-qualified
+ * domain names.
+ *
+ * Returns:
+ * 0 The specified domain is not the local domain,
+ * 1 The Specified domain is the local domain.
+ * -1 Invalid parameter or unable to get the local
+ * system information.
+ */
+int
+mlsvc_is_local_domain(const char *domain)
+{
+ char hostname[MAXHOSTNAMELEN];
+ uint32_t mode;
+ int rc;
+
+ if (strchr(domain, '.') != NULL)
+ rc = smb_getfqhostname(hostname, MAXHOSTNAMELEN);
+ else
+ rc = smb_gethostname(hostname, MAXHOSTNAMELEN, 1);
+
+ if (rc != 0)
+ return (-1);
+
+ rc = strcasecmp(domain, hostname);
+ mode = smb_get_security_mode();
+
+ if ((rc == 0) || (mode == SMB_SECMODE_WORKGRP))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * mlsvc_lookup_name
+ *
+ * Lookup a name in the specified domain and translate it to a SID.
+ * If the name is in the NT domain, it may refer to a user, group or
+ * alias. Otherwise it must refer to a UNIX username. The memory for
+ * the sid is allocated using malloc so the caller should call free
+ * when it is no longer required.
+ *
+ * On success, 0 will be returned and sid will point to a local domain
+ * user SID. Otherwise -1 will be returned.
+ */
+int
+mlsvc_lookup_name(char *domain, char *name, nt_sid_t **sid)
+{
+ if (domain == NULL || name == NULL || sid == NULL)
+ return (-1);
+
+ if (mlsvc_is_local_domain(domain) == 1)
+ return (mlsvc_lookup_local_name(name, sid));
+ else
+ return (mlsvc_lookup_nt_name(name, sid));
+}
+
+/*
+ * mlsvc_lookup_local_name
+ *
+ * Lookup a name in the local password file and translate it to a SID.
+ * The name must refer to a user. This is a private function intended
+ * to support mlsvc_lookup_name so it doesn't perform any parameter
+ * validation. The memory for the sid is allocated using malloc so the
+ * caller must call free when it is no longer required.
+ *
+ * On success, 0 will be returned and sid will point to a local domain
+ * user SID. Otherwise -1 will be returned.
+ */
+static int
+mlsvc_lookup_local_name(char *name, nt_sid_t **sid)
+{
+ struct passwd *pw;
+ nt_sid_t *domain_sid;
+
+ if ((pw = getpwnam(name)) == NULL)
+ return (-1);
+
+ if ((domain_sid = nt_domain_local_sid()) == NULL)
+ return (-1);
+
+ *sid = nt_sid_splice(domain_sid, pw->pw_uid);
+ return (0);
+}
+
+/*
+ * mlsvc_lookup_nt_name
+ *
+ * Lookup a name in the specified NT domain and translate it to a SID.
+ * The name may refer to a user, group or alias. This is a private
+ * function intended to support mlsvc_lookup_name so it doesn't do any
+ * parameter validation. The memory for the sid is allocated using
+ * malloc so the caller should call free when it is no longer required.
+ *
+ * On success, 0 will be returned and sid will point to an NT domain
+ * user SID. Otherwise -1 will be returned.
+ */
+static int
+mlsvc_lookup_nt_name(char *name, nt_sid_t **sid)
+{
+ smb_userinfo_t *user_info;
+
+ if ((user_info = mlsvc_alloc_user_info()) == NULL)
+ return (-1);
+
+ if (lsa_lookup_name(0, 0, name, user_info) != 0)
+ return (-1);
+
+ *sid = nt_sid_splice(user_info->domain_sid, user_info->rid);
+ mlsvc_free_user_info(user_info);
+ return (0);
+}
+
+/*
+ * mlsvc_lookup_sid
+ *
+ * Lookup a SID and translate it to a name. The name returned may refer
+ * to a domain, user, group or alias dependent on the SID. On success 0
+ * will be returned. Otherwise -1 will be returned.
+ */
+int
+mlsvc_lookup_sid(nt_sid_t *sid, char *buf, int bufsize)
+{
+ struct passwd *pw;
+ struct group *gr;
+ nt_group_t *grp;
+ DWORD rid;
+
+ if (sid == NULL || buf == NULL)
+ return (-1);
+
+ if (nt_sid_is_local(sid)) {
+ (void) nt_sid_get_rid(sid, &rid);
+
+ switch (SAM_RID_TYPE(rid)) {
+ case SAM_RT_NT_UID:
+ break;
+
+ case SAM_RT_NT_GID:
+ if ((grp = nt_groups_lookup_rid(rid)) == NULL)
+ return (-1);
+
+ (void) strlcpy(buf, grp->name, bufsize);
+ break;
+
+ case SAM_RT_UNIX_UID:
+ if ((pw = getpwuid(SAM_DECODE_RID(rid))) == NULL)
+ return (-1);
+
+ (void) strlcpy(buf, pw->pw_name, bufsize);
+ break;
+
+ case SAM_RT_UNIX_GID:
+ if ((gr = getgrgid(SAM_DECODE_RID(rid))) == NULL)
+ return (-1);
+
+ (void) strlcpy(buf, gr->gr_name, bufsize);
+ break;
+ }
+
+ return (0);
+ }
+
+ return (mlsvc_lookup_nt_sid(sid, buf, bufsize));
+}
+
+/*
+ * mlsvc_lookup_nt_sid
+ *
+ * Lookup an NT SID and translate it to a name. This is a private
+ * function intended to support mlsvc_lookup_sid so it doesn't do any
+ * parameter validation. The input account_name specifies the logon/
+ * session to be used for the lookup. It doesn't need to have any
+ * association with the SID being looked up. The name returned may
+ * refer to a domain, user, group or alias dependent on the SID.
+ *
+ * On success the name will be copied into buf and 0 will be returned.
+ * Otherwise -1 will be returned.
+ */
+static int
+mlsvc_lookup_nt_sid(nt_sid_t *sid, char *buf, int bufsize)
+{
+ smb_userinfo_t *user_info;
+ int rc;
+
+ if ((user_info = mlsvc_alloc_user_info()) == NULL)
+ return (-1);
+
+ if ((rc = lsa_lookup_sid(sid, user_info)) == 0)
+ (void) strlcpy(buf, user_info->name, bufsize);
+
+ mlsvc_free_user_info(user_info);
+ return (rc);
+}
+
+/*
+ * mlsvc_alloc_user_info
+ *
+ * Allocate a user_info structure and set the contents to zero. A
+ * pointer to the user_info structure is returned.
+ */
+smb_userinfo_t *
+mlsvc_alloc_user_info(void)
+{
+ smb_userinfo_t *user_info;
+
+ user_info = (smb_userinfo_t *)malloc(sizeof (smb_userinfo_t));
+ if (user_info == NULL)
+ return (NULL);
+
+ bzero(user_info, sizeof (smb_userinfo_t));
+ return (user_info);
+}
+
+/*
+ * mlsvc_free_user_info
+ *
+ * Free a user_info structure. This function ensures that the contents
+ * of the user_info are freed as well as the user_info itself.
+ */
+void
+mlsvc_free_user_info(smb_userinfo_t *user_info)
+{
+ if (user_info) {
+ mlsvc_release_user_info(user_info);
+ free(user_info);
+ }
+}
+
+/*
+ * mlsvc_release_user_info
+ *
+ * Release the contents of a user_info structure and zero out the
+ * elements but do not free the user_info structure itself. This
+ * function cleans out the structure so that it can be reused without
+ * worrying about stale contents.
+ */
+void
+mlsvc_release_user_info(smb_userinfo_t *user_info)
+{
+ int i;
+
+ if (user_info == NULL)
+ return;
+
+ free(user_info->name);
+ free(user_info->domain_sid);
+ free(user_info->domain_name);
+ free(user_info->groups);
+
+ if (user_info->n_other_grps) {
+ for (i = 0; i < user_info->n_other_grps; i++)
+ free(user_info->other_grps[i].sid);
+
+ free(user_info->other_grps);
+ }
+
+ free(user_info->user_sid);
+ free(user_info->pgrp_sid);
+ bzero(user_info, sizeof (smb_userinfo_t));
+}
+
+/*
+ * mlsvc_setadmin_user_info
+ *
+ * Determines if the given user is the domain Administrator or a
+ * member of Domain Admins or Administrators group and set the
+ * user_info->flags accordingly.
+ */
+void
+mlsvc_setadmin_user_info(smb_userinfo_t *user_info)
+{
+ nt_domain_t *domain;
+ nt_group_t *grp;
+ int i;
+
+ if ((domain = nt_domain_lookupbytype(NT_DOMAIN_PRIMARY)) == NULL)
+ return;
+
+ if (!nt_sid_is_equal((nt_sid_t *)user_info->domain_sid, domain->sid))
+ return;
+
+ if (user_info->rid == DOMAIN_USER_RID_ADMIN)
+ user_info->flags |= SMB_UINFO_FLAG_DADMIN;
+ else if (user_info->primary_group_rid == DOMAIN_GROUP_RID_ADMINS)
+ user_info->flags |= SMB_UINFO_FLAG_DADMIN;
+ else {
+ for (i = 0; i < user_info->n_groups; i++)
+ if (user_info->groups[i].rid == DOMAIN_GROUP_RID_ADMINS)
+ user_info->flags |= SMB_UINFO_FLAG_DADMIN;
+ }
+
+ grp = nt_group_getinfo("Administrators", RWLOCK_READER);
+ if (grp) {
+ i = nt_group_is_member(grp, user_info->user_sid);
+ nt_group_putinfo(grp);
+ if (i)
+ user_info->flags |= SMB_UINFO_FLAG_LADMIN;
+ }
+}
+
+/*
+ * mlsvc_string_save
+ *
+ * This is a convenience function to prepare strings for an RPC call.
+ * An ms_string_t is set up with the appropriate lengths and str is
+ * set up to point to a copy of the original string on the heap. The
+ * macro MLRPC_HEAP_STRSAVE is an alias for mlrpc_heap_strsave, which
+ * extends the heap and copies the string into the new area.
+ */
+int
+mlsvc_string_save(ms_string_t *ms, char *str, struct mlrpc_xaction *mxa)
+{
+ int length;
+ char *p;
+
+ if (ms == NULL || str == NULL || mxa == NULL)
+ return (0);
+
+ /*
+ * Windows NT expects the name length to exclude the
+ * terminating wchar null but doesn't care whether or
+ * not the allosize includes it. Windows 2000 insists
+ * that both the length and the allosize include the
+ * wchar null.
+ */
+ length = mts_wcequiv_strlen(str);
+ ms->allosize = length + sizeof (mts_wchar_t);
+
+ if (mxa->context->user_ctx->du_native_os == NATIVE_OS_WIN2000)
+ ms->length = ms->allosize;
+ else
+ ms->length = length;
+
+ if ((p = MLRPC_HEAP_STRSAVE(mxa, str)) == NULL) {
+ return (0);
+ }
+
+ ms->str = (LPTSTR)p;
+ return (1);
+}
+
+/*
+ * mlsvc_sid_save
+ *
+ * Expand the heap and copy the sid into the new area.
+ * Returns a pointer to the copy of the sid on the heap.
+ */
+nt_sid_t *
+mlsvc_sid_save(nt_sid_t *sid, struct mlrpc_xaction *mxa)
+{
+ nt_sid_t *heap_sid;
+ unsigned size;
+
+ if (sid == NULL)
+ return (NULL);
+
+ size = nt_sid_length(sid);
+
+ if ((heap_sid = (nt_sid_t *)MLRPC_HEAP_MALLOC(mxa, size)) == NULL)
+ return (0);
+
+ bcopy(sid, heap_sid, size);
+ return (heap_sid);
+}
+
+/*
+ * mlsvc_is_null_handle
+ *
+ * Check a handle against a null handle. Returns 1 if the handle is
+ * null. Otherwise returns 0.
+ */
+int
+mlsvc_is_null_handle(mlsvc_handle_t *handle)
+{
+ static ms_handle_t zero_handle;
+
+ if (handle == NULL || handle->context == NULL)
+ return (1);
+
+ if (!memcmp(&handle->handle, &zero_handle, sizeof (ms_handle_t)))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * mlsvc_validate_user
+ *
+ * Returns NT status codes.
+ */
+DWORD
+mlsvc_validate_user(char *server, char *domain, char *plain_user,
+ char *plain_text)
+{
+ smb_auth_info_t auth;
+ smb_ntdomain_t *di;
+ int erc;
+ DWORD status;
+ mlsvc_handle_t netr_handle;
+ char machine_passwd[MLSVC_MACHINE_ACCT_PASSWD_MAX];
+
+ machine_passwd[0] = '\0';
+
+ /*
+ * Ensure that the domain name is uppercase.
+ */
+ (void) utf8_strupr(domain);
+
+ /*
+ * There is no point continuing if the domain information is
+ * not available. Wait for up to 10 seconds and then give up.
+ */
+ if ((di = smb_getdomaininfo(10)) == 0) {
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ return (status);
+ }
+
+ if (strcasecmp(domain, di->domain) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ return (status);
+ }
+
+ erc = mlsvc_user_logon(server, domain, plain_user, plain_text);
+
+ if (erc == AUTH_USER_GRANT) {
+ int isenabled;
+
+ smb_config_rdlock();
+ isenabled = smb_config_getyorn(SMB_CI_ADS_ENABLE);
+ smb_config_unlock();
+ if (isenabled) {
+ if (adjoin(machine_passwd,
+ sizeof (machine_passwd)) == ADJOIN_SUCCESS) {
+ status = NT_STATUS_SUCCESS;
+ } else {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ /*
+ * Ensure that we don't have an old account in
+ * this domain. There's no need to check the
+ * return status.
+ */
+ (void) sam_remove_trust_account(server, domain);
+
+ if (mlsvc_user_getauth(server, plain_user, &auth)
+ != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ return (status);
+ }
+
+ status = sam_create_trust_account(server, domain,
+ &auth);
+ if (status == NT_STATUS_SUCCESS) {
+ (void) smb_gethostname(machine_passwd,
+ sizeof (machine_passwd), 0);
+ (void) utf8_strlwr(machine_passwd);
+ }
+ }
+
+ if (status == NT_STATUS_SUCCESS) {
+ smb_config_wrlock();
+ if (smb_config_set(SMB_CI_MACHINE_PASSWD,
+ machine_passwd) != 0) {
+ smb_config_unlock();
+ return (NT_STATUS_UNSUCCESSFUL);
+ }
+ smb_config_unlock();
+
+ /*
+ * If we successfully create a trust account, we mark
+ * ourselves as a domain member in the environment so
+ * that we use the SAMLOGON version of the NETLOGON
+ * PDC location protocol.
+ */
+ smb_set_domain_member(1);
+
+ if (netr_open(server, domain, &netr_handle) == 0) {
+ status = netlogon_auth(server, &netr_handle,
+ NETR_FLG_INIT);
+ (void) netr_close(&netr_handle);
+ } else {
+ status = NT_STATUS_OPEN_FAILED;
+ }
+ }
+ } else {
+ status = NT_STATUS_LOGON_FAILURE;
+ }
+
+ return (status);
+}
+
+/*ARGSUSED*/
+void
+nt_group_ht_lock(krwmode_t locktype)
+{
+}
+
+void
+nt_group_ht_unlock(void)
+{
+}
+
+int
+nt_group_num_groups(void)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_group_add(char *gname, char *comment)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_group_modify(char *gname, char *new_gname, char *comment)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_group_delete(char *gname)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+/*ARGSUSED*/
+nt_group_t *
+nt_group_getinfo(char *gname, krwmode_t locktype)
+{
+ return (NULL);
+}
+
+/*ARGSUSED*/
+void
+nt_group_putinfo(nt_group_t *grp)
+{
+}
+
+/*ARGSUSED*/
+int
+nt_group_getpriv(nt_group_t *grp, uint32_t priv_id)
+{
+ return (SE_PRIVILEGE_DISABLED);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_group_setpriv(nt_group_t *grp, uint32_t priv_id, uint32_t new_attr)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+/*ARGSUSED*/
+int
+nt_group_is_member(nt_group_t *grp, nt_sid_t *sid)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_group_add_member(nt_group_t *grp, nt_sid_t *msid, uint16_t sid_name_use,
+ char *account)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_group_del_member(nt_group_t *grp, void *key, int keytype)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+/*ARGSUSED*/
+int
+nt_group_num_members(nt_group_t *grp)
+{
+ return (0);
+}
+
+nt_group_iterator_t *
+nt_group_open_iterator(void)
+{
+ return (NULL);
+}
+
+/*ARGSUSED*/
+void
+nt_group_close_iterator(nt_group_iterator_t *gi)
+{
+}
+
+/*ARGSUSED*/
+nt_group_t *
+nt_group_iterate(nt_group_iterator_t *gi)
+{
+ return (NULL);
+}
+
+int
+nt_group_cache_size(void)
+{
+ return (0);
+}
+
+uint32_t
+sam_init(void)
+{
+ return (NT_STATUS_SUCCESS);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_group_add_member_byname(char *gname, char *account)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_group_del_member_byname(nt_group_t *grp, char *member_name)
+{
+ return (NT_STATUS_NOT_SUPPORTED);
+}
+
+/*ARGSUSED*/
+void
+nt_group_add_groupprivs(nt_group_t *grp, smb_privset_t *priv)
+{
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_groups_member_privs(nt_sid_t *sid, smb_privset_t *priv)
+{
+ return (NT_STATUS_SUCCESS);
+}
+
+/*ARGSUSED*/
+int
+nt_groups_member_ngroups(nt_sid_t *sid)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+uint32_t
+nt_groups_member_groups(nt_sid_t *sid, smb_id_t *grps, int ngrps)
+{
+ return (NT_STATUS_SUCCESS);
+}
+
+/*ARGSUSED*/
+nt_group_t *
+nt_groups_lookup_rid(uint32_t rid)
+{
+ return (NULL);
+}
+
+/*ARGSUSED*/
+int
+nt_groups_count(int cnt_opt)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+nt_group_member_list(int offset, nt_group_t *grp,
+ ntgrp_member_list_t *rmembers)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+void
+nt_group_list(int offset, char *pattern, ntgrp_list_t *list)
+{
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_winreg.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_winreg.c
new file mode 100644
index 0000000000..bd2d5ee26f
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_winreg.c
@@ -0,0 +1,454 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Windows Registry RPC (WINREG) server-side interface.
+ *
+ * The WINREG RPC interface returns Win32 error codes.
+ *
+ * HKLM Hive Key Local Machine
+ * HKU Hive Key Users
+ */
+
+#include <sys/utsname.h>
+#include <strings.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/winreg.ndl>
+
+/*
+ * List of mlsvc handle (local handle management) keys.
+ */
+#define WINREG_HKLM "WinregOpenHKLM"
+#define WINREG_HKU "WinregOpenUser"
+#define WINREG_KEY "WinregOpenKey"
+
+/*
+ * List of supported registry keys (case-insensitive).
+ * "System\\CurrentControlSet\\Services\\Alerter\\Parameters"
+ */
+static char *winreg_keys[] = {
+ "System\\CurrentControlSet\\Control\\ProductOptions",
+ "System\\CurrentControlSet\\Services\\Eventlog\\System"
+};
+
+static char *winreg_lookup_value(const char *);
+
+static int winreg_s_OpenHKLM(void *, struct mlrpc_xaction *);
+static int winreg_s_OpenHKUsers(void *, struct mlrpc_xaction *);
+static int winreg_s_Close(void *, struct mlrpc_xaction *);
+static int winreg_s_CreateKey(void *, struct mlrpc_xaction *);
+static int winreg_s_DeleteKey(void *, struct mlrpc_xaction *);
+static int winreg_s_DeleteValue(void *, struct mlrpc_xaction *);
+static int winreg_s_OpenKey(void *, struct mlrpc_xaction *);
+static int winreg_s_QueryKey(void *, struct mlrpc_xaction *);
+static int winreg_s_QueryValue(void *, struct mlrpc_xaction *);
+static int winreg_s_CreateValue(void *, struct mlrpc_xaction *);
+static int winreg_s_Shutdown(void *, struct mlrpc_xaction *);
+static int winreg_s_GetVersion(void *, struct mlrpc_xaction *);
+
+static mlrpc_stub_table_t winreg_stub_table[] = {
+ { winreg_s_OpenHKLM, WINREG_OPNUM_OpenHKLM },
+ { winreg_s_OpenHKUsers, WINREG_OPNUM_OpenHKUsers },
+ { winreg_s_Close, WINREG_OPNUM_Close },
+ { winreg_s_CreateKey, WINREG_OPNUM_CreateKey },
+ { winreg_s_DeleteKey, WINREG_OPNUM_DeleteKey },
+ { winreg_s_DeleteValue, WINREG_OPNUM_DeleteValue },
+ { winreg_s_OpenKey, WINREG_OPNUM_OpenKey },
+ { winreg_s_QueryKey, WINREG_OPNUM_QueryKey },
+ { winreg_s_QueryValue, WINREG_OPNUM_QueryValue },
+ { winreg_s_CreateValue, WINREG_OPNUM_CreateValue },
+ { winreg_s_Shutdown, WINREG_OPNUM_Shutdown },
+ { winreg_s_GetVersion, WINREG_OPNUM_GetVersion },
+ {0}
+};
+
+static mlrpc_service_t winreg_service = {
+ "Winreg", /* name */
+ "Windows Registry", /* desc */
+ "\\winreg", /* endpoint */
+ PIPE_WINREG, /* sec_addr_port */
+ "338cd001-2244-31f1-aaaa900038001003", 1, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(winreg_interface), /* interface ti */
+ winreg_stub_table /* stub_table */
+};
+
+static char winreg_sysname[SYS_NMLN];
+
+/*
+ * winreg_initialize
+ *
+ * This function registers the WINREG RPC interface with the RPC runtime
+ * library. It must be called in order to use either the client side
+ * or the server side functions.
+ */
+void
+winreg_initialize(void)
+{
+ struct utsname name;
+ char *sysname;
+
+ if (uname(&name) < 0)
+ sysname = "Solaris";
+ else
+ sysname = name.sysname;
+
+ (void) strlcpy(winreg_sysname, sysname, SYS_NMLN);
+ (void) mlrpc_register_service(&winreg_service);
+}
+
+/*
+ * winreg_s_OpenHKLM
+ *
+ * This is a request to open the HKLM and get a handle. The client
+ * should treat the handle as an opaque object.
+ *
+ * Status:
+ * ERROR_SUCCESS Valid handle returned.
+ * ERROR_ACCESS_DENIED Unable to allocate a handle.
+ */
+/*ARGSUSED*/
+static int
+winreg_s_OpenHKLM(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_OpenHKLM *param = arg;
+ ms_handle_t *handle;
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_WINREG, WINREG_HKLM, 0);
+ if (handle == NULL) {
+ bzero(&param->handle, sizeof (msreg_handle_t));
+ param->status = ERROR_ACCESS_DENIED;
+ } else {
+ bcopy(handle, &param->handle, sizeof (msreg_handle_t));
+ param->status = ERROR_SUCCESS;
+ }
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_OpenHKUsers
+ *
+ * This is a request to get a HKUsers handle. I'm not sure we are
+ * ready to fully support this interface yet, mostly due to the need
+ * to support subsequent requests, but we may support enough now. It
+ * seems okay with regedt32.
+ */
+/*ARGSUSED*/
+static int
+winreg_s_OpenHKUsers(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_OpenHKUsers *param = arg;
+ ms_handle_t *handle;
+
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_WINREG, WINREG_HKU, 0);
+ if (handle == NULL) {
+ bzero(&param->handle, sizeof (msreg_handle_t));
+ param->status = ERROR_ACCESS_DENIED;
+ } else {
+ bcopy(handle, &param->handle, sizeof (msreg_handle_t));
+ param->status = ERROR_SUCCESS;
+ }
+
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_Close
+ *
+ * This is a request to close the WINREG interface specified by the
+ * handle. We don't track handles (yet), so just zero out the handle
+ * and return MLRPC_DRC_OK. Setting the handle to zero appears to be
+ * standard behaviour.
+ */
+/*ARGSUSED*/
+static int
+winreg_s_Close(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_Close *param = arg;
+
+ (void) mlsvc_put_handle((ms_handle_t *)&param->handle);
+ bzero(&param->result_handle, sizeof (msreg_handle_t));
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_CreateKey
+ */
+/*ARGSUSED*/
+static int
+winreg_s_CreateKey(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_CreateKey *param = arg;
+
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_DeleteKey
+ */
+/*ARGSUSED*/
+static int
+winreg_s_DeleteKey(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_DeleteKey *param = arg;
+
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_DeleteValue
+ */
+/*ARGSUSED*/
+static int
+winreg_s_DeleteValue(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_DeleteValue *param = arg;
+
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_OpenKey
+ *
+ * This is a request to open a windows registry key. The list
+ * of supported keys is listed in the winreg_keys table. If we
+ * recognize the key, we return a handle.
+ *
+ * Returns:
+ * ERROR_SUCCESS Valid handle returned.
+ * ERROR_FILE_NOT_FOUND No key or unable to allocate a handle.
+ */
+/*ARGSUSED*/
+static int
+winreg_s_OpenKey(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_OpenKey *param = arg;
+ ms_handle_t *handle;
+ char *key = (char *)param->name.str;
+ int i;
+
+ for (i = 0; i < sizeof (winreg_keys)/sizeof (winreg_keys[0]); ++i) {
+ if (strcasecmp(key, winreg_keys[i]) == 0) {
+ handle = mlsvc_get_handle(MLSVC_IFSPEC_WINREG,
+ WINREG_KEY, 0);
+
+ if (handle == NULL)
+ break;
+
+ bcopy(handle, &param->result_handle,
+ sizeof (msreg_handle_t));
+
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+ }
+ }
+
+ bzero(&param->result_handle, sizeof (msreg_handle_t));
+ param->status = ERROR_FILE_NOT_FOUND;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_QueryKey
+ */
+/*ARGSUSED*/
+static int
+winreg_s_QueryKey(void *arg, struct mlrpc_xaction *mxa)
+{
+ static char nullstr[2] = { 0, 0 };
+ struct msreg_QueryKey *param = arg;
+
+ bzero(param, sizeof (struct msreg_QueryKey));
+
+ param->name.length = 2;
+ param->name.allosize = 0;
+ param->name.str = (unsigned char *)nullstr;
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_QueryValue
+ *
+ * This is a request to get the value associated with a specified name.
+ *
+ * Returns:
+ * ERROR_SUCCESS Value returned.
+ * ERROR_FILE_NOT_FOUND PrimaryModule is not supported.
+ * ERROR_CANTREAD No such name or memory problem.
+ */
+static int
+winreg_s_QueryValue(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_QueryValue *param = arg;
+ struct msreg_value *pv;
+ char *name;
+ char *value;
+ DWORD slen;
+ DWORD msize;
+
+ name = (char *)param->value_name.str;
+
+ if (strcasecmp(name, "PrimaryModule") == 0) {
+ param->status = ERROR_FILE_NOT_FOUND;
+ return (MLRPC_DRC_OK);
+ }
+
+ if ((value = winreg_lookup_value(name)) == NULL) {
+ param->status = ERROR_CANTREAD;
+ return (MLRPC_DRC_OK);
+ }
+
+ slen = mts_wcequiv_strlen(value) + sizeof (mts_wchar_t);
+ msize = sizeof (struct msreg_value) + slen;
+
+ param->value = (struct msreg_value *)MLRPC_HEAP_MALLOC(mxa, msize);
+ param->type = MLRPC_HEAP_NEW(mxa, DWORD);
+ param->value_size = MLRPC_HEAP_NEW(mxa, DWORD);
+ param->value_size_total = MLRPC_HEAP_NEW(mxa, DWORD);
+
+ if (param->value == NULL || param->type == NULL ||
+ param->value_size == NULL || param->value_size_total == NULL) {
+ param->status = ERROR_CANTREAD;
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param->value, msize);
+ pv = param->value;
+ pv->vc_first_is = 0;
+ pv->vc_length_is = slen;
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ (void) mts_mbstowcs((mts_wchar_t *)pv->value, value, slen);
+
+ *param->type = 1;
+ *param->value_size = slen;
+ *param->value_size_total = slen;
+
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Lookup a name in the registry and return the associated value.
+ * Our registry is a case-insensitive, name-value pair table.
+ *
+ * Windows ProductType: WinNT, ServerNT, LanmanNT.
+ * Windows NT4.0 workstation: WinNT
+ * Windows NT4.0 server: ServerNT
+ *
+ * If LanmanNT is used here, Windows 2000 sends LsarQueryInfoPolicy
+ * with info level 6, which we don't support. If we use ServerNT
+ * (as reported by NT4.0 Server) Windows 2000 send requests for
+ * levels 3 and 5, which are support.
+ *
+ * On success, returns a pointer to the value. Otherwise returns
+ * a null pointer.
+ */
+static char *
+winreg_lookup_value(const char *name)
+{
+ static struct registry {
+ char *name;
+ char *value;
+ } registry[] = {
+ { "ProductType", "ServerNT" },
+ { "Sources", NULL } /* product name */
+ };
+
+ int i;
+
+ for (i = 0; i < sizeof (registry)/sizeof (registry[0]); ++i) {
+ if (strcasecmp(registry[i].name, name) == 0) {
+ if (registry[i].value == NULL)
+ return (winreg_sysname);
+ else
+ return (registry[i].value);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * winreg_s_CreateValue
+ */
+/*ARGSUSED*/
+static int
+winreg_s_CreateValue(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_CreateValue *param = arg;
+
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_Shutdown
+ *
+ * Attempt to shutdown or reboot the system: access denied.
+ */
+/*ARGSUSED*/
+static int
+winreg_s_Shutdown(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_Shutdown *param = arg;
+
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * winreg_s_GetVersion
+ *
+ * Return the windows registry version. The current version is 5.
+ * This call is usually made prior to enumerating or querying registry
+ * keys or values.
+ */
+/*ARGSUSED*/
+static int
+winreg_s_GetVersion(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct msreg_GetVersion *param = arg;
+
+ param->version = 5;
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_wkssvc.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_wkssvc.c
new file mode 100644
index 0000000000..b7453b171e
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_wkssvc.c
@@ -0,0 +1,142 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <netdb.h>
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/lmerr.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/srvsvc.ndl>
+
+static int wkssvc_s_NetWkstaGetInfo(void *, struct mlrpc_xaction *);
+
+static mlrpc_stub_table_t wkssvc_stub_table[] = {
+ { wkssvc_s_NetWkstaGetInfo, WKSSVC_OPNUM_NetWkstaGetInfo },
+ {0}
+};
+
+static mlrpc_service_t wkssvc_service = {
+ "Workstation", /* name (WKSSVC or WKSTA) */
+ "Workstation services", /* desc */
+ "\\wkssvc", /* endpoint */
+ PIPE_NTSVCS, /* sec_addr_port */
+ "6bffd098-a112-3610-983346c3f87e345a", 1, /* abstract */
+ "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+ &TYPEINFO(wkssvc_interface), /* interface ti */
+ wkssvc_stub_table /* stub_table */
+};
+
+void
+wkssvc_initialize(void)
+{
+ (void) mlrpc_register_service(&wkssvc_service);
+}
+
+/*
+ * WKSSVC NetWkstaGetInfo (
+ * IN LPTSTR servername,
+ * IN DWORD level,
+ * OUT union switch(level) {
+ * case 100: _WKSTA_INFO_100 * p100;
+ * case 101: _WKSTA_INFO_101 * p101;
+ * case 102: _WKSTA_INFO_102 * p102;
+ * } bufptr,
+ * OUT DWORD status
+ * )
+ */
+static int
+wkssvc_s_NetWkstaGetInfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct mslm_NetWkstaGetInfo *param = arg;
+ mslm_NetWkstaGetInfo_rb *rb;
+ char hostname[MAXHOSTNAMELEN];
+ char *resource_domain;
+ char *p;
+ DWORD status;
+ int rc;
+
+ rc = smb_getnetbiosname(hostname, MAXHOSTNAMELEN);
+ rb = MLRPC_HEAP_NEW(mxa, mslm_NetWkstaGetInfo_rb);
+
+ if ((rc != 0) || (rb == NULL)) {
+ bzero(param, sizeof (struct mslm_NetWkstaGetInfo));
+ param->status = ERROR_NOT_ENOUGH_MEMORY;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->result.level = param->level;
+ param->result.bufptr.nullptr = (void *) rb;
+
+ switch (param->level) {
+ case 100:
+ rb->buf100.wki100_platform_id = SV_PLATFORM_ID_NT;
+ rb->buf100.wki100_ver_major = 4;
+ rb->buf100.wki100_ver_minor = 0;
+
+ if ((p = MLRPC_HEAP_STRSAVE(mxa, hostname)) == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ rb->buf100.wki100_computername = (unsigned char *)p;
+
+ smb_config_rdlock();
+ resource_domain = smb_config_getstr(SMB_CI_DOMAIN_NAME);
+
+ if ((p = MLRPC_HEAP_STRSAVE(mxa, resource_domain)) == NULL) {
+ smb_config_unlock();
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ smb_config_unlock();
+ rb->buf100.wki100_langroup = (unsigned char *)p;
+ status = ERROR_SUCCESS;
+ break;
+
+ default:
+ param->result.bufptr.nullptr = 0;
+ status = ERROR_INVALID_LEVEL;
+ break;
+ }
+
+ if (status != ERROR_SUCCESS) {
+ bzero(param, sizeof (struct mslm_NetWkstaGetInfo));
+ param->status = status;
+ }
+
+ return (MLRPC_DRC_OK);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c b/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c
new file mode 100644
index 0000000000..aad7e1bbd0
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c
@@ -0,0 +1,519 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Net DFS server side RPC service.
+ */
+
+#include <sys/types.h>
+#include <strings.h>
+#include <string.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/lmerr.h>
+#include <smbsrv/lmdfs.h>
+#include <smbsrv/nmpipes.h>
+#include <smbsrv/nterror.h>
+#include <smbsrv/mlrpc.h>
+#include <smbsrv/ndl/netdfs.ndl>
+
+typedef struct {
+ char *server;
+ char *share;
+ char *path;
+ char *buf;
+} netdfs_unc_t;
+
+static int netdfs_unc_parse(struct mlrpc_xaction *, const char *,
+ netdfs_unc_t *);
+
+static int netdfs_s_getver(void *, struct mlrpc_xaction *);
+static int netdfs_s_add(void *, struct mlrpc_xaction *);
+static int netdfs_s_remove(void *, struct mlrpc_xaction *);
+static int netdfs_s_setinfo(void *, struct mlrpc_xaction *);
+static int netdfs_s_getinfo(void *, struct mlrpc_xaction *);
+static int netdfs_s_enum(void *, struct mlrpc_xaction *);
+static int netdfs_s_move(void *, struct mlrpc_xaction *);
+static int netdfs_s_rename(void *, struct mlrpc_xaction *);
+static int netdfs_s_addstdroot(void *, struct mlrpc_xaction *);
+static int netdfs_s_remstdroot(void *, struct mlrpc_xaction *);
+static int netdfs_s_enumex(void *, struct mlrpc_xaction *);
+
+static mlrpc_stub_table_t netdfs_stub_table[] = {
+ { netdfs_s_getver, NETDFS_OPNUM_GETVER },
+ { netdfs_s_add, NETDFS_OPNUM_ADD },
+ { netdfs_s_remove, NETDFS_OPNUM_REMOVE },
+ { netdfs_s_setinfo, NETDFS_OPNUM_SETINFO },
+ { netdfs_s_getinfo, NETDFS_OPNUM_GETINFO },
+ { netdfs_s_enum, NETDFS_OPNUM_ENUM },
+ { netdfs_s_rename, NETDFS_OPNUM_RENAME },
+ { netdfs_s_move, NETDFS_OPNUM_MOVE },
+ { netdfs_s_addstdroot, NETDFS_OPNUM_ADDSTDROOT },
+ { netdfs_s_remstdroot, NETDFS_OPNUM_REMSTDROOT },
+ { netdfs_s_enumex, NETDFS_OPNUM_ENUMEX },
+ {0}
+};
+
+static mlrpc_service_t netdfs_service = {
+ "NETDFS", /* name */
+ "DFS", /* desc */
+ "\\dfs", /* endpoint */
+ PIPE_NTSVCS, /* sec_addr_port */
+ NETDFS_ABSTRACT_UUID, NETDFS_ABSTRACT_VERS,
+ NETDFS_TRANSFER_UUID, NETDFS_TRANSFER_VERS,
+
+ 0, /* no bind_instance_size */
+ 0, /* no bind_req() */
+ 0, /* no unbind_and_close() */
+ 0, /* use generic_call_stub() */
+
+ &TYPEINFO(netdfs_interface), /* interface ti */
+ netdfs_stub_table /* stub_table */
+};
+
+/*
+ * Register the NETDFS RPC interface with the RPC runtime library.
+ * The service must be registered in order to use either the client
+ * side or the server side functions.
+ */
+void
+netdfs_initialize(void)
+{
+ (void) mlrpc_register_service(&netdfs_service);
+}
+
+/*
+ * Return the version.
+ *
+ * We have to indicate that we emulate a Windows 2003 Server or the
+ * client will not use the EnumEx RPC and this would limit support
+ * to a single DFS root.
+ */
+/*ARGSUSED*/
+static int
+netdfs_s_getver(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_getver *param = arg;
+
+ param->version = DFS_MANAGER_VERSION_W2K3;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Add a new volume or additional storage for an existing volume at
+ * dfs_path.
+ */
+static int
+netdfs_s_add(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_add *param = arg;
+ netdfs_unc_t unc;
+ DWORD status = ERROR_SUCCESS;
+
+ if (param->dfs_path == NULL || param->server == NULL ||
+ param->share == NULL) {
+ bzero(param, sizeof (struct netdfs_add));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
+ status = ERROR_INVALID_PARAMETER;
+ } else {
+ if (unc.path == NULL)
+ status = ERROR_BAD_PATHNAME;
+
+ if (unc.share == NULL)
+ status = ERROR_INVALID_SHARENAME;
+ }
+
+ if (param->status != ERROR_SUCCESS) {
+ bzero(param, sizeof (struct netdfs_add));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param, sizeof (struct netdfs_add));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * netdfs_s_remove
+ *
+ * Remove a volume or additional storage for volume from the DFS at
+ * dfs_path. When applied to the last storage in a volume, removes
+ * the volume from the DFS.
+ */
+static int
+netdfs_s_remove(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_remove *param = arg;
+ netdfs_unc_t unc;
+ DWORD status = ERROR_SUCCESS;
+
+ if (param->dfs_path == NULL || param->server == NULL ||
+ param->share == NULL) {
+ bzero(param, sizeof (struct netdfs_remove));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
+ status = ERROR_INVALID_PARAMETER;
+ } else {
+ if (unc.path == NULL)
+ status = ERROR_BAD_PATHNAME;
+
+ if (unc.share == NULL)
+ status = ERROR_INVALID_SHARENAME;
+ }
+
+ if (param->status != ERROR_SUCCESS) {
+ bzero(param, sizeof (struct netdfs_remove));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param, sizeof (struct netdfs_remove));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Set information about the volume or storage. If the server and share
+ * are specified, the information set is specific to that server and
+ * share. Otherwise the information is specific to the volume as a whole.
+ *
+ * Valid levels are 100-102.
+ */
+/*ARGSUSED*/
+static int
+netdfs_s_setinfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_setinfo *param = arg;
+ netdfs_unc_t unc;
+ DWORD status = ERROR_SUCCESS;
+
+ if (param->dfs_path == NULL) {
+ bzero(param, sizeof (struct netdfs_setinfo));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
+ status = ERROR_INVALID_PARAMETER;
+ } else {
+ if (unc.share == NULL)
+ status = ERROR_INVALID_SHARENAME;
+ }
+
+ if (param->status != ERROR_SUCCESS) {
+ bzero(param, sizeof (struct netdfs_setinfo));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ switch (param->info.level) {
+ case 100:
+ case 101:
+ case 102:
+ break;
+
+ default:
+ bzero(param, sizeof (struct netdfs_setinfo));
+ param->status = ERROR_INVALID_LEVEL;
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param, sizeof (struct netdfs_setinfo));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Get information about the volume or storage. If the server and share
+ * are specified, the information returned is specific to that server
+ * and share. Otherwise the information is specific to the volume as a
+ * whole.
+ *
+ * Valid levels are 1-4, 100-104.
+ */
+/*ARGSUSED*/
+static int
+netdfs_s_getinfo(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_getinfo *param = arg;
+ netdfs_unc_t unc;
+ DWORD status = ERROR_SUCCESS;
+
+ if (param->dfs_path == NULL) {
+ bzero(param, sizeof (struct netdfs_getinfo));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
+ status = ERROR_INVALID_PARAMETER;
+ } else {
+ if (unc.share == NULL)
+ status = ERROR_INVALID_SHARENAME;
+ }
+
+ if (param->status != ERROR_SUCCESS) {
+ bzero(param, sizeof (struct netdfs_getinfo));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ switch (param->level) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 100:
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ break;
+
+ default:
+ bzero(param, sizeof (struct netdfs_getinfo));
+ param->status = ERROR_INVALID_LEVEL;
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param, sizeof (struct netdfs_getinfo));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Get information about all of the volumes in the DFS. dfs_name is
+ * the "server" part of the UNC name used to refer to this particular
+ * DFS.
+ *
+ * Valid levels are 1-3.
+ */
+/*ARGSUSED*/
+static int
+netdfs_s_enum(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_enum *param = arg;
+
+ switch (param->level) {
+ case 1:
+ case 2:
+ case 3:
+ break;
+
+ default:
+ (void) bzero(param, sizeof (struct netdfs_enum));
+ param->status = ERROR_INVALID_LEVEL;
+ return (MLRPC_DRC_OK);
+ }
+
+ (void) bzero(param, sizeof (struct netdfs_enum));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Move a DFS volume and all subordinate volumes from one place in the
+ * DFS to another place in the DFS.
+ */
+/*ARGSUSED*/
+static int
+netdfs_s_move(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_move *param = arg;
+
+ if (param->dfs_path == NULL || param->new_path == NULL) {
+ bzero(param, sizeof (struct netdfs_move));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param, sizeof (struct netdfs_move));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Rename the current path in a DFS to a new path in the same DFS.
+ */
+/*ARGSUSED*/
+static int
+netdfs_s_rename(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_rename *param = arg;
+
+ if (param->dfs_path == NULL || param->new_path == NULL) {
+ bzero(param, sizeof (struct netdfs_rename));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param, sizeof (struct netdfs_rename));
+ param->status = ERROR_ACCESS_DENIED;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Add a DFS root share.
+ */
+/*ARGSUSED*/
+static int
+netdfs_s_addstdroot(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_addstdroot *param = arg;
+
+ bzero(param, sizeof (struct netdfs_addstdroot));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Remove a DFS root share.
+ */
+/*ARGSUSED*/
+static int
+netdfs_s_remstdroot(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_remstdroot *param = arg;
+
+ bzero(param, sizeof (struct netdfs_remstdroot));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Get information about all of the volumes in the DFS. dfs_path is
+ * the "server" part of the UNC name used to refer to this particular
+ * DFS.
+ *
+ * Valid levels are 1-3, 300.
+ */
+static int
+netdfs_s_enumex(void *arg, struct mlrpc_xaction *mxa)
+{
+ struct netdfs_enumex *param = arg;
+ netdfs_unc_t unc;
+ DWORD status = ERROR_SUCCESS;
+
+ if (param->dfs_path == NULL) {
+ bzero(param, sizeof (struct netdfs_enumex));
+ param->status = ERROR_INVALID_PARAMETER;
+ return (MLRPC_DRC_OK);
+ }
+
+ if (param->resume_handle == NULL)
+ param->resume_handle = MLRPC_HEAP_NEW(mxa, DWORD);
+
+ if (param->resume_handle)
+ *(param->resume_handle) = 0;
+
+ if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
+ status = ERROR_INVALID_PARAMETER;
+ } else {
+ if (unc.path == NULL)
+ status = ERROR_BAD_PATHNAME;
+
+ if (unc.share == NULL)
+ status = ERROR_INVALID_SHARENAME;
+ }
+
+ if (param->status != ERROR_SUCCESS) {
+ bzero(param, sizeof (struct netdfs_enumex));
+ param->status = status;
+ return (MLRPC_DRC_OK);
+ }
+
+ param->info = MLRPC_HEAP_NEW(mxa, struct netdfs_enum_info);
+ if (param->info == NULL) {
+ bzero(param, sizeof (struct netdfs_enumex));
+ param->status = ERROR_NOT_ENOUGH_MEMORY;
+ return (MLRPC_DRC_OK);
+ }
+
+ bzero(param->info, sizeof (struct netdfs_enumex));
+ param->status = ERROR_SUCCESS;
+ return (MLRPC_DRC_OK);
+}
+
+/*
+ * Parse a UNC path (\\server\share\path) into components.
+ * Path separators are converted to forward slashes.
+ *
+ * Returns 0 on success, otherwise -1 to indicate an error.
+ */
+static int
+netdfs_unc_parse(struct mlrpc_xaction *mxa, const char *path, netdfs_unc_t *unc)
+{
+ char *p;
+
+ if (path == NULL || unc == NULL)
+ return (-1);
+
+ if ((unc->buf = MLRPC_HEAP_STRSAVE(mxa, (char *)path)) == NULL)
+ return (-1);
+
+ if ((p = strchr(unc->buf, '\n')) != NULL)
+ *p = '\0';
+
+ (void) strsubst(unc->buf, '\\', '/');
+ (void) strcanon(unc->buf, "/");
+
+ unc->server = unc->buf;
+ unc->server += strspn(unc->buf, "/");
+
+ if (unc->server) {
+ unc->share = strchr(unc->server, '/');
+ if ((p = unc->share) != NULL) {
+ unc->share += strspn(unc->share, "/");
+ *p = '\0';
+ }
+ }
+
+ if (unc->share) {
+ unc->path = strchr(unc->share, '/');
+ if ((p = unc->path) != NULL) {
+ unc->path += strspn(unc->path, "/");
+ *p = '\0';
+ }
+ }
+
+ if (unc->path) {
+ if ((p = strchr(unc->path, '\0')) != NULL) {
+ if (*(--p) == '/')
+ *p = '\0';
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
new file mode 100644
index 0000000000..2eb3e78bb7
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
@@ -0,0 +1,502 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * NETR challenge/response client functions.
+ *
+ * NT_STATUS_INVALID_PARAMETER
+ * NT_STATUS_NO_TRUST_SAM_ACCOUNT
+ * NT_STATUS_ACCESS_DENIED
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/ndl/netlogon.ndl>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/mlsvc.h>
+#include <smbsrv/netrauth.h>
+
+int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *,
+ struct netr_authenticator *);
+DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *);
+
+static int netr_server_req_challenge(mlsvc_handle_t *, netr_info_t *);
+static int netr_server_authenticate2(mlsvc_handle_t *, netr_info_t *);
+static int netr_gen_password(BYTE *, BYTE *, BYTE *);
+
+/*
+ * Shared with netr_logon.c
+ */
+netr_info_t netr_global_info;
+
+/*
+ * netlogon_auth
+ *
+ * This is the core of the NETLOGON authentication protocol.
+ * Do the challenge response authentication.
+ *
+ * Prior to calling this function, an anonymous session to the NETLOGON
+ * pipe on a domain controller(server) should have already been opened.
+ */
+DWORD
+netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags)
+{
+ netr_info_t *netr_info;
+ int rc;
+ DWORD random_challenge[2];
+
+ netr_info = &netr_global_info;
+ bzero(netr_info, sizeof (netr_info_t));
+
+ netr_info->flags |= flags;
+
+ rc = smb_getnetbiosname(netr_info->hostname, MLSVC_DOMAIN_NAME_MAX);
+ if (rc != 0)
+ return (NT_STATUS_UNSUCCESSFUL);
+
+ (void) snprintf(netr_info->server, sizeof (netr_info->server),
+ "\\\\%s", server);
+
+ random_challenge[0] = random();
+ random_challenge[1] = random();
+
+ (void) memcpy(&netr_info->client_challenge, random_challenge,
+ sizeof (struct netr_credential));
+
+ if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) {
+ rc = netr_server_authenticate2(netr_handle, netr_info);
+ if (rc == 0)
+ netr_info->flags |= NETR_FLG_VALID;
+ }
+
+ return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS);
+}
+
+/*
+ * 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.
+ */
+int
+netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle)
+{
+ int fid;
+ int remote_os = 0;
+ int remote_lm = 0;
+ int server_pdc;
+ char *username;
+
+ if (mlsvc_anonymous_logon(server, domain, &username) != 0)
+ return (-1);
+
+ fid = mlsvc_open_pipe(server, domain, username, "\\NETLOGON");
+ if (fid < 0)
+ return (-1);
+
+ if (mlsvc_rpc_bind(netr_handle, fid, "NETR") < 0) {
+ (void) mlsvc_close_pipe(fid);
+ return (-1);
+ }
+
+ (void) mlsvc_session_native_values(fid, &remote_os, &remote_lm,
+ &server_pdc);
+ netr_handle->context->server_os = remote_os;
+ netr_handle->context->server_pdc = server_pdc;
+ return (0);
+}
+
+/*
+ * netr_close
+ *
+ * Close a NETLOGON pipe and free the RPC context.
+ */
+int
+netr_close(mlsvc_handle_t *netr_handle)
+{
+ (void) mlsvc_close_pipe(netr_handle->context->fid);
+ free(netr_handle->context);
+ return (0);
+}
+
+/*
+ * netr_server_req_challenge
+ */
+static int
+netr_server_req_challenge(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
+{
+ struct netr_ServerReqChallenge arg;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+
+ bzero(&arg, sizeof (struct netr_ServerReqChallenge));
+ opnum = NETR_OPNUM_ServerReqChallenge;
+
+ arg.servername = (unsigned char *)netr_info->server;
+ arg.hostname = (unsigned char *)netr_info->hostname;
+
+ (void) memcpy(&arg.client_challenge, &netr_info->client_challenge,
+ sizeof (struct netr_credential));
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ rc = -1;
+ } else {
+ (void) memcpy(&netr_info->server_challenge,
+ &arg.server_challenge,
+ sizeof (struct netr_credential));
+ }
+ }
+
+ mlsvc_rpc_free(netr_handle->context, &heap);
+ return (rc);
+}
+
+/*
+ * netr_server_authenticate2
+ */
+static int
+netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
+{
+ struct netr_ServerAuthenticate2 arg;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ char account_name[MLSVC_DOMAIN_NAME_MAX * 2];
+
+ bzero(&arg, sizeof (struct netr_ServerAuthenticate2));
+ opnum = NETR_OPNUM_ServerAuthenticate2;
+
+ (void) snprintf(account_name, sizeof (account_name), "%s$",
+ netr_info->hostname);
+
+ arg.servername = (unsigned char *)netr_info->server;
+ arg.account_name = (unsigned char *)account_name;
+ arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE;
+ arg.hostname = (unsigned char *)netr_info->hostname;
+ arg.negotiate_flags = NETR_NEGOTIATE_FLAGS;
+
+ smb_tracef("server=[%s] account_name=[%s] hostname=[%s]\n",
+ netr_info->server, account_name, netr_info->hostname);
+
+ if (netr_gen_session_key(netr_info) != SMBAUTH_SUCCESS)
+ return (-1);
+
+ if (netr_gen_credentials(netr_info->session_key,
+ &netr_info->client_challenge,
+ 0,
+ &netr_info->client_credential) != SMBAUTH_SUCCESS) {
+ return (-1);
+ }
+
+ if (netr_gen_credentials(netr_info->session_key,
+ &netr_info->server_challenge,
+ 0,
+ &netr_info->server_credential) != SMBAUTH_SUCCESS) {
+ return (-1);
+ }
+
+ (void) memcpy(&arg.client_credential, &netr_info->client_credential,
+ sizeof (struct netr_credential));
+
+ (void) mlsvc_rpc_init(&heap);
+
+ rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ rc = -1;
+ } else {
+ rc = memcmp(&netr_info->server_credential,
+ &arg.server_credential,
+ sizeof (struct netr_credential));
+ }
+ }
+
+ mlsvc_rpc_free(netr_handle->context, &heap);
+ return (rc);
+}
+
+/*
+ * netr_gen_session_key
+ *
+ * Generate a session key from the client and server challenges. The
+ * algorithm is a two stage hash. For the first hash, the input is
+ * the combination of the client and server challenges, the key is
+ * the first 8 bytes of the password. The initial password is formed
+ * using the NT password hash on the local hostname in lower case.
+ * The result is stored in a temporary buffer.
+ *
+ * input: challenge
+ * key: passwd lower 8 bytes
+ * output: intermediate result
+ *
+ * For the second hash, the input is the result of the first hash and
+ * the key is the last 8 bytes of the password.
+ *
+ * input: result of first hash
+ * key: passwd upper 8 bytes
+ * output: session_key
+ *
+ * The final output should be the session key.
+ *
+ * FYI: smb_auth_DES(output, key, input)
+ *
+ * If any difficulties occur using the cryptographic framework, the
+ * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is
+ * returned.
+ */
+int
+netr_gen_session_key(netr_info_t *netr_info)
+{
+ unsigned char md4hash[32];
+ unsigned char buffer[8];
+ DWORD data[2];
+ DWORD *client_challenge;
+ DWORD *server_challenge;
+ int rc;
+ char *machine_passwd;
+ DWORD new_data[2];
+
+ client_challenge = (DWORD *)(uintptr_t)&netr_info->client_challenge;
+ server_challenge = (DWORD *)(uintptr_t)&netr_info->server_challenge;
+ bzero(md4hash, 32);
+
+ /*
+ * We should check (netr_info->flags & NETR_FLG_INIT) and use
+ * the appropriate password but it isn't working yet. So we
+ * always use the default one for now.
+ */
+ smb_config_rdlock();
+ machine_passwd = smb_config_getstr(SMB_CI_MACHINE_PASSWD);
+
+ if (!machine_passwd || *machine_passwd == 0) {
+ smb_config_unlock();
+ return (-1);
+ }
+
+ bzero(netr_info->password, sizeof (netr_info->password));
+ (void) strlcpy((char *)netr_info->password, (char *)machine_passwd,
+ sizeof (netr_info->password));
+
+ rc = smb_auth_ntlm_hash((char *)machine_passwd, md4hash);
+ smb_config_unlock();
+
+ if (rc != SMBAUTH_SUCCESS)
+ return (SMBAUTH_FAILURE);
+
+ data[0] = LE_IN32(&client_challenge[0]) + LE_IN32(&server_challenge[0]);
+ data[1] = LE_IN32(&client_challenge[1]) + LE_IN32(&server_challenge[1]);
+ LE_OUT32(&new_data[0], data[0]);
+ LE_OUT32(&new_data[1], data[1]);
+
+ rc = smb_auth_DES(buffer, 8, md4hash, 8, (unsigned char *)new_data, 8);
+ if (rc != SMBAUTH_SUCCESS)
+ return (rc);
+
+ rc = smb_auth_DES(netr_info->session_key, 8, &md4hash[9], 8, buffer, 8);
+ return (rc);
+}
+
+/*
+ * netr_gen_credentials
+ *
+ * Generate a set of credentials from a challenge and a session key.
+ * The algorithm is a two stage hash. For the first hash, the
+ * timestamp is added to the challenge and the result is stored in a
+ * temporary buffer:
+ *
+ * input: challenge (including timestamp)
+ * key: session_key
+ * output: intermediate result
+ *
+ * For the second hash, the input is the result of the first hash and
+ * a strange partial key is used:
+ *
+ * input: result of first hash
+ * key: funny partial key
+ * output: credentiails
+ *
+ * The final output should be an encrypted set of credentials.
+ *
+ * FYI: smb_auth_DES(output, key, input)
+ *
+ * If any difficulties occur using the cryptographic framework, the
+ * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is
+ * returned.
+ */
+int
+netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge,
+ DWORD timestamp, netr_cred_t *out_cred)
+{
+ unsigned char buffer[8];
+ unsigned char partial_key[8];
+ DWORD data[2];
+ DWORD *p;
+ int rc;
+
+ p = (DWORD *)(uintptr_t)challenge;
+ data[0] = p[0] + LE_IN32(&timestamp);
+ data[1] = p[1];
+
+ if (smb_auth_DES(buffer, 8, session_key, 8,
+ (unsigned char *)data, 8) != SMBAUTH_SUCCESS)
+ return (SMBAUTH_FAILURE);
+
+ bzero(partial_key, 8);
+ partial_key[0] = session_key[7];
+
+ rc = smb_auth_DES((unsigned char *)out_cred, 8, partial_key, 8,
+ buffer, 8);
+ return (rc);
+}
+
+/*
+ * netr_server_password_set
+ *
+ * Attempt to change the trust account password for this system.
+ *
+ * Note that this call may legitimately fail if the registry on the
+ * domain controller has been setup to deny attempts to change the
+ * trust account password. In this case we should just continue to
+ * use the original password.
+ *
+ * Possible status values:
+ * NT_STATUS_ACCESS_DENIED
+ */
+int
+netr_server_password_set(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
+{
+ struct netr_PasswordSet arg;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ BYTE new_password[NETR_OWF_PASSWORD_SZ];
+ char account_name[MLSVC_DOMAIN_NAME_MAX * 2];
+
+ bzero(&arg, sizeof (struct netr_PasswordSet));
+ opnum = NETR_OPNUM_ServerPasswordSet;
+
+ (void) snprintf(account_name, sizeof (account_name), "%s$",
+ netr_info->hostname);
+
+ arg.servername = (unsigned char *)netr_info->server;
+ arg.account_name = (unsigned char *)account_name;
+ arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE;
+ arg.hostname = (unsigned char *)netr_info->hostname;
+
+ /*
+ * Set up the client side authenticator.
+ */
+ if (netr_setup_authenticator(netr_info, &arg.auth, 0) !=
+ SMBAUTH_SUCCESS) {
+ return (-1);
+ }
+
+ /*
+ * Generate a new password from the old password.
+ */
+ if (netr_gen_password(netr_info->session_key,
+ netr_info->password, new_password) == SMBAUTH_FAILURE) {
+ return (-1);
+ }
+
+ (void) memcpy(&arg.uas_new_password, &new_password,
+ NETR_OWF_PASSWORD_SZ);
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
+ if ((rc != 0) || (arg.status != 0)) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ mlsvc_rpc_free(netr_handle->context, &heap);
+ return (-1);
+ }
+
+ /*
+ * Check the returned credentials. The server returns the new
+ * client credential rather than the new server credentiali,
+ * as documented elsewhere.
+ *
+ * Generate the new seed for the credential chain. Increment
+ * the timestamp and add it to the client challenge. Then we
+ * need to copy the challenge to the credential field in
+ * preparation for the next cycle.
+ */
+ if (netr_validate_chain(netr_info, &arg.auth) == 0) {
+ /*
+ * Save the new password.
+ */
+ (void) memcpy(netr_info->password, new_password,
+ NETR_OWF_PASSWORD_SZ);
+ }
+
+ mlsvc_rpc_free(netr_handle->context, &heap);
+ return (0);
+}
+
+/*
+ * netr_gen_password
+ *
+ * Generate a new pasword from the old password and the session key.
+ * The algorithm is a two stage hash. The session key is used in the
+ * first hash but only part of the session key is used in the second
+ * hash.
+ *
+ * If any difficulties occur using the cryptographic framework, the
+ * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is
+ * returned.
+ */
+static int
+netr_gen_password(BYTE *session_key, BYTE *old_password, BYTE *new_password)
+{
+ unsigned char partial_key[8];
+ int rv;
+
+ rv = smb_auth_DES(new_password, 8, session_key, 8, old_password, 8);
+ if (rv != SMBAUTH_SUCCESS)
+ return (rv);
+
+ bzero(partial_key, 8);
+ partial_key[0] = session_key[7];
+
+ rv = smb_auth_DES(&new_password[8], 8, partial_key, 8,
+ &old_password[8], 8);
+ return (rv);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c
new file mode 100644
index 0000000000..d26d6f0dc7
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c
@@ -0,0 +1,584 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * NETR SamLogon and SamLogoff RPC client functions.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <time.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ndl/netlogon.ndl>
+#include <smbsrv/mlsvc_util.h>
+#include <smbsrv/mlsvc.h>
+#include <smbsrv/netrauth.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/mlrpc.h>
+#include <smbsrv/smb_token.h>
+
+extern int netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle);
+extern int netr_close(mlsvc_handle_t *netr_handle);
+extern DWORD netlogon_auth(char *server, mlsvc_handle_t *netr_handle,
+ DWORD flags);
+extern int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *,
+ struct netr_authenticator *);
+extern DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *);
+
+static DWORD netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
+ netr_client_t *, smb_userinfo_t *);
+static void netr_invalidate_chain(void);
+static void netr_interactive_samlogon(netr_info_t *, netr_client_t *,
+ struct netr_logon_info1 *);
+static void netr_network_samlogon(netr_info_t *, netr_client_t *,
+ netr_response_t *, netr_response_t *, struct netr_logon_info2 *);
+static void netr_setup_identity(mlrpc_heap_t *, netr_client_t *,
+ netr_logon_id_t *);
+
+/*
+ * Shared with netr_auth.c
+ */
+extern netr_info_t netr_global_info;
+
+/*
+ * netlogon_logon
+ *
+ * This is the entry point for authenticating a remote logon. The
+ * parameters here all refer to the remote user and workstation, i.e.
+ * the domain is the user's account domain, not our primary domain.
+ * In order to make it easy to track which domain is being used at
+ * each stage, and to reduce the number of things being pushed on the
+ * stack, the client information is bundled up in the clnt structure.
+ *
+ * If the user is successfully authenticated, an access token will be
+ * built and NT_STATUS_SUCCESS will be returned. Otherwise a non-zero
+ * NT status will be returned, in which case the token contents will
+ * be invalid.
+ */
+DWORD
+netlogon_logon(netr_client_t *clnt, smb_userinfo_t *user_info)
+{
+ char resource_domain[SMB_PI_MAX_DOMAIN];
+ mlsvc_handle_t netr_handle;
+ smb_ntdomain_t *di;
+ DWORD status;
+ int retries = 0;
+
+ smb_config_rdlock();
+ (void) strlcpy(resource_domain, smb_config_getstr(SMB_CI_DOMAIN_NAME),
+ sizeof (resource_domain));
+ smb_config_unlock();
+
+ /*
+ * If the SMB info cache is not valid,
+ * try to locate a domain controller.
+ */
+ if ((di = smb_getdomaininfo(0)) == NULL) {
+ (void) mlsvc_locate_domain_controller(resource_domain);
+
+ if ((di = smb_getdomaininfo(0)) == NULL)
+ return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ }
+
+ if ((mlsvc_echo(di->server)) < 0) {
+ /*
+ * We had a session to the DC but it's not responding.
+ * So drop the credential chain and find another DC.
+ */
+ netr_invalidate_chain();
+ (void) mlsvc_locate_domain_controller(resource_domain);
+
+ if ((di = smb_getdomaininfo(0)) == NULL)
+ return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ }
+
+ do {
+ status = netr_open(di->server, di->domain, &netr_handle);
+ if (status != 0)
+ return (status);
+
+ if ((netr_global_info.flags & NETR_FLG_VALID) == 0) {
+ status = netlogon_auth(di->server, &netr_handle,
+ NETR_FLG_NULL);
+
+ if (status != 0) {
+ (void) netr_close(&netr_handle);
+ return (NT_STATUS_LOGON_FAILURE);
+ }
+
+ netr_global_info.flags |= NETR_FLG_VALID;
+ }
+
+ status = netr_server_samlogon(&netr_handle,
+ &netr_global_info, di->server, clnt, user_info);
+
+ (void) netr_close(&netr_handle);
+ } while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3);
+
+ if (retries >= 3)
+ status = NT_STATUS_LOGON_FAILURE;
+
+ return (status);
+}
+
+static DWORD
+netr_setup_userinfo(struct netr_validation_info3 *info3,
+ smb_userinfo_t *user_info, netr_client_t *clnt)
+{
+ smb_sid_attrs_t *other_grps;
+ char *username, *domain;
+ int i, nbytes;
+
+ user_info->sid_name_use = SidTypeUser;
+ user_info->rid = info3->UserId;
+ user_info->primary_group_rid = info3->PrimaryGroupId;
+ user_info->domain_sid = nt_sid_dup((nt_sid_t *)info3->LogonDomainId);
+
+ if (user_info->domain_sid == NULL)
+ return (NT_STATUS_NO_MEMORY);
+
+ user_info->user_sid = nt_sid_splice(user_info->domain_sid,
+ user_info->rid);
+ if (user_info->user_sid == NULL)
+ return (NT_STATUS_NO_MEMORY);
+
+ user_info->pgrp_sid = nt_sid_splice(user_info->domain_sid,
+ user_info->primary_group_rid);
+ if (user_info->pgrp_sid == NULL)
+ return (NT_STATUS_NO_MEMORY);
+
+ username = (info3->EffectiveName.str)
+ ? (char *)info3->EffectiveName.str : clnt->username;
+ domain = (info3->LogonDomainName.str)
+ ? (char *)info3->LogonDomainName.str : clnt->domain;
+
+ if (username)
+ user_info->name = strdup(username);
+ if (domain)
+ user_info->domain_name = strdup(domain);
+
+ if (user_info->name == NULL || user_info->domain_name == NULL)
+ return (NT_STATUS_NO_MEMORY);
+
+ nbytes = info3->GroupCount * sizeof (smb_rid_attrs_t);
+ if (nbytes) {
+ if ((user_info->groups = malloc(nbytes)) != NULL) {
+ user_info->n_groups = info3->GroupCount;
+ (void) memcpy(user_info->groups,
+ info3->GroupIds, nbytes);
+ } else {
+ return (NT_STATUS_NO_MEMORY);
+ }
+ }
+
+ nbytes = info3->SidCount * sizeof (smb_sid_attrs_t);
+ if (nbytes) {
+ if ((other_grps = malloc(nbytes)) != NULL) {
+ user_info->other_grps = other_grps;
+ for (i = 0; i < info3->SidCount; i++) {
+ other_grps[i].attrs =
+ info3->ExtraSids[i].attributes;
+
+ other_grps[i].sid = nt_sid_dup(
+ (nt_sid_t *)info3->ExtraSids[i].sid);
+
+ if (other_grps[i].sid == NULL)
+ break;
+ }
+ user_info->n_other_grps = i;
+ } else {
+ return (NT_STATUS_NO_MEMORY);
+ }
+ }
+
+ mlsvc_setadmin_user_info(user_info);
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * netr_server_samlogon
+ *
+ * NetrServerSamLogon RPC: interactive or network. It is assumed that
+ * we have already authenticated with the PDC. If everything works,
+ * we build a user info structure and return it, where the caller will
+ * probably build an access token.
+ *
+ * Returns an NT status. There are numerous possibilities here.
+ * For example:
+ * NT_STATUS_INVALID_INFO_CLASS
+ * NT_STATUS_INVALID_PARAMETER
+ * NT_STATUS_ACCESS_DENIED
+ * NT_STATUS_PASSWORD_MUST_CHANGE
+ * NT_STATUS_NO_SUCH_USER
+ * NT_STATUS_WRONG_PASSWORD
+ * NT_STATUS_LOGON_FAILURE
+ * NT_STATUS_ACCOUNT_RESTRICTION
+ * NT_STATUS_INVALID_LOGON_HOURS
+ * NT_STATUS_INVALID_WORKSTATION
+ * NT_STATUS_INTERNAL_ERROR
+ * NT_STATUS_PASSWORD_EXPIRED
+ * NT_STATUS_ACCOUNT_DISABLED
+ */
+DWORD
+netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
+ char *server, netr_client_t *clnt, smb_userinfo_t *user_info)
+{
+ struct netr_SamLogon arg;
+ struct netr_authenticator auth;
+ struct netr_authenticator ret_auth;
+ struct netr_logon_info1 info1;
+ struct netr_logon_info2 info2;
+ struct netr_validation_info3 *info3;
+ netr_response_t nt_rsp;
+ netr_response_t lm_rsp;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc, len;
+ DWORD status;
+
+ bzero(&arg, sizeof (struct netr_SamLogon));
+ opnum = NETR_OPNUM_SamLogon;
+ (void) mlsvc_rpc_init(&heap);
+
+ /*
+ * Should we get the server and hostname from netr_info?
+ */
+ len = strlen(server) + 4;
+ arg.servername = alloca(len);
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+
+ arg.hostname = alloca(MLSVC_DOMAIN_NAME_MAX);
+ rc = smb_gethostname((char *)arg.hostname, MLSVC_DOMAIN_NAME_MAX, 0);
+ if (rc != 0) {
+ mlrpc_heap_destroy(heap.heap);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ rc = netr_setup_authenticator(netr_info, &auth, &ret_auth);
+ if (rc != SMBAUTH_SUCCESS) {
+ mlrpc_heap_destroy(heap.heap);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ arg.auth = &auth;
+ arg.ret_auth = &ret_auth;
+ arg.validation_level = NETR_VALIDATION_LEVEL3;
+ arg.logon_info.logon_level = clnt->logon_level;
+ arg.logon_info.switch_value = clnt->logon_level;
+
+ switch (clnt->logon_level) {
+ case NETR_INTERACTIVE_LOGON:
+ netr_setup_identity(heap.heap, clnt, &info1.identity);
+ netr_interactive_samlogon(netr_info, clnt, &info1);
+ arg.logon_info.ru.info1 = &info1;
+ break;
+
+ case NETR_NETWORK_LOGON:
+ netr_setup_identity(heap.heap, clnt, &info2.identity);
+ netr_network_samlogon(netr_info, clnt, &nt_rsp, &lm_rsp,
+ &info2);
+ arg.logon_info.ru.info2 = &info2;
+ break;
+
+ default:
+ mlrpc_heap_destroy(heap.heap);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
+ if (rc != 0) {
+ bzero(netr_info, sizeof (netr_info_t));
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ status = NT_SC_VALUE(arg.status);
+
+ /*
+ * We need to validate the chain even though we have
+ * a non-zero status. If the status is ACCESS_DENIED
+ * this will trigger a new credential chain. However,
+ * a valid credential is returned with some status
+ * codes; for example, WRONG_PASSWORD.
+ */
+ (void) netr_validate_chain(netr_info, arg.ret_auth);
+ } else {
+ status = netr_validate_chain(netr_info, arg.ret_auth);
+ if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
+ mlsvc_rpc_free(netr_handle->context, &heap);
+ return (status);
+ }
+
+ info3 = arg.ru.info3;
+ status = netr_setup_userinfo(info3, user_info, clnt);
+ }
+
+ mlsvc_rpc_free(netr_handle->context, &heap);
+ return (status);
+}
+
+/*
+ * netr_interactive_samlogon
+ *
+ * Set things up for an interactive SamLogon. Copy the NT and LM
+ * passwords to the logon structure and hash them with the session
+ * key.
+ */
+static void
+netr_interactive_samlogon(netr_info_t *netr_info, netr_client_t *clnt,
+ struct netr_logon_info1 *info1)
+{
+ BYTE key[NETR_OWF_PASSWORD_SZ];
+
+ (void) memcpy(&info1->lm_owf_password,
+ clnt->lm_password.lm_password_val, sizeof (netr_owf_password_t));
+
+ (void) memcpy(&info1->nt_owf_password,
+ clnt->nt_password.nt_password_val, sizeof (netr_owf_password_t));
+
+ (void) memset(key, 0, NETR_OWF_PASSWORD_SZ);
+ (void) memcpy(key, netr_info->session_key, NETR_SESSION_KEY_SZ);
+
+ rand_hash((unsigned char *)&info1->lm_owf_password,
+ NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
+
+ rand_hash((unsigned char *)&info1->nt_owf_password,
+ NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
+}
+
+/*
+ * netr_network_samlogon
+ *
+ * Set things up for a network SamLogon. We provide a copy of the random
+ * challenge, that we sent to the client, to the domain controller. This
+ * is the key that the client will have used to encrypt the NT and LM
+ * passwords. Note that Windows 9x clients may not provide both passwords.
+ */
+/*ARGSUSED*/
+static void
+netr_network_samlogon(netr_info_t *netr_info, netr_client_t *clnt,
+ netr_response_t *ntr, netr_response_t *lmr, struct netr_logon_info2 *info2)
+{
+ bcopy(clnt->challenge_key.challenge_key_val, info2->lm_challenge.data,
+ 8);
+
+ if (clnt->nt_password.nt_password_len == NETR_CR_PASSWORD_SIZE) {
+ ntr->length = NETR_CR_PASSWORD_SIZE;
+ ntr->start = 0;
+ ntr->max_length = NETR_CR_PASSWORD_SIZE;
+ bcopy(clnt->nt_password.nt_password_val, ntr->data,
+ NETR_CR_PASSWORD_SIZE);
+
+ info2->nt_response.length = NETR_CR_PASSWORD_SIZE;
+ info2->nt_response.max_length = NETR_CR_PASSWORD_SIZE;
+ info2->nt_response.data = ntr;
+ } else {
+ info2->nt_response.length = 0;
+ info2->nt_response.max_length = 0;
+ info2->nt_response.data = 0;
+ }
+
+ if (clnt->lm_password.lm_password_len == NETR_CR_PASSWORD_SIZE) {
+ lmr->length = NETR_CR_PASSWORD_SIZE;
+ lmr->start = 0;
+ lmr->max_length = NETR_CR_PASSWORD_SIZE;
+ bcopy(clnt->lm_password.lm_password_val, lmr->data,
+ NETR_CR_PASSWORD_SIZE);
+
+ info2->lm_response.length = NETR_CR_PASSWORD_SIZE;
+ info2->lm_response.max_length = NETR_CR_PASSWORD_SIZE;
+ info2->lm_response.data = lmr;
+ } else {
+ info2->lm_response.length = 0;
+ info2->lm_response.max_length = 0;
+ info2->lm_response.data = 0;
+ }
+}
+
+/*
+ * netr_setup_authenticator
+ *
+ * Set up the request and return authenticators. A new credential is
+ * generated from the session key, the current client credential and
+ * the current time, i.e.
+ *
+ * NewCredential = Cred(SessionKey, OldCredential, time);
+ *
+ * The timestamp, which is used as a random seed, is stored in both
+ * the request and return authenticators.
+ *
+ * If any difficulties occur using the cryptographic framework, the
+ * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is
+ * returned.
+ */
+int
+netr_setup_authenticator(netr_info_t *netr_info,
+ struct netr_authenticator *auth, struct netr_authenticator *ret_auth)
+{
+ bzero(auth, sizeof (struct netr_authenticator));
+
+#ifdef _BIG_ENDIAN
+ netr_info->timestamp = 0;
+#else
+ netr_info->timestamp = time(0) << 8;
+#endif
+ auth->timestamp = netr_info->timestamp;
+
+ if (netr_gen_credentials(netr_info->session_key,
+ &netr_info->client_credential,
+ netr_info->timestamp,
+ (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS)
+ return (SMBAUTH_FAILURE);
+
+ if (ret_auth) {
+ bzero(ret_auth, sizeof (struct netr_authenticator));
+ ret_auth->timestamp = netr_info->timestamp;
+ }
+
+ return (SMBAUTH_SUCCESS);
+}
+
+/*
+ * Validate the returned credentials and update the credential chain.
+ * The server returns an updated client credential rather than a new
+ * server credential. The server uses (timestamp + 1) when generating
+ * the credential.
+ *
+ * Generate the new seed for the credential chain. The new seed is
+ * formed by adding (timestamp + 1) to the current client credential.
+ * The only quirk is the DWORD style addition.
+ *
+ * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a
+ * NULL pointer. The Authenticator field of the SamLogon response packet
+ * sent by the Samba 3 PDC always return NULL pointer if the received
+ * SamLogon request is not immediately followed by the ServerReqChallenge
+ * and ServerAuthenticate2 requests.
+ *
+ * Returns NT_STATUS_SUCCESS if the server returned a valid credential.
+ * Otherwise we retirm NT_STATUS_UNSUCCESSFUL.
+ */
+DWORD
+netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth)
+{
+ netr_cred_t cred;
+ DWORD result = NT_STATUS_SUCCESS;
+ DWORD *dwp;
+
+ ++netr_info->timestamp;
+
+ if (netr_gen_credentials(netr_info->session_key,
+ &netr_info->client_credential,
+ netr_info->timestamp, &cred) != SMBAUTH_SUCCESS)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ if (&auth->credential == 0) {
+ /*
+ * If the validation fails, destroy the credential chain.
+ * This should trigger a new authentication chain.
+ */
+ bzero(netr_info, sizeof (netr_info_t));
+ return (NT_STATUS_INSUFFICIENT_LOGON_INFO);
+ }
+
+ result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t));
+ if (result != 0) {
+ /*
+ * If the validation fails, destroy the credential chain.
+ * This should trigger a new authentication chain.
+ */
+ bzero(netr_info, sizeof (netr_info_t));
+ result = NT_STATUS_UNSUCCESSFUL;
+ } else {
+ /*
+ * Otherwise generate the next step in the chain.
+ */
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ dwp = (DWORD *)&netr_info->client_credential;
+ dwp[0] += netr_info->timestamp;
+
+ netr_info->flags |= NETR_FLG_VALID;
+ }
+
+ return (result);
+}
+
+/*
+ * netr_invalidate_chain
+ *
+ * Mark the credential chain as invalid so that it will be recreated
+ * on the next attempt.
+ */
+static void
+netr_invalidate_chain(void)
+{
+ netr_global_info.flags &= ~NETR_FLG_VALID;
+}
+
+/*
+ * netr_setup_identity
+ *
+ * Set up the client identity information. All of this information is
+ * specifically related to the client user and workstation attempting
+ * to access this system. It may not be in our primary domain.
+ *
+ * I don't know what logon_id is, it seems to be a unique identifier.
+ * Increment it before each use.
+ */
+static void
+netr_setup_identity(mlrpc_heap_t *heap, netr_client_t *clnt,
+ netr_logon_id_t *identity)
+{
+ static DWORD logon_id;
+
+ if (logon_id == 0)
+ logon_id = 0xDCD0;
+
+ ++logon_id;
+ clnt->logon_id = logon_id;
+
+ identity->parameter_control = 0;
+ identity->logon_id.LowPart = logon_id;
+ identity->logon_id.HighPart = 0;
+
+ mlrpc_heap_mkvcs(heap, clnt->domain,
+ (mlrpc_vcbuf_t *)&identity->domain_name);
+
+ mlrpc_heap_mkvcs(heap, clnt->username,
+ (mlrpc_vcbuf_t *)&identity->username);
+
+ /*
+ * Some systems prefix the client workstation name with \\.
+ * It doesn't seem to make any difference whether it's there
+ * or not.
+ */
+ mlrpc_heap_mkvcs(heap, clnt->workstation,
+ (mlrpc_vcbuf_t *)&identity->workstation);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samlib.c b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c
new file mode 100644
index 0000000000..aa75c2678e
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c
@@ -0,0 +1,512 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module provides the high level interface to the SAM RPC
+ * functions.
+ */
+
+#include <unistd.h>
+#include <netdb.h>
+#include <alloca.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/ntaccess.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/lsalib.h>
+#include <smbsrv/samlib.h>
+
+/*
+ * Valid values for the OEM OWF password encryption.
+ */
+#define SAM_PASSWORD_516 516
+#define SAM_KEYLEN 16
+
+extern DWORD samr_set_user_info(mlsvc_handle_t *, smb_auth_info_t *);
+static int get_user_group_info(mlsvc_handle_t *, smb_userinfo_t *);
+
+/*
+ * sam_lookup_user_info
+ *
+ * Lookup user information in the SAM database on the specified server
+ * (domain controller). The LSA interface is used to obtain the user
+ * RID, the domain name and the domain SID (user privileges are TBD).
+ * Then the various SAM layers are opened using the domain SID and the
+ * user RID to obtain the users's group membership information.
+ *
+ * The information is returned in the user_info structure. The caller
+ * is responsible for allocating and releasing this structure. If the
+ * lookup is successful, sid_name_use will be set to SidTypeUser.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+int
+sam_lookup_user_info(char *server, char *domain_name,
+ char *account_name, char *password, smb_userinfo_t *user_info)
+{
+ mlsvc_handle_t samr_handle;
+ mlsvc_handle_t domain_handle;
+ mlsvc_handle_t user_handle;
+ struct samr_sid *sid;
+ int rc;
+ DWORD access_mask;
+ DWORD status;
+
+ if (lsa_lookup_name(server, domain_name, account_name, user_info) != 0)
+ return (-1);
+
+ if (user_info->sid_name_use != SidTypeUser ||
+ user_info->rid == 0 || user_info->domain_sid == 0) {
+ return (-1);
+ }
+
+ rc = samr_open(MLSVC_IPC_USER, server, domain_name, account_name,
+ password, SAM_LOOKUP_INFORMATION, &samr_handle);
+ if (rc != 0)
+ return (-1);
+#if 0
+ rc = samr_lookup_domain(&samr_handle, domain_name, user_info);
+ if (rc != 0)
+ return (-1);
+#endif
+ sid = (struct samr_sid *)user_info->domain_sid;
+
+ status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
+ sid, &domain_handle);
+ if (status == 0) {
+#if 0
+ (void) samr_lookup_domain_names(&domain_handle, account_name,
+ user_info);
+#endif
+ access_mask = STANDARD_RIGHTS_EXECUTE | SAM_ACCESS_USER_READ;
+
+ rc = samr_open_user(&domain_handle, access_mask,
+ user_info->rid, &user_handle);
+
+ if (rc == 0) {
+ (void) get_user_group_info(&user_handle, user_info);
+ (void) samr_close_handle(&user_handle);
+ }
+
+ (void) samr_close_handle(&domain_handle);
+ }
+
+ (void) samr_close_handle(&samr_handle);
+ return (rc);
+}
+
+/*
+ * get_user_group_info
+ *
+ * This is a private function to obtain the primary group and group
+ * memberships for the user specified by the user_handle. This function
+ * should only be called from sam_lookup_user_info.
+ *
+ * On success 0 is returned. Otherwise -1 is returned.
+ */
+static int
+get_user_group_info(mlsvc_handle_t *user_handle, smb_userinfo_t *user_info)
+{
+ union samr_user_info sui;
+ int rc;
+
+ rc = samr_query_user_info(user_handle, SAMR_QUERY_USER_GROUPRID, &sui);
+ if (rc != 0)
+ return (-1);
+
+ rc = samr_query_user_groups(user_handle, user_info);
+ if (rc != 0)
+ return (-1);
+
+ user_info->primary_group_rid = sui.info9.group_rid;
+ return (0);
+}
+
+/*
+ * sam_create_trust_account
+ *
+ * Create a trust account for this system.
+ *
+ * SAMR_AF_WORKSTATION_TRUST_ACCOUNT: servers and workstations.
+ * SAMR_AF_SERVER_TRUST_ACCOUNT: domain controllers.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+sam_create_trust_account(char *server, char *domain, smb_auth_info_t *auth)
+{
+ smb_userinfo_t *user_info;
+ char account_name[MAXHOSTNAMELEN];
+ DWORD status;
+
+ if (smb_gethostname(account_name, MAXHOSTNAMELEN - 2, 1) != 0)
+ return (NT_STATUS_NO_MEMORY);
+
+ (void) strlcat(account_name, "$", MAXHOSTNAMELEN);
+
+ if ((user_info = mlsvc_alloc_user_info()) == 0)
+ return (NT_STATUS_NO_MEMORY);
+
+ /*
+ * The trust account value here should match
+ * the value that will be used when the user
+ * information is set on this account.
+ */
+ status = sam_create_account(server, domain, account_name,
+ auth, SAMR_AF_WORKSTATION_TRUST_ACCOUNT, user_info);
+
+ mlsvc_free_user_info(user_info);
+ return (status);
+}
+
+
+/*
+ * sam_create_account
+ *
+ * Create the specified domain account in the SAM database on the
+ * domain controller.
+ *
+ * Account flags:
+ * SAMR_AF_NORMAL_ACCOUNT
+ * SAMR_AF_WORKSTATION_TRUST_ACCOUNT
+ * SAMR_AF_SERVER_TRUST_ACCOUNT
+ *
+ * Returns NT status codes.
+ */
+DWORD
+sam_create_account(char *server, char *domain_name, char *account_name,
+ smb_auth_info_t *auth, DWORD account_flags, smb_userinfo_t *user_info)
+{
+ mlsvc_handle_t samr_handle;
+ mlsvc_handle_t domain_handle;
+ mlsvc_handle_t user_handle;
+ union samr_user_info sui;
+ struct samr_sid *sid;
+ DWORD rid;
+ DWORD status;
+ int rc;
+
+ rc = samr_open(MLSVC_IPC_ADMIN, server, domain_name, 0, 0,
+ SAM_CONNECT_CREATE_ACCOUNT, &samr_handle);
+
+ if (rc != 0) {
+ status = NT_STATUS_OPEN_FAILED;
+ smb_tracef("SamCreateAccount[%s\\%s]: %s",
+ domain_name, account_name, xlate_nt_status(status));
+ return (status);
+ }
+
+ if (samr_handle.context->server_os == NATIVE_OS_WIN2000) {
+ nt_domain_t *ntdp;
+
+ if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
+ (void) lsa_query_account_domain_info();
+ if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
+ (void) samr_close_handle(&samr_handle);
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ smb_tracef("SamCreateAccount[%s\\%s]: %s",
+ domain_name, account_name,
+ xlate_nt_status(status));
+ return (status);
+ }
+ }
+
+ sid = (struct samr_sid *)ntdp->sid;
+ } else {
+ if (samr_lookup_domain(&samr_handle,
+ domain_name, user_info) != 0) {
+ (void) samr_close_handle(&samr_handle);
+ smb_tracef("SamCreateAccount[%s]: lookup failed",
+ account_name);
+
+ return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ }
+
+ sid = (struct samr_sid *)user_info->domain_sid;
+ }
+
+ status = samr_open_domain(&samr_handle,
+ SAM_DOMAIN_CREATE_ACCOUNT, sid, &domain_handle);
+
+ if (status == NT_STATUS_SUCCESS) {
+ status = samr_create_user(&domain_handle, account_name,
+ account_flags, &rid, &user_handle);
+
+ if (status == NT_STATUS_SUCCESS) {
+ (void) samr_query_user_info(&user_handle,
+ SAMR_QUERY_USER_UNKNOWN16, &sui);
+
+ (void) samr_get_user_pwinfo(&user_handle);
+ (void) samr_set_user_info(&user_handle, auth);
+ (void) samr_close_handle(&user_handle);
+ } else if (status == NT_STATUS_USER_EXISTS) {
+ mlsvc_release_user_info(user_info);
+
+ rc = lsa_lookup_name(server, domain_name, account_name,
+ user_info);
+ if (rc == 0)
+ rid = user_info->rid;
+ status = 0;
+ } else {
+ smb_tracef("SamCreateAccount[%s]: %s",
+ account_name, xlate_nt_status(status));
+ }
+
+ (void) samr_close_handle(&domain_handle);
+ } else {
+ smb_tracef("SamCreateAccount[%s]: open domain failed",
+ account_name);
+ status = (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ }
+
+ (void) samr_close_handle(&samr_handle);
+ return (status);
+}
+
+
+/*
+ * sam_remove_trust_account
+ *
+ * Attempt to remove the workstation trust account for this system.
+ * Administrator access is required to perform this operation.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+sam_remove_trust_account(char *server, char *domain)
+{
+ char account_name[MAXHOSTNAMELEN];
+
+ if (smb_gethostname(account_name, MAXHOSTNAMELEN - 2, 1) != 0)
+ return (NT_STATUS_NO_MEMORY);
+
+ (void) strcat(account_name, "$");
+
+ return (sam_delete_account(server, domain, account_name));
+}
+
+
+/*
+ * sam_delete_account
+ *
+ * Attempt to remove an account from the SAM database on the specified
+ * server.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+sam_delete_account(char *server, char *domain_name, char *account_name)
+{
+ mlsvc_handle_t samr_handle;
+ mlsvc_handle_t domain_handle;
+ mlsvc_handle_t user_handle;
+ smb_userinfo_t *user_info;
+ struct samr_sid *sid;
+ DWORD rid;
+ DWORD access_mask;
+ DWORD status;
+ int rc;
+
+ if ((user_info = mlsvc_alloc_user_info()) == 0)
+ return (NT_STATUS_NO_MEMORY);
+
+ rc = samr_open(MLSVC_IPC_ADMIN, server, domain_name, 0, 0,
+ SAM_LOOKUP_INFORMATION, &samr_handle);
+
+ if (rc != 0) {
+ mlsvc_free_user_info(user_info);
+ return (NT_STATUS_OPEN_FAILED);
+ }
+
+ if (samr_handle.context->server_os == NATIVE_OS_WIN2000) {
+ nt_domain_t *ntdp;
+
+ if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
+ (void) lsa_query_account_domain_info();
+ if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
+
+ (void) samr_close_handle(&samr_handle);
+ return (NT_STATUS_NO_SUCH_DOMAIN);
+ }
+ }
+
+ sid = (struct samr_sid *)ntdp->sid;
+ } else {
+ if (samr_lookup_domain(
+ &samr_handle, domain_name, user_info) != 0) {
+ (void) samr_close_handle(&samr_handle);
+ mlsvc_free_user_info(user_info);
+ return (NT_STATUS_NO_SUCH_DOMAIN);
+ }
+
+ sid = (struct samr_sid *)user_info->domain_sid;
+ }
+
+ status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
+ sid, &domain_handle);
+ if (status == 0) {
+ mlsvc_release_user_info(user_info);
+ status = samr_lookup_domain_names(&domain_handle,
+ account_name, user_info);
+
+ if (status == 0) {
+ rid = user_info->rid;
+ access_mask = STANDARD_RIGHTS_EXECUTE | DELETE;
+
+ rc = samr_open_user(&domain_handle, access_mask,
+ rid, &user_handle);
+ if (rc == 0) {
+ if (samr_delete_user(&user_handle) != 0)
+ (void) samr_close_handle(&user_handle);
+ }
+ }
+
+ (void) samr_close_handle(&domain_handle);
+ }
+
+ (void) samr_close_handle(&samr_handle);
+ mlsvc_free_user_info(user_info);
+ return (status);
+}
+
+/*
+ * sam_lookup_name
+ *
+ * Lookup an account name in the SAM database on the specified domain
+ * controller. Provides the account RID on success.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+sam_lookup_name(char *server, char *domain_name, char *account_name,
+ DWORD *rid_ret)
+{
+ mlsvc_handle_t samr_handle;
+ mlsvc_handle_t domain_handle;
+ smb_userinfo_t *user_info;
+ struct samr_sid *domain_sid;
+ int rc;
+ DWORD status;
+
+ *rid_ret = 0;
+
+ if ((user_info = mlsvc_alloc_user_info()) == 0)
+ return (NT_STATUS_NO_MEMORY);
+
+ rc = samr_open(MLSVC_IPC_ANON, server, domain_name, 0, 0,
+ SAM_LOOKUP_INFORMATION, &samr_handle);
+
+ if (rc != 0) {
+ mlsvc_free_user_info(user_info);
+ return (NT_STATUS_OPEN_FAILED);
+ }
+
+ rc = samr_lookup_domain(&samr_handle, domain_name, user_info);
+ if (rc != 0) {
+ (void) samr_close_handle(&samr_handle);
+ mlsvc_free_user_info(user_info);
+ return (NT_STATUS_NO_SUCH_DOMAIN);
+ }
+
+ domain_sid = (struct samr_sid *)user_info->domain_sid;
+
+ status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
+ domain_sid, &domain_handle);
+ if (status == 0) {
+ mlsvc_release_user_info(user_info);
+
+ status = samr_lookup_domain_names(&domain_handle,
+ account_name, user_info);
+ if (status == 0)
+ *rid_ret = user_info->rid;
+
+ (void) samr_close_handle(&domain_handle);
+ }
+
+ (void) samr_close_handle(&samr_handle);
+ mlsvc_free_user_info(user_info);
+ return (status);
+}
+
+
+/*
+ * sam_get_local_domains
+ *
+ * Query a remote server to get the list of local domains that it
+ * supports.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+sam_get_local_domains(char *server, char *domain_name)
+{
+ mlsvc_handle_t samr_handle;
+ DWORD status;
+ int rc;
+
+ rc = samr_open(MLSVC_IPC_ANON, server, domain_name, 0, 0,
+ SAM_ENUM_LOCAL_DOMAIN, &samr_handle);
+ if (rc != 0)
+ return (NT_STATUS_OPEN_FAILED);
+
+ status = samr_enum_local_domains(&samr_handle);
+ (void) samr_close_handle(&samr_handle);
+ return (status);
+}
+
+/*
+ * sam_oem_password
+ *
+ * Generate an OEM password.
+ */
+int sam_oem_password(oem_password_t *oem_password, unsigned char *new_password,
+ unsigned char *old_password)
+{
+ mts_wchar_t *unicode_password;
+ int length;
+
+#ifdef PBSHORTCUT
+ assert(sizeof (oem_password_t) == SAM_PASSWORD_516);
+#endif /* PBSHORTCUT */
+
+ length = strlen((char const *)new_password);
+ unicode_password = alloca((length + 1) * sizeof (mts_wchar_t));
+
+ length = smb_auth_qnd_unicode((unsigned short *)unicode_password,
+ (char *)new_password, length);
+ oem_password->length = length;
+
+ (void) memcpy(&oem_password->data[512 - length],
+ unicode_password, length);
+
+ rand_hash((unsigned char *)oem_password, sizeof (oem_password_t),
+ old_password, SAM_KEYLEN);
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samr_lookup.c b/usr/src/lib/smbsrv/libmlsvc/common/samr_lookup.c
new file mode 100644
index 0000000000..7eccd83e22
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/samr_lookup.c
@@ -0,0 +1,565 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Security Access Manager RPC (SAMR) library interface functions for
+ * query and lookup calls.
+ */
+
+
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/samlib.h>
+#include <smbsrv/mlrpc.h>
+#include <smbsrv/mlsvc.h>
+
+static int samr_setup_user_info(WORD, struct samr_QueryUserInfo *,
+ union samr_user_info *);
+static void samr_set_user_unknowns(struct samr_SetUserInfo23 *);
+static void samr_set_user_logon_hours(struct samr_SetUserInfo *);
+static int samr_set_user_password(smb_auth_info_t *, BYTE *);
+
+/*
+ * samr_lookup_domain
+ *
+ * Lookup up the domain SID for the specified domain name. The handle
+ * should be one returned from samr_connect. The results will be
+ * returned in user_info - which should have been allocated by the
+ * caller. On success sid_name_use will be set to SidTypeDomain.
+ *
+ * Returns 0 on success, otherwise returns -ve error code.
+ */
+int
+samr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name,
+ smb_userinfo_t *user_info)
+{
+ struct samr_LookupDomain arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ size_t length;
+
+ if (mlsvc_is_null_handle(samr_handle) ||
+ domain_name == NULL || user_info == NULL) {
+ return (-1);
+ }
+
+ context = samr_handle->context;
+ opnum = SAMR_OPNUM_LookupDomain;
+ bzero(&arg, sizeof (struct samr_LookupDomain));
+
+ (void) memcpy(&arg.handle, &samr_handle->handle,
+ sizeof (samr_handle_t));
+
+ length = mts_wcequiv_strlen(domain_name);
+ if (context->server_os == NATIVE_OS_WIN2000)
+ length += sizeof (mts_wchar_t);
+
+ arg.domain_name.length = length;
+ arg.domain_name.allosize = length;
+ arg.domain_name.str = (unsigned char *)domain_name;
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ user_info->sid_name_use = SidTypeDomain;
+ user_info->domain_sid = nt_sid_dup((nt_sid_t *)arg.sid);
+ user_info->domain_name = MEM_STRDUP("mlrpc", domain_name);
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * samr_enum_local_domains
+ *
+ * Get the list of local domains supported by a server.
+ *
+ * Returns NT status codes.
+ */
+DWORD
+samr_enum_local_domains(mlsvc_handle_t *samr_handle)
+{
+ struct samr_EnumLocalDomain arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ DWORD status;
+
+ if (mlsvc_is_null_handle(samr_handle))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ context = samr_handle->context;
+ opnum = SAMR_OPNUM_EnumLocalDomains;
+ bzero(&arg, sizeof (struct samr_EnumLocalDomain));
+
+ (void) memcpy(&arg.handle, &samr_handle->handle,
+ sizeof (samr_handle_t));
+ arg.enum_context = 0;
+ arg.max_length = 0x00002000; /* Value used by NT */
+
+ (void) mlsvc_rpc_init(&heap);
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else {
+ status = NT_SC_VALUE(arg.status);
+
+ /*
+ * Handle none-mapped status quietly.
+ */
+ if (status != NT_STATUS_NONE_MAPPED)
+ mlsvc_rpc_report_status(opnum, arg.status);
+ }
+
+ return (status);
+}
+
+/*
+ * samr_lookup_domain_names
+ *
+ * Lookup up a name
+ * returned in user_info - which should have been allocated by the
+ * caller. On success sid_name_use will be set to SidTypeDomain.
+ *
+ * Returns 0 on success. Otherwise returns an NT status code.
+ */
+DWORD
+samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name,
+ smb_userinfo_t *user_info)
+{
+ struct samr_LookupNames arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ DWORD status;
+ size_t length;
+
+ if (mlsvc_is_null_handle(domain_handle) ||
+ name == NULL || user_info == NULL) {
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ context = domain_handle->context;
+ opnum = SAMR_OPNUM_LookupNames;
+ bzero(&arg, sizeof (struct samr_LookupNames));
+
+ (void) memcpy(&arg.handle, &domain_handle->handle,
+ sizeof (samr_handle_t));
+ arg.n_entry = 1;
+ arg.max_n_entry = 1000;
+ arg.index = 0;
+ arg.total = 1;
+
+ length = mts_wcequiv_strlen(name);
+ if (context->server_os == NATIVE_OS_WIN2000)
+ length += sizeof (mts_wchar_t);
+
+ arg.name.length = length;
+ arg.name.allosize = length;
+ arg.name.str = (unsigned char *)name;
+
+ (void) mlsvc_rpc_init(&heap);
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ status = NT_SC_VALUE(arg.status);
+
+ /*
+ * Handle none-mapped status quietly.
+ */
+ if (status != NT_STATUS_NONE_MAPPED)
+ mlsvc_rpc_report_status(opnum, arg.status);
+ } else {
+ user_info->name = MEM_STRDUP("mlrpc", name);
+ user_info->sid_name_use = arg.rid_types.rid_type[0];
+ user_info->rid = arg.rids.rid[0];
+ status = 0;
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+/*
+ * samr_query_user_info
+ *
+ * Query information on a specific user. The handle must be a valid
+ * user handle obtained via samr_open_user.
+ *
+ * Returns 0 on success, otherwise returns -ve error code.
+ */
+int
+samr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value,
+ union samr_user_info *user_info)
+{
+ struct samr_QueryUserInfo arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+
+ if (mlsvc_is_null_handle(user_handle) || user_info == 0)
+ return (-1);
+
+ context = user_handle->context;
+ opnum = SAMR_OPNUM_QueryUserInfo;
+ bzero(&arg, sizeof (struct samr_QueryUserInfo));
+
+ (void) memcpy(&arg.user_handle, &user_handle->handle,
+ sizeof (samr_handle_t));
+ arg.switch_value = switch_value;
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0)
+ rc = -1;
+ else
+ rc = samr_setup_user_info(switch_value, &arg,
+ user_info);
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+
+/*
+ * samr_setup_user_info
+ *
+ * Private function to set up the samr_user_info data. Dependent on
+ * the switch value this function may use strdup which will malloc
+ * memory. The caller is responsible for deallocating this memory.
+ *
+ * Returns 0 on success, otherwise returns -1.
+ */
+static int samr_setup_user_info(WORD switch_value,
+ struct samr_QueryUserInfo *arg, union samr_user_info *user_info)
+{
+ struct samr_QueryUserInfo1 *info1;
+ struct samr_QueryUserInfo6 *info6;
+
+ switch (switch_value) {
+ case 1:
+ info1 = &arg->ru.info1;
+ user_info->info1.username = strdup(
+ (char const *)info1->username.str);
+ user_info->info1.fullname = strdup(
+ (char const *)info1->fullname.str);
+ user_info->info1.description = strdup(
+ (char const *)info1->description.str);
+ user_info->info1.unknown = 0;
+ user_info->info1.group_rid = info1->group_rid;
+ return (0);
+
+ case 6:
+ info6 = &arg->ru.info6;
+ user_info->info6.username = strdup(
+ (char const *)info6->username.str);
+ user_info->info6.fullname = strdup(
+ (char const *)info6->fullname.str);
+ return (0);
+
+ case 7:
+ user_info->info7.username = strdup(
+ (char const *)arg->ru.info7.username.str);
+ return (0);
+
+ case 8:
+ user_info->info8.fullname = strdup(
+ (char const *)arg->ru.info8.fullname.str);
+ return (0);
+
+ case 9:
+ user_info->info9.group_rid = arg->ru.info9.group_rid;
+ return (0);
+
+ case 16:
+ return (0);
+
+ default:
+ break;
+ };
+
+ return (-1);
+}
+
+/*
+ * samr_query_user_groups
+ *
+ * Query the groups for a specific user. The handle must be a valid
+ * user handle obtained via samr_open_user. The list of groups is
+ * returned in group_info. Note that group_info->groups is allocated
+ * using malloc. The caller is responsible for deallocating this
+ * memory when it is no longer required. If group_info->n_entry is 0
+ * then no memory was allocated.
+ *
+ * Returns 0 on success, otherwise returns -1.
+ */
+int
+samr_query_user_groups(mlsvc_handle_t *user_handle, smb_userinfo_t *user_info)
+{
+ struct samr_QueryUserGroups arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ int nbytes;
+
+ if (mlsvc_is_null_handle(user_handle) || user_info == NULL)
+ return (-1);
+
+ context = user_handle->context;
+ opnum = SAMR_OPNUM_QueryUserGroups;
+ bzero(&arg, sizeof (struct samr_QueryUserGroups));
+
+ (void) memcpy(&arg.user_handle, &user_handle->handle,
+ sizeof (samr_handle_t));
+
+ (void) mlsvc_rpc_init(&heap);
+
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.info == 0) {
+ rc = -1;
+ } else {
+ nbytes = arg.info->n_entry *
+ sizeof (struct samr_UserGroups);
+ user_info->groups = malloc(nbytes);
+
+ if (user_info->groups == NULL) {
+ user_info->n_groups = 0;
+ rc = -1;
+ } else {
+ user_info->n_groups = arg.info->n_entry;
+ (void) memcpy(user_info->groups,
+ arg.info->groups, nbytes);
+ }
+ }
+ }
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+
+/*
+ * samr_get_user_pwinfo
+ *
+ * Get some user password info. I'm not sure what this is yet but it is
+ * part of the create user sequence. The handle must be a valid user
+ * handle. Since I don't know what this is returning, I haven't provided
+ * any return data yet.
+ *
+ * Returns 0 on success. Otherwise returns an NT status code.
+ */
+DWORD
+samr_get_user_pwinfo(mlsvc_handle_t *user_handle)
+{
+ struct samr_GetUserPwInfo arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ DWORD status;
+
+ if (mlsvc_is_null_handle(user_handle))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ context = user_handle->context;
+ opnum = SAMR_OPNUM_GetUserPwInfo;
+ bzero(&arg, sizeof (struct samr_GetUserPwInfo));
+ (void) memcpy(&arg.user_handle, &user_handle->handle,
+ sizeof (samr_handle_t));
+
+ (void) mlsvc_rpc_init(&heap);
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ status = NT_SC_VALUE(arg.status);
+ } else {
+ status = 0;
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+
+/*
+ * samr_set_user_info
+ *
+ * Returns 0 on success. Otherwise returns an NT status code.
+ * NT status codes observed so far:
+ * NT_STATUS_WRONG_PASSWORD
+ */
+/*ARGSUSED*/
+DWORD
+samr_set_user_info(mlsvc_handle_t *user_handle, smb_auth_info_t *auth)
+{
+ struct samr_SetUserInfo arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ DWORD status = 0;
+
+ if (mlsvc_is_null_handle(user_handle))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ (void) mlsvc_rpc_init(&heap);
+
+ context = user_handle->context;
+ opnum = SAMR_OPNUM_SetUserInfo;
+ bzero(&arg, sizeof (struct samr_SetUserInfo));
+ (void) memcpy(&arg.user_handle, &user_handle->handle,
+ sizeof (samr_handle_t));
+
+ arg.info.index = SAMR_SET_USER_INFO_23;
+ arg.info.switch_value = SAMR_SET_USER_INFO_23;
+
+ samr_set_user_unknowns(&arg.info.ru.info23);
+ samr_set_user_logon_hours(&arg);
+
+ if (samr_set_user_password(auth, arg.info.ru.info23.password) < 0)
+ status = NT_STATUS_INTERNAL_ERROR;
+
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ status = NT_SC_VALUE(arg.status);
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+static void
+samr_set_user_unknowns(struct samr_SetUserInfo23 *info)
+{
+ bzero(info, sizeof (struct samr_SetUserInfo23));
+
+ info->sd.length = 0;
+ info->sd.data = 0;
+ info->user_rid = 0;
+ info->group_rid = MLSVC_DOMAIN_GROUP_RID_USERS;
+
+ /*
+ * The trust account value used here should probably
+ * match the one used to create the trust account.
+ */
+ info->acct_info = SAMR_AF_WORKSTATION_TRUST_ACCOUNT;
+ info->flags = 0x09F827FA;
+}
+
+/*
+ * samr_set_user_logon_hours
+ *
+ * SamrSetUserInfo appears to contain some logon hours information, which
+ * looks like a varying, conformant array. The top level contains a value
+ * (units), which probably indicates the how to interpret the array. The
+ * array definition looks like it contains a maximum size, an initial
+ * offset and a bit length (units/8), followed by the bitmap.
+ *
+ * (info)
+ * +-------+
+ * | units |
+ * +-------+ (hours)
+ * | hours |-->+-----------+
+ * +-------+ | max_is |
+ * +-----------+
+ * | first_is |
+ * +-----------+
+ * | length_is |
+ * +------------------------+
+ * | bitmap[length_is] |
+ * +---------+--------------+
+ *
+ * 10080 minutes/week => 10080/8 = 1260 (0x04EC) bytes.
+ * 168 hours/week => 168/8 = 21 (0xA8) bytes.
+ * In the netmon examples seen so far, all bits are set to 1, i.e.
+ * an array containing 0xff. This is probably the default setting.
+ *
+ * ndrgen has a problem with complex [size_is] statements (length/8).
+ * So, for now, we fake it using two separate components.
+ */
+static void
+samr_set_user_logon_hours(struct samr_SetUserInfo *sui)
+{
+ sui->logon_hours.size = SAMR_HOURS_MAX_SIZE;
+ sui->logon_hours.first = 0;
+ sui->logon_hours.length = SAMR_SET_USER_HOURS_SZ;
+ (void) memset(sui->logon_hours.bitmap, 0xFF, SAMR_SET_USER_HOURS_SZ);
+
+ sui->info.ru.info23.logon_info.units = SAMR_HOURS_PER_WEEK;
+ sui->info.ru.info23.logon_info.hours = (DWORD)sui->logon_hours.bitmap;
+}
+
+/*
+ * samr_set_user_password
+ *
+ * Set the initial password for the user.
+ *
+ * Returns 0 if everything goes well, -1 if there is trouble generating a
+ * key.
+ */
+static int
+samr_set_user_password(smb_auth_info_t *auth, BYTE *oem_password)
+{
+ unsigned char nt_key[SMBAUTH_SESSION_KEY_SZ];
+ char hostname[64];
+
+ randomize((char *)oem_password, SAMR_SET_USER_DATA_SZ);
+
+ /*
+ * The new password is going to be
+ * the hostname in lower case.
+ */
+ if (smb_gethostname(hostname, 64, 0) != 0)
+ return (-1);
+
+ (void) utf8_strlwr(hostname);
+
+ if (smb_auth_gen_session_key(auth, nt_key) != SMBAUTH_SUCCESS)
+ return (-1);
+
+ /*
+ * Generate the OEM password from the hostname and the user session
+ * key(nt_key).
+ */
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ (void) sam_oem_password((oem_password_t *)oem_password,
+ (unsigned char *)hostname, nt_key);
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samr_open.c b/usr/src/lib/smbsrv/libmlsvc/common/samr_open.c
new file mode 100644
index 0000000000..38dca838cd
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/samr_open.c
@@ -0,0 +1,684 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Security Access Manager RPC (SAMR) library interface functions for
+ * connect, open and close calls. The SAM is a hierarchical database.
+ * If you want to talk to the SAM you need a SAM handle, if you want
+ * to work with a domain, you need to use the SAM handle to obtain a
+ * domain handle. Then you can use the domain handle to obtain a user
+ * handle etc. Be careful about returning null handles to the
+ * application. Use of a null handle may crash the domain controller
+ * if you attempt to use it.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/param.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbrdr.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/ntaccess.h>
+#include <smbsrv/samlib.h>
+#include <smbsrv/mlrpc.h>
+#include <smbsrv/mlsvc.h>
+
+/*LINTED E_STATIC_UNUSED*/
+static DWORD samr_connect1(char *, char *, char *, DWORD, mlsvc_handle_t *);
+static DWORD samr_connect2(char *, char *, char *, DWORD, mlsvc_handle_t *);
+static DWORD samr_connect3(char *, char *, char *, DWORD, mlsvc_handle_t *);
+static DWORD samr_connect4(char *, char *, char *, DWORD, mlsvc_handle_t *);
+
+/*
+ * samr_open
+ *
+ * This is a wrapper round samr_connect to ensure that we connect using
+ * the appropriate session and logon. We default to the resource domain
+ * information if the caller doesn't supply a server name and a domain
+ * name. We store the remote server's native OS type - we may need it
+ * due to differences between platforms like NT and Windows 2000.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+int
+samr_open(int ipc_mode, char *server, char *domain, char *username,
+ char *password, DWORD access_mask, mlsvc_handle_t *samr_handle)
+{
+ smb_ntdomain_t *di;
+ int remote_os;
+ int remote_lm;
+ int rc;
+
+ if ((di = smb_getdomaininfo(0)) == NULL)
+ return (-1);
+
+ if (server == NULL || domain == NULL) {
+ server = di->server;
+ domain = di->domain;
+ }
+
+ switch (ipc_mode) {
+ case MLSVC_IPC_USER:
+ /*
+ * Use the supplied credentials.
+ */
+ rc = mlsvc_user_logon(server, domain, username, password);
+ break;
+
+ case MLSVC_IPC_ADMIN:
+ /*
+ * Use the resource domain administrator credentials.
+ */
+ server = di->server;
+ domain = di->domain;
+ username = smbrdr_ipc_get_user();
+
+ rc = mlsvc_admin_logon(server, domain);
+ break;
+
+ case MLSVC_IPC_ANON:
+ default:
+ rc = mlsvc_anonymous_logon(server, domain, &username);
+ break;
+ }
+
+ if (rc != 0)
+ return (-1);
+
+ rc = samr_connect(server, domain, username, access_mask, samr_handle);
+ if (rc == 0) {
+ (void) mlsvc_session_native_values(samr_handle->context->fid,
+ &remote_os, &remote_lm, 0);
+ samr_handle->context->server_os = remote_os;
+ }
+ return (rc);
+}
+
+
+/*
+ * samr_connect
+ *
+ * Connect to the SAM on the specified server (domain controller).
+ * This is the entry point for the various SAM connect calls. We do
+ * parameter validation and open the samr named pipe here. The actual
+ * RPC is based on the native OS of the server.
+ *
+ * Returns 0 on success. Otherwise returns a -ve error code.
+ */
+int
+samr_connect(char *server, char *domain, char *username, DWORD access_mask,
+ mlsvc_handle_t *samr_handle)
+{
+ DWORD status;
+ int remote_os;
+ int remote_lm;
+ int fid;
+ int rc = 0;
+
+ if (server == NULL || domain == NULL ||
+ username == NULL || samr_handle == NULL)
+ return (-1);
+
+ if ((fid = mlsvc_open_pipe(server, domain, username, "\\samr")) < 0)
+ return (-1);
+
+ if (mlsvc_rpc_bind(samr_handle, fid, "SAMR") < 0) {
+ (void) mlsvc_close_pipe(fid);
+ return (-1);
+ }
+
+ (void) mlsvc_session_native_values(fid, &remote_os, &remote_lm, 0);
+
+ switch (remote_os) {
+ case NATIVE_OS_NT5_1:
+ status = samr_connect4(server, domain, username, access_mask,
+ samr_handle);
+ break;
+
+ case NATIVE_OS_NT5_0:
+ status = samr_connect3(server, domain, username, access_mask,
+ samr_handle);
+ break;
+
+ case NATIVE_OS_NT4_0:
+ default:
+ status = samr_connect2(server, domain, username, access_mask,
+ samr_handle);
+ break;
+ }
+
+ if (status != NT_STATUS_SUCCESS) {
+ (void) mlsvc_close_pipe(fid);
+ free(samr_handle->context);
+ rc = -1;
+ }
+ return (rc);
+}
+
+/*
+ * samr_connect1
+ *
+ * Original SAMR connect call; probably used on Windows NT 3.51.
+ * Windows 95 uses this call with the srvmgr tools update.
+ * Servername appears to be a dword rather than a string.
+ * The first word contains '\' and the second word contains 0x001,
+ * (which is probably uninitialized junk: 0x0001005c.
+ */
+/*ARGSUSED*/
+static DWORD
+samr_connect1(char *server, char *domain, char *username, DWORD access_mask,
+ mlsvc_handle_t *samr_handle)
+{
+ struct samr_ConnectAnon arg;
+ mlrpc_heapref_t heapref;
+ int opnum;
+ DWORD status;
+
+ bzero(&arg, sizeof (struct samr_ConnectAnon));
+ opnum = SAMR_OPNUM_ConnectAnon;
+ status = NT_STATUS_SUCCESS;
+
+ (void) mlsvc_rpc_init(&heapref);
+ arg.servername = (DWORD *)mlrpc_heap_malloc(heapref.heap,
+ sizeof (DWORD));
+ *(arg.servername) = 0x0001005c;
+ arg.access_mask = access_mask;
+
+ if (mlsvc_rpc_call(samr_handle->context, opnum, &arg, &heapref) != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ } else if (arg.status != 0) {
+ status = NT_SC_VALUE(arg.status);
+ } else {
+ (void) memcpy(&samr_handle->handle, &arg.handle,
+ sizeof (ms_handle_t));
+
+ if (mlsvc_is_null_handle(samr_handle))
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+
+ mlsvc_rpc_free(samr_handle->context, &heapref);
+ return (status);
+}
+
+/*
+ * samr_connect2
+ *
+ * Connect to the SAM on a Windows NT 4.0 server (domain controller).
+ * We need the domain controller name and, if everything works, we
+ * return a handle. This function adds the double backslash prefx to
+ * make it easy for applications.
+ *
+ * Returns 0 on success. Otherwise returns a -ve error code.
+ */
+/*ARGSUSED*/
+static DWORD
+samr_connect2(char *server, char *domain, char *username, DWORD access_mask,
+ mlsvc_handle_t *samr_handle)
+{
+ struct samr_Connect arg;
+ mlrpc_heapref_t heapref;
+ int opnum;
+ DWORD status;
+ int len;
+
+ bzero(&arg, sizeof (struct samr_Connect));
+ opnum = SAMR_OPNUM_Connect;
+ status = NT_STATUS_SUCCESS;
+
+ (void) mlsvc_rpc_init(&heapref);
+ len = strlen(server) + 4;
+ arg.servername = mlrpc_heap_malloc(heapref.heap, len);
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+ arg.access_mask = access_mask;
+
+ if (mlsvc_rpc_call(samr_handle->context, opnum, &arg, &heapref) != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ } else if (arg.status != 0) {
+ status = NT_SC_VALUE(arg.status);
+ } else {
+ (void) memcpy(&samr_handle->handle, &arg.handle,
+ sizeof (ms_handle_t));
+
+ if (mlsvc_is_null_handle(samr_handle))
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+
+ mlsvc_rpc_free(samr_handle->context, &heapref);
+ return (status);
+}
+
+/*
+ * samr_connect3
+ *
+ * Connect to the SAM on a Windows 2000 domain controller.
+ */
+/*ARGSUSED*/
+static DWORD
+samr_connect3(char *server, char *domain, char *username, DWORD access_mask,
+ mlsvc_handle_t *samr_handle)
+{
+ struct samr_Connect3 arg;
+ mlrpc_heapref_t heapref;
+ int opnum;
+ DWORD status;
+ int len;
+
+ bzero(&arg, sizeof (struct samr_Connect3));
+ opnum = SAMR_OPNUM_Connect3;
+ status = NT_STATUS_SUCCESS;
+
+ (void) mlsvc_rpc_init(&heapref);
+ len = strlen(server) + 4;
+ arg.servername = mlrpc_heap_malloc(heapref.heap, len);
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+ arg.unknown_02 = 0x00000002;
+ arg.access_mask = access_mask;
+
+ if (mlsvc_rpc_call(samr_handle->context, opnum, &arg, &heapref) != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ } else if (arg.status != 0) {
+ status = NT_SC_VALUE(arg.status);
+ } else {
+ (void) memcpy(&samr_handle->handle, &arg.handle,
+ sizeof (ms_handle_t));
+
+ if (mlsvc_is_null_handle(samr_handle))
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+
+ mlsvc_rpc_free(samr_handle->context, &heapref);
+ return (status);
+}
+
+/*
+ * samr_connect4
+ *
+ * Connect to the SAM on a Windows XP domain controller. On Windows
+ * XP, the server should be the fully qualified DNS domain name with
+ * a double backslash prefix. At this point, it is assumed that we
+ * need to add the prefix and the DNS domain name here.
+ *
+ * If this call succeeds, a SAMR handle is placed in samr_handle and
+ * zero is returned. Otherwise, a -ve error code is returned.
+ */
+/*ARGSUSED*/
+static DWORD
+samr_connect4(char *server, char *domain, char *username, DWORD access_mask,
+ mlsvc_handle_t *samr_handle)
+{
+ struct samr_Connect4 arg;
+ mlrpc_heapref_t heapref;
+ char *dns_name;
+ int len;
+ int opnum;
+ DWORD status;
+
+ bzero(&arg, sizeof (struct samr_Connect4));
+ opnum = SAMR_OPNUM_Connect;
+ status = NT_STATUS_SUCCESS;
+
+ (void) mlsvc_rpc_init(&heapref);
+ dns_name = mlrpc_heap_malloc(heapref.heap, MAXHOSTNAMELEN);
+ (void) smb_getdomainname(dns_name, MAXHOSTNAMELEN);
+
+ if (strlen(dns_name) > 0) {
+ len = strlen(server) + strlen(dns_name) + 4;
+ arg.servername = mlrpc_heap_malloc(heapref.heap, len);
+ (void) snprintf((char *)arg.servername, len, "\\\\%s.%s",
+ server, dns_name);
+ } else {
+ len = strlen(server) + 4;
+ arg.servername = mlrpc_heap_malloc(heapref.heap, len);
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+ }
+
+ arg.access_mask = SAM_ENUM_LOCAL_DOMAIN;
+ arg.unknown2_00000001 = 0x00000001;
+ arg.unknown3_00000001 = 0x00000001;
+ arg.unknown4_00000003 = 0x00000003;
+ arg.unknown5_00000000 = 0x00000000;
+
+ if (mlsvc_rpc_call(samr_handle->context, opnum, &arg, &heapref) != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ } else if (arg.status != 0) {
+ status = NT_SC_VALUE(arg.status);
+ } else {
+
+ (void) memcpy(&samr_handle->handle, &arg.handle,
+ sizeof (ms_handle_t));
+
+ if (mlsvc_is_null_handle(samr_handle))
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+
+ mlsvc_rpc_free(samr_handle->context, &heapref);
+
+ return (status);
+}
+
+
+/*
+ * samr_close_handle
+ *
+ * This is function closes any valid handle, i.e. sam, domain, user etc.
+ * Just to be safe we check for, and reject, null handles. The handle
+ * returned by the SAM server is all null. If the handle being closed is
+ * the top level connect handle, we also close the pipe. Then we zero
+ * out the handle to invalidate it. Things go badly if you attempt to
+ * use an invalid handle, i.e. the DC crashes.
+ */
+int
+samr_close_handle(mlsvc_handle_t *desc)
+{
+ struct samr_CloseHandle arg;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+
+ if (mlsvc_is_null_handle(desc))
+ return (-1);
+
+ opnum = SAMR_OPNUM_CloseHandle;
+ bzero(&arg, sizeof (struct samr_CloseHandle));
+ (void) memcpy(&arg.handle, &desc->handle, sizeof (ms_handle_t));
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(desc->context, opnum, &arg, &heap);
+ mlsvc_rpc_free(desc->context, &heap);
+
+ if (desc->context->handle == &desc->handle) {
+ (void) mlsvc_close_pipe(desc->context->fid);
+ free(desc->context);
+ }
+
+ bzero(desc, sizeof (mlsvc_handle_t));
+ return (rc);
+}
+
+/*
+ * samr_open_domain
+ *
+ * We use a SAM handle to obtain a handle for a domain, specified by
+ * the SID. The SID can be obtain via the LSA interface. A handle for
+ * the domain is returned in domain_handle.
+ */
+DWORD
+samr_open_domain(mlsvc_handle_t *samr_handle, DWORD access_mask,
+ struct samr_sid *sid, mlsvc_handle_t *domain_handle)
+{
+ struct samr_OpenDomain arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ DWORD status;
+
+ if (mlsvc_is_null_handle(samr_handle) ||
+ sid == 0 || domain_handle == 0) {
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ context = samr_handle->context;
+ opnum = SAMR_OPNUM_OpenDomain;
+ bzero(&arg, sizeof (struct samr_OpenDomain));
+ (void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ms_handle_t));
+
+ arg.access_mask = access_mask;
+ arg.sid = sid;
+
+ (void) mlsvc_rpc_init(&heap);
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ } else if (arg.status != 0) {
+ status = arg.status;
+ } else {
+ status = NT_STATUS_SUCCESS;
+ (void) memcpy(&domain_handle->handle, &arg.domain_handle,
+ sizeof (ms_handle_t));
+ domain_handle->context = context;
+ if (mlsvc_is_null_handle(domain_handle))
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (status != NT_STATUS_SUCCESS)
+ mlsvc_rpc_report_status(opnum, status);
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+/*
+ * samr_open_user
+ *
+ * Use a domain handle to obtain a handle for a user, specified by the
+ * user RID. A user RID (effectively a uid) can be obtained via the
+ * LSA interface. A handle for the user is returned in user_handle.
+ * Once you have a user handle it should be possible to query the SAM
+ * for information on that user.
+ */
+int
+samr_open_user(mlsvc_handle_t *domain_handle, DWORD access_mask, DWORD rid,
+ mlsvc_handle_t *user_handle)
+{
+ struct samr_OpenUser arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+
+ if (mlsvc_is_null_handle(domain_handle) || user_handle == NULL)
+ return (-1);
+
+ context = domain_handle->context;
+ opnum = SAMR_OPNUM_OpenUser;
+ bzero(&arg, sizeof (struct samr_OpenUser));
+ (void) memcpy(&arg.handle, &domain_handle->handle,
+ sizeof (ms_handle_t));
+ arg.access_mask = access_mask;
+ arg.rid = rid;
+
+ (void) mlsvc_rpc_init(&heap);
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ rc = -1;
+ } else {
+ (void) memcpy(&user_handle->handle, &arg.user_handle,
+ sizeof (ms_handle_t));
+ user_handle->context = context;
+
+ if (mlsvc_is_null_handle(user_handle))
+ rc = -1;
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * samr_delete_user
+ *
+ * Delete the user specified by the user_handle.
+ */
+DWORD
+samr_delete_user(mlsvc_handle_t *user_handle)
+{
+ struct samr_DeleteUser arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ DWORD status;
+
+ if (mlsvc_is_null_handle(user_handle))
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ context = user_handle->context;
+ opnum = SAMR_OPNUM_DeleteUser;
+ bzero(&arg, sizeof (struct samr_DeleteUser));
+ (void) memcpy(&arg.user_handle, &user_handle->handle,
+ sizeof (ms_handle_t));
+
+ (void) mlsvc_rpc_init(&heap);
+ if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ status = NT_SC_VALUE(arg.status);
+ } else {
+ status = 0;
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
+
+/*
+ * samr_open_group
+ *
+ * Use a domain handle to obtain a handle for a group, specified by the
+ * group RID. A group RID (effectively a gid) can be obtained via the
+ * LSA interface. A handle for the group is returned in group_handle.
+ * Once you have a group handle it should be possible to query the SAM
+ * for information on that group.
+ */
+int
+samr_open_group(
+ mlsvc_handle_t *domain_handle,
+ DWORD rid,
+ mlsvc_handle_t *group_handle)
+{
+ struct samr_OpenGroup arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+
+ if (mlsvc_is_null_handle(domain_handle) || group_handle == 0)
+ return (-1);
+
+ context = domain_handle->context;
+ opnum = SAMR_OPNUM_OpenGroup;
+ bzero(&arg, sizeof (struct samr_OpenUser));
+ (void) memcpy(&arg.handle, &domain_handle->handle,
+ sizeof (ms_handle_t));
+ arg.access_mask = SAM_LOOKUP_INFORMATION | SAM_ACCESS_USER_READ;
+ arg.rid = rid;
+
+ (void) mlsvc_rpc_init(&heap);
+
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc == 0) {
+ if (arg.status != 0) {
+ mlsvc_rpc_report_status(opnum, arg.status);
+ rc = -1;
+ } else {
+ (void) memcpy(&group_handle->handle, &arg.group_handle,
+ sizeof (ms_handle_t));
+ group_handle->context = context;
+ if (mlsvc_is_null_handle(group_handle))
+ rc = -1;
+ }
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (rc);
+}
+
+/*
+ * samr_create_user
+ *
+ * Create a user in the domain specified by the domain handle. If this
+ * call is successful, the server will return the RID for the user and
+ * a user handle, which may be used to set or query the SAM.
+ *
+ * Observed status codes:
+ * NT_STATUS_INVALID_PARAMETER
+ * NT_STATUS_INVALID_ACCOUNT_NAME
+ * NT_STATUS_ACCESS_DENIED
+ * NT_STATUS_USER_EXISTS
+ *
+ * Returns 0 on success. Otherwise returns an NT status code.
+ */
+DWORD
+samr_create_user(mlsvc_handle_t *domain_handle, char *username,
+ DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle)
+{
+ struct samr_CreateUser arg;
+ struct mlsvc_rpc_context *context;
+ mlrpc_heapref_t heap;
+ int opnum;
+ int rc;
+ DWORD status = 0;
+
+ if (mlsvc_is_null_handle(domain_handle) ||
+ username == NULL || rid == NULL) {
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ context = domain_handle->context;
+ opnum = SAMR_OPNUM_CreateUser;
+
+ bzero(&arg, sizeof (struct samr_CreateUser));
+ (void) memcpy(&arg.handle, &domain_handle->handle,
+ sizeof (ms_handle_t));
+
+ (void) mlsvc_rpc_init(&heap);
+ mlrpc_heap_mkvcs(heap.heap, username, (mlrpc_vcbuf_t *)&arg.username);
+
+ arg.account_flags = account_flags;
+ arg.unknown_e00500b0 = 0xE00500B0;
+
+ rc = mlsvc_rpc_call(context, opnum, &arg, &heap);
+ if (rc != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ } else if (arg.status != 0) {
+ status = NT_SC_VALUE(arg.status);
+
+ if (status != NT_STATUS_USER_EXISTS) {
+ smb_tracef("SamrCreateUser[%s]: %s", username,
+ xlate_nt_status(status));
+ }
+ } else {
+ (void) memcpy(&user_handle->handle, &arg.user_handle,
+ sizeof (ms_handle_t));
+ user_handle->context = context;
+ *rid = arg.rid;
+
+ if (mlsvc_is_null_handle(user_handle))
+ status = NT_STATUS_INVALID_HANDLE;
+ else
+ status = 0;
+ }
+
+ mlsvc_rpc_free(context, &heap);
+ return (status);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/secdb.c b/usr/src/lib/smbsrv/libmlsvc/common/secdb.c
new file mode 100644
index 0000000000..cc4d96456f
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/secdb.c
@@ -0,0 +1,932 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Security database interface.
+ */
+#include <unistd.h>
+#include <strings.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <syslog.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlsvc.h>
+
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/smb_token.h>
+#include <smbsrv/lsalib.h>
+#include <smbsrv/alloc.h>
+
+extern uint32_t netlogon_logon(netr_client_t *clnt, smb_userinfo_t *uinfo);
+static uint32_t smb_logon_domain(netr_client_t *clnt, smb_userinfo_t *uinfo);
+static uint32_t smb_logon_local(netr_client_t *clnt, smb_userinfo_t *uinfo);
+static uint32_t smb_logon_none(netr_client_t *clnt, smb_userinfo_t *uinfo);
+
+static uint32_t smb_setup_luinfo(smb_userinfo_t *, netr_client_t *, uid_t);
+
+static int smb_token_is_member(smb_token_t *token, nt_sid_t *sid);
+static int smb_token_is_valid(smb_token_t *token);
+static smb_win_grps_t *smb_token_create_wingrps(smb_userinfo_t *user_info);
+
+static smb_posix_grps_t *smb_token_create_pxgrps(uid_t uid);
+
+/* Consolidation private function from Network Repository */
+extern int _getgroupsbymember(const char *, gid_t[], int, int);
+
+static idmap_stat
+smb_token_idmap(smb_token_t *token, smb_idmap_batch_t *sib)
+{
+ idmap_stat stat;
+ smb_idmap_t *sim;
+ smb_id_t *id;
+ int i;
+
+ if (!token || !sib)
+ return (IDMAP_ERR_ARG);
+
+ sim = sib->sib_maps;
+
+ if (token->tkn_flags & SMB_ATF_ANON) {
+ token->tkn_user->i_id = UID_NOBODY;
+ token->tkn_owner->i_id = UID_NOBODY;
+ } else {
+ /* User SID */
+ id = token->tkn_user;
+ sim->sim_id = &id->i_id;
+ stat = smb_idmap_batch_getid(sib->sib_idmaph, sim++,
+ id->i_sidattr.sid, SMB_IDMAP_USER);
+
+ if (stat != IDMAP_SUCCESS)
+ return (stat);
+
+ /* Owner SID */
+ id = token->tkn_owner;
+ sim->sim_id = &id->i_id;
+ stat = smb_idmap_batch_getid(sib->sib_idmaph, sim++,
+ id->i_sidattr.sid, SMB_IDMAP_UNKNOWN);
+
+ if (stat != IDMAP_SUCCESS)
+ return (stat);
+ }
+
+ /* Primary Group SID */
+ id = token->tkn_primary_grp;
+ sim->sim_id = &id->i_id;
+ stat = smb_idmap_batch_getid(sib->sib_idmaph, sim++,
+ id->i_sidattr.sid, SMB_IDMAP_GROUP);
+
+ if (stat != IDMAP_SUCCESS)
+ return (stat);
+
+ /* Other Windows Group SIDs */
+ for (i = 0; i < token->tkn_win_grps->wg_count; i++, sim++) {
+ id = &token->tkn_win_grps->wg_groups[i];
+ sim->sim_id = &id->i_id;
+ stat = smb_idmap_batch_getid(sib->sib_idmaph, sim,
+ id->i_sidattr.sid, SMB_IDMAP_GROUP);
+
+ if (stat != IDMAP_SUCCESS)
+ break;
+ }
+
+ return (stat);
+}
+
+/*
+ * smb_token_sids2ids
+ *
+ * This will map all the SIDs of the access token to UIDs/GIDs.
+ *
+ * Returns 0 upon success. Otherwise, returns -1.
+ */
+static int
+smb_token_sids2ids(smb_token_t *token)
+{
+ idmap_stat stat;
+ int nmaps, retries = 0;
+ smb_idmap_batch_t sib;
+
+ /*
+ * Number of idmap lookups: user SID, owner SID, primary group SID,
+ * and all Windows group SIDs
+ */
+ if (token->tkn_flags & SMB_ATF_ANON)
+ /*
+ * Don't include user and owner SID, they're Anonymous
+ */
+ nmaps = 1;
+ else
+ nmaps = 3;
+
+ nmaps += token->tkn_win_grps->wg_count;
+
+ do {
+ stat = smb_idmap_batch_create(&sib, nmaps, SMB_IDMAP_SID2ID);
+ if (stat != IDMAP_SUCCESS) {
+ syslog(LOG_ERR, "smb_token_sids2ids:"
+ " idmap_get_create failed (%d)", stat);
+ return (-1);
+ }
+
+ stat = smb_token_idmap(token, &sib);
+ if (stat != IDMAP_SUCCESS) {
+ smb_idmap_batch_destroy(&sib);
+ return (-1);
+ }
+
+ stat = smb_idmap_batch_getmappings(&sib);
+ smb_idmap_batch_destroy(&sib);
+ if (stat == IDMAP_ERR_RPC_HANDLE)
+ if (smb_idmap_restart() < 0)
+ break;
+ } while (stat == IDMAP_ERR_RPC_HANDLE && retries++ < 3);
+
+ return (stat == IDMAP_SUCCESS ? 0 : -1);
+}
+
+/*
+ * smb_token_create_pxgrps
+ *
+ * Setup the POSIX group membership of the access token if the given UID is
+ * a POSIX UID (non-ephemeral). Both the user's primary group and
+ * supplementary groups will be added to the POSIX group array of the access
+ * token.
+ */
+static smb_posix_grps_t *
+smb_token_create_pxgrps(uid_t uid)
+{
+ struct passwd *pwd;
+ smb_posix_grps_t *pgrps;
+ int ngroups_max, num;
+ gid_t *gids;
+
+ if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) < 0) {
+ syslog(LOG_ERR, "smb_logon: failed to get _SC_NGROUPS_MAX");
+ return (NULL);
+ }
+
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ pgrps = malloc(sizeof (smb_posix_grps_t));
+ if (pgrps == NULL)
+ return (NULL);
+
+ pgrps->pg_ngrps = 0;
+ return (pgrps);
+ }
+
+ if (pwd->pw_name == NULL) {
+ pgrps = malloc(sizeof (smb_posix_grps_t));
+ if (pgrps == NULL)
+ return (NULL);
+
+ pgrps->pg_ngrps = 1;
+ pgrps->pg_grps[0] = pwd->pw_gid;
+ return (pgrps);
+ }
+
+ gids = (gid_t *)malloc(ngroups_max * sizeof (gid_t));
+ if (gids == NULL) {
+ return (NULL);
+ }
+ bzero(gids, ngroups_max * sizeof (gid_t));
+
+ gids[0] = pwd->pw_gid;
+
+ /*
+ * Setup the groups starting at index 1 (the last arg)
+ * of gids array.
+ */
+ num = _getgroupsbymember(pwd->pw_name, gids, ngroups_max, 1);
+
+ if (num == -1) {
+ syslog(LOG_ERR, "smb_logon: unable "
+ "to get user's supplementary groups");
+ num = 1;
+ }
+
+ pgrps = (smb_posix_grps_t *)malloc(SMB_POSIX_GRPS_SIZE(num));
+ if (pgrps) {
+ pgrps->pg_ngrps = num;
+ bcopy(gids, pgrps->pg_grps, num * sizeof (gid_t));
+ }
+
+ free(gids);
+ return (pgrps);
+}
+
+/*
+ * smb_token_destroy
+ *
+ * Release all of the memory associated with a token structure. Ensure
+ * that the token has been unlinked before calling.
+ */
+void
+smb_token_destroy(smb_token_t *token)
+{
+ smb_win_grps_t *groups;
+ int i;
+
+ if (token == NULL)
+ return;
+
+ if (token->tkn_user) {
+ free(token->tkn_user->i_sidattr.sid);
+ free(token->tkn_user);
+ }
+
+ if (token->tkn_owner) {
+ free(token->tkn_owner->i_sidattr.sid);
+ free(token->tkn_owner);
+ }
+
+ if (token->tkn_primary_grp) {
+ free(token->tkn_primary_grp->i_sidattr.sid);
+ free(token->tkn_primary_grp);
+ }
+
+ if ((groups = token->tkn_win_grps) != NULL) {
+ for (i = 0; i < groups->wg_count; ++i)
+ free(groups->wg_groups[i].i_sidattr.sid);
+ free(groups);
+ }
+
+ smb_privset_free(token->tkn_privileges);
+
+ free(token->tkn_posix_grps);
+ free(token->tkn_account_name);
+ free(token->tkn_domain_name);
+ free(token->tkn_session_key);
+
+ free(token);
+}
+
+static smb_id_t *
+smb_token_create_id(nt_sid_t *sid)
+{
+ smb_id_t *id;
+
+ id = (smb_id_t *)malloc(sizeof (smb_id_t));
+ if (id == NULL)
+ return (NULL);
+
+ id->i_id = (uid_t)-1;
+ id->i_sidattr.attrs = 7;
+ id->i_sidattr.sid = nt_sid_dup(sid);
+
+ if (id->i_sidattr.sid == NULL) {
+ free(id);
+ id = NULL;
+ }
+
+ return (id);
+}
+
+/*
+ * Token owner should be set to local Administrators group
+ * in two cases:
+ * 1. The logged on user is a member of Domain Admins group
+ * 2. he/she is a member of local Administrators group
+ */
+static smb_id_t *
+smb_token_create_owner(smb_userinfo_t *user_info)
+{
+ smb_id_t *owner;
+
+#ifdef PBSHORTCUT
+ if (user_info->flags & SMB_UINFO_FLAG_ADMIN) {
+ well_known_account_t *wka;
+ /*
+ * Need Winchester update on Group ID as file owner issue.
+ * For now, the file owner will always be set with user SID.
+ */
+ wka = nt_builtin_lookup("Administratrors");
+ owner = smb_token_create_id(wka->binsid);
+ } else
+#endif
+ owner = smb_token_create_id(user_info->user_sid);
+ return (owner);
+}
+
+static smb_privset_t *
+smb_token_create_privs(smb_userinfo_t *user_info)
+{
+ smb_privset_t *privs;
+ nt_group_t *grp;
+
+ privs = smb_privset_new();
+ if (privs == NULL)
+ return (NULL);
+
+ (void) nt_groups_member_privs(user_info->user_sid, privs);
+
+ if (user_info->flags & SMB_UINFO_FLAG_ADMIN) {
+ grp = nt_group_getinfo("Administrators", RWLOCK_READER);
+ if (grp) {
+ nt_group_add_groupprivs(grp, privs);
+ nt_group_putinfo(grp);
+ }
+
+ /*
+ * This privilege is required for view/edit of SACL
+ */
+ smb_privset_enable(privs, SE_SECURITY_LUID);
+ }
+
+ return (privs);
+}
+
+static void
+smb_token_set_flags(smb_token_t *token, smb_userinfo_t *user_info)
+{
+ well_known_account_t *wka;
+
+ if (user_info->flags & SMB_UINFO_FLAG_ANON) {
+ token->tkn_flags |= SMB_ATF_ANON;
+ return;
+ }
+
+ if (user_info->rid == DOMAIN_USER_RID_GUEST) {
+ token->tkn_flags |= SMB_ATF_GUEST;
+ return;
+ }
+
+ wka = nt_builtin_lookup("Administrators");
+ if (wka->binsid && smb_token_is_member(token, wka->binsid))
+ token->tkn_flags |= SMB_ATF_ADMIN;
+
+ wka = nt_builtin_lookup("Power Users");
+ if (wka->binsid && smb_token_is_member(token, wka->binsid))
+ token->tkn_flags |= SMB_ATF_POWERUSER;
+
+ wka = nt_builtin_lookup("Backup Operators");
+ if (wka->binsid && smb_token_is_member(token, wka->binsid))
+ token->tkn_flags |= SMB_ATF_BACKUPOP;
+
+}
+
+/*
+ * smb_token_create
+ *
+ * Build an access token based on the given user information (user_info).
+ *
+ * If everything is successful, a pointer to an access token is
+ * returned. Otherwise a null pointer is returned.
+ */
+static smb_token_t *
+smb_token_create(smb_userinfo_t *user_info)
+{
+ smb_token_t *token;
+
+ if (user_info->sid_name_use != SidTypeUser) {
+ return (NULL);
+ }
+
+ token = (smb_token_t *)malloc(sizeof (smb_token_t));
+ if (token == NULL) {
+ syslog(LOG_ERR, "smb_token_create: resource shortage");
+ return (NULL);
+ }
+ bzero(token, sizeof (smb_token_t));
+
+ /* User */
+ token->tkn_user = smb_token_create_id(user_info->user_sid);
+ if (token->tkn_user == NULL) {
+ smb_token_destroy(token);
+ syslog(LOG_ERR, "smb_token_create: resource shortage");
+ return (NULL);
+ }
+
+ /* Owner */
+ token->tkn_owner = smb_token_create_owner(user_info);
+ if (token->tkn_owner == NULL) {
+ syslog(LOG_ERR, "smb_token_create: resource shortage");
+ smb_token_destroy(token);
+ return (NULL);
+ }
+
+ /* Primary Group */
+ token->tkn_primary_grp = smb_token_create_id(user_info->pgrp_sid);
+ if (token->tkn_primary_grp == NULL) {
+ syslog(LOG_ERR, "smb_token_create: resource shortage");
+ smb_token_destroy(token);
+ return (NULL);
+ }
+
+ /* Privileges */
+ token->tkn_privileges = smb_token_create_privs(user_info);
+ if (token->tkn_privileges == NULL) {
+ smb_token_destroy(token);
+ syslog(LOG_ERR, "smb_token_create: resource shortage");
+ return (NULL);
+ }
+
+ /* Windows Groups */
+ token->tkn_win_grps = smb_token_create_wingrps(user_info);
+
+ smb_token_set_flags(token, user_info);
+
+ /*
+ * IMPORTANT
+ *
+ * This function has to be called after all the SIDs in the
+ * token are setup (i.e. user, owner, primary and supplementary
+ * groups) and before setting up Solaris groups.
+ */
+ if (smb_token_sids2ids(token) != 0) {
+ syslog(LOG_ERR, "smb_token_create: idmap failed");
+ smb_token_destroy(token);
+ return (NULL);
+ }
+
+ /* Solaris Groups */
+ token->tkn_posix_grps = smb_token_create_pxgrps(token->tkn_user->i_id);
+
+ if (user_info->session_key) {
+ token->tkn_session_key = malloc(sizeof (smb_session_key_t));
+ if (token->tkn_session_key == NULL) {
+ syslog(LOG_ERR, "smb_token_create: resource shortage");
+ smb_token_destroy(token);
+ return (NULL);
+ }
+
+ (void) memcpy(token->tkn_session_key,
+ user_info->session_key, sizeof (smb_session_key_t));
+ }
+
+ token->tkn_account_name = strdup(user_info->name);
+ token->tkn_domain_name = strdup(user_info->domain_name);
+
+ if (!smb_token_is_valid(token)) {
+ smb_token_destroy(token);
+ return (NULL);
+ }
+
+ return (token);
+}
+
+/*
+ * smb_token_create_wingrps
+ *
+ * This private function supports smb_token_create() by mapping the group
+ * information in the user_info structure to the form required in an
+ * access token. The main difference is that the user_info contains
+ * RIDs while and access token contains full SIDs. Memory allocated
+ * here will be deallocated as part of smb_token_destroy().
+ *
+ * If everything is successful, a pointer to a smb_win_grps_t
+ * structure is returned. Otherwise a null pointer is returned.
+ */
+static smb_win_grps_t *
+smb_token_create_wingrps(smb_userinfo_t *user_info)
+{
+ static char *wk_grps[] =
+ {"Authenticated Users", "NETWORK", "Administrators"};
+ smb_win_grps_t *tkn_grps;
+ smb_sid_attrs_t *dlg_grps;
+ smb_rid_attrs_t *g_grps;
+ smb_sid_attrs_t *grp;
+ nt_sid_t *builtin_sid;
+ uint32_t n_gg, n_lg, n_dlg, n_wg;
+ uint32_t i, j;
+ int size, count;
+
+ if (user_info == NULL)
+ return (NULL);
+
+ n_gg = user_info->n_groups; /* Global Groups */
+ n_dlg = user_info->n_other_grps; /* Domain Local Groups */
+
+ /* Local Groups */
+ n_lg = nt_groups_member_ngroups(user_info->user_sid);
+
+ /* Well known Groups */
+ if ((user_info->flags & SMB_UINFO_FLAG_ADMIN) == SMB_UINFO_FLAG_DADMIN)
+ /* if user is a domain admin but not a local admin */
+ n_wg = 3;
+ else if (user_info->flags & SMB_UINFO_FLAG_ANON)
+ n_wg = 0;
+ else
+ n_wg = 2;
+
+ count = n_gg + n_dlg + n_lg + n_wg;
+ size = sizeof (smb_win_grps_t) + (count * sizeof (smb_id_t));
+
+ tkn_grps = (smb_win_grps_t *)malloc(size);
+ if (tkn_grps == NULL) {
+ return (NULL);
+ }
+ bzero(tkn_grps, size);
+
+ tkn_grps->wg_count = count;
+
+ /* Add global groups */
+ g_grps = user_info->groups;
+ for (i = 0; i < n_gg; ++i) {
+ grp = &tkn_grps->wg_groups[i].i_sidattr;
+ grp->attrs = g_grps[i].attributes;
+ grp->sid = nt_sid_splice(user_info->domain_sid, g_grps[i].rid);
+ }
+
+ if (n_gg == 0) {
+ /*
+ * if there's no global group should add the
+ * primary group.
+ */
+ grp = &tkn_grps->wg_groups[i++].i_sidattr;
+ grp->attrs = 0x7;
+ grp->sid = nt_sid_dup(user_info->pgrp_sid);
+ tkn_grps->wg_count++;
+ }
+
+ /* Add domain local groups */
+ dlg_grps = user_info->other_grps;
+ for (j = 0; j < n_dlg; ++j, ++i) {
+ grp = &tkn_grps->wg_groups[i].i_sidattr;
+ grp->attrs = dlg_grps[j].attrs;
+ grp->sid = nt_sid_dup(dlg_grps[j].sid);
+ }
+
+ if (n_lg) {
+ /* Add local groups */
+ (void) nt_groups_member_groups(user_info->user_sid,
+ &tkn_grps->wg_groups[i], n_lg);
+ i += n_lg;
+ }
+
+ /* Add well known groups */
+ for (j = 0; j < n_wg; ++j, ++i) {
+ builtin_sid = nt_builtin_lookup_name(wk_grps[j], NULL);
+ tkn_grps->wg_groups[i].i_sidattr.sid = builtin_sid;
+ tkn_grps->wg_groups[i].i_sidattr.attrs = 0x7;
+ }
+
+ return (tkn_grps);
+}
+
+/*
+ * smb_logon
+ *
+ * Performs user authentication and creates a token if the
+ * authentication is successful.
+ *
+ * Returns pointer to the created token.
+ */
+smb_token_t *
+smb_logon(netr_client_t *clnt)
+{
+ smb_token_t *token = NULL;
+ smb_userinfo_t *uinfo;
+ uint32_t status;
+
+ if ((uinfo = mlsvc_alloc_user_info()) == 0)
+ return (NULL);
+
+ switch (clnt->flags) {
+ case NETR_CFLG_DOMAIN:
+ /* Pass through authentication with DC */
+ status = smb_logon_domain(clnt, uinfo);
+ break;
+
+ case NETR_CFLG_LOCAL:
+ /* Local authentication */
+ status = smb_logon_local(clnt, uinfo);
+ break;
+
+ case NETR_CFLG_ANON:
+ /* Anonymous user; no authentication */
+ status = smb_logon_none(clnt, uinfo);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (status == NT_STATUS_SUCCESS)
+ token = smb_token_create(uinfo);
+
+ mlsvc_free_user_info(uinfo);
+ return (token);
+}
+
+/*
+ * smb_logon_domain
+ *
+ * Performs pass through authentication with PDC.
+ */
+static uint32_t
+smb_logon_domain(netr_client_t *clnt, smb_userinfo_t *uinfo)
+{
+ uint32_t status;
+
+ if ((status = netlogon_logon(clnt, uinfo)) != 0) {
+ if (status == NT_STATUS_CANT_ACCESS_DOMAIN_INFO) {
+ if ((status = netlogon_logon(clnt, uinfo)) != 0) {
+ syslog(LOG_INFO, "SmbLogon[%s\\%s]: %s",
+ clnt->domain, clnt->username,
+ xlate_nt_status(status));
+ return (status);
+ }
+ }
+ }
+
+ return (status);
+}
+
+/*
+ * smb_logon_local
+ *
+ * Check to see if connected user has an entry in the local
+ * smbpasswd database. If it has, tries both LM hash and NT
+ * hash with user's password(s) to authenticate the user.
+ */
+static uint32_t
+smb_logon_local(netr_client_t *clnt, smb_userinfo_t *uinfo)
+{
+ smb_passwd_t smbpw;
+ boolean_t lm_ok, nt_ok;
+ uint32_t status;
+
+ if (smb_pwd_getpasswd(clnt->username, &smbpw) == NULL) {
+ /*
+ * If user doesn't have entry either in smbpasswd
+ * or passwd it's considered as an invalid user.
+ */
+ status = NT_STATUS_NO_SUCH_USER;
+ syslog(LOG_NOTICE, "SmbLogon[%s\\%s]: %s",
+ clnt->domain, clnt->username,
+ xlate_nt_status(status));
+ return (status);
+ }
+
+ if (smbpw.pw_flags & SMB_PWF_DISABLE)
+ return (NT_STATUS_ACCOUNT_DISABLED);
+
+ nt_ok = lm_ok = B_FALSE;
+ if ((smbpw.pw_flags & SMB_PWF_LM) &&
+ (clnt->lm_password.lm_password_len != 0)) {
+ lm_ok = smb_auth_validate_lm(
+ clnt->challenge_key.challenge_key_val,
+ clnt->challenge_key.challenge_key_len,
+ &smbpw,
+ clnt->lm_password.lm_password_val,
+ clnt->lm_password.lm_password_len,
+ clnt->username);
+ }
+
+ if (!lm_ok && (clnt->nt_password.nt_password_len != 0)) {
+ nt_ok = smb_auth_validate_nt(
+ clnt->challenge_key.challenge_key_val,
+ clnt->challenge_key.challenge_key_len,
+ &smbpw,
+ clnt->nt_password.nt_password_val,
+ clnt->nt_password.nt_password_len,
+ clnt->username);
+ }
+
+ if (!nt_ok && !lm_ok) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ syslog(LOG_NOTICE, "SmbLogon[%s\\%s]: %s",
+ clnt->domain, clnt->username,
+ xlate_nt_status(status));
+ return (status);
+ }
+
+ status = smb_setup_luinfo(uinfo, clnt, smbpw.pw_uid);
+ return (status);
+}
+
+/*
+ * smb_logon_none
+ *
+ * Setup user information for anonymous user.
+ * No authentication is required.
+ */
+static uint32_t
+smb_logon_none(netr_client_t *clnt, smb_userinfo_t *uinfo)
+{
+ return (smb_setup_luinfo(uinfo, clnt, (uid_t)-1));
+}
+
+/*
+ * smb_setup_luinfo
+ *
+ * Setup local user information based on the client information and
+ * user's record in the local password file.
+ */
+static uint32_t
+smb_setup_luinfo(smb_userinfo_t *lui, netr_client_t *clnt, uid_t uid)
+{
+ idmap_stat stat;
+ smb_idmap_batch_t sib;
+ smb_idmap_t *umap, *gmap;
+ nt_group_t *grp;
+ struct passwd pw;
+ char pwbuf[1024];
+
+ lui->sid_name_use = SidTypeUser;
+ lui->domain_sid = nt_sid_dup(nt_domain_local_sid());
+ lui->name = strdup(clnt->username);
+ lui->domain_name = strdup(clnt->domain);
+ lui->n_groups = 0;
+ lui->groups = NULL;
+ lui->n_other_grps = 0;
+ lui->other_grps = NULL;
+ lui->flags = 0;
+
+ if (lui->name == NULL || lui->domain_name == NULL ||
+ lui->domain_sid == NULL)
+ return (NT_STATUS_INVALID_PARAMETER);
+
+ if (clnt->flags & NETR_CFLG_ANON) {
+ lui->user_sid = nt_builtin_lookup_name("Anonymous", NULL);
+ lui->pgrp_sid = nt_builtin_lookup_name("Anonymous", NULL);
+ lui->flags = SMB_UINFO_FLAG_ANON;
+
+ if (lui->user_sid == NULL || lui->pgrp_sid == NULL)
+ return (NT_STATUS_NO_MEMORY);
+
+ return (NT_STATUS_SUCCESS);
+ }
+
+ if (getpwuid_r(uid, &pw, pwbuf, sizeof (pwbuf)) == NULL)
+ return (NT_STATUS_NO_SUCH_USER);
+
+ /* Get the SID for user's uid & gid */
+ stat = smb_idmap_batch_create(&sib, 2, SMB_IDMAP_ID2SID);
+ if (stat != IDMAP_SUCCESS) {
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ umap = &sib.sib_maps[0];
+ stat = smb_idmap_batch_getsid(sib.sib_idmaph, umap, pw.pw_uid,
+ SMB_IDMAP_USER);
+
+ if (stat != IDMAP_SUCCESS) {
+ smb_idmap_batch_destroy(&sib);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ gmap = &sib.sib_maps[1];
+ stat = smb_idmap_batch_getsid(sib.sib_idmaph, gmap, pw.pw_gid,
+ SMB_IDMAP_GROUP);
+
+ if (stat != IDMAP_SUCCESS) {
+ smb_idmap_batch_destroy(&sib);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ stat = smb_idmap_batch_getmappings(&sib);
+
+ if (stat != IDMAP_SUCCESS) {
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ lui->rid = umap->sim_rid;
+ lui->user_sid = nt_sid_dup(umap->sim_sid);
+
+ lui->primary_group_rid = gmap->sim_rid;
+ lui->pgrp_sid = nt_sid_dup(gmap->sim_sid);
+
+ smb_idmap_batch_destroy(&sib);
+
+ if ((lui->user_sid == NULL) || (lui->pgrp_sid == NULL))
+ return (NT_STATUS_NO_MEMORY);
+
+ grp = nt_group_getinfo("Administrators", RWLOCK_READER);
+ if (grp) {
+ if (nt_group_is_member(grp, lui->user_sid))
+ lui->flags = SMB_UINFO_FLAG_LADMIN;
+ nt_group_putinfo(grp);
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * smb_token_is_valid
+ *
+ * check to see if specified fields of the given access
+ * token are valid.
+ * Returns 1 if all of them are valid; otherwise 0.
+ */
+static int
+smb_token_is_valid(smb_token_t *token)
+{
+ int valid;
+
+ valid = (token->tkn_user != 0) &&
+ (token->tkn_user->i_sidattr.sid != 0) &&
+ (token->tkn_privileges != 0) &&
+ (token->tkn_win_grps != 0) &&
+ (token->tkn_owner != 0) &&
+ (token->tkn_owner->i_sidattr.sid != 0) &&
+ (token->tkn_primary_grp != 0) &&
+ (token->tkn_primary_grp->i_sidattr.sid != 0);
+
+ return (valid);
+}
+
+/*
+ * smb_token_user_sid
+ *
+ * Return a pointer to the user SID in the specified token. A null
+ * pointer indicates an error.
+ */
+static nt_sid_t *
+smb_token_user_sid(smb_token_t *token)
+{
+ if (token && token->tkn_user)
+ return ((token)->tkn_user->i_sidattr.sid);
+
+ return (NULL);
+}
+
+/*
+ * smb_token_group_sid
+ *
+ * Return a pointer to the group SID as indicated by the iterator.
+ * Setting the iterator to 0 before calling this function will return
+ * the first group, which will always be the primary group. The
+ * iterator will be incremented before returning the SID so that this
+ * function can be used to cycle through the groups. The caller can
+ * adjust the iterator as required between calls to obtain any specific
+ * group.
+ *
+ * On success a pointer to the appropriate group SID will be returned.
+ * Otherwise a null pointer will be returned.
+ */
+static nt_sid_t *
+smb_token_group_sid(smb_token_t *token, int *iterator)
+{
+ smb_win_grps_t *groups;
+ int index;
+
+ if (token == NULL || iterator == NULL) {
+ return (NULL);
+ }
+
+ if ((groups = token->tkn_win_grps) == NULL) {
+ return (NULL);
+ }
+
+ index = *iterator;
+
+ if (index < 0 || index >= groups->wg_count) {
+ return (NULL);
+ }
+
+ ++(*iterator);
+ return (groups->wg_groups[index].i_sidattr.sid);
+}
+
+/*
+ * smb_token_is_member
+ *
+ * This function will determine whether or not the specified SID is a
+ * member of a token. The user SID and all group SIDs are tested.
+ * Returns 1 if the SID is a member of the token. Otherwise returns 0.
+ */
+static int
+smb_token_is_member(smb_token_t *token, nt_sid_t *sid)
+{
+ nt_sid_t *tsid;
+ int iterator = 0;
+
+ tsid = smb_token_user_sid(token);
+ while (tsid) {
+ if (nt_sid_is_equal(tsid, sid))
+ return (1);
+
+ tsid = smb_token_group_sid(token, &iterator);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c
new file mode 100644
index 0000000000..a9810bd538
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c
@@ -0,0 +1,413 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/smbinfo.h>
+
+#define SMB_AUTOHOME_KEYSIZ 128
+#define SMB_AUTOHOME_MAXARG 4
+#define SMB_AUTOHOME_BUFSIZ 2048
+
+typedef struct smb_autohome_info {
+ struct smb_autohome_info *magic1;
+ FILE *fp;
+ smb_autohome_t autohome;
+ char buf[SMB_AUTOHOME_BUFSIZ];
+ char *argv[SMB_AUTOHOME_MAXARG];
+ int lineno;
+ struct smb_autohome_info *magic2;
+} smb_autohome_info_t;
+
+static smb_autohome_info_t smb_ai;
+
+static smb_autohome_t *smb_autohome_make_entry(smb_autohome_info_t *);
+static char *smb_autohome_keysub(const char *, char *, int);
+static smb_autohome_info_t *smb_autohome_getinfo(void);
+
+/*
+ * Add an autohome share. See smb_autohome(4) for details.
+ *
+ * If share directory contains backslash path separators, they will
+ * be converted to forward slash to support NT/DOS path style for
+ * autohome shares.
+ *
+ * Returns 0 on success or -1 to indicate an error.
+ */
+int
+smb_autohome_add(const char *username)
+{
+ lmshare_info_t si;
+ char *sharename;
+ smb_autohome_t *ai;
+
+ if (username == NULL)
+ return (-1);
+
+ if ((sharename = strdup(username)) == NULL)
+ return (-1);
+
+ if ((ai = smb_autohome_lookup(sharename)) == NULL) {
+ free(sharename);
+ return (0);
+ }
+
+ (void) memset(&si, 0, sizeof (lmshare_info_t));
+ (void) strlcpy(si.directory, ai->ah_path, MAXPATHLEN);
+ (void) strsubst(si.directory, '\\', '/');
+ (void) strlcpy(si.container, ai->ah_container, MAXPATHLEN);
+
+ if (lmshare_is_dir(si.directory) == 0) {
+ free(sharename);
+ return (0);
+ }
+
+ if (lmshare_exists(sharename) != 0) {
+ (void) lmshare_getinfo(sharename, &si);
+ if (!(si.mode & LMSHRM_TRANS)) {
+ free(sharename);
+ return (0);
+ }
+ } else {
+ (void) strcpy(si.share_name, sharename);
+ si.mode = LMSHRM_TRANS;
+ }
+
+ (void) lmshare_add(&si, 0);
+ free(sharename);
+ return (0);
+}
+
+/*
+ * Remove an autohome share.
+ *
+ * Returns 0 on success or -1 to indicate an error.
+ */
+int
+smb_autohome_remove(const char *username)
+{
+ lmshare_info_t si;
+ char *sharename;
+
+ if (username == NULL)
+ return (-1);
+
+ if ((sharename = strdup(username)) == NULL)
+ return (-1);
+
+ if (lmshare_getinfo(sharename, &si) == NERR_Success) {
+ if (si.mode & LMSHRM_TRANS) {
+ (void) lmshare_delete(sharename, 0);
+ }
+ }
+
+ free(sharename);
+ return (0);
+}
+
+/*
+ * Find out if a share is an autohome share.
+ *
+ * Returns 1 if the share is an autohome share.
+ * Otherwise returns 0.
+ */
+int
+smb_is_autohome(const lmshare_info_t *si)
+{
+ if (si && (si->mode & LMSHRM_TRANS) &&
+ (lmshare_is_restricted((char *)si->share_name) == 0)) {
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Search the autohome database for the specified name. The name cannot
+ * be an empty string or begin with * or +.
+ * 1. Search the file for the specified name.
+ * 2. Check for the wildcard rule and, if present, treat it as a match.
+ * 3. Check for the nsswitch rule and, if present, lookup the name
+ * via the name services. Note that the nsswitch rule will never
+ * be applied if the wildcard rule is present.
+ *
+ * Returns a pointer to the entry on success or null on failure.
+ */
+smb_autohome_t *
+smb_autohome_lookup(const char *name)
+{
+ struct passwd *pw;
+ smb_autohome_t *ah = 0;
+
+ if (name == NULL)
+ return (NULL);
+
+ if (*name == '\0' || *name == '*' || *name == '+')
+ return (NULL);
+
+ smb_autohome_setent();
+
+ while ((ah = smb_autohome_getent(name)) != NULL) {
+ if (strcasecmp(ah->ah_name, name) == 0)
+ break;
+ }
+
+ if (ah == NULL) {
+ smb_autohome_setent();
+
+ while ((ah = smb_autohome_getent(name)) != NULL) {
+ if (strcasecmp(ah->ah_name, "*") == 0) {
+ ah->ah_name = (char *)name;
+ break;
+ }
+ }
+ }
+
+ if (ah == NULL) {
+ smb_autohome_setent();
+
+ while ((ah = smb_autohome_getent("+nsswitch")) != NULL) {
+ if (strcasecmp("+nsswitch", ah->ah_name) != 0)
+ continue;
+ if ((pw = getpwnam(name)) == NULL) {
+ ah = 0;
+ break;
+ }
+
+ ah->ah_name = pw->pw_name;
+
+ if (ah->ah_path)
+ ah->ah_container = ah->ah_path;
+
+ ah->ah_path = pw->pw_dir;
+ break;
+ }
+ }
+
+ smb_autohome_endent();
+ return (ah);
+}
+
+/*
+ * Open or rewind the autohome database.
+ */
+void
+smb_autohome_setent(void)
+{
+ smb_autohome_info_t *si;
+ char *mappath;
+ char filename[MAXNAMELEN];
+
+ if ((si = smb_autohome_getinfo()) != 0) {
+ (void) fseek(si->fp, 0L, SEEK_SET);
+ si->lineno = 0;
+ return;
+ }
+
+ if ((si = &smb_ai) == 0)
+ return;
+
+ smb_config_rdlock();
+ if ((mappath = smb_config_get(SMB_CI_AUTOHOME_MAP)) == NULL)
+ mappath = SMB_AUTOHOME_PATH;
+ (void) snprintf(filename, MAXNAMELEN, "%s/%s", mappath,
+ SMB_AUTOHOME_FILE);
+ smb_config_unlock();
+
+ if ((si->fp = fopen(filename, "r")) == NULL)
+ return;
+
+ si->magic1 = si;
+ si->magic2 = si;
+ si->lineno = 0;
+}
+
+/*
+ * Close the autohome database and invalidate the autohome info.
+ * We can't zero the whole info structure because the application
+ * should still have access to the data after the file is closed.
+ */
+void
+smb_autohome_endent(void)
+{
+ smb_autohome_info_t *si;
+
+ if ((si = smb_autohome_getinfo()) != 0) {
+ (void) fclose(si->fp);
+ si->fp = 0;
+ si->magic1 = 0;
+ si->magic2 = 0;
+ }
+}
+
+/*
+ * Return the next entry in the autohome database, opening the file
+ * if necessary. Returns null on EOF or error.
+ *
+ * Note that we are not looking for the specified name. The name is
+ * only used for key substitution, so that the caller sees the entry
+ * in expanded form.
+ */
+smb_autohome_t *
+smb_autohome_getent(const char *name)
+{
+ smb_autohome_info_t *si;
+ char *bp;
+
+ if ((si = smb_autohome_getinfo()) == 0) {
+ smb_autohome_setent();
+
+ if ((si = smb_autohome_getinfo()) == 0)
+ return (0);
+ }
+
+ /*
+ * Find the next non-comment, non-empty line.
+ * Anything after a # is a comment and can be discarded.
+ * Discard a newline to avoid it being included in the parsing
+ * that follows.
+ * Leading and training whitespace is discarded, and replicated
+ * whitespace is compressed to simplify the token parsing,
+ * although strsep() deals with that better than strtok().
+ */
+ do {
+ if (fgets(si->buf, SMB_AUTOHOME_BUFSIZ, si->fp) == 0)
+ return (0);
+
+ ++si->lineno;
+
+ if ((bp = strpbrk(si->buf, "#\r\n")) != 0)
+ *bp = '\0';
+
+ (void) trim_whitespace(si->buf);
+ bp = strcanon(si->buf, " \t");
+ } while (*bp == '\0');
+
+ (void) smb_autohome_keysub(name, si->buf, SMB_AUTOHOME_BUFSIZ);
+ return (smb_autohome_make_entry(si));
+}
+
+/*
+ * Set up an autohome entry from the line buffer. The line should just
+ * contain tokens separated by single whitespace. The line format is:
+ * <username> <home-dir-path> <ADS container>
+ */
+static smb_autohome_t *
+smb_autohome_make_entry(smb_autohome_info_t *si)
+{
+ char *bp;
+ int i;
+
+ bp = si->buf;
+
+ for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i)
+ si->argv[i] = 0;
+
+ for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) {
+ do {
+ if ((si->argv[i] = strsep((char **)&bp, " \t")) == 0)
+ break;
+ } while (*(si->argv[i]) == '\0');
+
+ if (si->argv[i] == 0)
+ break;
+ }
+
+ if ((si->autohome.ah_name = si->argv[0]) == NULL) {
+ /*
+ * Sanity check: the name could be an empty
+ * string but it can't be a null pointer.
+ */
+ return (0);
+ }
+
+ if ((si->autohome.ah_path = si->argv[1]) == NULL)
+ si->autohome.ah_path = "";
+
+ if ((si->autohome.ah_container = si->argv[2]) == NULL)
+ si->autohome.ah_container = "";
+
+ return (&si->autohome);
+}
+
+/*
+ * Substitute the ? and & map keys.
+ * ? is replaced by the first character of the name
+ * & is replaced by the whole name.
+ */
+static char *
+smb_autohome_keysub(const char *name, char *buf, int buflen)
+{
+ char key[SMB_AUTOHOME_KEYSIZ];
+ char *ampersand;
+ char *tmp;
+
+ (void) strlcpy(key, buf, SMB_AUTOHOME_KEYSIZ);
+
+ if ((tmp = strpbrk(key, " \t")) == NULL)
+ return (NULL);
+
+ *tmp = '\0';
+
+ if (strcmp(key, "*") == 0 && name != NULL)
+ (void) strlcpy(key, name, SMB_AUTOHOME_KEYSIZ);
+
+ (void) strsubst(buf, '?', *key);
+
+ while ((ampersand = strchr(buf, '&')) != NULL) {
+ if ((tmp = strdup(ampersand + 1)) == NULL)
+ return (0);
+
+ (void) strlcpy(ampersand, key, buflen);
+ (void) strlcat(ampersand, tmp, buflen);
+ free(tmp);
+ }
+
+ return (buf);
+}
+
+/*
+ * Get a pointer to the context buffer and validate it.
+ */
+static smb_autohome_info_t *
+smb_autohome_getinfo(void)
+{
+ smb_autohome_info_t *si;
+
+ if ((si = &smb_ai) == 0)
+ return (0);
+
+ if ((si->magic1 == si) && (si->magic2 == si) && (si->fp != NULL))
+ return (si);
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_share_util.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_share_util.c
new file mode 100644
index 0000000000..8e5a7ca031
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_share_util.c
@@ -0,0 +1,106 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <pthread.h>
+#include <synch.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <libshare.h>
+#include <smbsrv/lmshare.h>
+
+static pthread_mutex_t smb_group_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Sharemanager shared API */
+
+void
+smb_build_lmshare_info(char *share_name, char *path,
+ sa_optionset_t opts, lmshare_info_t *si)
+{
+ sa_property_t prop;
+ char *val = NULL;
+
+ bzero(si, sizeof (lmshare_info_t));
+ /* Share is read from SMF so it should be permanent */
+ si->mode = LMSHRM_PERM;
+
+ (void) strlcpy(si->directory, path, sizeof (si->directory));
+ (void) strlcpy(si->share_name, share_name, sizeof (si->share_name));
+
+ if (opts == NULL)
+ return;
+
+ prop = (sa_property_t)sa_get_property(opts, SHOPT_AD_CONTAINER);
+ if (prop != NULL) {
+ if ((val = sa_get_property_attr(prop, "value")) != NULL) {
+ (void) strlcpy(si->container, val,
+ sizeof (si->container));
+ free(val);
+ }
+ }
+
+ prop = (sa_property_t)sa_get_property(opts, "description");
+ if (prop != NULL) {
+ if ((val = sa_get_property_attr(prop, "value")) != NULL) {
+ (void) strlcpy(si->comment, val, sizeof (si->comment));
+ free(val);
+ }
+ }
+}
+
+/*
+ * smb_get_smb_share_group
+ *
+ * Creates "smb" share group for putting in shares
+ * created by windows client.
+ */
+sa_group_t
+smb_get_smb_share_group(sa_handle_t handle)
+{
+ sa_group_t group = NULL;
+ int err;
+
+ (void) pthread_mutex_lock(&smb_group_mutex);
+ group = sa_get_group(handle, SMB_DEFAULT_SHARE_GROUP);
+ if (group != NULL) {
+ (void) pthread_mutex_unlock(&smb_group_mutex);
+ return (group);
+ }
+ group = sa_create_group(handle, SMB_DEFAULT_SHARE_GROUP, &err);
+ if (group == NULL) {
+ (void) pthread_mutex_unlock(&smb_group_mutex);
+ return (NULL);
+ }
+ if (group != NULL) {
+ if (sa_create_optionset(group,
+ SMB_DEFAULT_SHARE_GROUP) == NULL) {
+ (void) sa_remove_group(group);
+ group = NULL;
+ }
+ }
+ (void) pthread_mutex_unlock(&smb_group_mutex);
+ return (group);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_client.c b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_client.c
new file mode 100644
index 0000000000..aa9b12d241
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_client.c
@@ -0,0 +1,554 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Server Service (srvsvc) client side RPC library interface. The
+ * srvsvc interface allows a client to query a server for information
+ * on shares, sessions, connections and files on the server. Some
+ * functions are available via anonymous IPC while others require
+ * administrator privilege. Also, some functions return NT status
+ * values while others return Win32 errors codes.
+ */
+
+#include <sys/errno.h>
+#include <stdio.h>
+#include <time.h>
+#include <strings.h>
+#include <time.h>
+
+#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbrdr.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/ndl/srvsvc.ndl>
+#include <smbsrv/mlsvc_util.h>
+
+/*
+ * Information level for NetShareGetInfo.
+ */
+DWORD srvsvc_info_level = 1;
+
+static int srvsvc_net_remote_tod(char *, char *, struct timeval *, struct tm *);
+
+/*
+ * Ensure that an appropriate session and logon exists for the srvsvc
+ * client calls. Open and bind the RPC interface.
+ *
+ * On success 0 is returned. Otherwise a -ve error code.
+ */
+int
+srvsvc_open(int ipc_mode, char *server, char *domain, char *username,
+ char *password, mlsvc_handle_t *handle, mlrpc_heapref_t *heapref)
+{
+ smb_ntdomain_t *di;
+ int fid;
+ int rc;
+
+ if ((di = smb_getdomaininfo(0)) == NULL)
+ return (-1);
+
+ if (server == NULL || domain == NULL) {
+ server = di->server;
+ domain = di->domain;
+ }
+
+ switch (ipc_mode) {
+ case MLSVC_IPC_USER:
+ /*
+ * Use the supplied credentials.
+ */
+ rc = mlsvc_user_logon(server, domain, username, password);
+ break;
+
+ case MLSVC_IPC_ADMIN:
+ /*
+ * Use the resource domain administrator credentials.
+ */
+ server = di->server;
+ domain = di->domain;
+ username = smbrdr_ipc_get_user();
+
+ rc = mlsvc_admin_logon(server, domain);
+ break;
+
+ case MLSVC_IPC_ANON:
+ default:
+ rc = mlsvc_anonymous_logon(server, domain, &username);
+ break;
+ }
+
+ if (rc != 0)
+ return (-1);
+
+ fid = mlsvc_open_pipe(server, domain, username, "\\srvsvc");
+ if (fid < 0)
+ return (-1);
+
+ if ((rc = mlsvc_rpc_bind(handle, fid, "SRVSVC")) < 0) {
+ (void) mlsvc_close_pipe(fid);
+ return (rc);
+ }
+
+ rc = mlsvc_rpc_init(heapref);
+ return (rc);
+}
+
+/*
+ * Close the srvsvc pipe and free the associated context. This function
+ * should only be called if the open was successful.
+ */
+void
+srvsvc_close(mlsvc_handle_t *handle, mlrpc_heapref_t *heapref)
+{
+ mlsvc_rpc_free(handle->context, heapref);
+ (void) mlsvc_close_pipe(handle->context->fid);
+ free(handle->context);
+}
+
+/*
+ * This is a client side routine for NetShareGetInfo.
+ * Levels 0 and 1 work with an anonymous connection but
+ * level 2 requires administrator access.
+ */
+int
+srvsvc_net_share_get_info(char *server, char *domain, char *netname)
+{
+ struct mlsm_NetShareGetInfo arg;
+ mlsvc_handle_t handle;
+ mlrpc_heapref_t heap;
+ int rc;
+ int opnum;
+ struct mslm_NetShareGetInfo0 *info0;
+ struct mslm_NetShareGetInfo1 *info1;
+ struct mslm_NetShareGetInfo2 *info2;
+ int ipc_mode;
+ int len;
+
+ if (netname == NULL)
+ return (-1);
+
+ if (srvsvc_info_level == 2)
+ ipc_mode = MLSVC_IPC_ADMIN;
+ else
+ ipc_mode = MLSVC_IPC_ANON;
+
+ rc = srvsvc_open(ipc_mode, server, domain, 0, 0, &handle, &heap);
+ if (rc != 0)
+ return (-1);
+
+ opnum = SRVSVC_OPNUM_NetShareGetInfo;
+ bzero(&arg, sizeof (struct mlsm_NetShareGetInfo));
+
+ len = strlen(server) + 4;
+ arg.servername = mlrpc_heap_malloc(heap.heap, len);
+ if (arg.servername == NULL) {
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+ arg.netname = (LPTSTR)netname;
+ arg.level = srvsvc_info_level; /* share information level */
+
+ rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap);
+ if ((rc != 0) || (arg.status != 0)) {
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ switch (arg.result.switch_value) {
+ case 0:
+ info0 = arg.result.ru.info0;
+ smb_tracef("srvsvc shi0_netname=%s", info0->shi0_netname);
+ break;
+
+ case 1:
+ info1 = arg.result.ru.info1;
+ smb_tracef("srvsvc shi1_netname=%s", info1->shi1_netname);
+ smb_tracef("srvsvc shi1_type=%u", info1->shi1_type);
+
+ if (info1->shi1_comment)
+ smb_tracef("srvsvc shi1_comment=%s",
+ info1->shi1_comment);
+ break;
+
+ case 2:
+ info2 = arg.result.ru.info2;
+ smb_tracef("srvsvc shi2_netname=%s", info2->shi2_netname);
+ smb_tracef("srvsvc shi2_type=%u", info2->shi2_type);
+
+ if (info2->shi2_comment)
+ smb_tracef("srvsvc shi2_comment=%s",
+ info2->shi2_comment);
+
+ smb_tracef("srvsvc shi2_perms=%d", info2->shi2_permissions);
+ smb_tracef("srvsvc shi2_max_use=%d", info2->shi2_max_uses);
+ smb_tracef("srvsvc shi2_cur_use=%d", info2->shi2_current_uses);
+
+ if (info2->shi2_path)
+ smb_tracef("srvsvc shi2_path=%s", info2->shi2_path);
+
+ if (info2->shi2_passwd)
+ smb_tracef("srvsvc shi2_passwd=%s", info2->shi2_passwd);
+ break;
+
+ default:
+ smb_tracef("srvsvc: unknown level");
+ break;
+ }
+
+ srvsvc_close(&handle, &heap);
+ return (0);
+}
+
+/*
+ * This is a client side routine for NetSessionEnum.
+ * NetSessionEnum requires administrator rights.
+ */
+int
+srvsvc_net_session_enum(char *server, char *domain, char *netname)
+{
+ struct mslm_NetSessionEnum arg;
+ mlsvc_handle_t handle;
+ mlrpc_heapref_t heap;
+ int rc;
+ int opnum;
+ struct mslm_infonres infonres;
+ struct mslm_SESSION_INFO_1 *nsi1;
+ int len;
+
+ if (netname == NULL)
+ return (-1);
+
+ rc = srvsvc_open(MLSVC_IPC_ADMIN, server, domain, 0, 0, &handle, &heap);
+ if (rc != 0)
+ return (-1);
+
+ opnum = SRVSVC_OPNUM_NetSessionEnum;
+ bzero(&arg, sizeof (struct mslm_NetSessionEnum));
+
+ len = strlen(server) + 4;
+ arg.servername = mlrpc_heap_malloc(heap.heap, len);
+ if (arg.servername == NULL) {
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+ infonres.entriesread = 0;
+ infonres.entries = 0;
+ arg.level = 1;
+ arg.result.level = 1;
+ arg.result.bufptr.p = &infonres;
+ arg.resume_handle = 0;
+ arg.pref_max_len = 0xFFFFFFFF;
+
+ rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap);
+ if ((rc != 0) || (arg.status != 0)) {
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ /* Only the first session info is dereferenced. */
+ nsi1 = ((struct mslm_infonres *)arg.result.bufptr.p)->entries;
+
+ smb_tracef("srvsvc switch_value=%d", arg.level);
+ smb_tracef("srvsvc sesi1_cname=%s", nsi1->sesi1_cname);
+ smb_tracef("srvsvc sesi1_uname=%s", nsi1->sesi1_uname);
+ smb_tracef("srvsvc sesi1_nopens=%u", nsi1->sesi1_nopens);
+ smb_tracef("srvsvc sesi1_time=%u", nsi1->sesi1_time);
+ smb_tracef("srvsvc sesi1_itime=%u", nsi1->sesi1_itime);
+ smb_tracef("srvsvc sesi1_uflags=%u", nsi1->sesi1_uflags);
+
+ srvsvc_close(&handle, &heap);
+ return (0);
+}
+
+/*
+ * This is a client side routine for NetConnectEnum.
+ * NetConnectEnum requires administrator rights.
+ * Level 0 and level 1 requests are supported.
+ */
+int
+srvsvc_net_connect_enum(char *server, char *domain, char *netname, int level)
+{
+ struct mslm_NetConnectEnum arg;
+ mlsvc_handle_t handle;
+ mlrpc_heapref_t heap;
+ int rc;
+ int opnum;
+ struct mslm_NetConnectInfo1 info1;
+ struct mslm_NetConnectInfo0 info0;
+ struct mslm_NetConnectInfoBuf1 *cib1;
+ int len;
+
+ if (netname == NULL)
+ return (-1);
+
+ rc = srvsvc_open(MLSVC_IPC_ADMIN, server, domain, 0, 0, &handle, &heap);
+ if (rc != 0)
+ return (-1);
+
+ opnum = SRVSVC_OPNUM_NetConnectEnum;
+ bzero(&arg, sizeof (struct mslm_NetConnectEnum));
+
+ len = strlen(server) + 4;
+ arg.servername = mlrpc_heap_malloc(heap.heap, len);
+ if (arg.servername == NULL) {
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+ arg.qualifier = (LPTSTR)netname;
+
+ switch (level) {
+ case 0:
+ arg.info.level = 0;
+ arg.info.switch_value = 0;
+ arg.info.ru.info0 = &info0;
+ info0.entries_read = 0;
+ info0.ci0 = 0;
+ break;
+ case 1:
+ arg.info.level = 1;
+ arg.info.switch_value = 1;
+ arg.info.ru.info1 = &info1;
+ info1.entries_read = 0;
+ info1.ci1 = 0;
+ break;
+ default:
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ arg.resume_handle = 0;
+ arg.pref_max_len = 0xFFFFFFFF;
+
+ rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap);
+ if ((rc != 0) || (arg.status != 0)) {
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ smb_tracef("srvsvc switch_value=%d", arg.info.switch_value);
+
+ switch (level) {
+ case 0:
+ if (arg.info.ru.info0 && arg.info.ru.info0->ci0) {
+ smb_tracef("srvsvc coni0_id=%x",
+ arg.info.ru.info0->ci0->coni0_id);
+ }
+ break;
+ case 1:
+ if (arg.info.ru.info1 && arg.info.ru.info1->ci1) {
+ cib1 = arg.info.ru.info1->ci1;
+
+ smb_tracef("srvsvc coni_uname=%s",
+ cib1->coni1_username ?
+ (char *)cib1->coni1_username : "(null)");
+ smb_tracef("srvsvc coni1_netname=%s",
+ cib1->coni1_netname ?
+ (char *)cib1->coni1_netname : "(null)");
+ smb_tracef("srvsvc coni1_nopens=%u",
+ cib1->coni1_num_opens);
+ smb_tracef("srvsvc coni1_time=%u", cib1->coni1_time);
+ smb_tracef("srvsvc coni1_num_users=%u",
+ cib1->coni1_num_users);
+ }
+ break;
+
+ default:
+ smb_tracef("srvsvc: unknown level");
+ break;
+ }
+
+ srvsvc_close(&handle, &heap);
+ return (0);
+}
+
+/*
+ * Synchronize the local system clock with the domain controller.
+ */
+void
+srvsvc_timesync(void)
+{
+ smb_ntdomain_t *di;
+ struct timeval tv;
+ struct tm tm;
+ time_t tsecs;
+
+ if ((di = smb_getdomaininfo(0)) == NULL)
+ return;
+
+ if (srvsvc_net_remote_tod(di->server, di->domain, &tv, &tm) != 0)
+ return;
+
+ if (settimeofday(&tv, 0))
+ smb_tracef("unable to set system time");
+
+ tsecs = time(0);
+ (void) localtime_r(&tsecs, &tm);
+ smb_tracef("SrvsvcTimeSync %s", ctime((time_t *)&tv.tv_sec));
+}
+
+/*
+ * NetRemoteTOD to get the current GMT time from a Windows NT server.
+ */
+int
+srvsvc_gettime(unsigned long *t)
+{
+ smb_ntdomain_t *di;
+ struct timeval tv;
+ struct tm tm;
+
+ if ((di = smb_getdomaininfo(0)) == NULL)
+ return (-1);
+
+ if (srvsvc_net_remote_tod(di->server, di->domain, &tv, &tm) != 0)
+ return (-1);
+
+ *t = tv.tv_sec;
+ return (0);
+}
+
+/*
+ * This is a client side routine for NetRemoteTOD, which gets the time
+ * and date from a remote system. The time information is returned in
+ * the timeval and tm.
+ *
+ * typedef struct _TIME_OF_DAY_INFO {
+ * DWORD tod_elapsedt; // seconds since 00:00:00 January 1 1970 GMT
+ * DWORD tod_msecs; // arbitrary milliseconds (since reset)
+ * DWORD tod_hours; // current hour [0-23]
+ * DWORD tod_mins; // current minute [0-59]
+ * DWORD tod_secs; // current second [0-59]
+ * DWORD tod_hunds; // current hundredth (0.01) second [0-99]
+ * LONG tod_timezone; // time zone of the server
+ * DWORD tod_tinterval; // clock tick time interval
+ * DWORD tod_day; // day of the month [1-31]
+ * DWORD tod_month; // month of the year [1-12]
+ * DWORD tod_year; // current year
+ * DWORD tod_weekday; // day of the week since sunday [0-6]
+ * } TIME_OF_DAY_INFO;
+ *
+ * The time zone of the server is calculated in minutes from Greenwich
+ * Mean Time (GMT). For time zones west of Greenwich, the value is
+ * positive; for time zones east of Greenwich, the value is negative.
+ * A value of -1 indicates that the time zone is undefined.
+ *
+ * The clock tick value represents a resolution of one ten-thousandth
+ * (0.0001) second.
+ */
+int
+srvsvc_net_remote_tod(char *server, char *domain, struct timeval *tv,
+ struct tm *tm)
+{
+ char timebuf[64];
+ struct mslm_NetRemoteTOD arg;
+ struct mslm_TIME_OF_DAY_INFO *tod;
+ mlsvc_handle_t handle;
+ mlrpc_heapref_t heap;
+ int rc;
+ int opnum;
+ int len;
+
+ rc = srvsvc_open(MLSVC_IPC_ANON, server, domain, 0, 0, &handle, &heap);
+ if (rc != 0)
+ return (-1);
+
+ opnum = SRVSVC_OPNUM_NetRemoteTOD;
+ bzero(&arg, sizeof (struct mslm_NetRemoteTOD));
+
+ len = strlen(server) + 4;
+ arg.servername = mlrpc_heap_malloc(heap.heap, len);
+ if (arg.servername == NULL) {
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
+
+ rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap);
+ if ((rc != 0) || (arg.status != 0)) {
+ srvsvc_close(&handle, &heap);
+ return (-1);
+ }
+
+ /*
+ * We're assigning milliseconds to microseconds
+ * here but the value's not really relevant.
+ */
+ tod = arg.bufptr;
+
+ if (tv) {
+ tv->tv_sec = tod->tod_elapsedt;
+ tv->tv_usec = tod->tod_msecs;
+ smb_tracef("RemoteTime: %s", ctime(&tv->tv_sec));
+ }
+
+ if (tm) {
+ tm->tm_sec = tod->tod_secs;
+ tm->tm_min = tod->tod_mins;
+ tm->tm_hour = tod->tod_hours;
+ tm->tm_mday = tod->tod_day;
+ tm->tm_mon = tod->tod_month - 1;
+ tm->tm_year = tod->tod_year - 1900;
+ tm->tm_wday = tod->tod_weekday;
+
+ (void) strftime(timebuf, sizeof (timebuf),
+ "NetRemoteTOD: %D %T", tm);
+ smb_tracef("NetRemoteTOD: %s", timebuf);
+ }
+
+ srvsvc_close(&handle, &heap);
+ return (0);
+}
+
+void
+srvsvc_net_test(char *server, char *domain, char *netname)
+{
+ smb_ntdomain_t *di;
+
+ (void) smb_tracef("%s %s %s", server, domain, netname);
+
+ if ((di = smb_getdomaininfo(0)) != NULL) {
+ server = di->server;
+ domain = di->domain;
+ }
+
+ (void) srvsvc_net_share_get_info(server, domain, netname);
+#if 0
+ /*
+ * The NetSessionEnum server-side definition was updated.
+ * Disabled until the client-side has been updated.
+ */
+ (void) srvsvc_net_session_enum(server, domain, netname);
+#endif
+ (void) srvsvc_net_connect_enum(server, domain, netname, 0);
+ (void) srvsvc_net_connect_enum(server, domain, netname, 1);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/i386/Makefile b/usr/src/lib/smbsrv/libmlsvc/i386/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libmlsvc/sparc/Makefile b/usr/src/lib/smbsrv/libmlsvc/sparc/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libmlsvc/sparcv9/Makefile b/usr/src/lib/smbsrv/libmlsvc/sparcv9/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/sparcv9/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libsmb/Makefile b/usr/src/lib/smbsrv/libsmb/Makefile
new file mode 100644
index 0000000000..cbe44c764c
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+HDRS= libsmb.h
+
+include ../Makefile.smbsrv
diff --git a/usr/src/lib/smbsrv/libsmb/Makefile.com b/usr/src/lib/smbsrv/libsmb/Makefile.com
new file mode 100644
index 0000000000..c479406282
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/Makefile.com
@@ -0,0 +1,86 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY= libsmb.a
+VERS= .1
+
+OBJS_SHARED = \
+ smb_common_door_decode.o \
+ smb_match.o \
+ smb_msgbuf.o \
+ smb_native.o \
+ smb_oem.o \
+ smb_opmlang.o \
+ smb_share_door_decode.o \
+ smb_sid.o \
+ smb_status_xlat.o \
+ smb_strcase.o \
+ smb_string.o \
+ smb_token.o \
+ smb_token_xdr.o \
+ smb_utf8.o \
+ smb_xdr_utils.o
+
+OBJS_COMMON = \
+ smb_api_door_calls.o \
+ smb_auth.o \
+ smb_cfg.o \
+ smb_crypt.o \
+ smb_ctxbuf.o \
+ smb_domain.o \
+ smb_door_client.o \
+ smb_door_encdec.o \
+ smb_doorclnt.o \
+ smb_downcalls.o \
+ smb_group_door_encdec.o \
+ smb_group_xdr.o \
+ smb_ht.o \
+ smb_idmap.o \
+ smb_info.o \
+ smb_mac.o \
+ smb_pwdutil.o \
+ smb_privilege.o \
+ smb_scfutil.o \
+ smb_util.o \
+ smb_wins.o \
+ smb_wksids.o
+
+OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED)
+
+include ../../../Makefile.lib
+include ../../Makefile.lib
+
+INCS += -I$(SRC)/common/smbsrv
+
+LDLIBS += -lscf -lmd -lnsl -lpkcs11 -lc -lidmap
+CPPFLAGS += $(INCS) -D_REENTRANT
+
+SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
+ $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c)
+
+include ../../Makefile.targ
+include ../../../Makefile.targ
diff --git a/usr/src/lib/smbsrv/libsmb/amd64/Makefile b/usr/src/lib/smbsrv/libsmb/amd64/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libsmb/common/libsmb.h b/usr/src/lib/smbsrv/libsmb/common/libsmb.h
new file mode 100644
index 0000000000..44da30085d
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h
@@ -0,0 +1,778 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBSMB_H
+#define _LIBSMB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <libscf.h>
+#include <libshare.h>
+
+#include <smbsrv/smb_idmap.h>
+
+/*
+ * XXX - These header files are here, only because other libraries
+ * can compile. Move the header files in to the internal header files
+ * of other libraries, once the restructure is complete. libsmb.h does not
+ * need these header files.
+ */
+#include <smbsrv/lmshare.h>
+#include <smbsrv/lmshare_door.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/smb_door_svc.h>
+#include <smbsrv/alloc.h>
+#include <smbsrv/codepage.h>
+#include <smbsrv/crypt.h>
+#include <smbsrv/ctype.h>
+#include <smbsrv/hash_table.h>
+#include <smbsrv/msgbuf.h>
+#include <smbsrv/oem.h>
+#include <smbsrv/string.h>
+#include <smbsrv/smb_i18n.h>
+#include <smbsrv/wintypes.h>
+#include <smbsrv/smb_xdr.h>
+#include <smbsrv/smbinfo.h>
+/* End of header files to be removed. */
+
+/* Max value length of all SMB properties */
+#define MAX_VALUE_BUFLEN 512
+#define SMB_PI_MAX_DOMAIN_U 48
+
+#define SMBD_FMRI_PREFIX "network/smb/server"
+#define SMBD_DEFAULT_INSTANCE_FMRI "svc:/network/smb/server:default"
+#define SMBD_PG_NAME "smbd"
+#define SMBD_PROTECTED_PG_NAME "read"
+
+#define SMBD_SMF_OK 0
+#define SMBD_SMF_NO_MEMORY 1 /* no memory for data structures */
+#define SMBD_SMF_SYSTEM_ERR 2 /* system error, use errno */
+#define SMBD_SMF_NO_PERMISSION 3 /* no permission for operation */
+
+#define SCH_STATE_UNINIT 0
+#define SCH_STATE_INITIALIZING 1
+#define SCH_STATE_INIT 2
+
+typedef struct smb_scfhandle {
+ scf_handle_t *scf_handle;
+ int scf_state;
+ scf_service_t *scf_service;
+ scf_scope_t *scf_scope;
+ scf_transaction_t *scf_trans;
+ scf_transaction_entry_t *scf_entry;
+ scf_propertygroup_t *scf_pg;
+ scf_instance_t *scf_instance;
+ scf_iter_t *scf_inst_iter;
+ scf_iter_t *scf_pg_iter;
+} smb_scfhandle_t;
+
+/*
+ * CIFS Configuration Management
+ */
+
+/* macros for the description of all config params */
+#define SMB_CD_RDR_IPCMODE "rdr_ipcmode"
+#define SMB_CD_RDR_IPCUSER "rdr_ipcuser"
+#define SMB_CD_RDR_IPCPWD "rdr_ipcpasswd"
+
+#define SMB_CD_OPLOCK_ENABLE "oplock_enable"
+#define SMB_CD_OPLOCK_TIMEOUT "oplock_timeout"
+
+#define SMB_CD_AUTOHOME_MAP "autohome_map"
+
+#define SMB_CD_DOMAIN_SID "domain_sid"
+#define SMB_CD_DOMAIN_MEMB "domain_member"
+#define SMB_CD_DOMAIN_NAME "domain_name"
+#define SMB_CD_DOMAIN_SRV "pdc"
+
+#define SMB_CD_WINS_SRV1 "wins_server_1"
+#define SMB_CD_WINS_SRV2 "wins_server_2"
+#define SMB_CD_WINS_EXCL "wins_exclude"
+
+#define SMB_CD_SRVSVC_SHRSET_ENABLE "srvsvc_sharesetinfo_enable"
+#define SMB_CD_LOGR_ENABLE "logr_enable"
+#define SMB_CD_MLRPC_KALIVE "mlrpc_keep_alive_interval"
+
+#define SMB_CD_MAX_BUFSIZE "max_bufsize"
+#define SMB_CD_MAX_WORKERS "max_workers"
+#define SMB_CD_MAX_CONNECTIONS "max_connections"
+#define SMB_CD_KEEPALIVE "keep_alive"
+#define SMB_CD_RESTRICT_ANON "restrict_anonymous"
+
+#define SMB_CD_SIGNING_ENABLE "signing_enabled"
+#define SMB_CD_SIGNING_REQD "signing_required"
+#define SMB_CD_SIGNING_CHECK "signing_check"
+
+#define SMB_CD_FLUSH_REQUIRED "flush_required"
+#define SMB_CD_SYNC_ENABLE "sync_enable"
+#define SMB_CD_DIRSYMLINK_DISABLE "dir_symlink_disable"
+#define SMB_CD_ANNONCE_QUOTA "announce_quota"
+
+#define SMB_CD_SECURITY "security"
+#define SMB_CD_NBSCOPE "netbios_scope"
+#define SMB_CD_SYS_CMNT "system_comment"
+#define SMB_CD_LM_LEVEL "lmauth_level"
+#define SMB_CD_MSDCS_DISABLE "msdcs_disable"
+
+#define SMB_CD_ADS_ENABLE "ads_enable"
+#define SMB_CD_ADS_USER "ads_user"
+#define SMB_CD_ADS_PASSWD "ads_passwd"
+#define SMB_CD_ADS_DOMAIN "ads_domain"
+#define SMB_CD_ADS_USER_CONTAINER "ads_user_container"
+#define SMB_CD_ADS_SITE "ads_site"
+#define SMB_CD_ADS_IPLOOKUP "ads_ip_lookup"
+
+#define SMB_CD_DYNDNS_ENABLE "ddns_enable"
+#define SMB_CD_DYNDNS_RETRY_COUNT "ddns_retry_cnt"
+#define SMB_CD_DYNDNS_RETRY_SEC "ddns_retry_sec"
+
+#define SMB_CD_MACHINE_PASSWD "machine_passwd"
+
+/* configuration identifier */
+typedef enum {
+ SMB_CI_RDR_IPCMODE = 0,
+ SMB_CI_RDR_IPCUSER,
+ SMB_CI_RDR_IPCPWD,
+
+ SMB_CI_OPLOCK_ENABLE,
+ SMB_CI_OPLOCK_TIMEOUT,
+
+ SMB_CI_AUTOHOME_MAP,
+
+ SMB_CI_DOMAIN_SID,
+ SMB_CI_DOMAIN_MEMB,
+ SMB_CI_DOMAIN_NAME,
+ SMB_CI_DOMAIN_SRV,
+
+ SMB_CI_WINS_SRV1,
+ SMB_CI_WINS_SRV2,
+ SMB_CI_WINS_EXCL,
+
+ SMB_CI_SRVSVC_SHRSET_ENABLE,
+ SMB_CI_LOGR_ENABLE,
+ SMB_CI_MLRPC_KALIVE,
+
+ SMB_CI_MAX_BUFSIZE,
+ SMB_CI_MAX_WORKERS,
+ SMB_CI_MAX_CONNECTIONS,
+ SMB_CI_KEEPALIVE,
+ SMB_CI_RESTRICT_ANON,
+
+ SMB_CI_SIGNING_ENABLE,
+ SMB_CI_SIGNING_REQD,
+ SMB_CI_SIGNING_CHECK,
+
+ SMB_CI_FLUSH_REQUIRED,
+ SMB_CI_SYNC_ENABLE,
+ SMB_CI_DIRSYMLINK_DISABLE,
+ SMB_CI_ANNONCE_QUOTA,
+
+ SMB_CI_SECURITY,
+ SMB_CI_NBSCOPE,
+ SMB_CI_SYS_CMNT,
+ SMB_CI_LM_LEVEL,
+ SMB_CI_MSDCS_DISABLE,
+
+ SMB_CI_ADS_ENABLE,
+ SMB_CI_ADS_USER,
+ SMB_CI_ADS_PASSWD,
+ SMB_CI_ADS_DOMAIN,
+ SMB_CI_ADS_USER_CONTAINER,
+ SMB_CI_ADS_SITE,
+ SMB_CI_ADS_IPLOOKUP,
+
+ SMB_CI_DYNDNS_ENABLE,
+ SMB_CI_DYNDNS_RETRY_COUNT,
+ SMB_CI_DYNDNS_RETRY_SEC,
+
+ SMB_CI_MACHINE_PASSWD,
+ SMB_CI_MAX
+} smb_cfg_id_t;
+
+/* SMF helper functions */
+extern smb_scfhandle_t *smb_smf_scf_init(char *);
+extern void smb_smf_scf_fini(smb_scfhandle_t *);
+extern int smb_smf_start_transaction(smb_scfhandle_t *);
+extern int smb_smf_end_transaction(smb_scfhandle_t *);
+extern int smb_smf_set_string_property(smb_scfhandle_t *, char *, char *);
+extern int smb_smf_get_string_property(smb_scfhandle_t *, char *,
+ char *, size_t);
+extern int smb_smf_set_integer_property(smb_scfhandle_t *, char *, int64_t);
+extern int smb_smf_get_integer_property(smb_scfhandle_t *, char *, int64_t *);
+extern int smb_smf_set_boolean_property(smb_scfhandle_t *, char *, uint8_t);
+extern int smb_smf_get_boolean_property(smb_scfhandle_t *, char *, uint8_t *);
+extern int smb_smf_set_opaque_property(smb_scfhandle_t *, char *,
+ void *, size_t);
+extern int smb_smf_get_opaque_property(smb_scfhandle_t *, char *,
+ void *, size_t);
+extern int smb_smf_create_service_pgroup(smb_scfhandle_t *, char *);
+extern int smb_smf_delete_service_pgroup(smb_scfhandle_t *, char *);
+extern int smb_smf_create_instance_pgroup(smb_scfhandle_t *, char *);
+extern int smb_smf_delete_instance_pgroup(smb_scfhandle_t *, char *);
+extern int smb_smf_delete_property(smb_scfhandle_t *, char *);
+extern int smb_smf_instance_exists(smb_scfhandle_t *, char *);
+extern int smb_smf_instance_create(smb_scfhandle_t *, char *, char *);
+extern int smb_smf_instance_delete(smb_scfhandle_t *, char *);
+extern smb_scfhandle_t *smb_smf_get_iterator(char *);
+extern int smb_smf_get_property(smb_scfhandle_t *, int, char *, char *,
+ size_t);
+extern int smb_smf_set_property(smb_scfhandle_t *, int, char *, char *);
+
+/* Configuration management functions */
+extern int smb_config_load(void);
+extern void smb_config_rdlock(void);
+extern void smb_config_wrlock(void);
+extern void smb_config_unlock(void);
+extern char *smb_config_get(smb_cfg_id_t);
+extern char *smb_config_getstr(smb_cfg_id_t);
+extern int smb_config_getyorn(smb_cfg_id_t);
+extern uint32_t smb_config_getnum(smb_cfg_id_t);
+
+/*
+ * smb_config_getenv
+ *
+ * Retrieves the property value from SMF.
+ * Caller must free the returned buffer.
+ *
+ */
+extern char *smb_config_getenv(smb_cfg_id_t id);
+
+extern int smb_config_set(smb_cfg_id_t, char *);
+extern int smb_config_setnum(smb_cfg_id_t, uint32_t);
+extern uint8_t smb_config_get_fg_flag(void);
+extern int smb_config_setenv(smb_cfg_id_t id, char *);
+extern char *smb_config_get_localsid(void);
+extern int smb_config_secmode_fromstr(char *secmode);
+extern char *smb_config_secmode_tostr(int secmode);
+extern int smb_config_get_secmode(void);
+extern int smb_config_set_secmode(int secmode);
+extern int smb_config_set_idmap_domain(char *value);
+extern int smb_config_set_idmap_gc(char *value);
+extern int smb_config_refresh_idmap(void);
+
+/* smb_door_client.c */
+typedef struct smb_joininfo {
+ char domain_name[SMB_PI_MAX_DOMAIN];
+ char domain_username[BUF_LEN + 1];
+ char domain_passwd[BUF_LEN + 1];
+ uint32_t mode;
+} smb_joininfo_t;
+
+/* APIs to communicate with SMB daemon via door calls */
+extern int smbd_set_param(smb_cfg_id_t, char *);
+extern int smbd_get_param(smb_cfg_id_t, char *);
+extern int smbd_get_security_mode(int *);
+extern int smbd_netbios_reconfig(void);
+extern uint32_t smb_join(smb_joininfo_t *info);
+
+
+#define SMB_DOMAIN_NOMACHINE_SID -1
+#define SMB_DOMAIN_NODOMAIN_SID -2
+
+extern int nt_domain_init(char *resource_domain, uint32_t secmode);
+
+/* Following set of functions, manipulate WINS server configuration */
+extern int smb_wins_allow_list(char *config_list, char *allow_list);
+extern int smb_wins_exclude_list(char *config_list, char *exclude_list);
+extern boolean_t smb_wins_is_excluded(in_addr_t ipaddr,
+ unsigned long *exclude_list, int nexclude);
+extern void smb_wins_build_list(char *buf, uint32_t iplist[], int max_naddr);
+extern int smb_wins_iplist(char *list, uint32_t iplist[], int max_naddr);
+
+/*
+ * Information on a particular domain: the domain name, the
+ * name of a controller (PDC or BDC) and it's ip address.
+ */
+typedef struct smb_ntdomain {
+ char domain[SMB_PI_MAX_DOMAIN_U];
+ char server[SMB_PI_MAX_DOMAIN_U];
+ uint32_t ipaddr;
+} smb_ntdomain_t;
+
+/* SMB domain information management functions */
+extern void smb_purge_domain_info(void);
+extern int smb_is_domain_member(void);
+extern uint8_t smb_get_fg_flag(void);
+extern void smb_set_domain_member(int set);
+extern smb_ntdomain_t *smb_getdomaininfo(uint32_t timeout);
+extern void smb_setdomaininfo(char *domain, char *server, uint32_t ipaddr);
+extern void smb_logdomaininfo(smb_ntdomain_t *di);
+extern uint32_t smb_get_security_mode(void);
+
+extern int nt_priv_presentable_num(void);
+
+/*
+ * Following set of function, handle calls to SMB Kernel driver, via
+ * Kernel doors interface.
+ */
+extern uint64_t smb_dwncall_user_num(void);
+extern int smb_dwncall_share(int, char *, char *);
+
+/*
+ * buffer context structure. This is used to keep track of the buffer
+ * context.
+ *
+ * basep: points to the beginning of the buffer
+ * curp: points to the current offset
+ * endp: points to the limit of the buffer
+ */
+typedef struct {
+ unsigned char *basep;
+ unsigned char *curp;
+ unsigned char *endp;
+} smb_ctxbuf_t;
+
+extern int smb_ctxbuf_init(smb_ctxbuf_t *ctx, unsigned char *buf,
+ size_t buflen);
+extern int smb_ctxbuf_len(smb_ctxbuf_t *ctx);
+extern int smb_ctxbuf_printf(smb_ctxbuf_t *ctx, const char *fmt, ...);
+
+/* Functions to handle SMB daemon communications with idmap service */
+extern int smb_idmap_start(void);
+extern void smb_idmap_stop(void);
+extern int smb_idmap_restart(void);
+
+/* Miscellaneous functions */
+extern void hexdump(unsigned char *, int);
+extern size_t bintohex(const char *, size_t, char *, size_t);
+extern size_t hextobin(const char *, size_t, char *, size_t);
+extern char *trim_whitespace(char *buf);
+extern void randomize(char *, unsigned);
+extern void rand_hash(unsigned char *, size_t, unsigned char *, size_t);
+
+extern int smb_getdomainname(char *, size_t);
+extern int smb_getfqhostname(char *, size_t);
+extern int smb_gethostname(char *, size_t, int);
+extern int smb_getnetbiosname(char *, size_t);
+
+void smb_trace(const char *s);
+void smb_tracef(const char *fmt, ...);
+
+/*
+ * Authentication
+ */
+
+#define SMBAUTH_LM_MAGIC_STR "KGS!@#$%"
+
+#define SMBAUTH_HASH_SZ 16 /* also LM/NTLM/NTLMv2 Hash size */
+#define SMBAUTH_LM_RESP_SZ 24 /* also NTLM Response size */
+#define SMBAUTH_LM_PWD_SZ 14 /* LM password size */
+#define SMBAUTH_V2_CLNT_CHALLENGE_SZ 8 /* both LMv2 and NTLMv2 */
+#define SMBAUTH_SESSION_KEY_SZ SMBAUTH_HASH_SZ
+#define SMBAUTH_HEXHASH_SZ (SMBAUTH_HASH_SZ * 2)
+
+#define SMBAUTH_FAILURE 1
+#define SMBAUTH_SUCCESS 0
+#define MD_DIGEST_LEN 16
+
+/*
+ * Name Types
+ *
+ * The list of names near the end of the data blob (i.e. the ndb_names
+ * field of the smb_auth_data_blob_t data structure) can be classify into
+ * the following types:
+ *
+ * 0x0000 Indicates the end of the list.
+ * 0x0001 The name is a NetBIOS machine name (e.g. server name)
+ * 0x0002 The name is an NT Domain NetBIOS name.
+ * 0x0003 The name is the server's DNS hostname.
+ * 0x0004 The name is a W2K Domain name (a DNS name).
+ */
+#define SMBAUTH_NAME_TYPE_LIST_END 0x0000
+#define SMBAUTH_NAME_TYPE_SERVER_NETBIOS 0x0001
+#define SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS 0x0002
+#define SMBAUTH_NAME_TYPE_SERVER_DNS 0x0003
+#define SMBAUTH_NAME_TYPE_DOMAIN_DNS 0x0004
+
+/*
+ * smb_auth_name_entry_t
+ *
+ * Each name entry in the data blob consists of the following 3 fields:
+ *
+ * nne_type - name type
+ * nne_len - the length of the name
+ * nne_name - the name, in uppercase UCS-2LE Unicode format
+ */
+typedef struct smb_auth_name_entry {
+ unsigned short nne_type;
+ unsigned short nne_len;
+ mts_wchar_t nne_name[SMB_PI_MAX_DOMAIN * 2];
+} smb_auth_name_entry_t;
+
+/*
+ * smb_auth_data_blob
+ *
+ * The format of this NTLMv2 data blob structure is as follow:
+ *
+ * - Blob Signature 0x01010000 (4 bytes)
+ * - Reserved (0x00000000) (4 bytes)
+ * - Timestamp Little-endian, 64-bit signed value representing
+ * the number of tenths of a microsecond since January 1, 1601.
+ * (8 bytes)
+ * - Client Challenge (8 bytes)
+ * - Unknown1 (4 bytes)
+ * - List of Target Information (variable length)
+ * - Unknown2 (4 bytes)
+ */
+typedef struct smb_auth_data_blob {
+ unsigned char ndb_signature[4];
+ unsigned char ndb_reserved[4];
+ uint64_t ndb_timestamp;
+ unsigned char ndb_clnt_challenge[SMBAUTH_V2_CLNT_CHALLENGE_SZ];
+ unsigned char ndb_unknown[4];
+ smb_auth_name_entry_t ndb_names[2];
+ unsigned char ndb_unknown2[4];
+} smb_auth_data_blob_t;
+
+#define SMBAUTH_BLOB_MAXLEN (sizeof (smb_auth_data_blob_t))
+#define SMBAUTH_CI_MAXLEN SMBAUTH_LM_RESP_SZ
+#define SMBAUTH_CS_MAXLEN (SMBAUTH_BLOB_MAXLEN + SMBAUTH_HASH_SZ)
+
+/*
+ * smb_auth_info_t
+ *
+ * The structure contains all the authentication information
+ * needed for the preparaton of the SMBSessionSetupAndx request
+ * and the user session key.
+ *
+ * hash - NTLM hash
+ * hash_v2 - NTLMv2 hash
+ * ci_len - the length of the case-insensitive password
+ * ci - case-insensitive password
+ * (If NTLMv2 authentication mechanism is used, it
+ * represents the LMv2 response. Otherwise, it
+ * is empty.)
+ * cs_len - the length of the case-sensitive password
+ * cs - case-sensitive password
+ * (If NTLMv2 authentication mechanism is used, it
+ * represents the NTLMv2 response. Otherwise, it
+ * represents the NTLM response.)
+ * data_blob - NTLMv2 data blob
+ */
+typedef struct smb_auth_info {
+ unsigned char hash[SMBAUTH_HASH_SZ];
+ unsigned char hash_v2[SMBAUTH_HASH_SZ];
+ unsigned short ci_len;
+ unsigned char ci[SMBAUTH_CI_MAXLEN];
+ unsigned short cs_len;
+ unsigned char cs[SMBAUTH_CS_MAXLEN];
+ int lmcompatibility_lvl;
+ smb_auth_data_blob_t data_blob;
+} smb_auth_info_t;
+
+extern int smb_getdomainname(char *, size_t);
+extern int smb_getfqhostname(char *, size_t);
+extern int smb_gethostname(char *, size_t, int);
+extern int smb_getnetbiosname(char *, size_t);
+
+void smb_trace(const char *s);
+void smb_tracef(const char *fmt, ...);
+
+/*
+ * SMB password management
+ */
+
+#define SMB_PWF_LM 0x01 /* LM hash is present */
+#define SMB_PWF_NT 0x02 /* NT hash is present */
+#define SMB_PWF_DISABLE 0x04 /* Account is disabled */
+
+typedef struct smb_passwd {
+ uid_t pw_uid;
+ uint32_t pw_flags;
+ unsigned char pw_lmhash[SMBAUTH_HASH_SZ];
+ unsigned char pw_nthash[SMBAUTH_HASH_SZ];
+} smb_passwd_t;
+
+/*
+ * Control flags passed to smb_pwd_setcntl
+ */
+#define SMB_PWC_DISABLE 0x01
+#define SMB_PWC_ENABLE 0x02
+#define SMB_PWC_NOLM 0x04
+
+#define SMB_PWE_SUCCESS 0
+#define SMB_PWE_USER_UNKNOWN 1
+#define SMB_PWE_USER_DISABLE 2
+#define SMB_PWE_CLOSE_FAILED 3
+#define SMB_PWE_OPEN_FAILED 4
+#define SMB_PWE_WRITE_FAILED 6
+#define SMB_PWE_UPDATE_FAILED 7
+#define SMB_PWE_STAT_FAILED 8
+#define SMB_PWE_BUSY 9
+#define SMB_PWE_DENIED 10
+#define SMB_PWE_SYSTEM_ERROR 11
+#define SMB_PWE_MAX 12
+
+extern smb_passwd_t *smb_pwd_getpasswd(const char *, smb_passwd_t *);
+extern int smb_pwd_setpasswd(const char *, const char *);
+extern int smb_pwd_setcntl(const char *, int);
+
+extern int smb_auth_qnd_unicode(mts_wchar_t *dst, char *src, int length);
+extern int smb_auth_hmac_md5(unsigned char *data, int data_len,
+ unsigned char *key, int key_len, unsigned char *digest);
+
+/*
+ * A variation on HMAC-MD5 known as HMACT64 is used by Windows systems.
+ * The HMACT64() function is the same as the HMAC-MD5() except that
+ * it truncates the input key to 64 bytes rather than hashing it down
+ * to 16 bytes using the MD5() function.
+ */
+#define SMBAUTH_HMACT64(D, Ds, K, Ks, digest) \
+ smb_auth_hmac_md5(D, Ds, K, (Ks > 64) ? 64 : Ks, digest)
+
+extern int smb_auth_DES(unsigned char *, int, unsigned char *, int,
+ unsigned char *, int);
+
+extern int smb_auth_md4(unsigned char *, unsigned char *, int);
+extern int smb_auth_lm_hash(char *, unsigned char *);
+extern int smb_auth_ntlm_hash(char *, unsigned char *);
+
+extern int smb_auth_set_info(char *, char *,
+ unsigned char *, char *, unsigned char *,
+ int, int, smb_auth_info_t *);
+
+extern int smb_auth_gen_session_key(smb_auth_info_t *, unsigned char *);
+
+boolean_t smb_auth_validate_lm(unsigned char *, uint32_t, smb_passwd_t *,
+ unsigned char *, int, char *);
+boolean_t smb_auth_validate_nt(unsigned char *, uint32_t, smb_passwd_t *,
+ unsigned char *, int, char *);
+
+/*
+ * SMB MAC Signing
+ */
+
+#define SMB_MAC_KEY_SZ (SMBAUTH_SESSION_KEY_SZ + SMBAUTH_CS_MAXLEN)
+#define SMB_SIG_OFFS 14 /* signature field offset within header */
+#define SMB_SIG_SIZE 8 /* SMB signature size */
+
+/*
+ * Signing flags:
+ *
+ * SMB_SCF_ENABLE Signing is enabled.
+ *
+ * SMB_SCF_REQUIRED Signing is enabled and required.
+ * This flag shouldn't be set if
+ * SMB_SCF_ENABLE isn't set.
+ *
+ * SMB_SCF_STARTED Signing will start after receiving
+ * the first non-anonymous SessionSetup
+ * request.
+ *
+ * SMB_SCF_KEY_ISSET_THIS_LOGON Indicates whether the MAC key has just
+ * been set for this logon. (prior to
+ * sending the SMBSessionSetup request)
+ *
+ */
+#define SMB_SCF_ENABLE 0x01
+#define SMB_SCF_REQUIRED 0x02
+#define SMB_SCF_STARTED 0x04
+#define SMB_SCF_KEY_ISSET_THIS_LOGON 0x08
+
+/*
+ * smb_sign_ctx
+ *
+ * SMB signing context.
+ *
+ * ssc_seqnum sequence number
+ * ssc_keylen mac key length
+ * ssc_mid multiplex id - reserved
+ * ssc_flags flags
+ * ssc_mackey mac key
+ * ssc_sign mac signature
+ *
+ */
+typedef struct smb_sign_ctx {
+ unsigned int ssc_seqnum;
+ unsigned short ssc_keylen;
+ unsigned short ssc_mid;
+ unsigned int ssc_flags;
+ unsigned char ssc_mackey[SMB_MAC_KEY_SZ];
+ unsigned char ssc_sign[SMB_SIG_SIZE];
+} smb_sign_ctx_t;
+
+extern int smb_mac_init(smb_sign_ctx_t *sign_ctx, smb_auth_info_t *auth);
+extern int smb_mac_calc(smb_sign_ctx_t *sign_ctx,
+ const unsigned char *buf, size_t buf_len, unsigned char *mac_sign);
+extern int smb_mac_chk(smb_sign_ctx_t *sign_ctx,
+ const unsigned char *buf, size_t buf_len);
+extern int smb_mac_sign(smb_sign_ctx_t *sign_ctx,
+ unsigned char *buf, size_t buf_len);
+extern void smb_mac_inc_seqnum(smb_sign_ctx_t *sign_ctx);
+extern void smb_mac_dec_seqnum(smb_sign_ctx_t *sign_ctx);
+
+/*
+ * Each domain is categorized using the enum values below.
+ * The local domain refers to the local machine and is named
+ * after the local hostname. The primary domain is the domain
+ * that the system joined. All other domains are either
+ * trusted or untrusted, as defined by the primary domain PDC.
+ *
+ * This enum must be kept in step with the table of strings
+ * in ntdomain.c.
+ */
+typedef enum nt_domain_type {
+ NT_DOMAIN_NULL,
+ NT_DOMAIN_BUILTIN,
+ NT_DOMAIN_LOCAL,
+ NT_DOMAIN_PRIMARY,
+ NT_DOMAIN_ACCOUNT,
+ NT_DOMAIN_TRUSTED,
+ NT_DOMAIN_UNTRUSTED,
+ NT_DOMAIN_NUM_TYPES
+} nt_domain_type_t;
+
+
+/*
+ * This is the information that is held about each domain. The database
+ * is a linked list that is threaded through the domain structures. As
+ * the number of domains in the database should be small (32 max), this
+ * should be sufficient.
+ */
+typedef struct nt_domain {
+ struct nt_domain *next;
+ nt_domain_type_t type;
+ char *name;
+ nt_sid_t *sid;
+} nt_domain_t;
+
+nt_domain_t *nt_domain_new(nt_domain_type_t type, char *name, nt_sid_t *sid);
+void nt_domain_delete(nt_domain_t *domain);
+nt_domain_t *nt_domain_add(nt_domain_t *new_domain);
+void nt_domain_remove(nt_domain_t *domain);
+void nt_domain_flush(nt_domain_type_t domain_type);
+void nt_domain_sync(void);
+char *nt_domain_xlat_type(nt_domain_type_t domain_type);
+nt_domain_type_t nt_domain_xlat_type_name(char *type_name);
+nt_domain_t *nt_domain_lookup_name(char *domain_name);
+nt_domain_t *nt_domain_lookup_sid(nt_sid_t *domain_sid);
+nt_domain_t *nt_domain_lookupbytype(nt_domain_type_t type);
+nt_sid_t *nt_domain_local_sid(void);
+
+#define SMB_GROUP_PER_LIST 5
+
+/*
+ * This structure takes different args passed from the client/server routines
+ * of the SMB local group door service. Extend this structure if a new type
+ * client paramater needs to be passed.
+ */
+typedef struct ntgrp_dr_arg {
+ char *gname;
+ char *desc;
+ char *member;
+ char *newgname;
+ uint32_t privid;
+ uint32_t priv_attr;
+ int offset;
+ char *scope;
+ int type;
+ int count;
+ uint32_t ntstatus;
+} ntgrp_dr_arg_t;
+
+typedef struct ntgrp {
+ DWORD rid; /* Rid of the group */
+ char *name; /* Name of the group */
+ char *desc; /* Desc of gruup */
+ char *type; /* sid_name_use */
+ char *sid; /* Sid */
+ DWORD attr; /* Attribute */
+} ntgrp_t;
+
+typedef struct ntgrp_list {
+ int cnt;
+ ntgrp_t groups[SMB_GROUP_PER_LIST];
+} ntgrp_list_t;
+
+typedef char *members_list;
+typedef struct ntgrp_member_list {
+ DWORD rid; /* Rid of the group in which members belong */
+ int cnt; /* members */
+ members_list members[SMB_GROUP_PER_LIST];
+} ntgrp_member_list_t;
+
+typedef struct ntpriv {
+ DWORD id; /* Id of priv */
+ char *name; /* Name of priv */
+} ntpriv_t;
+typedef ntpriv_t *privs_t;
+
+typedef struct ntpriv_list {
+ int cnt; /* Number of privs */
+ privs_t privs[ANY_SIZE_ARRAY]; /* privs only presentable ones */
+} ntpriv_list_t;
+
+
+/* the xdr functions */
+extern bool_t xdr_ntgrp_dr_arg_t(XDR *, ntgrp_dr_arg_t *);
+extern bool_t xdr_ntgrp_t(XDR *, ntgrp_t *);
+extern bool_t xdr_ntgrp_list_t(XDR *, ntgrp_list_t *);
+extern bool_t xdr_members_list(XDR *, members_list *);
+extern bool_t xdr_ntgrp_member_list_t(XDR *, ntgrp_member_list_t *);
+extern bool_t xdr_ntpriv_t(XDR *, ntpriv_t *);
+extern bool_t xdr_privs_t(XDR *, privs_t *);
+extern bool_t xdr_ntpriv_list_t(XDR *, ntpriv_list_t *);
+
+extern void smb_group_free_memberlist(ntgrp_member_list_t *, int);
+extern void smb_group_free_list(ntgrp_list_t *, int);
+extern void smb_group_free_privlist(ntpriv_list_t *, int);
+
+extern uint32_t smb_group_add(char *, char *);
+extern uint32_t smb_group_modify(char *, char *, char *);
+extern uint32_t smb_group_delete(char *);
+extern uint32_t smb_group_member_remove(char *, char *);
+extern uint32_t smb_group_member_add(char *, char *);
+extern uint32_t smb_group_priv_num(int *);
+extern uint32_t smb_group_priv_list(ntpriv_list_t **);
+extern uint32_t smb_group_priv_get(char *, uint32_t, uint32_t *);
+extern uint32_t smb_group_priv_set(char *, uint32_t, uint32_t);
+extern uint32_t smb_group_count(int *);
+extern uint32_t smb_group_list(int, ntgrp_list_t **, char *, int);
+extern uint32_t smb_group_member_count(char *, int *);
+extern uint32_t smb_group_member_list(char *, int, ntgrp_member_list_t **);
+
+extern char *smb_dr_encode_grp_privlist(uint32_t, ntpriv_list_t *, size_t *);
+extern ntpriv_list_t *smb_dr_decode_grp_privlist(char *, size_t);
+
+extern char *smb_dr_encode_grp_list(uint32_t, ntgrp_list_t *, size_t *);
+extern ntgrp_list_t *smb_dr_decode_grp_list(char *, size_t);
+
+extern char *smb_dr_encode_grp_memberlist(uint32_t, ntgrp_member_list_t *,
+ size_t *);
+extern ntgrp_member_list_t *smb_dr_decode_grp_memberlist(char *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSMB_H */
diff --git a/usr/src/lib/smbsrv/libsmb/common/llib-lsmb b/usr/src/lib/smbsrv/libsmb/common/llib-lsmb
new file mode 100644
index 0000000000..e900ce8047
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/llib-lsmb
@@ -0,0 +1,31 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <smbsrv/libsmb.h>
diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
new file mode 100644
index 0000000000..05c89f93ff
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
@@ -0,0 +1,325 @@
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SUNWprivate {
+ global:
+ bintohex;
+ codepage_islower;
+ codepage_isupper;
+ codepage_tolower;
+ codepage_toupper;
+ hexdump;
+ hextobin;
+ ht_add_item;
+ ht_clean_table;
+ ht_clear_delete;
+ ht_create_table;
+ ht_destroy_table;
+ ht_find_item;
+ ht_findfirst;
+ ht_findnext;
+ ht_findnext;
+ ht_get_total_items;
+ ht_mark_delete;
+ ht_register_callback;
+ ht_remove_item;
+ ht_replace_item;
+ ht_set_cmpfn;
+ smb_msgbuf_base;
+ smb_msgbuf_decode;
+ smb_msgbuf_dword_align;
+ smb_msgbuf_encode;
+ smb_msgbuf_fclear;
+ smb_msgbuf_fset;
+ smb_msgbuf_has_space;
+ smb_msgbuf_init;
+ smb_msgbuf_size;
+ smb_msgbuf_term;
+ smb_msgbuf_used;
+ smb_msgbuf_word_align;
+ mts_mbstos;
+ mts_mbstowcs;
+ mts_mbtowc;
+ mts_sbequiv_strlen;
+ mts_stombs;
+ mts_wcequiv_strlen;
+ mts_wcstombs;
+ mts_wctomb;
+ netr_client_mkabsolute;
+ nt_builtin_findfirst;
+ nt_builtin_findnext;
+ nt_builtin_fini;
+ nt_builtin_init;
+ nt_builtin_is_wellknown;
+ nt_builtin_lookup;
+ nt_builtin_lookup_domain;
+ nt_builtin_lookup_name;
+ nt_builtin_lookup_sid;
+ nt_domain_add;
+ nt_domain_flush;
+ nt_domain_init;
+ nt_domain_local_sid;
+ nt_domain_lookup_name;
+ nt_domain_lookup_sid;
+ nt_domain_lookupbytype;
+ nt_domain_new;
+ nt_sid_dup;
+ nt_sid_format2;
+ nt_sid_format;
+ nt_sid_gen_null_sid;
+ nt_sid_get_rid;
+ nt_sid_is_builtin;
+ nt_sid_is_equal;
+ nt_sid_is_indomain;
+ nt_sid_is_local;
+ nt_sid_is_valid;
+ nt_sid_length;
+ nt_sid_logf;
+ nt_sid_name_use;
+ nt_sid_splice;
+ nt_sid_split;
+ nt_sid_strtosid;
+ oem_get_smb_cpid;
+ oem_get_telnet_cpid;
+ oem_language_set;
+ oemstounicodes;
+ rand_hash;
+ randomize;
+ smb_auth_DES;
+ smb_auth_gen_session_key;
+ smb_auth_ntlm_hash;
+ smb_auth_qnd_unicode;
+ smb_auth_set_info;
+ smb_auth_validate_lm;
+ smb_auth_validate_nt;
+ smb_config_get;
+ smb_config_get_fg_flag;
+ smb_config_get_localsid;
+ smb_config_get_secmode;
+ smb_config_getenv;
+ smb_config_getnum;
+ smb_config_getstr;
+ smb_config_getyorn;
+ smb_config_load;
+ smb_config_rdlock;
+ smb_config_refresh_idmap;
+ smb_config_secmode_fromstr;
+ smb_config_secmode_tostr;
+ smb_config_set;
+ smb_config_set_idmap_domain;
+ smb_config_set_idmap_gc;
+ smb_config_set_secmode;
+ smb_config_setenv;
+ smb_config_setnum;
+ smb_config_unlock;
+ smb_config_wrlock;
+ smb_ctxbuf_init;
+ smb_ctxbuf_len;
+ smb_ctxbuf_printf;
+ smb_dr_decode_arg_get_token;
+ smb_dr_decode_common;
+ smb_dr_decode_finish;
+ smb_dr_decode_grp_list;
+ smb_dr_decode_grp_memberlist;
+ smb_dr_decode_grp_privlist;
+ smb_dr_decode_start;
+ smb_dr_decode_string;
+ smb_dr_encode_common;
+ smb_dr_encode_finish;
+ smb_dr_encode_grp_list;
+ smb_dr_encode_grp_memberlist;
+ smb_dr_encode_grp_privlist;
+ smb_dr_encode_res_token;
+ smb_dr_encode_start;
+ smb_dr_encode_string;
+ smb_dr_free_string;
+ smb_dr_get_BYTE;
+ smb_dr_get_buf;
+ smb_dr_get_dword;
+ smb_dr_get_int32;
+ smb_dr_get_lmshare;
+ smb_dr_get_lmshr_iterator;
+ smb_dr_get_lmshr_list;
+ smb_dr_get_opcode;
+ smb_dr_get_res_stat;
+ smb_dr_get_short;
+ smb_dr_get_string;
+ smb_dr_get_uint32;
+ smb_dr_get_uint64;
+ smb_dr_get_ushort;
+ smb_dr_get_word;
+ smb_dr_put_BYTE;
+ smb_dr_put_buf;
+ smb_dr_put_dword;
+ smb_dr_put_int32;
+ smb_dr_put_kconfig;
+ smb_dr_put_lmshare;
+ smb_dr_put_lmshr_iterator;
+ smb_dr_put_lmshr_list;
+ smb_dr_put_short;
+ smb_dr_put_string;
+ smb_dr_put_uint32;
+ smb_dr_put_uint64;
+ smb_dr_put_ushort;
+ smb_dr_put_word;
+ smb_dr_set_opcode;
+ smb_dr_set_res_stat;
+ smb_dr_ulist_free;
+ smb_dwncall_get_users;
+ smb_dwncall_install_callback;
+ smb_dwncall_share;
+ smb_dwncall_user_num;
+ smb_get_fg_flag;
+ smb_get_security_mode;
+ smb_getdomaininfo;
+ smb_getdomainname;
+ smb_getfqhostname;
+ smb_gethostname;
+ smb_getnetbiosname;
+ smb_group_add;
+ smb_group_count;
+ smb_group_delete;
+ smb_group_free_list;
+ smb_group_free_memberlist;
+ smb_group_free_privlist;
+ smb_group_list;
+ smb_group_member_add;
+ smb_group_member_count;
+ smb_group_member_list;
+ smb_group_member_remove;
+ smb_group_modify;
+ smb_group_priv_get;
+ smb_group_priv_list;
+ smb_group_priv_num;
+ smb_group_priv_set;
+ smb_idmap_batch_create;
+ smb_idmap_batch_destroy;
+ smb_idmap_batch_getid;
+ smb_idmap_batch_getmappings;
+ smb_idmap_batch_getsid;
+ smb_idmap_getsid;
+ smb_idmap_restart;
+ smb_idmap_start;
+ smb_idmap_stop;
+ smb_is_domain_member;
+ smb_join;
+ smb_load_kconfig;
+ smb_mac_chk;
+ smb_mac_dec_seqnum;
+ smb_mac_inc_seqnum;
+ smb_mac_init;
+ smb_mac_sign;
+ smb_match83;
+ smb_match;
+ smb_match_ci;
+ smb_priv_getbyname;
+ smb_priv_getbyvalue;
+ smb_priv_presentable_ids;
+ smb_priv_presentable_num;
+ smb_privset_copy;
+ smb_privset_enable;
+ smb_privset_free;
+ smb_privset_init;
+ smb_privset_log;
+ smb_privset_new;
+ smb_privset_query;
+ smb_privset_size;
+ smb_privset_validate;
+ smb_purge_domain_info;
+ smb_trace;
+ smb_tracef;
+ xdr_ntgrp_dr_arg_t;
+ xdr_ntgrp_list_t;
+ xdr_ntgrp_member_list_t;
+ xdr_ntpriv_list_t;
+ smb_pwd_getpasswd;
+ smb_pwd_setcntl;
+ smb_pwd_setpasswd;
+ smb_set_domain_member;
+ smb_setdomaininfo;
+ smb_smf_create_instance_pgroup;
+ smb_smf_create_service_pgroup;
+ smb_smf_delete_instance_pgroup;
+ smb_smf_delete_property;
+ smb_smf_delete_service_pgroup;
+ smb_smf_end_transaction;
+ smb_smf_get_boolean_property;
+ smb_smf_get_integer_property;
+ smb_smf_get_iterator;
+ smb_smf_get_opaque_property;
+ smb_smf_get_string_property;
+ smb_smf_instance_create;
+ smb_smf_instance_delete;
+ smb_smf_instance_exists;
+ smb_smf_scf_fini;
+ smb_smf_scf_init;
+ smb_smf_set_boolean_property;
+ smb_smf_set_integer_property;
+ smb_smf_set_opaque_property;
+ smb_smf_set_string_property;
+ smb_smf_start_transaction;
+ smb_token_log;
+ smb_token_mkselfrel;
+ smb_token_print;
+ smb_token_query_privilege;
+ smb_trace;
+ smb_tracef;
+ smb_wins_allow_list;
+ smb_wins_build_list;
+ smb_wins_exclude_list;
+ smb_wins_iplist;
+ smb_wins_is_excluded;
+ smbd_get_param;
+ smbd_get_security_mode;
+ smbd_netbios_reconfig;
+ smbd_set_param;
+ smbnative_lm_value;
+ smbnative_os_value;
+ smbnative_pdc_value;
+ strcanon;
+ strsep;
+ strsubst;
+ trim_whitespace;
+ unicodestooems;
+ utf8_isstrascii;
+ utf8_isstrlwr;
+ utf8_isstrupr;
+ utf8_strcasecmp;
+ utf8_strlwr;
+ utf8_strncasecmp;
+ utf8_strupr;
+ xdr_ntgrp_dr_arg_t;
+ xdr_ntgrp_list_t;
+ xdr_ntgrp_member_list_t;
+ xdr_ntpriv_list_t;
+ xdr_smb_dr_bytes_t;
+ xdr_smb_dr_string_t;
+ xdr_smb_dr_ulist_t;
+ xdr_smb_dr_user_ctx_t;
+ xlate_nt_status;
+ local:
+ *;
+};
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_api_door_calls.c b/usr/src/lib/smbsrv/libsmb/common/smb_api_door_calls.c
new file mode 100644
index 0000000000..4a84b5d462
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_api_door_calls.c
@@ -0,0 +1,864 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Door calls invoked by CLIs to obtain various SMB door service provided
+ * by SMB daemon.
+ */
+
+#include <syslog.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <smbsrv/libsmb.h>
+#include <smbsrv/wintypes.h>
+#include <smbsrv/smb_door_svc.h>
+#include <smbsrv/smb_common_door.h>
+
+/* indexed via opcode (smb_dr_opcode_t) */
+char *smbapi_desc[] = {
+ "",
+ "",
+ "",
+ "",
+ "SmbapiUserList",
+ "SmbGroupAdd",
+ "SmbGroupDelete",
+ "SmbGroupAddMember",
+ "SmbGroupRemoveMember",
+ "SmbGroupGetCount",
+ "SmbGroupGetCacheSize",
+ "SmbGroupModify",
+ "SmbGroupPresentablePrivNum",
+ "SmbGroupPresentablePriv",
+ "SmbGroupGetPriv",
+ "SmbGroupSetPriv",
+ "SmbGroupListGroups",
+ "SmbGroupListMembers",
+ "SmbGroupMembersCount",
+ 0
+};
+
+/*
+ * This function will return information on the connected users
+ * starting at the given offset.
+ *
+ * At most 50 users (i.e. SMB_DR_MAX_USER) will be returned via this
+ * function. Multiple calls might be needed to obtain all connected
+ * users.
+ *
+ * smb_dr_ulist_free must be called to free memory allocated for the
+ * account and workstation fields of each user in the returned list.
+ */
+int
+smb_api_ulist(int offset, smb_dr_ulist_t *users)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ int rc = -1;
+ uint_t opcode = SMB_DR_USER_LIST;
+ int fd;
+
+ bzero(users, sizeof (smb_dr_ulist_t));
+ buf = smb_dr_encode_common(opcode, &offset, xdr_uint32_t, &buflen);
+ if (!buf)
+ return (-1);
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (-1);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+ if (rbufp) {
+ rc = smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_smb_dr_ulist_t, users);
+
+ }
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+/* Routines for SMB Group Door Client APIs */
+uint32_t
+smb_group_add(char *gname, char *desc)
+{
+ ntgrp_dr_arg_t *args;
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_ADD;
+ int fd;
+
+ if ((gname == 0) || (*gname == 0)) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(args, sizeof (ntgrp_dr_arg_t));
+ args->gname = gname;
+ args->desc = desc;
+ if ((buf = smb_dr_encode_common(opcode, args,
+ xdr_ntgrp_dr_arg_t, &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ free(args);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ free(args);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_delete(char *gname)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_DELETE;
+ int fd;
+
+ if ((gname == 0) || (*gname == 0)) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((buf = smb_dr_encode_string(opcode, gname, &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_member_add(char *gname, char *member)
+{
+ ntgrp_dr_arg_t *args;
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_MEMBER_ADD;
+ int fd;
+
+ if ((gname == 0) || (*gname == 0) ||
+ (member == 0) || (*member == 0)) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(args, sizeof (ntgrp_dr_arg_t));
+ args->gname = gname;
+ args->member = member;
+ if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t,
+ &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ free(args);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ free(args);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_member_remove(char *gname, char *member)
+{
+ ntgrp_dr_arg_t *args;
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_MEMBER_REMOVE;
+ int fd;
+
+ if ((gname == 0) || (*gname == 0) ||
+ (member == 0) || (*member == 0)) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(args, sizeof (ntgrp_dr_arg_t));
+ args->gname = gname;
+ args->member = member;
+ if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t,
+ &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ free(args);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ free(args);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+
+uint32_t
+smb_group_count(int *cnt)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ uint_t opcode = SMB_DR_GROUP_COUNT;
+ int fd;
+
+ if (cnt == 0) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+ *cnt = 0;
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME,
+ smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ if ((buf = smb_dr_set_opcode(opcode, &buflen)) == 0) {
+ (void) close(fd);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, cnt) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+ rc = NT_STATUS_SUCCESS;
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_cachesize(int *sz)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ uint_t opcode = SMB_DR_GROUP_CACHE_SIZE;
+ int fd;
+
+ if (sz == 0) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+ *sz = 0;
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME,
+ smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ if ((buf = smb_dr_set_opcode(opcode, &buflen)) == 0) {
+ (void) close(fd);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, sz) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+ rc = NT_STATUS_SUCCESS;
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_modify(char *gname, char *newgname, char *desc)
+{
+ ntgrp_dr_arg_t *args;
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_MODIFY;
+ int fd;
+
+ if ((gname == 0) || (*gname == 0) ||
+ (newgname == 0) || (*newgname == 0)) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(args, sizeof (ntgrp_dr_arg_t));
+ args->gname = gname;
+ args->desc = desc;
+ args->newgname = newgname;
+ if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t,
+ &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ free(args);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ free(args);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_priv_num(int *num)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_PRIV_NUM;
+ int fd;
+
+ if (num == 0) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+ *num = 0;
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME,
+ smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ if ((buf = smb_dr_set_opcode(opcode, &buflen)) == 0) {
+ (void) close(fd);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, num) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ rc = NT_STATUS_SUCCESS;
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_priv_list(ntpriv_list_t **list)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_PRIV_LIST;
+ int fd;
+ *list = NULL;
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME,
+ smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ if ((buf = smb_dr_set_opcode(opcode, &buflen)) == 0) {
+ (void) close(fd);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result */
+ if (rbufp) {
+ if ((*list = smb_dr_decode_grp_privlist(
+ rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET)) == 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ rc = NT_STATUS_SUCCESS;
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_priv_get(char *gname, uint32_t privid, uint32_t *privval)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ ntgrp_dr_arg_t *args;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_PRIV_GET;
+ int fd;
+ uint32_t retval;
+
+ *privval = SE_PRIVILEGE_DISABLED;
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(args, sizeof (ntgrp_dr_arg_t));
+ args->gname = gname;
+ args->privid = privid;
+ if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t,
+ &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ free(args);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ free(args);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t,
+ &retval) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ *privval = retval;
+ rc = NT_STATUS_SUCCESS;
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_priv_set(char *gname, uint32_t privid, uint32_t priv_attr)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ ntgrp_dr_arg_t *args;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_PRIV_SET;
+ int fd;
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(args, sizeof (ntgrp_dr_arg_t));
+ args->gname = gname;
+ args->privid = privid;
+ args->priv_attr = priv_attr;
+ if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t,
+ &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ free(args);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ free(args);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_list(int offset, ntgrp_list_t **list, char *scope, int type)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ ntgrp_dr_arg_t *args;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_LIST;
+ int fd;
+ *list = NULL;
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(args, sizeof (ntgrp_dr_arg_t));
+ args->offset = offset;
+ args->type = type;
+ args->scope = scope;
+ if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t,
+ &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ free(args);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ free(args);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if ((*list = smb_dr_decode_grp_list(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET)) == 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ rc = NT_STATUS_SUCCESS;
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_member_list(char *gname, int offset, ntgrp_member_list_t **members)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ ntgrp_dr_arg_t *args;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_MEMBER_LIST;
+ int fd;
+ *members = NULL;
+
+ if ((gname == 0) || (*gname == 0)) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory for ret_mem_list",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(args, sizeof (ntgrp_dr_arg_t));
+ args->gname = gname;
+ args->offset = offset;
+ if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t,
+ &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ free(args);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ free(args);
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if (rbufp) {
+ if ((*members = smb_dr_decode_grp_memberlist(
+ rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET)) == 0) {
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+ rc = NT_STATUS_SUCCESS;
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ (void) close(fd);
+ return (rc);
+}
+
+uint32_t
+smb_group_member_count(char *gname, int *cnt)
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ ntgrp_dr_arg_t *dec_args;
+ uint32_t rc = NT_STATUS_UNSUCCESSFUL;
+ int opcode = SMB_DR_GROUP_MEMBER_COUNT;
+ int fd;
+
+ if ((gname == 0) || (*gname == 0) || (cnt == 0)) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)",
+ smbapi_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ /* Encode */
+ if ((buf = smb_dr_encode_string(opcode, gname, &buflen)) == 0) {
+ syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smbapi_desc[opcode]);
+
+ /* Decode Result. */
+ if ((dec_args = (ntgrp_dr_arg_t *)
+ malloc(sizeof (ntgrp_dr_arg_t))) == 0) {
+ syslog(LOG_ERR, "%s: cannot allocate memory",
+ smbapi_desc[opcode]);
+ (void) close(fd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(dec_args, sizeof (ntgrp_dr_arg_t));
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_ntgrp_dr_arg_t, dec_args)
+ != 0) {
+ free(dec_args);
+ (void) close(fd);
+ return (dec_args->ntstatus);
+ }
+ }
+ *cnt = dec_args->count;
+ rc = dec_args->ntstatus;
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ free(dec_args);
+ (void) close(fd);
+ return (rc);
+}
+
+/* Helper functions for local group door service to free up data structures */
+void
+smb_group_free_privlist(ntpriv_list_t *list, int deletelist)
+{
+ int i;
+ if (!list)
+ return;
+ if (list->privs != NULL) {
+ for (i = 0; i < list->cnt; i++) {
+ if (list->privs[i] != NULL) {
+ free(list->privs[i]->name);
+ free(list->privs[i]);
+ }
+ }
+ if (deletelist)
+ free(list);
+ }
+}
+
+void
+smb_group_free_list(ntgrp_list_t *list, int entries_only)
+{
+ int i;
+
+ if (!list) {
+ return;
+ }
+
+ for (i = 0; i < list->cnt; i++) {
+ free(list->groups[i].name);
+ free(list->groups[i].desc);
+ free(list->groups[i].type);
+ free(list->groups[i].sid);
+ }
+ if (!entries_only)
+ free(list);
+}
+
+void
+smb_group_free_memberlist(ntgrp_member_list_t *members,
+ int entries_only)
+{
+ int i;
+
+ if (!members) {
+ return;
+ }
+
+ for (i = 0; i < members->cnt; i++) {
+ free(members->members[i]);
+ }
+ if (!entries_only)
+ free(members);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_auth.c b/usr/src/lib/smbsrv/libsmb/common/smb_auth.c
new file mode 100644
index 0000000000..d8950616db
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_auth.c
@@ -0,0 +1,706 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <strings.h>
+#include <stdlib.h>
+#include <smbsrv/alloc.h>
+#include <smbsrv/codepage.h>
+#include <smbsrv/oem.h>
+#include <smbsrv/ctype.h>
+#include <smbsrv/crypt.h>
+#include <smbsrv/libsmb.h>
+
+extern void randomize(char *data, unsigned len);
+static uint64_t unix_micro_to_nt_time(struct timeval *unix_time);
+
+/*
+ * smb_auth_qnd_unicode
+ *
+ * Quick and dirty unicode conversion!
+ * Returns the length of dst in bytes.
+ */
+int
+smb_auth_qnd_unicode(mts_wchar_t *dst, char *src, int length)
+{
+ int i;
+
+ unsigned int cpid = oem_get_telnet_cpid();
+ unsigned int count;
+ mts_wchar_t new_char;
+
+ if ((count = oemstounicodes(dst, src, length, cpid)) == 0) {
+ for (i = 0; i < length; ++i) {
+ new_char = (mts_wchar_t)src[i] & 0xff;
+ dst[i] = LE_IN16(&new_char);
+ }
+ dst[i] = 0;
+ count = length;
+ }
+
+ return (count * sizeof (mts_wchar_t));
+}
+
+/*
+ * smb_auth_lmupr
+ *
+ * Converts the given LM password to all uppercase.
+ * The standard strupr cannot
+ * be used here because lm_pwd doesn't have to be
+ * nul terminated.
+ */
+static void
+smb_auth_lmupr(unsigned char *lm_pwd)
+{
+ unsigned char *p = lm_pwd;
+ int i;
+
+ for (i = 0; (*p) && (i < SMBAUTH_LM_PWD_SZ); i++) {
+ if (mts_isascii(*p)) {
+ *p = codepage_toupper(*p);
+ p++;
+ }
+ }
+}
+
+/*
+ * smb_auth_lm_hash
+ *
+ * Source: Implementing CIFS (Chris Hertel)
+ *
+ * 1. The password, as entered by user, is either padded with nulls
+ * or trimmed to 14 bytes.
+ * . Note that the 14-byte result string is not handled as a
+ * nul-terminated string.
+ * . The given password is OEM not Unicode
+ *
+ * 2. The 14-byte password is converted to all uppercase
+ *
+ * 3. The result is used as key to encrypt the KGS magic string to
+ * make a 16-byte hash.
+ */
+int
+smb_auth_lm_hash(char *password, unsigned char *lm_hash)
+{
+ unsigned char lm_pwd[SMBAUTH_LM_PWD_SZ];
+
+ bzero((void *)lm_pwd, SMBAUTH_LM_PWD_SZ);
+ (void) strncpy((char *)lm_pwd, password, SMBAUTH_LM_PWD_SZ);
+ smb_auth_lmupr(lm_pwd);
+
+ return (smb_auth_DES(lm_hash, SMBAUTH_HASH_SZ, lm_pwd,
+ SMBAUTH_LM_PWD_SZ, (unsigned char *)SMBAUTH_LM_MAGIC_STR,
+ sizeof (SMBAUTH_LM_MAGIC_STR)));
+}
+
+/*
+ * smb_auth_lm_response
+ *
+ * Create a LM response from the given LM hash and challenge.
+ *
+ * Returns SMBAUTH_FAILURE if any problems occur, SMBAUTH_SUCCESS if
+ * all goes well.
+ */
+static int
+smb_auth_lm_response(unsigned char *hash,
+ unsigned char *challenge, int clen,
+ unsigned char *lm_rsp)
+{
+ unsigned char S21[21];
+
+ /*
+ * 14-byte LM Hash should be padded with 5 nul bytes to create
+ * a 21-byte string to be used in producing LM response
+ */
+ bzero(&S21[SMBAUTH_HASH_SZ], 5);
+ bcopy(hash, S21, SMBAUTH_HASH_SZ);
+
+ /* padded LM Hash -> LM Response */
+ return (smb_auth_DES(lm_rsp, SMBAUTH_LM_RESP_SZ, S21, 21,
+ challenge, clen));
+}
+
+/*
+ * smb_auth_ntlm_hash
+ *
+ * Make NTLM Hash (using MD4) from the given password.
+ * The result will contain a 16-byte NTLM hash.
+ */
+int
+smb_auth_ntlm_hash(char *password, unsigned char *hash)
+{
+ mts_wchar_t *unicode_password;
+ int length;
+ int rc;
+
+ if (password == NULL || hash == NULL)
+ return (SMBAUTH_FAILURE);
+
+ length = strlen(password);
+ unicode_password = (mts_wchar_t *)
+ malloc((length + 1) * sizeof (mts_wchar_t));
+
+ if (unicode_password == NULL)
+ return (SMBAUTH_FAILURE);
+
+ length = smb_auth_qnd_unicode(unicode_password, password, length);
+ rc = smb_auth_md4(hash, (unsigned char *)unicode_password, length);
+
+ free(unicode_password);
+ return (rc);
+}
+
+/*
+ * smb_auth_ntlm_response
+ *
+ * Make LM/NTLM response from the given LM/NTLM Hash and given
+ * challenge.
+ */
+static int
+smb_auth_ntlm_response(unsigned char *hash,
+ unsigned char *challenge, int clen,
+ unsigned char *ntlm_rsp)
+{
+ unsigned char S21[21];
+
+ bcopy(hash, S21, SMBAUTH_HASH_SZ);
+ bzero(&S21[SMBAUTH_HASH_SZ], 5);
+ if (smb_auth_DES((unsigned char *)ntlm_rsp, SMBAUTH_LM_RESP_SZ,
+ S21, 21, challenge, clen) == SMBAUTH_FAILURE)
+ return (0);
+ return (SMBAUTH_LM_RESP_SZ);
+}
+
+/*
+ * smb_auth_gen_data_blob
+ *
+ * Fill the NTLMv2 data blob structure with information as described in
+ * "Implementing CIFS, The Common Internet File System". (pg. 282)
+ */
+static void
+smb_auth_gen_data_blob(smb_auth_data_blob_t *blob, char *ntdomain)
+{
+ struct timeval now;
+
+ (void) memset(blob->ndb_signature, 1, 2);
+ (void) memset(&blob->ndb_signature[2], 0, 2);
+ (void) memset(blob->ndb_reserved, 0, sizeof (blob->ndb_reserved));
+
+ (void) gettimeofday(&now, 0);
+ blob->ndb_timestamp = unix_micro_to_nt_time(&now);
+ randomize((char *)blob->ndb_clnt_challenge,
+ SMBAUTH_V2_CLNT_CHALLENGE_SZ);
+ (void) memset(blob->ndb_unknown, 0, sizeof (blob->ndb_unknown));
+ blob->ndb_names[0].nne_len = smb_auth_qnd_unicode(
+ blob->ndb_names[0].nne_name, ntdomain, strlen(ntdomain));
+ blob->ndb_names[0].nne_type = SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS;
+ blob->ndb_names[1].nne_len = 0;
+ blob->ndb_names[1].nne_type = SMBAUTH_NAME_TYPE_LIST_END;
+ *blob->ndb_names[1].nne_name = 0;
+ (void) memset(blob->ndb_unknown2, 0, sizeof (blob->ndb_unknown2));
+}
+
+/*
+ * smb_auth_memcpy
+ *
+ * It increments the pointer to the destination buffer for the easy of
+ * concatenation.
+ */
+static void
+smb_auth_memcpy(unsigned char **dstbuf,
+ unsigned char *srcbuf,
+ int srcbuf_len)
+{
+ (void) memcpy(*dstbuf, srcbuf, srcbuf_len);
+ *dstbuf += srcbuf_len;
+}
+
+/*
+ * smb_auth_blob_to_string
+ *
+ * Prepare the data blob string which will be used in NTLMv2 response
+ * generation.
+ *
+ * Assumption: Caller must allocate big enough buffer to prevent buffer
+ * overrun.
+ *
+ * Returns the len of the data blob string.
+ */
+static int
+smb_auth_blob_to_string(smb_auth_data_blob_t *blob, unsigned char *data_blob)
+{
+ unsigned char *bufp = data_blob;
+
+ smb_auth_memcpy(&bufp, blob->ndb_signature,
+ sizeof (blob->ndb_signature));
+ smb_auth_memcpy(&bufp, blob->ndb_reserved,
+ sizeof (blob->ndb_reserved));
+ smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_timestamp,
+ sizeof (blob->ndb_timestamp));
+ smb_auth_memcpy(&bufp, blob->ndb_clnt_challenge,
+ SMBAUTH_V2_CLNT_CHALLENGE_SZ);
+ smb_auth_memcpy(&bufp, blob->ndb_unknown, sizeof (blob->ndb_unknown));
+ smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_type,
+ sizeof (blob->ndb_names[0].nne_type));
+ smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_len,
+ sizeof (blob->ndb_names[0].nne_len));
+ smb_auth_memcpy(&bufp, (unsigned char *)blob->ndb_names[0].nne_name,
+ blob->ndb_names[0].nne_len);
+ smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_type,
+ sizeof (blob->ndb_names[1].nne_type));
+ smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_len,
+ sizeof (blob->ndb_names[1].nne_len));
+ smb_auth_memcpy(&bufp, blob->ndb_unknown2, sizeof (blob->ndb_unknown2));
+
+ /*LINTED E_PTRDIFF_OVERFLOW*/
+ return (bufp - data_blob);
+}
+
+/*
+ * smb_auth_ntlmv2_hash
+ *
+ * The NTLM v2 hash will be created from the given NTLM hash, username,
+ * and the NETBIOS name of the domain.
+ *
+ * The NTLMv2 hash will be returned via the ntlmv2_hash parameter which
+ * will be used in the calculation of the NTLMv2 and LMv2 responses.
+ */
+static int
+smb_auth_ntlmv2_hash(unsigned char *ntlm_hash,
+ char *username,
+ char *ntdomain,
+ unsigned char *ntlmv2_hash)
+{
+ mts_wchar_t *data;
+ int data_len;
+ unsigned char *buf;
+ int rc;
+
+ if (username == NULL || ntdomain == NULL)
+ return (SMBAUTH_FAILURE);
+
+ (void) utf8_strupr(username);
+ (void) utf8_strupr(ntdomain);
+
+ data_len = strlen(username) + strlen(ntdomain);
+ buf = (unsigned char *)malloc((data_len + 1) * sizeof (char));
+ if (buf == NULL)
+ return (SMBAUTH_FAILURE);
+
+ (void) snprintf((char *)buf, data_len + 1, "%s%s", username, ntdomain);
+ data = (mts_wchar_t *)malloc((data_len + 1) * sizeof (mts_wchar_t));
+ if (data == NULL) {
+ free(buf);
+ return (SMBAUTH_FAILURE);
+ }
+
+ data_len = smb_auth_qnd_unicode(data, (char *)buf, data_len);
+ rc = SMBAUTH_HMACT64((unsigned char *)data, data_len, ntlm_hash,
+ SMBAUTH_HASH_SZ, ntlmv2_hash);
+
+ free(buf);
+ free(data);
+ return (rc);
+}
+
+/*
+ * smb_auth_v2_response
+ *
+ * Caculates either the LMv2 or NTLMv2 response.
+ *
+ * Same algorithm is used for calculating both LMv2 or NTLMv2 responses.
+ * This routine will return NTLMv2 response if the data blob information
+ * is passed in as the clnt_data. Otherwise, it will return LMv2 response
+ * with the 8-byte client challenge(a.k.a blip) as the clnt_data.
+ *
+ * (LM/NTLM)v2 response is the hmac-md5 hash of the specified data
+ * (server challenge + NTLMv2 data blob or LMv2 client challenge)
+ * using the NTLMv2 hash as the key.
+ *
+ * Returns the size of the corresponding v2 response upon success.
+ * Otherwise, returns -1 on error.
+ */
+static int
+smb_auth_v2_response(
+ unsigned char *hash,
+ unsigned char *srv_challenge, int slen,
+ unsigned char *clnt_data, int clen,
+ unsigned char *v2_rsp)
+{
+ unsigned char *hmac_data;
+
+ hmac_data = (unsigned char *)malloc((slen + clen) * sizeof (char));
+ if (!hmac_data) {
+ return (-1);
+ }
+
+ (void) memcpy(hmac_data, srv_challenge, slen);
+ (void) memcpy(&hmac_data[slen], clnt_data, clen);
+ if (SMBAUTH_HMACT64(hmac_data, slen + clen, (unsigned char *)hash,
+ SMBAUTH_HASH_SZ, (unsigned char *)v2_rsp) != SMBAUTH_SUCCESS)
+ return (-1);
+ (void) memcpy(&v2_rsp[SMBAUTH_HASH_SZ], clnt_data, clen);
+
+ free(hmac_data);
+ return (SMBAUTH_HASH_SZ + clen);
+}
+
+/*
+ * smb_auth_set_info
+ *
+ * Fill the smb_auth_info instance with either NTLM or NTLMv2 related
+ * authentication information based on the LMCompatibilityLevel.
+ *
+ * If the LMCompatibilityLevel equals 2, the SMB Redirector will perform
+ * NTLM challenge/response authentication which requires the NTLM hash and
+ * NTLM response.
+ *
+ * If the LMCompatibilityLevel is 3 or above, the SMB Redirector will
+ * perfrom NTLMv2 challenge/response authenticatoin which requires the
+ * NTLM hash, NTLMv2 hash, NTLMv2 response and LMv2 response.
+ *
+ * Returns -1 on error. Otherwise, returns 0 upon success.
+ */
+int
+smb_auth_set_info(char *username,
+ char *password,
+ unsigned char *ntlm_hash,
+ char *domain,
+ unsigned char *srv_challenge_key,
+ int srv_challenge_len,
+ int lmcomp_lvl,
+ smb_auth_info_t *auth)
+{
+ unsigned short blob_len;
+ unsigned char blob_buf[SMBAUTH_BLOB_MAXLEN];
+ int rc;
+
+ auth->lmcompatibility_lvl = lmcomp_lvl;
+ if (lmcomp_lvl == 2) {
+ auth->ci_len = 0;
+ *auth->ci = 0;
+ if (!ntlm_hash) {
+ if (smb_auth_ntlm_hash(password, auth->hash) !=
+ SMBAUTH_SUCCESS)
+ return (-1);
+ } else {
+ (void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ);
+ }
+
+ auth->cs_len = smb_auth_ntlm_response(auth->hash,
+ srv_challenge_key, srv_challenge_len, auth->cs);
+ } else {
+ if (!ntlm_hash) {
+ if (smb_auth_ntlm_hash(password, auth->hash) !=
+ SMBAUTH_SUCCESS)
+ return (-1);
+ } else {
+ (void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ);
+ }
+
+ if (smb_auth_ntlmv2_hash(auth->hash, username,
+ domain, auth->hash_v2) != SMBAUTH_SUCCESS)
+ return (-1);
+
+ /* generate data blob */
+ smb_auth_gen_data_blob(&auth->data_blob, domain);
+ blob_len = smb_auth_blob_to_string(&auth->data_blob, blob_buf);
+
+ /* generate NTLMv2 response */
+ rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key,
+ srv_challenge_len, blob_buf, blob_len, auth->cs);
+
+ if (rc < 0)
+ return (-1);
+
+ auth->cs_len = rc;
+
+ /* generate LMv2 response */
+ rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key,
+ srv_challenge_len, auth->data_blob.ndb_clnt_challenge,
+ SMBAUTH_V2_CLNT_CHALLENGE_SZ, auth->ci);
+
+ if (rc < 0)
+ return (-1);
+
+ auth->ci_len = rc;
+ }
+
+ return (0);
+}
+
+/*
+ * smb_auth_gen_session_key
+ *
+ * Generate the NTLM user session key if LMCompatibilityLevel is 2 or
+ * NTLMv2 user session key if LMCompatibilityLevel is 3 or above.
+ *
+ * NTLM_Session_Key = MD4(NTLM_Hash);
+ *
+ * NTLMv2_Session_Key = HMAC_MD5(NTLMv2Hash, 16, NTLMv2_HMAC, 16)
+ *
+ * Prior to calling this function, the auth instance should be set
+ * via smb_auth_set_info().
+ *
+ * Returns the appropriate session key.
+ */
+int
+smb_auth_gen_session_key(smb_auth_info_t *auth, unsigned char *session_key)
+{
+ int rc;
+
+ if (auth->lmcompatibility_lvl == 2)
+ rc = smb_auth_md4(session_key, auth->hash, SMBAUTH_HASH_SZ);
+ else
+ rc = SMBAUTH_HMACT64((unsigned char *)auth->cs,
+ SMBAUTH_HASH_SZ, (unsigned char *)auth->hash_v2,
+ SMBAUTH_SESSION_KEY_SZ, session_key);
+
+ return (rc);
+}
+
+/* 100's of ns between 1/1/1970 and 1/1/1601 */
+#define NT_TIME_BIAS (134774LL * 24LL * 60LL * 60LL * 10000000LL)
+
+static uint64_t
+unix_micro_to_nt_time(struct timeval *unix_time)
+{
+ uint64_t nt_time;
+
+ nt_time = unix_time->tv_sec;
+ nt_time *= 10000000; /* seconds to 100ns */
+ nt_time += unix_time->tv_usec * 10;
+ return (nt_time + NT_TIME_BIAS);
+}
+
+static boolean_t
+smb_lm_password_ok(
+ unsigned char *challenge,
+ uint32_t clen,
+ unsigned char *lm_hash,
+ unsigned char *passwd)
+{
+ unsigned char lm_resp[SMBAUTH_LM_RESP_SZ];
+ int rc;
+
+ rc = smb_auth_lm_response(lm_hash, challenge, clen, lm_resp);
+ if (rc != SMBAUTH_SUCCESS)
+ return (B_FALSE);
+
+ return (bcmp(lm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0);
+}
+
+static boolean_t
+smb_ntlm_password_ok(
+ unsigned char *challenge,
+ uint32_t clen,
+ unsigned char *ntlm_hash,
+ unsigned char *passwd)
+{
+ unsigned char ntlm_resp[SMBAUTH_LM_RESP_SZ];
+ int rc;
+
+ rc = smb_auth_ntlm_response(ntlm_hash, challenge, clen, ntlm_resp);
+
+ if (rc != SMBAUTH_LM_RESP_SZ)
+ return (B_FALSE);
+
+ return (bcmp(ntlm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0);
+}
+
+static boolean_t
+smb_ntlmv2_password_ok(
+ unsigned char *challenge,
+ uint32_t clen,
+ unsigned char *ntlm_hash,
+ unsigned char *passwd,
+ int pwdlen,
+ char *username)
+{
+ unsigned char *clnt_blob;
+ int clnt_blob_len;
+ unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
+ unsigned char *ntlmv2_resp;
+ boolean_t ok;
+
+ clnt_blob_len = pwdlen - SMBAUTH_HASH_SZ;
+ clnt_blob = &passwd[SMBAUTH_HASH_SZ];
+
+ /*
+ * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
+ *
+ * * The NTLMv2 Hash is created from:
+ * - NTLM hash
+ * - user's username, and
+ * - the name of the logon destination(i.e. the NetBIOS name of either
+ * the SMB server or NT Domain against which the suer is trying to
+ * authenticate.
+ *
+ * (N.L.) With my experience, this is not exactly true. It's really
+ * tricky how the NTLMv2 hash is generated by the Windows client when
+ * logging into a standalone server using NTLMv2 challenge / response.
+ * The NTLMv2 hash is actually created with the destination info=""
+ * as opposed to the SMB server name mentioned in the book.
+ */
+ if (smb_auth_ntlmv2_hash(ntlm_hash, username, "", ntlmv2_hash) !=
+ SMBAUTH_SUCCESS) {
+ return (B_FALSE);
+ }
+
+ ntlmv2_resp = (unsigned char *)malloc(SMBAUTH_HASH_SZ + clnt_blob_len);
+ if (ntlmv2_resp == NULL)
+ return (B_FALSE);
+
+ if (smb_auth_v2_response(ntlmv2_hash, challenge,
+ clen, clnt_blob, clnt_blob_len, ntlmv2_resp) < 0) {
+ free(ntlmv2_resp);
+ return (B_FALSE);
+ }
+
+ ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0);
+
+ free(ntlmv2_resp);
+ return (ok);
+}
+
+static int
+smb_lmv2_password_ok(
+ unsigned char *challenge,
+ uint32_t clen,
+ unsigned char *ntlm_hash,
+ unsigned char *passwd,
+ char *username)
+{
+ unsigned char *clnt_challenge;
+ unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ];
+ unsigned char lmv2_resp[SMBAUTH_LM_RESP_SZ];
+
+ clnt_challenge = &passwd[SMBAUTH_HASH_SZ];
+
+ /*
+ * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS"
+ *
+ * The NTLMv2 Hash is created from:
+ * - NTLM hash
+ * - user's username, and
+ * - the name of the logon destination(i.e. the NetBIOS name of either
+ * the SMB server or NT Domain against which the suer is trying to
+ * authenticate.
+ *
+ * (N.L.) With my experience, this is not exactly true. It's really
+ * tricky how the NTLMv2 hash is generated by the Windows client when
+ * logging into a standalone server using LMv2 challenge/response.
+ * The NTLMv2 hash is actually created with the destination info = ""
+ * as opposed to the SMB server name mentioned in the book.
+ */
+ if (smb_auth_ntlmv2_hash(ntlm_hash, username, "", ntlmv2_hash) !=
+ SMBAUTH_SUCCESS) {
+ return (B_FALSE);
+ }
+ if (smb_auth_v2_response(ntlmv2_hash, challenge,
+ clen, clnt_challenge, SMBAUTH_V2_CLNT_CHALLENGE_SZ,
+ lmv2_resp) < 0) {
+ return (B_FALSE);
+ }
+
+ return (bcmp(passwd, lmv2_resp, SMBAUTH_LM_RESP_SZ) == 0);
+}
+
+/*
+ * smb_auth_validate_lm
+ *
+ * Validates given LM/LMv2 client response, passed in passwd arg, against
+ * stored user's password, passed in smbpw
+ *
+ * If LM level <=3 server accepts LM responses, otherwise LMv2
+ */
+boolean_t
+smb_auth_validate_lm(
+ unsigned char *challenge,
+ uint32_t clen,
+ smb_passwd_t *smbpw,
+ unsigned char *passwd,
+ int pwdlen,
+ char *username)
+{
+ int lmlevel;
+ boolean_t ok = B_FALSE;
+
+ if (pwdlen != SMBAUTH_LM_RESP_SZ)
+ return (B_FALSE);
+
+ smb_config_rdlock();
+ lmlevel = smb_config_getnum(SMB_CI_LM_LEVEL);
+ smb_config_unlock();
+
+ if (lmlevel <= 3) {
+ ok = smb_lm_password_ok(challenge, clen, smbpw->pw_lmhash,
+ passwd);
+ }
+
+ if (!ok)
+ ok = smb_lmv2_password_ok(challenge, clen, smbpw->pw_nthash,
+ passwd, username);
+
+ return (ok);
+}
+
+/*
+ * smb_auth_validate_nt
+ *
+ * Validates given NTLM/NTLMv2 client response, passed in passwd arg, against
+ * stored user's password, passed in smbpw
+ *
+ * If LM level <=4 server accepts NTLM/NTLMv2 responses, otherwise only NTLMv2
+ */
+boolean_t
+smb_auth_validate_nt(
+ unsigned char *challenge,
+ uint32_t clen,
+ smb_passwd_t *smbpw,
+ unsigned char *passwd,
+ int pwdlen,
+ char *username)
+{
+ int lmlevel;
+ boolean_t ok;
+
+ smb_config_rdlock();
+ lmlevel = smb_config_getnum(SMB_CI_LM_LEVEL);
+ smb_config_unlock();
+
+ if ((lmlevel == 5) && (pwdlen <= SMBAUTH_LM_RESP_SZ))
+ return (B_FALSE);
+
+ if (pwdlen > SMBAUTH_LM_RESP_SZ)
+ ok = smb_ntlmv2_password_ok(challenge, clen,
+ smbpw->pw_nthash, passwd, pwdlen, username);
+ else
+ ok = smb_ntlm_password_ok(challenge, clen,
+ smbpw->pw_nthash, passwd);
+
+ return (ok);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
new file mode 100644
index 0000000000..844789e367
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
@@ -0,0 +1,1090 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * CIFS configuration management library
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <synch.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <libscf.h>
+#include <smbsrv/libsmb.h>
+
+typedef struct smb_cfg_param {
+ char *sc_pg;
+ char *sc_name;
+ int sc_type;
+ char *sc_value;
+ uint32_t sc_flags;
+} smb_cfg_param_t;
+
+/*
+ * config parameter flags
+ */
+#define SMB_CF_NOTINIT 0x00 /* Not initialized yet */
+#define SMB_CF_DEFINED 0x01 /* Defined/read from env */
+#define SMB_CF_MODIFIED 0x02 /* Has been modified */
+#define SMB_CF_SYSTEM 0x04 /* system; not part of cifs config */
+
+#define SMB_CL_NONE 0
+#define SMB_CL_READ 1
+#define SMB_CL_WRITE 2
+
+/* idmap SMF fmri and Property Group */
+#define IDMAP_FMRI_PREFIX "system/idmap"
+#define MACHINE_SID "machine_sid"
+#define MAPPING_DOMAIN "mapping_domain"
+#define GLOBAL_CATALOG "global_catalog"
+#define IDMAP_PG_NAME "config"
+
+#define SMB_SECMODE_WORKGRP_STR "workgroup"
+#define SMB_SECMODE_DOMAIN_STR "domain"
+
+#define SMB_ENC_LEN 1024
+#define SMB_DEC_LEN 256
+
+static char *b64_data =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static rwlock_t smb_cfg_rwlk;
+static int lock_type = SMB_CL_NONE;
+
+/*
+ * IMPORTANT: any changes to the order of this table's entries
+ * need to be reflected in smb_cfg_id_t enum in libsmb.h
+ */
+static smb_cfg_param_t smb_cfg_table[] =
+{
+ /* Redirector configuration, User space */
+ {SMBD_PG_NAME, SMB_CD_RDR_IPCMODE, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PROTECTED_PG_NAME, SMB_CD_RDR_IPCUSER,
+ SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PROTECTED_PG_NAME, SMB_CD_RDR_IPCPWD,
+ SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+
+ /* Oplock configuration, Kernel Only */
+ {SMBD_PG_NAME, SMB_CD_OPLOCK_ENABLE,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_OPLOCK_TIMEOUT,
+ SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+
+ /* Autohome configuration */
+ {SMBD_PG_NAME, SMB_CD_AUTOHOME_MAP,
+ SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+
+ /* Domain/PDC configuration */
+ {SMBD_PG_NAME, SMB_CD_DOMAIN_SID, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_DOMAIN_MEMB, SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_DOMAIN_NAME, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_DOMAIN_SRV, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+
+ /* WINS configuration */
+ {SMBD_PG_NAME, SMB_CD_WINS_SRV1, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_WINS_SRV2, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_WINS_EXCL, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+
+ /* RPC services configuration */
+ {SMBD_PG_NAME, SMB_CD_SRVSVC_SHRSET_ENABLE,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_LOGR_ENABLE, SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_MLRPC_KALIVE,
+ SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+
+ /* Kmod specific configuration */
+ {SMBD_PG_NAME, SMB_CD_MAX_BUFSIZE, SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_MAX_WORKERS, SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_MAX_CONNECTIONS,
+ SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_KEEPALIVE, SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_RESTRICT_ANON,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+
+ {SMBD_PG_NAME, SMB_CD_SIGNING_ENABLE,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_SIGNING_REQD,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_SIGNING_CHECK,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+
+ /* Kmod tuning configuration */
+ {SMBD_PG_NAME, SMB_CD_FLUSH_REQUIRED,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_SYNC_ENABLE, SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_DIRSYMLINK_DISABLE,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_ANNONCE_QUOTA,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+
+ /* SMBd configuration */
+ {SMBD_PG_NAME, SMB_CD_SECURITY, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_NBSCOPE, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_SYS_CMNT, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_LM_LEVEL, SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_MSDCS_DISABLE,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+
+ /* ADS Configuration */
+ {SMBD_PG_NAME, SMB_CD_ADS_ENABLE, SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PROTECTED_PG_NAME, SMB_CD_ADS_USER,
+ SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PROTECTED_PG_NAME, SMB_CD_ADS_PASSWD,
+ SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_ADS_DOMAIN, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_ADS_USER_CONTAINER,
+ SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_ADS_SITE, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_ADS_IPLOOKUP,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+
+ /* Dynamic DNS */
+ {SMBD_PG_NAME, SMB_CD_DYNDNS_ENABLE,
+ SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_DYNDNS_RETRY_COUNT,
+ SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+ {SMBD_PG_NAME, SMB_CD_DYNDNS_RETRY_SEC,
+ SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT},
+
+ {SMBD_PROTECTED_PG_NAME, SMB_CD_MACHINE_PASSWD,
+ SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}
+ /* SMB_CI_MAX */
+};
+
+static boolean_t smb_is_base64(unsigned char c);
+static char *smb_base64_encode(char *str_to_encode);
+static char *smb_base64_decode(char *encoded_str);
+static int smb_config_update(smb_cfg_param_t *cfg, char *value);
+static int smb_config_save_all();
+static int smb_config_save(char *pgname);
+
+static boolean_t
+smb_is_base64(unsigned char c)
+{
+ return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+/*
+ * smb_base64_encode
+ *
+ * Encode a string using base64 algorithm.
+ * Caller should free the returned buffer when done.
+ */
+static char *
+smb_base64_encode(char *str_to_encode)
+{
+ int ret_cnt = 0;
+ int i = 0, j = 0;
+ char arr_3[3], arr_4[4];
+ int len = strlen(str_to_encode);
+ char *ret = malloc(SMB_ENC_LEN);
+
+ if (ret == NULL) {
+ return (NULL);
+ }
+
+ while (len--) {
+ arr_3[i++] = *(str_to_encode++);
+ if (i == 3) {
+ arr_4[0] = (arr_3[0] & 0xfc) >> 2;
+ arr_4[1] = ((arr_3[0] & 0x03) << 4) +
+ ((arr_3[1] & 0xf0) >> 4);
+ arr_4[2] = ((arr_3[1] & 0x0f) << 2) +
+ ((arr_3[2] & 0xc0) >> 6);
+ arr_4[3] = arr_3[2] & 0x3f;
+
+ for (i = 0; i < 4; i++)
+ ret[ret_cnt++] = b64_data[arr_4[i]];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 3; j++)
+ arr_3[j] = '\0';
+
+ arr_4[0] = (arr_3[0] & 0xfc) >> 2;
+ arr_4[1] = ((arr_3[0] & 0x03) << 4) +
+ ((arr_3[1] & 0xf0) >> 4);
+ arr_4[2] = ((arr_3[1] & 0x0f) << 2) +
+ ((arr_3[2] & 0xc0) >> 6);
+ arr_4[3] = arr_3[2] & 0x3f;
+
+ for (j = 0; j < (i + 1); j++)
+ ret[ret_cnt++] = b64_data[arr_4[j]];
+
+ while (i++ < 3)
+ ret[ret_cnt++] = '=';
+ }
+
+ ret[ret_cnt++] = '\0';
+ return (ret);
+}
+
+/*
+ * smb_base64_decode
+ *
+ * Decode using base64 algorithm.
+ * Caller should free the returned buffer when done.
+ */
+static char *
+smb_base64_decode(char *encoded_str)
+{
+ int len = strlen(encoded_str);
+ int i = 0, j = 0;
+ int en_ind = 0;
+ char arr_4[4], arr_3[3];
+ int ret_cnt = 0;
+ char *ret = malloc(SMB_DEC_LEN);
+ char *p;
+
+ if (ret == NULL) {
+ return (NULL);
+ }
+
+ while (len-- && (encoded_str[en_ind] != '=') &&
+ smb_is_base64(encoded_str[en_ind])) {
+ arr_4[i++] = encoded_str[en_ind];
+ en_ind++;
+ if (i == 4) {
+ for (i = 0; i < 4; i++) {
+ if ((p = strchr(b64_data, arr_4[i])) == NULL)
+ return (NULL);
+
+ arr_4[i] = (int)(p - b64_data);
+ }
+
+ arr_3[0] = (arr_4[0] << 2) +
+ ((arr_4[1] & 0x30) >> 4);
+ arr_3[1] = ((arr_4[1] & 0xf) << 4) +
+ ((arr_4[2] & 0x3c) >> 2);
+ arr_3[2] = ((arr_4[2] & 0x3) << 6) +
+ arr_4[3];
+
+ for (i = 0; i < 3; i++)
+ ret[ret_cnt++] = arr_3[i];
+
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 4; j++)
+ arr_4[j] = 0;
+
+ for (j = 0; j < 4; j++) {
+ if ((p = strchr(b64_data, arr_4[j])) == NULL)
+ return (NULL);
+
+ arr_4[j] = (int)(p - b64_data);
+ }
+ arr_3[0] = (arr_4[0] << 2) +
+ ((arr_4[1] & 0x30) >> 4);
+ arr_3[1] = ((arr_4[1] & 0xf) << 4) +
+ ((arr_4[2] & 0x3c) >> 2);
+ arr_3[2] = ((arr_4[2] & 0x3) << 6) +
+ arr_4[3];
+ for (j = 0; j < (i - 1); j++)
+ ret[ret_cnt++] = arr_3[j];
+ }
+
+ ret[ret_cnt++] = '\0';
+ return (ret);
+}
+
+/*
+ * Basically commit the transaction.
+ */
+static int
+smb_config_saveenv(smb_scfhandle_t *handle)
+{
+ int ret = 0;
+
+ ret = smb_smf_end_transaction(handle);
+
+ smb_smf_scf_fini(handle);
+ return (ret);
+}
+
+/*
+ * smb_config_getenv
+ *
+ * Get the property value from SMF.
+ */
+char *
+smb_config_getenv(smb_cfg_id_t id)
+{
+ smb_scfhandle_t *handle;
+ char *value;
+
+ if ((value = malloc(MAX_VALUE_BUFLEN * sizeof (char))) == NULL)
+ return (NULL);
+
+ handle = smb_smf_scf_init(SMBD_FMRI_PREFIX);
+ if (handle == NULL) {
+ free(value);
+ return (NULL);
+ }
+
+ (void) smb_smf_create_service_pgroup(handle, smb_cfg_table[id].sc_pg);
+
+ if (smb_smf_get_property(handle, smb_cfg_table[id].sc_type,
+ smb_cfg_table[id].sc_name, value,
+ sizeof (char) * MAX_VALUE_BUFLEN) != 0) {
+ smb_smf_scf_fini(handle);
+ free(value);
+ return (NULL);
+ }
+
+ smb_smf_scf_fini(handle);
+ return (value);
+}
+
+/*
+ * smb_config_getenv_dec
+ *
+ * For protected property, the value obtained from SMF will be decoded.
+ * The decoded property value will be returned.
+ *
+ * This function should only be called by smb_config_load to populate
+ * the SMB config cache.
+ */
+static char *
+smb_config_getenv_dec(smb_cfg_id_t id)
+{
+ smb_scfhandle_t *handle;
+ char *value;
+ char *dec;
+
+ if ((value = malloc(MAX_VALUE_BUFLEN * sizeof (char))) == NULL)
+ return (NULL);
+
+ handle = smb_smf_scf_init(SMBD_FMRI_PREFIX);
+ if (handle == NULL) {
+ free(value);
+ return (NULL);
+ }
+
+ (void) smb_smf_create_service_pgroup(handle, smb_cfg_table[id].sc_pg);
+
+ if (smb_smf_get_property(handle, smb_cfg_table[id].sc_type,
+ smb_cfg_table[id].sc_name, value,
+ sizeof (char) * MAX_VALUE_BUFLEN) != 0) {
+ smb_smf_scf_fini(handle);
+ free(value);
+ return (NULL);
+ }
+ smb_smf_scf_fini(handle);
+ if (strcmp(smb_cfg_table[id].sc_pg, SMBD_PROTECTED_PG_NAME))
+ return (value);
+
+ if (!value)
+ return (NULL);
+
+ if (*value == '\0') {
+ free(value);
+ return (NULL);
+ }
+
+ dec = smb_base64_decode(value);
+ free(value);
+ return (dec);
+}
+
+static char *
+smb_config_getenv_generic(char *name, char *svc_fmri_prefix, char *svc_propgrp)
+{
+ smb_scfhandle_t *handle;
+ char *value;
+
+ if ((value = malloc(MAX_VALUE_BUFLEN * sizeof (char))) == NULL)
+ return (NULL);
+
+ handle = smb_smf_scf_init(svc_fmri_prefix);
+ if (handle == NULL) {
+ free(value);
+ return (NULL);
+ }
+
+ (void) smb_smf_create_service_pgroup(handle, svc_propgrp);
+
+ if (smb_smf_get_string_property(handle, name, value,
+ sizeof (char) * MAX_VALUE_BUFLEN) != 0) {
+ smb_smf_scf_fini(handle);
+ free(value);
+ return (NULL);
+ }
+
+ smb_smf_scf_fini(handle);
+ return (value);
+
+}
+
+int
+smb_config_setenv_generic(char *svc_fmri_prefix, char *svc_propgrp,
+ char *name, char *value)
+{
+ smb_scfhandle_t *handle = NULL;
+ int rc = 0;
+
+
+ handle = smb_smf_scf_init(svc_fmri_prefix);
+ if (handle == NULL) {
+ return (1);
+ }
+
+ (void) smb_smf_create_service_pgroup(handle, svc_propgrp);
+
+ if (smb_smf_start_transaction(handle) != SMBD_SMF_OK) {
+ smb_smf_scf_fini(handle);
+ return (1);
+ }
+
+ if (smb_smf_set_string_property(handle, name, value) != SMBD_SMF_OK)
+ rc = 1;
+
+ if (smb_smf_end_transaction(handle) != SMBD_SMF_OK)
+ rc = 1;
+
+ smb_smf_scf_fini(handle);
+ return (rc);
+}
+
+/*
+ * smb_config_setenv
+ *
+ * For protected properties, the value will be encoded using base64
+ * algorithm. The encoded string will be stored in SMF.
+ */
+int
+smb_config_setenv(smb_cfg_id_t id, char *value)
+{
+ smb_scfhandle_t *handle = NULL;
+ char *enc = NULL;
+ int is_protected = 0;
+
+ if ((id >= SMB_CI_MAX) || (id < 0)) {
+ return (1);
+ }
+
+ handle = smb_smf_scf_init(SMBD_FMRI_PREFIX);
+ if (handle == NULL) {
+ return (1);
+ }
+
+ (void) smb_smf_create_service_pgroup(handle, smb_cfg_table[id].sc_pg);
+
+ if (smb_smf_start_transaction(handle) != SMBD_SMF_OK) {
+ smb_smf_scf_fini(handle);
+ return (1);
+ }
+
+ if (strcmp(smb_cfg_table[id].sc_pg, SMBD_PROTECTED_PG_NAME) == 0) {
+ if ((value == NULL) || (*value == '\0')) {
+ (void) smb_smf_end_transaction(handle);
+ smb_smf_scf_fini(handle);
+ return (1);
+ }
+
+ if ((enc = smb_base64_encode(value)) == NULL) {
+ (void) smb_smf_end_transaction(handle);
+ smb_smf_scf_fini(handle);
+ return (1);
+ }
+
+ is_protected = 1;
+ }
+
+ if (smb_smf_set_property(handle, smb_cfg_table[id].sc_type,
+ smb_cfg_table[id].sc_name, is_protected ? enc : value)
+ != SMBD_SMF_OK) {
+ if (enc)
+ free(enc);
+ (void) smb_smf_end_transaction(handle);
+ smb_smf_scf_fini(handle);
+ return (1);
+ }
+
+ if (enc)
+ free(enc);
+
+ if (smb_smf_end_transaction(handle) != SMBD_SMF_OK) {
+ smb_smf_scf_fini(handle);
+ return (1);
+ }
+
+ smb_smf_scf_fini(handle);
+ return (0);
+}
+
+static void
+smb_config_setenv_trans(smb_scfhandle_t *handle, int type,
+ char *name, char *value)
+{
+ if (smb_smf_set_property(handle, type, name, value) != SMBD_SMF_OK) {
+ syslog(LOG_ERR, "Failed to save service property %s", name);
+ }
+}
+
+/*
+ * smb_config_setenv_trans_protected
+ *
+ * This function should only be called to set protected properties
+ * in SMF. The argument 'value' will be encoded using base64 algorithm.
+ * The encoded string will be stored in SMF.
+ */
+static void
+smb_config_setenv_trans_protected(smb_scfhandle_t *handle, char *name,
+ char *value)
+{
+ char *enc;
+
+ if ((value == NULL) || (*value == '\0'))
+ return;
+
+ if ((enc = smb_base64_encode(value)) == NULL)
+ return;
+
+ if (smb_smf_set_string_property(handle, name, enc) != SMBD_SMF_OK) {
+ syslog(LOG_ERR, "Failed to save service protected property"
+ " %s", name);
+ }
+
+ free(enc);
+}
+
+int
+smb_config_unsetenv(smb_cfg_id_t id)
+{
+ smb_scfhandle_t *handle = NULL;
+ int ret = 1;
+
+ handle = smb_smf_scf_init(SMBD_FMRI_PREFIX);
+ if (handle == NULL) {
+ return (ret);
+ }
+
+ (void) smb_smf_create_service_pgroup(handle, smb_cfg_table[id].sc_pg);
+ if (smb_smf_start_transaction(handle) != SMBD_SMF_OK) {
+ smb_smf_scf_fini(handle);
+ return (ret);
+ }
+ ret = smb_smf_delete_property(handle, smb_cfg_table[id].sc_name);
+ (void) smb_smf_end_transaction(handle);
+
+ smb_smf_scf_fini(handle);
+ return (ret);
+}
+
+static int
+smb_config_unsetenv_trans(smb_scfhandle_t *handle, char *name)
+{
+ return (smb_smf_delete_property(handle, name));
+}
+
+/*
+ * smb_config_load
+ *
+ * Loads all the CIFS configuration parameters and sets up the
+ * config table.
+ */
+int
+smb_config_load()
+{
+ smb_cfg_id_t id;
+ smb_cfg_param_t *cfg;
+ char *value;
+
+ (void) rw_rdlock(&smb_cfg_rwlk);
+ for (id = 0; id < SMB_CI_MAX; id++) {
+ value = smb_config_getenv_dec(id);
+ cfg = &smb_cfg_table[id];
+ /*
+ * enval == 0 could mean two things, either the
+ * config param is not defined, or it has been
+ * removed. If the variable has already been defined
+ * and now enval is 0, it should be removed, otherwise
+ * we don't need to do anything in this case.
+ */
+ if ((cfg->sc_flags & SMB_CF_DEFINED) || value) {
+ if (smb_config_update(cfg, value) != 0) {
+ (void) rw_unlock(&smb_cfg_rwlk);
+ if (value)
+ free(value);
+ return (1);
+ }
+ }
+ if (value) {
+ free(value);
+ }
+ }
+
+ (void) rw_unlock(&smb_cfg_rwlk);
+
+ return (0);
+}
+
+/*
+ * smb_config_get
+ *
+ * Returns value of the specified config param.
+ * The return value is a string pointer to the locally
+ * allocated memory if the config param is defined
+ * otherwise it would be NULL.
+ *
+ * This function MUST be called after a smb_config_rd/wrlock
+ * function. Caller MUST NOT modify the returned buffer directly.
+ */
+char *
+smb_config_get(smb_cfg_id_t id)
+{
+ if (id < SMB_CI_MAX)
+ return (smb_cfg_table[id].sc_value);
+
+ return (0);
+}
+
+/*
+ * smb_config_getstr
+ *
+ * Returns value of the specified config param.
+ * The returned pointer never will be NULL if the given
+ * 'id' is valid. If the config param is not defined its
+ * default value will be returned.
+ *
+ * This function MUST be called after a smb_config_rd/wrlock
+ * function. Caller MUST NOT modify the returned buffer directly.
+ */
+char *
+smb_config_getstr(smb_cfg_id_t id)
+{
+ smb_cfg_param_t *cfg;
+
+ if (id < SMB_CI_MAX) {
+ cfg = &smb_cfg_table[id];
+ if (cfg->sc_value)
+ return (cfg->sc_value);
+ }
+
+ return (NULL);
+}
+
+/*
+ * smb_config_getnum
+ *
+ * Returns the value of a numeric config param.
+ * If the config param is not defined it'll return the
+ * default value.
+ *
+ * This function MUST be called after a smb_config_rd/wrlock
+ * function.
+ */
+uint32_t
+smb_config_getnum(smb_cfg_id_t id)
+{
+ smb_cfg_param_t *cfg;
+ char *strval = NULL;
+
+ if (id < SMB_CI_MAX) {
+ cfg = &smb_cfg_table[id];
+ if (cfg->sc_value)
+ strval = cfg->sc_value;
+
+ if (strval)
+ return (strtol(strval, 0, 10));
+ }
+
+ return (0);
+}
+
+/*
+ * smb_config_getyorn
+ *
+ * Returns the value of a yes/no config param.
+ * Returns 1 is config is set to "yes", otherwise 0.
+ *
+ * This function MUST be called after a smb_config_rd/wrlock
+ * function.
+ */
+int
+smb_config_getyorn(smb_cfg_id_t id)
+{
+ char *val;
+
+ val = smb_config_get(id);
+ if (val) {
+ if (strcasecmp(val, "true") == 0)
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * smb_config_set
+ *
+ * Set/update the specified config param with the given
+ * value. If the value is NULL the config param will be
+ * unset as if it is not defined.
+ *
+ * This function MUST be called after a smb_config_wrlock
+ * function.
+ */
+int
+smb_config_set(smb_cfg_id_t id, char *value)
+{
+ smb_cfg_param_t *cfg;
+ int rc = 0;
+
+ if (id < SMB_CI_MAX) {
+ cfg = &smb_cfg_table[id];
+ rc = smb_config_update(cfg, value);
+ if (rc == 0)
+ cfg->sc_flags |= SMB_CF_MODIFIED;
+ return (rc);
+ }
+
+ return (1);
+}
+
+/*
+ * smb_config_setnum
+ *
+ * Set/update the specified config param with the given
+ * value. This is used for numeric config params. The given
+ * number will be converted to string before setting the
+ * config param.
+ *
+ * This function MUST be called after a smb_config_wrlock
+ * function.
+ */
+int
+smb_config_setnum(smb_cfg_id_t id, uint32_t num)
+{
+ smb_cfg_param_t *cfg;
+ char value[32];
+ int rc = 0;
+
+ if (id < SMB_CI_MAX) {
+ cfg = &smb_cfg_table[id];
+ (void) snprintf(value, sizeof (value), "%u", num);
+ rc = smb_config_update(cfg, value);
+ if (rc == 0)
+ cfg->sc_flags |= SMB_CF_MODIFIED;
+ return (rc);
+ }
+
+ return (1);
+}
+
+/*
+ * smb_config_rdlock
+ *
+ * Lock the config table for read access.
+ * This function MUST be called before any kind of
+ * read access to the config table i.e. all flavors of
+ * smb_config_get function
+ */
+void
+smb_config_rdlock()
+{
+ (void) rw_rdlock(&smb_cfg_rwlk);
+ lock_type = SMB_CL_READ;
+}
+
+/*
+ * smb_config_wrlock
+ *
+ * Lock the config table for write access.
+ * This function MUST be called before any kind of
+ * write access to the config table i.e. all flavors of
+ * smb_config_set function
+ */
+void
+smb_config_wrlock()
+{
+ (void) rw_wrlock(&smb_cfg_rwlk);
+ lock_type = SMB_CL_WRITE;
+}
+
+/*
+ * smb_config_wrlock
+ *
+ * Unlock the config table.
+ * If the config table has been locked for write access
+ * smb_config_save_all() will be called to save the changes
+ * before unlocking the table.
+ *
+ * This function MUST be called after smb_config_rd/wrlock
+ */
+void
+smb_config_unlock()
+{
+ if (lock_type == SMB_CL_WRITE)
+ (void) smb_config_save_all();
+ (void) rw_unlock(&smb_cfg_rwlk);
+}
+
+/*
+ * smb_config_save_all
+ *
+ * Save all modified parameters to SMF.
+ */
+static int
+smb_config_save_all()
+{
+ int rc;
+
+ if ((rc = smb_config_save(SMBD_PG_NAME)) != 0)
+ return (rc);
+
+ return (smb_config_save(SMBD_PROTECTED_PG_NAME));
+}
+
+/*
+ * smb_config_save
+ *
+ * Scan the config table and call smb_config_setenv/smb_config_unsetenv
+ * for params in the specified property group that has been modified.
+ * When the scan is finished, smb_config_saveenv() will be called to
+ * make the changes persistent.
+ */
+static int
+smb_config_save(char *pgname)
+{
+ smb_cfg_id_t id;
+ smb_cfg_param_t *cfg;
+ smb_scfhandle_t *handle = NULL;
+ int dorefresh = 0;
+
+ handle = smb_smf_scf_init(SMBD_FMRI_PREFIX);
+ if (handle == NULL) {
+ syslog(LOG_ERR, "smbd: cannot save configuration");
+ return (1);
+ }
+
+ (void) smb_smf_create_service_pgroup(handle, pgname);
+ if (smb_smf_start_transaction(handle) != SMBD_SMF_OK) {
+ syslog(LOG_ERR, "smbd: cannot save configuration");
+ return (1);
+ }
+
+ for (id = 0; id < SMB_CI_MAX; id++) {
+ cfg = &smb_cfg_table[id];
+ if (strcmp(cfg->sc_pg, pgname))
+ continue;
+
+ if (cfg->sc_flags & SMB_CF_MODIFIED) {
+ if (cfg->sc_value) {
+ if (strcmp(pgname, SMBD_PG_NAME) == 0)
+ smb_config_setenv_trans(handle,
+ cfg->sc_type, cfg->sc_name,
+ cfg->sc_value);
+ else
+ smb_config_setenv_trans_protected(
+ handle, cfg->sc_name,
+ cfg->sc_value);
+ } else {
+ (void) smb_config_unsetenv_trans(handle,
+ cfg->sc_name);
+ }
+ cfg->sc_flags &= ~SMB_CF_MODIFIED;
+ dorefresh = 1;
+ }
+ }
+
+ if (smb_config_saveenv(handle) != 0) {
+ syslog(LOG_ERR, "smbd: cannot save configuration");
+ return (1);
+ }
+ if (dorefresh)
+ (void) smf_refresh_instance(SMBD_DEFAULT_INSTANCE_FMRI);
+ return (0);
+}
+
+/*
+ * smb_config_update
+ *
+ * Updates the specified config param with the given value.
+ * This function is called both on (re)load and set.
+ */
+static int
+smb_config_update(smb_cfg_param_t *cfg, char *value)
+{
+ char *curval;
+ int rc = 0;
+ int len;
+
+ if (value) {
+ len = strlen(value);
+ if (cfg->sc_value) {
+ curval = (char *)realloc(cfg->sc_value,
+ (len + 1));
+ } else {
+ curval = (char *)malloc(len + 1);
+ }
+
+ if (curval) {
+ cfg->sc_value = curval;
+ (void) strcpy(cfg->sc_value, value);
+ cfg->sc_flags |= SMB_CF_DEFINED;
+ } else {
+ rc = 1;
+ }
+ } else if (cfg->sc_value) {
+ free(cfg->sc_value);
+ cfg->sc_value = NULL;
+ cfg->sc_flags &= ~SMB_CF_DEFINED;
+ }
+
+ return (rc);
+}
+
+uint8_t
+smb_config_get_fg_flag()
+{
+ uint8_t run_fg = 0; /* Default is to run in daemon mode */
+ smb_scfhandle_t *handle = NULL;
+
+ handle = smb_smf_scf_init(SMBD_FMRI_PREFIX);
+ if (handle == NULL) {
+ return (run_fg);
+ }
+
+ if (smb_smf_create_service_pgroup(handle,
+ SMBD_PG_NAME) != SMBD_SMF_OK) {
+ smb_smf_scf_fini(handle);
+ return (run_fg);
+ }
+
+ if (smb_smf_get_boolean_property(handle, "run_fg", &run_fg) != 0) {
+ smb_smf_scf_fini(handle);
+ return (run_fg);
+ }
+
+ smb_smf_scf_fini(handle);
+
+ return (run_fg);
+}
+
+/*
+ * smb_config_get_localsid
+ *
+ * Returns value of the "config/machine_sid" parameter
+ * from the IDMAP SMF configuration repository.
+ *
+ */
+char *
+smb_config_get_localsid(void)
+{
+ return (smb_config_getenv_generic(MACHINE_SID, IDMAP_FMRI_PREFIX,
+ IDMAP_PG_NAME));
+}
+
+/*
+ * smb_config_set_idmap_domain
+ *
+ * Set the "config/mapping_domain" parameter from IDMAP SMF repository.
+ */
+int
+smb_config_set_idmap_domain(char *value)
+{
+ return (smb_config_setenv_generic(IDMAP_FMRI_PREFIX, IDMAP_PG_NAME,
+ MAPPING_DOMAIN, value));
+}
+
+/*
+ * smb_config_set_idmap_gc
+ *
+ * Set the "config/global_catalog" parameter from IDMAP SMF repository.
+ */
+int
+smb_config_set_idmap_gc(char *value)
+{
+ return (smb_config_setenv_generic(IDMAP_FMRI_PREFIX, IDMAP_PG_NAME,
+ GLOBAL_CATALOG, value));
+}
+
+/*
+ * smb_config_refresh_idmap
+ *
+ * Refresh IDMAP SMF service after making changes to its configuration.
+ */
+int
+smb_config_refresh_idmap(void)
+{
+ char instance[32];
+
+ (void) snprintf(instance, sizeof (instance), "%s:default",
+ IDMAP_FMRI_PREFIX);
+ return (smf_refresh_instance(instance));
+}
+
+int
+smb_config_secmode_fromstr(char *secmode)
+{
+ if (secmode == NULL)
+ return (SMB_SECMODE_WORKGRP);
+
+ if (strcasecmp(secmode, SMB_SECMODE_DOMAIN_STR) == 0)
+ return (SMB_SECMODE_DOMAIN);
+
+ return (SMB_SECMODE_WORKGRP);
+}
+
+char *
+smb_config_secmode_tostr(int secmode)
+{
+ if (secmode == SMB_SECMODE_DOMAIN)
+ return (SMB_SECMODE_DOMAIN_STR);
+
+ return (SMB_SECMODE_WORKGRP_STR);
+}
+
+int
+smb_config_get_secmode()
+{
+ char *p;
+
+ p = smb_config_getstr(SMB_CI_SECURITY);
+ return (smb_config_secmode_fromstr(p));
+}
+
+int
+smb_config_set_secmode(int secmode)
+{
+ char *p;
+
+ p = smb_config_secmode_tostr(secmode);
+ return (smb_config_set(SMB_CI_SECURITY, p));
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_crypt.c b/usr/src/lib/smbsrv/libsmb/common/smb_crypt.c
new file mode 100644
index 0000000000..94489b0b40
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_crypt.c
@@ -0,0 +1,204 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/md4.h>
+#include <sys/types.h>
+#include <string.h>
+#include <security/cryptoki.h>
+#include <security/pkcs11.h>
+#include <cryptoutil.h>
+#include <smbsrv/libsmb.h>
+
+static void smb_auth_keyprep(const unsigned char *pwstr, unsigned char *keystr);
+
+/*
+ * smb_auth_md4
+ *
+ * Compute an MD4 digest.
+ */
+int
+smb_auth_md4(unsigned char *result, unsigned char *input, int length)
+{
+ MD4_CTX md4_context;
+
+ MD4Init(&md4_context);
+ MD4Update(&md4_context, input, length);
+ MD4Final(result, &md4_context);
+ return (SMBAUTH_SUCCESS);
+}
+
+int
+smb_auth_hmac_md5(unsigned char *data,
+ int data_len,
+ unsigned char *key,
+ int key_len,
+ unsigned char *digest)
+{
+ CK_RV rv;
+ CK_MECHANISM mechanism;
+ CK_OBJECT_HANDLE hKey;
+ CK_SESSION_HANDLE hSession;
+ unsigned long diglen = MD_DIGEST_LEN;
+
+ mechanism.mechanism = CKM_MD5_HMAC;
+ mechanism.pParameter = 0;
+ mechanism.ulParameterLen = 0;
+ rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
+ if (rv != CKR_OK) {
+ return (SMBAUTH_FAILURE);
+ }
+
+ rv = SUNW_C_KeyToObject(hSession, mechanism.mechanism,
+ key, key_len, &hKey);
+ if (rv != CKR_OK) {
+ (void) C_CloseSession(hSession);
+ return (SMBAUTH_FAILURE);
+ }
+
+ /* Initialize the digest operation in the session */
+ rv = C_SignInit(hSession, &mechanism, hKey);
+ if (rv != CKR_OK) {
+ (void) C_DestroyObject(hSession, hKey);
+ (void) C_CloseSession(hSession);
+ return (SMBAUTH_FAILURE);
+ }
+ rv = C_SignUpdate(hSession, (CK_BYTE_PTR)data, data_len);
+ if (rv != CKR_OK) {
+ (void) C_DestroyObject(hSession, hKey);
+ (void) C_CloseSession(hSession);
+ return (SMBAUTH_FAILURE);
+ }
+ rv = C_SignFinal(hSession, (CK_BYTE_PTR)digest, &diglen);
+ if (rv != CKR_OK) {
+ (void) C_DestroyObject(hSession, hKey);
+ (void) C_CloseSession(hSession);
+ return (SMBAUTH_FAILURE);
+ }
+ (void) C_DestroyObject(hSession, hKey);
+ (void) C_CloseSession(hSession);
+ if (diglen != MD_DIGEST_LEN) {
+ return (SMBAUTH_FAILURE);
+ }
+ return (SMBAUTH_SUCCESS);
+}
+
+int
+smb_auth_DES(unsigned char *Result, int ResultLen,
+ unsigned char *Key, int KeyLen,
+ unsigned char *Data, int DataLen)
+{
+ CK_RV rv;
+ CK_MECHANISM mechanism;
+ CK_OBJECT_HANDLE hKey;
+ CK_SESSION_HANDLE hSession;
+ CK_ULONG ciphertext_len;
+ uchar_t des_key[8];
+ int error = 0;
+ int K, D;
+ int k, d;
+
+ /* Calculate proper number of iterations */
+ K = KeyLen / 7;
+ D = DataLen / 8;
+
+ if (ResultLen < (K * 8 * D)) {
+ return (SMBAUTH_FAILURE);
+ }
+
+ /*
+ * Use SUNW convenience function to initialize the cryptoki
+ * library, and open a session with a slot that supports
+ * the mechanism we plan on using.
+ */
+ mechanism.mechanism = CKM_DES_ECB;
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
+ if (rv != CKR_OK) {
+ return (SMBAUTH_FAILURE);
+ }
+
+ for (k = 0; k < K; k++) {
+ smb_auth_keyprep(&Key[k * 7], des_key);
+ rv = SUNW_C_KeyToObject(hSession, mechanism.mechanism,
+ des_key, 8, &hKey);
+ if (rv != CKR_OK) {
+ error = 1;
+ goto exit_session;
+ }
+ /* Initialize the encryption operation in the session */
+ rv = C_EncryptInit(hSession, &mechanism, hKey);
+ if (rv != CKR_OK) {
+ error = 1;
+ goto exit_encrypt;
+ }
+ ciphertext_len = DataLen;
+ for (d = 0; d < D; d++) {
+ /* Read in the data and encrypt this portion */
+ rv = C_EncryptUpdate(hSession,
+ (CK_BYTE_PTR)Data + (d * 8), 8,
+ &Result[(k * (8 * D)) + (d * 8)],
+ &ciphertext_len);
+ if (rv != CKR_OK) {
+ error = 1;
+ goto exit_encrypt;
+ }
+ }
+ (void) C_DestroyObject(hSession, hKey);
+ }
+ goto exit_session;
+
+exit_encrypt:
+ (void) C_DestroyObject(hSession, hKey);
+exit_session:
+ (void) C_CloseSession(hSession);
+
+ if (error)
+ return (SMBAUTH_FAILURE);
+
+ return (SMBAUTH_SUCCESS);
+}
+
+/*
+ * smb_auth_keyprep
+ *
+ * Takes 7 bytes of keying material and expands it into 8 bytes with
+ * the keying material in the upper 7 bits of each byte.
+ */
+static void
+smb_auth_keyprep(const unsigned char *pwstr, unsigned char *keystr)
+{
+ keystr[0] = pwstr[0];
+ keystr[1] = (pwstr[0] << 7) | (pwstr[1] >> 1);
+ keystr[2] = (pwstr[1] << 6) | (pwstr[2] >> 2);
+ keystr[3] = (pwstr[2] << 5) | (pwstr[3] >> 3);
+ keystr[4] = (pwstr[3] << 4) | (pwstr[4] >> 4);
+ keystr[5] = (pwstr[4] << 3) | (pwstr[5] >> 5);
+ keystr[6] = (pwstr[5] << 2) | (pwstr[6] >> 6);
+ keystr[7] = pwstr[6] << 1;
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_ctxbuf.c b/usr/src/lib/smbsrv/libsmb/common/smb_ctxbuf.c
new file mode 100644
index 0000000000..2b51f2b6af
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_ctxbuf.c
@@ -0,0 +1,120 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Buffer manipulation routines. These routines can be used to format
+ * data within a data buffer without worrying about overrunning the
+ * buffer.
+ *
+ * A ctxbuf_t structure is used to track the current location within
+ * the buffer. The ctxbuf_init() must be called first to initialize the
+ * context structure. ctxbuf_printf() can then be called to fill the buffer.
+ * ctxbuf_printf will discard any data that would overrun the buffer and
+ * the buffer will always be null terminated.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <smbsrv/libsmb.h>
+
+/*
+ * smb_ctxbuf_init
+ *
+ * Initialize the buffer context structure.
+ * This must be called before any of the other
+ * buffer routines can be used.
+ *
+ * Returns -1 if invalid parameters, 0 otherwise
+ */
+int
+smb_ctxbuf_init(smb_ctxbuf_t *ctx, unsigned char *buf, size_t buflen)
+{
+ if (ctx == 0 || buf == 0 || buflen == 0)
+ return (-1);
+
+ buf[0] = '\0';
+
+ ctx->basep = buf;
+ ctx->curp = buf;
+ ctx->endp = &buf[buflen];
+
+ return (0);
+}
+
+/*
+ * smb_ctxbuf_len
+ *
+ * Return the amount of data stored in the buffer,
+ * excluding the terminating null character. Similar
+ * to strlen()
+ *
+ * Returns 0 if the ctx is invalid.
+ */
+int
+smb_ctxbuf_len(smb_ctxbuf_t *ctx)
+{
+ if (ctx == 0 || ctx->basep == 0 ||
+ ctx->curp == 0 || ctx->endp == 0)
+ return (0);
+ else
+ /*LINTED E_PTRDIFF_OVERFLOW*/
+ return (ctx->curp - ctx->basep);
+}
+
+/*
+ * smb_ctxbuf_printf
+ *
+ * Move formatted output (based on fmt string) to the buffer
+ * identified in ctxbuf. Any output characters beyond the buffer
+ * are discarded and a null character is written at the end of the
+ * characters actually written.
+ *
+ * Returns
+ * Always return the number of bytes actually written (excluding the
+ * terminating null).
+ */
+int
+smb_ctxbuf_printf(smb_ctxbuf_t *ctx, const char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ if (ctx == 0 || ctx->basep == 0 ||
+ ctx->curp == 0 || ctx->endp == 0)
+ return (-1);
+
+ va_start(args, fmt);
+ /*LINTED E_PTRDIFF_OVERFLOW*/
+ n = vsnprintf((char *)ctx->curp, ctx->endp-ctx->curp, fmt, args);
+ ctx->curp += n;
+ va_end(args);
+
+ /*
+ * return the number of bytes moved into the buffer.
+ */
+ return (n);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_domain.c b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c
new file mode 100644
index 0000000000..22ea5e61fb
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c
@@ -0,0 +1,394 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file defines the NT domain environment values and the domain
+ * database interface. The database is a single linked list of
+ * structures containing domain type, name and SID information.
+ */
+
+#include <strings.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <synch.h>
+
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/string.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/alloc.h>
+
+#include <smbsrv/libsmb.h>
+
+
+static void nt_domain_unlist(nt_domain_t *);
+
+/*
+ * Valid domain type identifiers as text. This table must be kept
+ * in step with the nt_domain_type_t enum in ntdomain.h.
+ */
+static char *nt_domain_type_name[NT_DOMAIN_NUM_TYPES] = {
+ "null",
+ "builtin",
+ "local",
+ "primary",
+ "account",
+ "trusted",
+ "untrusted"
+};
+
+
+static rwlock_t nt_domain_lock;
+static nt_domain_t *nt_domain_list;
+
+/*
+ * nt_domain_init
+ *
+ * NT domain database one time initialization. This function should
+ * be called during module installation.
+ *
+ * Returns 0 on successful domain initialization. Less than zero otherwise.
+ */
+int
+nt_domain_init(char *resource_domain, uint32_t secmode)
+{
+ nt_domain_t *domain;
+ nt_sid_t *sid;
+ char *sidstr;
+ char *lsidstr;
+ char hostname[MAXHOSTNAMELEN];
+
+ if (rwlock_init(&nt_domain_lock, USYNC_THREAD, NULL))
+ return (SMB_DOMAIN_NODOMAIN_SID);
+
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) {
+ (void) rwlock_destroy(&nt_domain_lock);
+ return (SMB_DOMAIN_NOMACHINE_SID);
+ }
+
+ lsidstr = smb_config_get_localsid();
+
+ if (lsidstr) {
+ sid = nt_sid_strtosid(lsidstr);
+
+ if (sid) {
+ domain = nt_domain_new(NT_DOMAIN_LOCAL, hostname, sid);
+ (void) nt_domain_add(domain);
+ free(sid);
+ }
+ free(lsidstr);
+ } else {
+ (void) rwlock_destroy(&nt_domain_lock);
+ return (SMB_DOMAIN_NOMACHINE_SID);
+ }
+
+ if (secmode == SMB_SECMODE_DOMAIN) {
+ sid = nt_sid_strtosid(NT_BUILTIN_DOMAIN_SIDSTR);
+ domain = nt_domain_new(NT_DOMAIN_BUILTIN, "BUILTIN", sid);
+ (void) nt_domain_add(domain);
+ free(sid);
+
+ smb_config_rdlock();
+ sidstr = smb_config_get(SMB_CI_DOMAIN_SID);
+ if (sidstr) {
+ sid = nt_sid_strtosid(sidstr);
+ smb_config_unlock();
+ domain = nt_domain_new(NT_DOMAIN_PRIMARY,
+ resource_domain, sid);
+ (void) nt_domain_add(domain);
+ free(sid);
+ } else {
+ smb_config_unlock();
+ (void) rwlock_destroy(&nt_domain_lock);
+ return (SMB_DOMAIN_NODOMAIN_SID);
+ }
+
+ }
+ return (0);
+}
+
+/*
+ * nt_domain_new
+ *
+ * Allocate and initialize a new domain structure. On success, a pointer to
+ * the new domain structure is returned. Otherwise a null pointer is returned.
+ */
+nt_domain_t *
+nt_domain_new(nt_domain_type_t type, char *name, nt_sid_t *sid)
+{
+ nt_domain_t *new_domain;
+
+ if ((name == NULL) || (sid == NULL))
+ return (NULL);
+
+ if (type == NT_DOMAIN_NULL || type >= NT_DOMAIN_NUM_TYPES)
+ return (NULL);
+
+ if ((new_domain = malloc(sizeof (nt_domain_t))) == NULL)
+ return (NULL);
+
+ bzero(new_domain, sizeof (nt_domain_t));
+ new_domain->type = type;
+ new_domain->name = strdup(name);
+ new_domain->sid = nt_sid_dup(sid);
+
+ return (new_domain);
+}
+
+/*
+ * nt_domain_delete
+ *
+ * Free the memory used by the specified domain structure.
+ */
+void
+nt_domain_delete(nt_domain_t *domain)
+{
+ if (domain) {
+ free(domain->name);
+ free(domain->sid);
+ free(domain);
+ }
+}
+
+
+/*
+ * nt_domain_add
+ *
+ * Add a domain structure to the global list. There is no checking
+ * for duplicates. If it's the primary domain, we save the SID in the
+ * environment. Returns a pointer to the new domain entry on success.
+ * Otherwise a null pointer is returned.
+ */
+nt_domain_t *
+nt_domain_add(nt_domain_t *new_domain)
+{
+ char *sidstr;
+
+ if (new_domain == NULL)
+ return (NULL);
+
+ (void) rw_wrlock(&nt_domain_lock);
+
+ new_domain->next = nt_domain_list;
+ nt_domain_list = new_domain;
+
+ if (new_domain->type == NT_DOMAIN_PRIMARY) {
+ sidstr = nt_sid_format(new_domain->sid);
+ smb_config_wrlock();
+ (void) smb_config_set(SMB_CI_DOMAIN_SID, sidstr);
+ smb_config_unlock();
+ free(sidstr);
+ }
+ (void) rw_unlock(&nt_domain_lock);
+
+ return (new_domain);
+}
+
+
+/*
+ * nt_domain_remove
+ *
+ * Remove a domain from the global list. The memory
+ * used by the structure is not freed.
+ */
+void
+nt_domain_remove(nt_domain_t *domain)
+{
+ (void) rw_wrlock(&nt_domain_lock);
+ nt_domain_unlist(domain);
+ (void) rw_unlock(&nt_domain_lock);
+}
+
+
+/*
+ * nt_domain_flush
+ *
+ * Flush all domains of the specified type from the list. This is
+ * useful for things like updating the list of trusted domains.
+ */
+void
+nt_domain_flush(nt_domain_type_t domain_type)
+{
+ nt_domain_t *domain = nt_domain_list;
+
+ (void) rw_wrlock(&nt_domain_lock);
+ while (domain) {
+ if (domain->type == domain_type) {
+ nt_domain_unlist(domain);
+ nt_domain_delete(domain);
+ domain = nt_domain_list;
+ continue;
+ }
+ domain = domain->next;
+ }
+ (void) rw_unlock(&nt_domain_lock);
+}
+
+/*
+ * nt_domain_xlat_type
+ *
+ * Translate a domain type into a text string.
+ */
+char *
+nt_domain_xlat_type(nt_domain_type_t domain_type)
+{
+ if (domain_type < NT_DOMAIN_NUM_TYPES)
+ return (nt_domain_type_name[domain_type]);
+ else
+ return ("unknown");
+}
+
+
+/*
+ * nt_domain_xlat_type_name
+ *
+ * Translate a domain type test string into a domain type.
+ */
+nt_domain_type_t
+nt_domain_xlat_type_name(char *type_name)
+{
+ int i;
+
+ for (i = 0; i < NT_DOMAIN_NUM_TYPES; ++i)
+ if (utf8_strcasecmp(nt_domain_type_name[i], type_name) == 0)
+ return (i);
+
+ return (NT_DOMAIN_NUM_TYPES);
+}
+
+
+/*
+ * nt_domain_lookup_name
+ *
+ * Lookup a domain by its domain name. If the domain is in the list,
+ * a pointer to it is returned. Otherwise a null pointer is returned.
+ */
+nt_domain_t *
+nt_domain_lookup_name(char *domain_name)
+{
+ nt_domain_t *domain = nt_domain_list;
+
+ (void) rw_rdlock(&nt_domain_lock);
+ while (domain) {
+ if (utf8_strcasecmp(domain->name, domain_name) == 0)
+ break;
+
+ domain = domain->next;
+ }
+ (void) rw_unlock(&nt_domain_lock);
+
+ return (domain);
+}
+
+
+/*
+ * nt_domain_lookup_sid
+ *
+ * Lookup a domain by its domain SID. If the domain is in the list,
+ * a pointer to it is returned. Otherwise a null pointer is returned.
+ */
+nt_domain_t *
+nt_domain_lookup_sid(nt_sid_t *domain_sid)
+{
+ nt_domain_t *domain = nt_domain_list;
+
+ (void) rw_rdlock(&nt_domain_lock);
+ while (domain) {
+ if (nt_sid_is_equal(domain->sid, domain_sid))
+ break;
+
+ domain = domain->next;
+ }
+ (void) rw_unlock(&nt_domain_lock);
+
+ return (domain);
+}
+
+
+/*
+ * nt_domain_lookupbytype
+ *
+ * Lookup a domain by its type. The first matching entry in the list
+ * is returned. Otherwise a null pointer is returned.
+ */
+nt_domain_t *
+nt_domain_lookupbytype(nt_domain_type_t type)
+{
+ nt_domain_t *domain = nt_domain_list;
+
+ (void) rw_rdlock(&nt_domain_lock);
+ while (domain) {
+ if (domain->type == type)
+ break;
+
+ domain = domain->next;
+ }
+ (void) rw_unlock(&nt_domain_lock);
+
+ return (domain);
+}
+
+
+/*
+ * nt_domain_local_sid
+ *
+ * Return a pointer to the local domain SID. Each system has a SID that
+ * represents the local domain, which is named after the local hostname.
+ * The local domain SID must exist.
+ */
+nt_sid_t *
+nt_domain_local_sid(void)
+{
+ nt_domain_t *domain = nt_domain_list;
+
+ (void) rw_rdlock(&nt_domain_lock);
+ while (domain) {
+ if (domain->type == NT_DOMAIN_LOCAL)
+ break;
+
+ domain = domain->next;
+ }
+ (void) rw_unlock(&nt_domain_lock);
+
+ return (domain->sid);
+}
+
+
+static void
+nt_domain_unlist(nt_domain_t *domain)
+{
+ nt_domain_t **ppdomain = &nt_domain_list;
+
+ while (*ppdomain) {
+ if (*ppdomain == domain) {
+ *ppdomain = domain->next;
+ domain->next = NULL;
+ return;
+ }
+ ppdomain = &(*ppdomain)->next;
+ }
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_door_client.c b/usr/src/lib/smbsrv/libsmb/common/smb_door_client.c
new file mode 100644
index 0000000000..7e09b237b3
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_door_client.c
@@ -0,0 +1,411 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * User-space door client for SMBd
+ */
+
+#include <fcntl.h>
+#include <syslog.h>
+#include <door.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/wintypes.h>
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/alloc.h>
+#include <smbsrv/smb_common_door.h>
+
+#include <smbsrv/libsmb.h>
+
+static int smb_door_fildes = -1;
+
+static char *smbd_desc[] = {
+ "",
+ "SmbdJoinDomain",
+ "SmbdGetParam",
+ "SmbdSetParam",
+ "SmbdNetbiosReconfig",
+ 0
+};
+
+/*
+ * Returns 0 on success. Otherwise, -1.
+ */
+static int
+smbd_door_open(int opcode)
+{
+ int rc = 0;
+
+ if (smb_door_fildes == -1 &&
+ (smb_door_fildes = open(SMBD_DOOR_NAME, O_RDONLY)) < 0) {
+ syslog(LOG_ERR, "%s: open %s failed %s", smbd_desc[opcode],
+ SMBD_DOOR_NAME, strerror(errno));
+ rc = -1;
+ }
+
+ return (rc);
+}
+
+/*
+ * Return 0 upon success. Otherwise, -1.
+ */
+static int
+smbd_door_check_srv_status(int opcode, smb_dr_ctx_t *dec_ctx)
+{
+ int status = smb_dr_get_int32(dec_ctx);
+ int err;
+ int rc = -1;
+
+ switch (status) {
+ case SMBD_DOOR_SRV_SUCCESS:
+ rc = 0;
+ break;
+
+ case SMBD_DOOR_SRV_ERROR:
+ err = smb_dr_get_uint32(dec_ctx);
+ syslog(LOG_ERR, "%s: Encountered door server error %s",
+ smbd_desc[opcode], strerror(err));
+ break;
+
+ default:
+ syslog(LOG_ERR, "%s: Unknown door server status",
+ smbd_desc[opcode]);
+ }
+
+ if (rc != 0) {
+ if ((err = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ smbd_desc[opcode], strerror(err));
+ }
+ }
+
+ return (rc);
+}
+
+uint32_t
+smb_join(smb_joininfo_t *jdi)
+{
+ door_arg_t arg;
+ char *buf;
+ uint32_t used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ uint32_t rc;
+ int opcode = SMBD_DOOR_JOIN;
+
+ if ((jdi == 0) || (*jdi->domain_name == 0)) {
+ syslog(LOG_ERR, "%s: invalid parameter(s)", smbd_desc[opcode]);
+ return (NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (smbd_door_open(opcode) == -1) {
+ syslog(LOG_ERR, "%s: cannot open the door", smbd_desc[opcode]);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ buf = MEM_MALLOC("smb_door_client", SMBD_DOOR_SIZE);
+ if (!buf) {
+ syslog(LOG_ERR, "%s: resource shortage", smbd_desc[opcode]);
+ return (NT_STATUS_NO_MEMORY);
+ }
+
+ enc_ctx = smb_dr_encode_start(buf, SMBD_DOOR_SIZE);
+ if (enc_ctx == 0) {
+ syslog(LOG_ERR, "%s: encode start failed", smbd_desc[opcode]);
+ MEM_FREE("smb_door_client", buf);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_uint32(enc_ctx, jdi->mode);
+ smb_dr_put_string(enc_ctx, jdi->domain_name);
+ smb_dr_put_string(enc_ctx, jdi->domain_username);
+ smb_dr_put_string(enc_ctx, jdi->domain_passwd);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ smbd_desc[opcode], strerror(status));
+ MEM_FREE("smb_door_client", buf);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = SMBD_DOOR_SIZE;
+
+ if (door_call(smb_door_fildes, &arg) < 0) {
+ syslog(LOG_ERR, "%s: Door call failed %s", smbd_desc[opcode],
+ strerror(errno));
+ MEM_FREE("smb_door_client", buf);
+ smb_door_fildes = -1;
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (smbd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ MEM_FREE("smb_door_client", buf);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ rc = smb_dr_get_uint32(dec_ctx);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ smbd_desc[opcode], strerror(status));
+ MEM_FREE("smb_door_client", buf);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ MEM_FREE("smb_door_client", buf);
+ return (rc);
+}
+
+int
+smbd_netbios_reconfig()
+{
+ door_arg_t arg;
+ char *buf;
+ uint32_t used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = SMBD_DOOR_NETBIOS_RECONFIG;
+
+ if (smbd_door_open(opcode) == -1) {
+ syslog(LOG_ERR, "%s: cannot open the door", smbd_desc[opcode]);
+ return (1);
+ }
+
+ buf = MEM_MALLOC("smb_door_client", SMBD_DOOR_SIZE);
+ if (!buf) {
+ syslog(LOG_ERR, "%s: resource shortage", smbd_desc[opcode]);
+ return (1);
+ }
+
+ enc_ctx = smb_dr_encode_start(buf, SMBD_DOOR_SIZE);
+ if (enc_ctx == 0) {
+ syslog(LOG_ERR, "%s: encode start failed", smbd_desc[opcode]);
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+
+ smb_dr_put_uint32(enc_ctx, opcode);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ smbd_desc[opcode], strerror(status));
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = SMBD_DOOR_SIZE;
+
+ if (door_call(smb_door_fildes, &arg) < 0) {
+ syslog(LOG_ERR, "%s: Door call failed %s", smbd_desc[opcode],
+ strerror(errno));
+ MEM_FREE("smb_door_client", buf);
+ smb_door_fildes = -1;
+ return (1);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ rc = smb_dr_get_uint32(dec_ctx);
+
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ smbd_desc[opcode], strerror(status));
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+ MEM_FREE("smb_door_client", buf);
+ return (rc);
+}
+
+int
+smbd_set_param(smb_cfg_id_t id, char *value)
+{
+ door_arg_t arg;
+ char *buf;
+ uint32_t used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = SMBD_DOOR_PARAM_SET;
+
+ if (smbd_door_open(opcode) == -1) {
+ syslog(LOG_ERR, "%s: cannot open the door", smbd_desc[opcode]);
+ return (1);
+ }
+
+ buf = MEM_MALLOC("smb_door_client", SMBD_DOOR_SIZE);
+ if (!buf) {
+ syslog(LOG_ERR, "%s: resource shortage", smbd_desc[opcode]);
+ return (1);
+ }
+
+ enc_ctx = smb_dr_encode_start(buf, SMBD_DOOR_SIZE);
+ if (enc_ctx == 0) {
+ syslog(LOG_ERR, "%s: encode start failed", smbd_desc[opcode]);
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_uint32(enc_ctx, id);
+ smb_dr_put_string(enc_ctx, value);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ smbd_desc[opcode], strerror(status));
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = SMBD_DOOR_SIZE;
+
+ if (door_call(smb_door_fildes, &arg) < 0) {
+ syslog(LOG_ERR, "%s: Door call failed %s", smbd_desc[opcode],
+ strerror(errno));
+ MEM_FREE("smb_door_client", buf);
+ smb_door_fildes = -1;
+ return (1);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ rc = smb_dr_get_uint32(dec_ctx);
+
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ smbd_desc[opcode], strerror(status));
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+ MEM_FREE("smb_door_client", buf);
+ return (rc);
+}
+
+int
+smbd_get_param(smb_cfg_id_t id, char *value)
+{
+ door_arg_t arg;
+ char *buf;
+ char *tmp = NULL;
+ uint32_t used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = SMBD_DOOR_PARAM_GET;
+
+ if (smbd_door_open(opcode) == -1) {
+ syslog(LOG_ERR, "%s: cannot open the door", smbd_desc[opcode]);
+ return (1);
+ }
+
+ buf = MEM_MALLOC("smb_door_client", SMBD_DOOR_SIZE);
+ if (!buf) {
+ syslog(LOG_ERR, "%s: resource shortage", smbd_desc[opcode]);
+ return (1);
+ }
+
+ enc_ctx = smb_dr_encode_start(buf, SMBD_DOOR_SIZE);
+ if (enc_ctx == 0) {
+ syslog(LOG_ERR, "%s: encode start failed", smbd_desc[opcode]);
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_uint32(enc_ctx, id);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ smbd_desc[opcode], strerror(status));
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = SMBD_DOOR_SIZE;
+
+ if (door_call(smb_door_fildes, &arg) < 0) {
+ syslog(LOG_ERR, "%s: Door call failed %s", smbd_desc[opcode],
+ strerror(errno));
+ MEM_FREE("smb_door_client", buf);
+ smb_door_fildes = -1;
+ return (1);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ rc = smb_dr_get_uint32(dec_ctx);
+ tmp = smb_dr_get_string(dec_ctx);
+ (void) strcpy(value, tmp);
+
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ smbd_desc[opcode], strerror(status));
+ MEM_FREE("smb_door_client", buf);
+ return (1);
+ }
+ MEM_FREE("smb_door_client", buf);
+ return (rc);
+}
+
+int
+smbd_get_security_mode(int *mode)
+{
+ char buf[64];
+ int rc;
+
+ buf[0] = '\0';
+ rc = smbd_get_param(SMB_CI_SECURITY, buf);
+ *mode = smb_config_secmode_fromstr(buf);
+ return (rc);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_door_encdec.c b/usr/src/lib/smbsrv/libsmb/common/smb_door_encdec.c
new file mode 100644
index 0000000000..3734f6e49c
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_door_encdec.c
@@ -0,0 +1,313 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <strings.h>
+#include <rpc/xdr.h>
+#include <errno.h>
+#include <smbsrv/libsmb.h>
+#include <smbsrv/smb_xdr.h>
+#include <smbsrv/smb_common_door.h>
+#include <smbsrv/smb_door_svc.h>
+
+/*
+ * smb_dr_decode_common
+ *
+ * This function can be used to decode both door request and result buffer.
+ * pre-condition: data is non-null pointer, and is bzero'd.
+ */
+int
+smb_dr_decode_common(char *buf, size_t len, xdrproc_t proc, void *data)
+{
+ XDR xdrs;
+ int rc = 0;
+
+ if (!data) {
+ syslog(LOG_ERR, "smb_dr_decode_common: invalid param");
+ return (-1);
+ }
+
+ xdrmem_create(&xdrs, buf, len, XDR_DECODE);
+ if (!proc(&xdrs, data)) {
+ rc = -1;
+ }
+ xdr_destroy(&xdrs);
+ return (rc);
+}
+
+/*
+ * smb_dr_encode_common
+ *
+ * This function can be used to encode both request and result door buffer.
+ * The 'opcode' paramater is set to the 'opcode' of the operation to be invoked
+ * on the server, by the client. The server sets the same 'opcode' paramater
+ * to indicate the 'status' of the door call.
+ *
+ * This function will first encode integer value 'opcode' (opcode/status),
+ * followed by the data (which will be encoded via the specified XDR routine).
+ *
+ * Returns encoded buffer upon success. Otherwise, returns NULL.
+ */
+char *
+smb_dr_encode_common(uint_t opcode, void *data, xdrproc_t proc, size_t *len)
+{
+ XDR xdrs;
+ char *buf;
+
+ if (proc && !data) {
+ syslog(LOG_ERR, "smb_dr_encode_common: invalid param");
+ *len = 0;
+ return (NULL);
+ }
+
+ *len = xdr_sizeof(xdr_uint32_t, &opcode);
+ if (proc)
+ *len += xdr_sizeof(proc, data);
+ buf = (char *)malloc(*len);
+ if (!buf) {
+ syslog(LOG_ERR, "smb_dr_encode_common: resource shortage");
+ *len = 0;
+ return (NULL);
+ }
+ xdrmem_create(&xdrs, buf, *len, XDR_ENCODE);
+ if (!xdr_uint32_t(&xdrs, &opcode)) {
+ syslog(LOG_DEBUG, "smb_dr_encode_common: encode error 1");
+ free(buf);
+ *len = 0;
+ xdr_destroy(&xdrs);
+ return (NULL);
+ }
+
+ if (proc && !proc(&xdrs, data)) {
+ syslog(LOG_DEBUG, "smb_dr_encode_common: encode error 2");
+ free(buf);
+ buf = NULL;
+ *len = 0;
+ }
+
+ xdr_destroy(&xdrs);
+ return (buf);
+}
+
+/*
+ * Get the opcode of the door argument buffer.
+ */
+int
+smb_dr_get_opcode(char *argp, size_t arg_size)
+{
+ int opcode;
+
+ if (smb_dr_decode_common(argp, arg_size, xdr_uint32_t, &opcode) != 0)
+ opcode = -1;
+ return (opcode);
+}
+
+/*
+ * Set the opcode of the door argument buffer.
+ */
+char *
+smb_dr_set_opcode(uint32_t opcode, size_t *len)
+{
+ char *buf;
+
+ buf = smb_dr_encode_common(opcode, NULL, NULL, len);
+ return (buf);
+}
+
+/*
+ * Get the status of the door result buffer.
+ */
+int
+smb_dr_get_res_stat(char *rbufp, size_t rbuf_size)
+{
+ int stat;
+ if (smb_dr_decode_common(rbufp, rbuf_size, xdr_uint32_t, &stat) != 0)
+ stat = -1;
+ return (stat);
+}
+
+/*
+ * Set the status of the door result buffer.
+ */
+char *
+smb_dr_set_res_stat(uint32_t stat, size_t *len)
+{
+ char *buf;
+
+ buf = smb_dr_encode_common(stat, NULL, NULL, len);
+ return (buf);
+}
+
+char *
+smb_dr_encode_res_token(smb_token_t *token, size_t *len)
+{
+ smb_dr_bytes_t res;
+ char *buf = NULL;
+
+ res.bytes_val = smb_token_mkselfrel(token, &res.bytes_len);
+ if (!res.bytes_val) {
+ syslog(LOG_ERR, "smb_dr_encode_res_token: mkselfrel error");
+ *len = 0;
+ return (NULL);
+ }
+
+ if ((buf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &res,
+ xdr_smb_dr_bytes_t, len)) == NULL) {
+ syslog(LOG_ERR, "smb_dr_encode_res_token: failed");
+ *len = 0;
+ free(res.bytes_val);
+ return (NULL);
+
+ }
+ free(res.bytes_val);
+ return (buf);
+}
+
+char *
+smb_dr_encode_kshare(smb_dr_kshare_t *kshare, size_t *buflen)
+{
+ smb_dr_bytes_t res;
+ char *buf = NULL;
+
+ res.bytes_val = smb_kshare_mkselfrel(kshare, &res.bytes_len);
+
+ free(kshare->k_path);
+ free(kshare->k_sharename);
+
+ if (!res.bytes_val)
+ return (NULL);
+
+ buf = smb_dr_encode_common(SMB_KDR_SHARE, &res, xdr_smb_dr_bytes_t,
+ buflen);
+
+ free(res.bytes_val);
+
+ return (buf);
+}
+
+/*
+ * smb_kshare_mkselfrel
+ *
+ * encode: structure -> flat buffer (buffer size)
+ * Pre-condition: kshare is non-null.
+ */
+
+uint8_t *
+smb_kshare_mkselfrel(smb_dr_kshare_t *kshare, uint32_t *len)
+{
+ uint8_t *buf;
+ XDR xdrs;
+
+ if (!kshare)
+ return (NULL);
+
+ *len = xdr_sizeof(xdr_smb_dr_kshare_t, kshare);
+ buf = (uint8_t *)malloc(*len);
+ if (!buf)
+ return (NULL);
+
+ xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE);
+
+ if (!xdr_smb_dr_kshare_t(&xdrs, kshare)) {
+ *len = 0;
+ free(buf);
+ buf = NULL;
+ }
+
+ xdr_destroy(&xdrs);
+ return (buf);
+}
+
+char *
+smb_dr_encode_string(uint32_t opcode, char *str, size_t *len)
+{
+ char *buf;
+ smb_dr_string_t res;
+
+ res.buf = str;
+
+ if ((buf = smb_dr_encode_common(opcode, &res,
+ xdr_smb_dr_string_t, len)) == NULL)
+ syslog(LOG_ERR, "smb_dr_encode_string: failed");
+ return (buf);
+}
+
+char *
+smb_dr_decode_string(char *buf, size_t len)
+{
+ smb_dr_string_t res;
+ char *str = NULL;
+
+ bzero(&res, sizeof (smb_dr_string_t));
+ if (smb_dr_decode_common(buf, len, xdr_smb_dr_string_t,
+ &res) == 0) {
+ str = res.buf;
+ } else {
+ syslog(LOG_ERR, "smb_dr_decode_string: failed");
+ }
+ return (str);
+}
+
+netr_client_t *
+smb_dr_decode_arg_get_token(char *buf, size_t len)
+{
+ smb_dr_bytes_t arg;
+ netr_client_t *clnt_info;
+
+ bzero(&arg, sizeof (smb_dr_bytes_t));
+ if (smb_dr_decode_common(buf, len, xdr_smb_dr_bytes_t, &arg)
+ != 0) {
+ syslog(LOG_ERR, "smb_dr_decode_arg_get_token: failed");
+ xdr_free(xdr_smb_dr_bytes_t, (char *)&arg);
+ return (NULL);
+ }
+ clnt_info = netr_client_mkabsolute(arg.bytes_val,
+ arg.bytes_len);
+ xdr_free(xdr_smb_dr_bytes_t, (char *)&arg);
+ return (clnt_info);
+}
+
+void
+smb_dr_ulist_free(smb_dr_ulist_t *ulist)
+{
+ int i;
+ smb_dr_user_ctx_t *uinfo;
+
+ if (!ulist)
+ return;
+
+ for (i = 0; i < ulist->dul_cnt; i++) {
+ uinfo = &ulist->dul_users[i];
+
+ if (!uinfo)
+ continue;
+
+ xdr_free(xdr_smb_dr_ulist_t, (char *)ulist);
+ }
+
+ free(ulist);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c b/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c
new file mode 100644
index 0000000000..36373f10f0
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c
@@ -0,0 +1,148 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * User-space door client routines for both SMB daemon and CLIs.
+ */
+
+#include <fcntl.h>
+#include <syslog.h>
+#include <door.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <smbsrv/libsmb.h>
+#include <smbsrv/wintypes.h>
+#include <smbsrv/smb_door_svc.h>
+#include <smbsrv/smb_common_door.h>
+
+/*
+ * Returns 0 on success. Otherwise, -1.
+ */
+int
+smb_dr_clnt_open(int *fd, char *path, char *op_desc)
+{
+ int rc = 0;
+
+ if (!op_desc)
+ op_desc = "unknown operation";
+
+ if (!path || !fd)
+ return (-1);
+
+ if ((*fd = open(path, O_RDONLY)) < 0) {
+ syslog(LOG_ERR, "%s: open %s failed %s", op_desc,
+ path, strerror(errno));
+ rc = -1;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_dr_clnt_call
+ *
+ * This function will make a door call to the server function
+ * associated with the door descriptor fd. The specified door
+ * request buffer (i.e. argp) will be passed as the argument to the
+ * door_call(). Upon success, the result buffer is returned. Otherwise,
+ * NULL pointer is returned. The size of the result buffer is returned
+ * via rbufsize.
+ */
+char *
+smb_dr_clnt_call(int fd, char *argp, size_t arg_size, size_t *rbufsize,
+ char *op_desc)
+{
+ door_arg_t arg;
+
+ if (!argp) {
+ syslog(LOG_ERR, "smb_dr_clnt_call: invalid parameter");
+ return (NULL);
+ }
+
+ arg.data_ptr = argp;
+ arg.data_size = arg_size;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = argp;
+ arg.rsize = arg_size;
+
+ if (!op_desc)
+ op_desc = "unknown operation";
+
+ if (door_call(fd, &arg) < 0) {
+ syslog(LOG_ERR, "%s: Door call failed %s", op_desc,
+ strerror(errno));
+ free(argp);
+ argp = NULL;
+ return (NULL);
+ }
+
+ if (smb_dr_get_res_stat(arg.data_ptr, arg.rsize)
+ != SMB_DR_OP_SUCCESS) {
+ smb_dr_clnt_free(argp, arg_size, arg.rbuf, arg.rsize);
+ *rbufsize = 0;
+ return (NULL);
+ }
+ *rbufsize = arg.rsize;
+ return (arg.data_ptr);
+}
+
+/*
+ * smb_dr_clnt_free
+ *
+ * This function should be invoked to free both the argument/result door buffer
+ * regardless of the status of the door call.
+ *
+ * The doorfs allocates a new buffer if the result buffer passed by the client
+ * is too small. This function will munmap if that happens.
+ */
+/*ARGSUSED*/
+void
+smb_dr_clnt_free(char *argp, size_t arg_size, char *rbufp, size_t rbuf_size)
+{
+ if (argp) {
+ if (argp == rbufp) {
+ free(argp);
+ argp = NULL;
+ } else if (rbufp) {
+ free(argp);
+ argp = NULL;
+ if (munmap(rbufp, rbuf_size) != 0) {
+ syslog(LOG_ERR, "munmap failed");
+ }
+ }
+ } else {
+ if (rbufp) {
+ if (munmap(rbufp, rbuf_size) != 0) {
+ syslog(LOG_ERR, "munmap failed");
+ }
+ }
+ }
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_downcalls.c b/usr/src/lib/smbsrv/libsmb/common/smb_downcalls.c
new file mode 100644
index 0000000000..2f76b4e875
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_downcalls.c
@@ -0,0 +1,195 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Down calls to SMB Kmod for obtaining various kernel door services.
+ */
+
+#include <syslog.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc/xdr.h>
+#include <smbsrv/smb_door_svc.h>
+#include <smbsrv/smb_common_door.h>
+#include <smbsrv/libsmb.h>
+
+/* indexed via opcode (smb_kdr_opcode_t) */
+char *smb_dwncall_info[] = {
+ "SmbDwncallNumUser",
+ "SmbDwncallUserList",
+ "SmbDwncallShare",
+ 0
+};
+
+static smb_dwncall_get_desc_t get_dwncall_desc;
+
+int
+smb_dwncall_install_callback(smb_dwncall_get_desc_t get_desc_cb)
+{
+ if (!get_desc_cb)
+ return (-1);
+
+ get_dwncall_desc = get_desc_cb;
+ return (0);
+}
+
+int
+smb_dwncall_init_fd(uint_t opcode)
+{
+ int fd;
+
+ if (!get_dwncall_desc) {
+ syslog(LOG_DEBUG, "%s: failed (unable to get fd)",
+ smb_dwncall_info[opcode]);
+ return (-1);
+ }
+
+ if ((fd = get_dwncall_desc()) == -1) {
+ syslog(LOG_ERR, "%s: failed (invalid fd)",
+ smb_dwncall_info[opcode]);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+uint64_t
+smb_dwncall_user_num()
+{
+ char *buf, *rbufp;
+ size_t buflen, rbufsize;
+ int64_t num;
+ uint_t opcode = SMB_KDR_USER_NUM;
+ int fd;
+
+ if ((fd = smb_dwncall_init_fd(opcode)) < 0)
+ return (0);
+
+ buf = smb_dr_set_opcode(opcode, &buflen);
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smb_dwncall_info[opcode]);
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &num) != 0) {
+ num = -1;
+ }
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ return (num);
+}
+
+/*
+ * smb_dwncall_get_users
+ *
+ * The calling function must free the output parameter 'users'.
+ */
+int
+smb_dwncall_get_users(int offset, smb_dr_ulist_t *users)
+{
+ char *buf = NULL, *rbufp;
+ size_t buflen, rbufsize;
+ uint_t opcode = SMB_KDR_USER_LIST;
+ int fd, rc = -1;
+
+ bzero(users, sizeof (smb_dr_ulist_t));
+ if ((fd = smb_dwncall_init_fd(opcode)) < 0)
+ return (-1);
+
+ buf = smb_dr_encode_common(opcode, &offset, xdr_uint32_t, &buflen);
+ if (!buf) {
+ syslog(LOG_ERR, "smb_dwncall_get_users: encode error");
+ return (-1);
+ }
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smb_dwncall_info[opcode]);
+
+
+ if (rbufp) {
+ rc = smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_smb_dr_ulist_t, users);
+ if (rc)
+ syslog(LOG_ERR, "smb_dwncall_get_users: decode error");
+ else
+ rc = users->dul_cnt;
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ return (rc);
+}
+
+/*
+ * smb_dwncall_share()
+ *
+ * This is a downcall to the kernel that is executed
+ * upon share enable and disable.
+ */
+
+int
+smb_dwncall_share(int op, char *path, char *sharename)
+{
+ char *buf = NULL, *rbufp;
+ size_t buflen, rbufsize;
+ int32_t opcode = SMB_KDR_SHARE;
+ smb_dr_kshare_t kshare;
+ int fd, rc = -1;
+
+ if ((op != LMSHR_ADD) &&
+ (op != LMSHR_DELETE))
+ return (-1);
+
+ if ((fd = smb_dwncall_init_fd(opcode)) < 0) {
+ syslog(LOG_ERR, "smb_dwncall_share: init error");
+ return (-1);
+ }
+
+ kshare.k_op = op;
+ kshare.k_path = strdup(path);
+ kshare.k_sharename = strdup(sharename);
+
+ buf = smb_dr_encode_kshare(&kshare, &buflen);
+
+ if (!buf) {
+ syslog(LOG_ERR, "smb_dwncall_share: encode error");
+ return (-1);
+ }
+
+ rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize,
+ smb_dwncall_info[opcode]);
+
+ if (rbufp) {
+ if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET,
+ rbufsize - SMB_DR_DATA_OFFSET, xdr_int32_t, &rc) != 0) {
+ rc = -1;
+ }
+ }
+
+ smb_dr_clnt_free(buf, buflen, rbufp, rbufsize);
+ return (rc);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_group_door_encdec.c b/usr/src/lib/smbsrv/libsmb/common/smb_group_door_encdec.c
new file mode 100644
index 0000000000..0b29764926
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_group_door_encdec.c
@@ -0,0 +1,337 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <smbsrv/libsmb.h>
+#include <smbsrv/smb_xdr.h>
+#include <smbsrv/smb_door_svc.h>
+
+/*
+ * smb_grplist_mkselfrel
+ *
+ * encode: structure -> flat buffer (buffer size)
+ * Pre-condition: obj is non-null.
+ */
+static uint8_t *
+smb_grplist_mkselfrel(ntgrp_list_t *obj, uint32_t *len)
+{
+ uint8_t *buf;
+ XDR xdrs;
+
+ if (!obj) {
+ syslog(LOG_ERR, "smb_grplist_mkselfrel: invalid parameter");
+ return (NULL);
+ }
+ *len = xdr_sizeof(xdr_ntgrp_list_t, obj);
+ buf = (uint8_t *)malloc(*len);
+
+ xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE);
+
+ if (!xdr_ntgrp_list_t(&xdrs, obj)) {
+ syslog(LOG_ERR, "smb_grplist_mkselfrel: XDR encode error");
+ free(buf);
+ *len = 0;
+ buf = NULL;
+ }
+
+ xdr_destroy(&xdrs);
+ return (buf);
+}
+
+/*
+ * smb_grplist_mkabsolute
+ *
+ * decode: flat buffer -> structure
+ */
+static ntgrp_list_t *
+smb_grplist_mkabsolute(uint8_t *buf, uint32_t len)
+{
+ ntgrp_list_t *obj;
+ XDR xdrs;
+
+ xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE);
+
+ if ((obj = (ntgrp_list_t *)
+ malloc(sizeof (ntgrp_list_t))) == 0) {
+ syslog(LOG_ERR, "smb_grplist_mkabsolute: resource shortage");
+ xdr_destroy(&xdrs);
+ return (NULL);
+ }
+ bzero(obj, sizeof (ntgrp_list_t));
+ if (!xdr_ntgrp_list_t(&xdrs, obj)) {
+ syslog(LOG_ERR, "smb_grplist_mkabsolute: XDR decode error");
+ smb_group_free_list(obj, 1);
+ obj = NULL;
+ }
+
+ xdr_destroy(&xdrs);
+ return (obj);
+}
+
+/*
+ * smb_grpmemberlist_mkselfrel
+ *
+ * encode: structure -> flat buffer (buffer size)
+ * Pre-condition: obj is non-null.
+ */
+static uint8_t *
+smb_grpmemberlist_mkselfrel(ntgrp_member_list_t *obj, uint32_t *len)
+{
+ uint8_t *buf;
+ XDR xdrs;
+
+ if (!obj) {
+ syslog(LOG_ERR,
+ "smb_grpmemberlist_mkselfrel: invalid parameter");
+ return (NULL);
+ }
+ *len = xdr_sizeof(xdr_ntgrp_member_list_t, obj);
+ buf = (uint8_t *)malloc(*len);
+ xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE);
+ if (!xdr_ntgrp_member_list_t(&xdrs, obj)) {
+ syslog(LOG_ERR,
+ "smb_grpmemberlist_mkselfrel: XDR encode error");
+ free(buf);
+ *len = 0;
+ buf = NULL;
+ }
+
+ xdr_destroy(&xdrs);
+ return (buf);
+}
+
+/*
+ * ntgrp_list_mkabsolute
+ *
+ * decode: flat buffer -> structure
+ */
+static ntgrp_member_list_t *
+smb_grpmemberlist_mkabsolute(uint8_t *buf, uint32_t len)
+{
+ ntgrp_member_list_t *obj = NULL;
+ XDR xdrs;
+
+ xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE);
+
+ if ((obj = (ntgrp_member_list_t *)
+ malloc(sizeof (ntgrp_member_list_t))) == 0) {
+ xdr_destroy(&xdrs);
+ syslog(LOG_ERR,
+ "smb_grpmemberlist_mkabsolute: resource shortage");
+ return (NULL);
+ }
+ bzero(obj, sizeof (ntgrp_member_list_t));
+ bzero(obj->members, SMB_GROUP_PER_LIST * sizeof (members_list));
+ if (!xdr_ntgrp_member_list_t(&xdrs, obj)) {
+ syslog(LOG_ERR,
+ "smb_grpmemberlist_mkabsolute: XDR decode error");
+ smb_group_free_memberlist(obj, 1);
+ obj = NULL;
+ }
+
+ xdr_destroy(&xdrs);
+ return (obj);
+}
+
+/*
+ * smb_privlist_mkselfrel
+ *
+ * encode: structure -> flat buffer (buffer size)
+ * Pre-condition: obj is non-null.
+ */
+static uint8_t *
+smb_grpprivlist_mkselfrel(ntpriv_list_t *obj, uint32_t *len)
+{
+ uint8_t *buf;
+ XDR xdrs;
+
+ if (!obj) {
+ syslog(LOG_ERR,
+ "smb_grpprivlist_mkselfrel: invalid parameter");
+ return (NULL);
+ }
+ *len = xdr_sizeof(xdr_ntpriv_list_t, obj);
+ buf = (uint8_t *)malloc(*len);
+ if (!buf) {
+ syslog(LOG_ERR,
+ "smb_grpprivlist_mkselfrel: resource shortage");
+ return (NULL);
+ }
+ xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE);
+ if (!xdr_ntpriv_list_t(&xdrs, obj)) {
+ syslog(LOG_ERR,
+ "smb_grpprivlist_mkselfrel: XDR encode error");
+ *len = 0;
+ free(buf);
+ buf = NULL;
+ }
+ xdr_destroy(&xdrs);
+ return (buf);
+}
+
+/*
+ * smb_privlist_mkabsolute
+ *
+ * decode: flat buffer -> structure
+ */
+static ntpriv_list_t *
+smb_grpprivlist_mkabsolute(uint8_t *buf, uint32_t len)
+{
+ ntpriv_list_t *obj = NULL;
+ XDR xdrs;
+ uint32_t status;
+ int length = 0, num_privs = 0;
+
+ xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE);
+ status = smb_group_priv_num(&num_privs);
+ if (status != 0) {
+ syslog(LOG_ERR,
+ "smb_grpprivlist_mkabsolute: Cannot get privlist.");
+ xdr_destroy(&xdrs);
+ return (NULL);
+ }
+
+ if (num_privs > 0) {
+ length = sizeof (int) + (num_privs * sizeof (privs_t));
+ if ((obj = (ntpriv_list_t *)malloc(length)) == 0) {
+ syslog(LOG_ERR,
+ "smb_grpprivlist_mkabsolute: resource shortage");
+ xdr_destroy(&xdrs);
+ return (NULL);
+ }
+ }
+ bzero(obj, sizeof (ntpriv_list_t));
+ bzero(obj->privs, num_privs * sizeof (privs_t));
+ if (!xdr_ntpriv_list_t(&xdrs, obj)) {
+ syslog(LOG_ERR, "smb_grpprivlist_mkabsolute: XDR decode error");
+ smb_group_free_privlist(obj, 1);
+ obj = NULL;
+ }
+ xdr_destroy(&xdrs);
+ return (obj);
+}
+
+char *
+smb_dr_encode_grp_list(uint32_t opcode, ntgrp_list_t *list,
+ size_t *len)
+{
+ char *buf;
+ smb_dr_bytes_t arg;
+
+ arg.bytes_val = smb_grplist_mkselfrel(list, &arg.bytes_len);
+
+ buf = smb_dr_encode_common(opcode, &arg, xdr_smb_dr_bytes_t, len);
+ free(arg.bytes_val);
+ return (buf);
+}
+
+ntgrp_list_t *
+smb_dr_decode_grp_list(char *buf, size_t len)
+{
+ smb_dr_bytes_t arg;
+ ntgrp_list_t *list;
+
+ bzero(&arg, sizeof (smb_dr_bytes_t));
+ if (smb_dr_decode_common(buf, len, xdr_smb_dr_bytes_t, &arg)
+ != 0) {
+ syslog(LOG_ERR, "smb_dr_decode_grplist: XDR decode error");
+ xdr_free(xdr_smb_dr_bytes_t, (char *)&arg);
+ return (NULL);
+ }
+ list = smb_grplist_mkabsolute(arg.bytes_val, arg.bytes_len);
+ xdr_free(xdr_smb_dr_bytes_t, (char *)&arg);
+ return (list);
+}
+
+char *
+smb_dr_encode_grp_memberlist(uint32_t opcode,
+ ntgrp_member_list_t *list, size_t *len)
+{
+ char *buf;
+ smb_dr_bytes_t arg;
+
+ arg.bytes_val = smb_grpmemberlist_mkselfrel(list, &arg.bytes_len);
+
+ buf = smb_dr_encode_common(opcode, &arg, xdr_smb_dr_bytes_t, len);
+ free(arg.bytes_val);
+ return (buf);
+}
+
+ntgrp_member_list_t *
+smb_dr_decode_grp_memberlist(char *buf, size_t len)
+{
+ smb_dr_bytes_t arg;
+ ntgrp_member_list_t *list;
+
+ bzero(&arg, sizeof (smb_dr_bytes_t));
+ if (smb_dr_decode_common(buf, len, xdr_smb_dr_bytes_t, &arg)
+ != 0) {
+ syslog(LOG_ERR,
+ "smb_dr_decode_grpmemberlist: XDR decode error");
+ xdr_free(xdr_smb_dr_bytes_t, (char *)&arg);
+ return (NULL);
+ }
+ list = smb_grpmemberlist_mkabsolute(arg.bytes_val, arg.bytes_len);
+ xdr_free(xdr_smb_dr_bytes_t, (char *)&arg);
+ return (list);
+}
+
+char *
+smb_dr_encode_grp_privlist(uint32_t opcode,
+ ntpriv_list_t *list, size_t *len)
+{
+ char *buf;
+ smb_dr_bytes_t arg;
+
+ arg.bytes_val = smb_grpprivlist_mkselfrel(list, &arg.bytes_len);
+
+ buf = smb_dr_encode_common(opcode, &arg, xdr_smb_dr_bytes_t, len);
+ free(arg.bytes_val);
+ return (buf);
+}
+
+ntpriv_list_t *
+smb_dr_decode_grp_privlist(char *buf, size_t len)
+{
+ smb_dr_bytes_t arg;
+ ntpriv_list_t *list;
+
+ bzero(&arg, sizeof (smb_dr_bytes_t));
+ if (smb_dr_decode_common(buf, len, xdr_smb_dr_bytes_t, &arg)
+ != 0) {
+ syslog(LOG_ERR, "smb_dr_decode_grp_privlist: XDR decode error");
+ xdr_free(xdr_smb_dr_bytes_t, (char *)&arg);
+ return (NULL);
+ }
+ list = smb_grpprivlist_mkabsolute(arg.bytes_val, arg.bytes_len);
+ xdr_free(xdr_smb_dr_bytes_t, (char *)&arg);
+ return (list);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_group_xdr.c b/usr/src/lib/smbsrv/libsmb/common/smb_group_xdr.c
new file mode 100644
index 0000000000..723a6471f9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_group_xdr.c
@@ -0,0 +1,158 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <rpc/rpc.h>
+#include <smbsrv/libsmb.h>
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file was originally generated using rpcgen.
+ */
+
+bool_t
+xdr_ntgrp_dr_arg_t(xdrs, objp)
+ XDR *xdrs;
+ ntgrp_dr_arg_t *objp;
+{
+ if (!xdr_string(xdrs, &objp->gname, ~0))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->desc, ~0))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->member, ~0))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->newgname, ~0))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->privid))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->priv_attr))
+ return (FALSE);
+ if (!xdr_int(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->scope, ~0))
+ return (FALSE);
+ if (!xdr_int(xdrs, &objp->type))
+ return (FALSE);
+ if (!xdr_int(xdrs, &objp->count))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->ntstatus))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ntgrp_t(xdrs, objp)
+ XDR *xdrs;
+ ntgrp_t *objp;
+{
+ if (!xdr_uint32_t(xdrs, &objp->rid))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->name, ~0))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->desc, ~0))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->type, ~0))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->sid, ~0))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->attr))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ntgrp_list_t(xdrs, objp)
+ XDR *xdrs;
+ ntgrp_list_t *objp;
+{
+ if (!xdr_int(xdrs, &objp->cnt))
+ return (FALSE);
+ if (!xdr_vector(xdrs, (char *)objp->groups, SMB_GROUP_PER_LIST,
+ sizeof (ntgrp_t), (xdrproc_t)xdr_ntgrp_t))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_members_list(xdrs, objp)
+ XDR *xdrs;
+ members_list *objp;
+{
+ if (!xdr_string(xdrs, objp, ~0))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ntgrp_member_list_t(xdrs, objp)
+ XDR *xdrs;
+ ntgrp_member_list_t *objp;
+{
+ if (!xdr_uint32_t(xdrs, &objp->rid))
+ return (FALSE);
+ if (!xdr_int(xdrs, &objp->cnt))
+ return (FALSE);
+ if (!xdr_vector(xdrs, (char *)objp->members, SMB_GROUP_PER_LIST,
+ sizeof (members_list), (xdrproc_t)xdr_members_list))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ntpriv_t(xdrs, objp)
+ XDR *xdrs;
+ ntpriv_t *objp;
+{
+ if (!xdr_uint32_t(xdrs, &objp->id))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->name, ~0))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_privs_t(xdrs, objp)
+ XDR *xdrs;
+ privs_t *objp;
+{
+ if (!xdr_pointer(xdrs, (char **)objp, sizeof (ntpriv_t),
+ (xdrproc_t)xdr_ntpriv_t))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ntpriv_list_t(xdrs, objp)
+ XDR *xdrs;
+ ntpriv_list_t *objp;
+{
+ if (!xdr_int(xdrs, &objp->cnt))
+ return (FALSE);
+ if (!xdr_vector(xdrs, (char *)objp->privs, objp->cnt,
+ sizeof (privs_t), (xdrproc_t)xdr_privs_t))
+ return (FALSE);
+ return (TRUE);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_ht.c b/usr/src/lib/smbsrv/libsmb/common/smb_ht.c
new file mode 100644
index 0000000000..d167931539
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_ht.c
@@ -0,0 +1,639 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Generic hash table library. The hash table is an array of pointers
+ * to items. Hash collisions are handled using linked lists from the
+ * table entries. A handle is associated with each table, which is used
+ * to maintain the hash table.
+ *
+ * +------+ +-------+ +----+ +----+
+ * |handle|---> |index 0|--->|item|--->|item|--->
+ * | ... | +-------+ +----+ +----+
+ * | ... | |index 1|--->
+ * +------+ +-------+ +----+ +----+ +----+
+ * |index 2|--->|item|--->|item|--->|item|--->
+ * +-------+ +----+ +----+ +----+
+ * | ... |--->
+ * +-------+
+ * | ... |--->
+ * +-------+
+ * |index n|--->
+ * +-------+
+ *
+ */
+
+#include <stdlib.h>
+#include <strings.h>
+#include <smbsrv/hash_table.h>
+
+static size_t ht_default_hash(HT_HANDLE *handle, const char *key);
+
+/*
+ * ht_is_power2
+ *
+ * Inline function to determine if a value is a power of two. This
+ * function is used by the library to validate the table size when
+ * a new table is created.
+ *
+ * Returns 1 if value given is power of two, otherwise returns 0.
+ */
+static size_t
+ht_is_power2(size_t value)
+{
+ return (((value & (value - 1)) == 0)? 1 : 0);
+}
+
+
+/*
+ * ht_create_table
+ *
+ * Create a hash table. The table size must be a positive integer and
+ * must be a power of two. The key size must be a positive integer.
+ * For null terminated keys, the key size does not need to include the
+ * null terminating character. The type of key is indicated by the
+ * flags (see hash_table.h).
+ *
+ * The handle and the table are are malloc'd using a single call, to
+ * avoid two allocations. The table is located immediately after the
+ * handle.
+ *
+ * On success a pointer to an opaque handle is returned. Otherwise a
+ * null pointer is returned.
+ */
+HT_HANDLE *
+ht_create_table(size_t table_size, size_t key_size, size_t flags)
+{
+ HT_HANDLE *ht;
+ size_t msize;
+ size_t i;
+
+ if ((table_size == 0) || (key_size == 0))
+ return (NULL);
+
+ if (ht_is_power2(table_size) == 0)
+ return (NULL);
+
+ msize = sizeof (HT_HANDLE) + (sizeof (HT_TABLE_ENTRY) * table_size);
+
+ if ((ht = (HT_HANDLE *)malloc(msize)) == 0)
+ return (NULL);
+
+ /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ ht->ht_table = (HT_TABLE_ENTRY *)((char *)ht + sizeof (HT_HANDLE));
+ ht->ht_table_size = table_size;
+ ht->ht_table_mask = table_size - 1;
+ ht->ht_key_size = key_size;
+ ht->ht_total_items = 0;
+ ht->ht_flags = flags;
+ ht->ht_hash = ht_default_hash;
+ ht->ht_callback = 0;
+ ht->ht_sequence = random();
+ ht->ht_cmp = ((flags & HTHF_FIXED_KEY) == 0)
+ ? (HT_CMP)strncmp : (HT_CMP)memcmp;
+
+ for (i = 0; i < table_size; i++)
+ bzero(&ht->ht_table[i], sizeof (HT_TABLE_ENTRY));
+
+ return (ht);
+}
+
+
+/*
+ * ht_destroy_table
+ *
+ * Destroy a hash table. All entries in the table are removed, which
+ * may invoke the callback if it's installed, and the memory is freed.
+ */
+void
+ht_destroy_table(HT_HANDLE *handle)
+{
+ HT_ITEM *item;
+ HT_ITERATOR iterator;
+
+ if (handle == 0)
+ return;
+
+ /* To remove marked entries */
+ (void) ht_clean_table(handle);
+ while ((item = ht_findfirst(handle, &iterator)) != 0)
+ (void) ht_remove_item(handle, item->hi_key);
+
+ free(handle);
+}
+
+
+/*
+ * ht_get_total_items
+ *
+ * Return the total number of items in the table. Returns -1 if the
+ * handle is invalid.
+ */
+size_t
+ht_get_total_items(HT_HANDLE *handle)
+{
+ if (handle == 0)
+ return ((size_t)-1);
+
+ return (handle->ht_total_items);
+}
+
+
+/*
+ * ht_default_hash
+ *
+ * Default hash function to compute the table index (hash value) based
+ * on the specified key. This will identify the location for the
+ * corresponding item in the hash table. The handle and key pointers
+ * should be validated before this function is called.
+ *
+ * Returns the table index location for the item.
+ */
+static size_t
+ht_default_hash(HT_HANDLE *handle, const char *key)
+{
+ unsigned int hash_ndx = 0;
+ size_t rval;
+
+ if ((handle->ht_flags & HTHF_FIXED_KEY) == 0) {
+ while (*key) {
+ hash_ndx += *key;
+ ++key;
+ }
+ } else {
+ int key_len = handle->ht_key_size;
+
+ while (key_len--) {
+ hash_ndx += *key;
+ ++key;
+ }
+ }
+
+ rval = (hash_ndx * HASH_MESH_VALUE) & handle->ht_table_mask;
+ return (rval);
+}
+
+
+/*
+ * ht_set_cmpfn
+ *
+ * Replace the current compare function. As the this is function
+ * for comparing items' key, it should not be called while there are
+ * items in the table.
+ */
+void
+ht_set_cmpfn(HT_HANDLE *handle, HT_CMP cmpfn)
+{
+ if (handle)
+ handle->ht_cmp = cmpfn;
+}
+
+/*
+ * ht_add_item
+ *
+ * Adds an item to a hash table. The hash table is identified by the
+ * handle and the key is used to generate a hashed index. The data
+ * item can be null; it is never dereferenced. We don't check for
+ * duplicates. If duplicate keys are added to the table, the last
+ * item added will be to the front of the duplicate list.
+ *
+ * The table sequence number may be modified here.
+ *
+ * If the item is successfully inserted, a pointer to the item object
+ * is returned. Otherwise a null pointer is returned.
+ */
+HT_ITEM *
+ht_add_item(HT_HANDLE *handle, const char *key, const void *data)
+{
+ size_t h_index, key_len;
+ size_t msize;
+ HT_ITEM *item;
+
+ if (handle == 0 || key == 0)
+ return (NULL);
+
+ if (handle->ht_flags & HTHF_FIXED_KEY) {
+ key_len = handle->ht_key_size;
+ } else {
+ key_len = strlen(key);
+
+ if (key_len > handle->ht_key_size)
+ return (NULL);
+
+ /* Include the null terminator */
+ ++key_len;
+ }
+
+ msize = key_len + sizeof (HT_ITEM);
+
+ if ((item = malloc(msize)) == 0)
+ return (NULL);
+
+ item->hi_key = (char *)item + sizeof (HT_ITEM);
+ (void) memcpy(item->hi_key, key, key_len);
+ item->hi_data = (void *)data;
+ item->hi_flags = 0;
+
+ h_index = handle->ht_hash(handle, key);
+
+ /*
+ * Add to the front of the list.
+ */
+ item->hi_next = handle->ht_table[h_index].he_head;
+ handle->ht_table[h_index].he_head = item;
+
+ handle->ht_table[h_index].he_count++;
+ handle->ht_total_items++;
+ handle->ht_sequence++;
+
+ return (item);
+}
+
+
+/*
+ * ht_replace_item
+ *
+ * Replace an item in a hash table. The item associated with key is removed
+ * using ht_remove_item and a new item is added using ht_add_item. We rely
+ * on parameter validation in ht_remove_item and ht_add_item.
+ *
+ * The table sequence number may be modified here.
+ */
+HT_ITEM *
+ht_replace_item(HT_HANDLE *handle, const char *key, const void *data)
+{
+ (void) ht_remove_item(handle, key);
+
+ return (ht_add_item(handle, key, data));
+}
+
+
+/*
+ * ht_remove_item
+ *
+ * Remove an item from a hash table. If there are duplicate keys, then the
+ * first key found will be deleted. Note that the data pointer is never
+ * dereferenced. If a callback is installed, it will be invoked and the
+ * return value will be null. Otherwise, the data pointer supplied by the
+ * application will be returned. If there is an error, a null pointer will
+ * be returned.
+ *
+ * The table sequence number may be modified here.
+ */
+void *
+ht_remove_item(HT_HANDLE *handle, const char *key)
+{
+ size_t h_index;
+ HT_ITEM *cur, *prev;
+ int key_len;
+ void *data = 0;
+
+ if (handle == 0 || key == 0)
+ return (NULL);
+
+ if ((handle->ht_flags & HTHF_FIXED_KEY) == 0)
+ key_len = strlen(key) + 1;
+ else
+ key_len = handle->ht_key_size;
+
+ h_index = handle->ht_hash(handle, key);
+
+ cur = handle->ht_table[h_index].he_head;
+ prev = 0;
+
+ while (cur) {
+ if (!(cur->hi_flags & HTIF_MARKED_DELETED) &&
+ (handle->ht_cmp(cur->hi_key, key, key_len) == 0)) {
+ /* found key */
+ if (prev == 0)
+ handle->ht_table[h_index].he_head =
+ cur->hi_next;
+ else
+ prev->hi_next = cur->hi_next;
+
+ if (handle->ht_callback)
+ handle->ht_callback(cur);
+ else
+ data = cur->hi_data;
+
+ /*
+ * Since the key and the item were allocated as
+ * a single chunk, we only need one free here.
+ */
+ free(cur);
+
+ handle->ht_table[h_index].he_count--;
+ handle->ht_total_items--;
+ handle->ht_sequence++;
+ break;
+ }
+
+ prev = cur;
+ cur = cur->hi_next;
+ }
+
+ return (data);
+}
+
+/*
+ * ht_find_item
+ *
+ * Find an item in a hash table. If there are duplicate keys then the
+ * first item found (which will be the last one added) will be returned.
+ *
+ * Returns a pointer to an item. Otherwise returns a null pointer to
+ * indicate an error or that the key didn't match anything in the table.
+ */
+HT_ITEM *
+ht_find_item(HT_HANDLE *handle, const char *key)
+{
+ size_t h_index;
+ HT_ITEM *cur;
+ int key_len;
+
+ if (handle == 0 || key == 0)
+ return (NULL);
+
+ if ((handle->ht_flags & HTHF_FIXED_KEY) == 0)
+ key_len = strlen(key) + 1;
+ else
+ key_len = handle->ht_key_size;
+
+ h_index = handle->ht_hash(handle, key);
+ cur = handle->ht_table[h_index].he_head;
+
+ while (cur) {
+ if (!(cur->hi_flags & HTIF_MARKED_DELETED) &&
+ (handle->ht_cmp(cur->hi_key, key, key_len) == 0))
+ return (cur);
+
+ cur = cur->hi_next;
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * ht_register_callback
+ *
+ * Register an application callback function that can be used to process
+ * an item when it is removed from the table, i.e. free any memory
+ * allocated for that data item.
+ *
+ * The previous callback function pointer, which may be null, before
+ * registering the new one. This provides the caller with the option to
+ * restore a previous callback as required.
+ */
+HT_CALLBACK
+ht_register_callback(HT_HANDLE *handle, HT_CALLBACK callback)
+{
+ HT_CALLBACK old_callback;
+
+ if (handle == 0)
+ return (NULL);
+
+ old_callback = handle->ht_callback;
+ handle->ht_callback = callback;
+
+ return (old_callback);
+}
+
+
+/*
+ * ht_clean_table
+ *
+ * This function removes all the items that are marked for deletion. Note
+ * that this will invoke the callback, if one has been installed. If this
+ * call is used, the callback mechanism is the only way for an application
+ * to free the item data if it was dynamically allocated.
+ *
+ * The table sequence number may be modified here.
+ *
+ * Returns 0 if the handle is valid; otherwise returns -1.
+ */
+size_t
+ht_clean_table(HT_HANDLE *handle)
+{
+ size_t i;
+ HT_ITEM *cur, *prev;
+
+ if (handle == 0)
+ return ((size_t)-1);
+
+ for (i = 0; i < handle->ht_table_size; i++) {
+ cur = handle->ht_table[i].he_head;
+ prev = 0;
+
+ while (cur) {
+ if (cur->hi_flags & HTIF_MARKED_DELETED) {
+ /*
+ * We have a marked item: remove it.
+ */
+ if (prev == 0)
+ handle->ht_table[i].he_head =
+ cur->hi_next;
+ else
+ prev->hi_next = cur->hi_next;
+
+ if (handle->ht_callback)
+ handle->ht_callback(cur);
+
+ /*
+ * Since the key and the item were allocated as
+ * a single chunk, we only need one free here.
+ */
+ free(cur);
+
+ handle->ht_table[i].he_count--;
+ handle->ht_sequence++;
+
+ if (prev == 0)
+ cur = handle->ht_table[i].he_head;
+ else
+ cur = prev->hi_next;
+ continue;
+ }
+
+ prev = cur;
+ cur = cur->hi_next;
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * ht_mark_delete
+ *
+ * This function marks an item for deletion, which may be useful when
+ * using findfirst/findnext to avoid modifying the table during the
+ * table scan. Marked items can be removed later using ht_clean_table.
+ */
+void
+ht_mark_delete(HT_HANDLE *handle, HT_ITEM *item)
+{
+ if (handle && item) {
+ item->hi_flags |= HTIF_MARKED_DELETED;
+ handle->ht_total_items--;
+ }
+}
+
+/*
+ * ht_clear_delete
+ *
+ * This function clear an item from marked for deletion list.
+ */
+void
+ht_clear_delete(HT_HANDLE *handle, HT_ITEM *item)
+{
+ if (handle && item) {
+ item->hi_flags &= ~HTIF_MARKED_DELETED;
+ handle->ht_total_items++;
+ }
+}
+
+/*
+ * ht_bucket_search
+ *
+ * Returns first item which is not marked as deleted
+ * in the specified bucket by 'head'
+ */
+static HT_ITEM *
+ht_bucket_search(HT_ITEM *head)
+{
+ HT_ITEM *item = head;
+ while ((item != 0) && (item->hi_flags & HTIF_MARKED_DELETED))
+ item = item->hi_next;
+
+ return (item);
+}
+
+/*
+ * ht_findfirst
+ *
+ * This function is used to begin an iteration through the hash table.
+ * The iterator is initialized and the first item in the table (as
+ * determined by the hash algorithm) is returned. The current sequence
+ * number is stored in the iterator to determine whether or not the
+ * the table has changed between calls. If the table is empty, a null
+ * pointer is returned.
+ */
+HT_ITEM *
+ht_findfirst(HT_HANDLE *handle, HT_ITERATOR *iterator)
+{
+ HT_ITEM *item;
+ size_t h_index;
+
+ if (handle == 0 || iterator == 0 || handle->ht_total_items == 0)
+ return (NULL);
+
+ (void) memset(iterator, 0, sizeof (HT_ITERATOR));
+ iterator->hti_handle = handle;
+ iterator->hti_sequence = handle->ht_sequence;
+
+ for (h_index = 0; h_index < handle->ht_table_size; ++h_index) {
+ item = ht_bucket_search(handle->ht_table[h_index].he_head);
+ if (item != 0) {
+ iterator->hti_index = h_index;
+ iterator->hti_item = item;
+ return (item);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * ht_findnext
+ *
+ * Find the next item in the table for the given iterator. Iterators must
+ * be initialized by ht_findfirst, which will also return the first item
+ * in the table. If an item is available, a pointer to it is returned.
+ * Otherwise a null pointer is returned. A null pointer may indicate:
+ *
+ * - an invalid iterator (i.e. ht_findfirst has not been called)
+ * - the table has changed since the previous findfirst/findnext
+ * - the entire table has been traversed
+ *
+ * The caller can use ht_get_total_items to determine whether or not all
+ * of the items in the table have been visited.
+ */
+HT_ITEM *
+ht_findnext(HT_ITERATOR *iterator)
+{
+ HT_HANDLE *handle;
+ HT_ITEM *item;
+ size_t total;
+ size_t index;
+
+ if (iterator == 0 || iterator->hti_handle == 0 ||
+ iterator->hti_sequence == 0) {
+ /* Invalid iterator */
+ return (NULL);
+ }
+
+ handle = iterator->hti_handle;
+
+ if (iterator->hti_item == 0 ||
+ iterator->hti_sequence != handle->ht_sequence) {
+ /*
+ * No more items or the table has changed
+ * since the last call.
+ */
+ return (NULL);
+ }
+
+ /*
+ * Check for another item in the current bucket.
+ */
+ item = ht_bucket_search(iterator->hti_item->hi_next);
+ if (item != 0) {
+ iterator->hti_item = item;
+ return (item);
+ }
+
+ /*
+ * Nothing else in the current bucket. Look for another
+ * bucket with something in it and return the head item.
+ */
+ total = handle->ht_table_size;
+ for (index = iterator->hti_index + 1; index < total; ++index) {
+ item = ht_bucket_search(handle->ht_table[index].he_head);
+ if (item != 0) {
+ iterator->hti_index = index;
+ iterator->hti_item = item;
+ return (item);
+ }
+ }
+
+ iterator->hti_index = 0;
+ iterator->hti_item = 0;
+ iterator->hti_sequence = 0;
+ return (NULL);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_idmap.c b/usr/src/lib/smbsrv/libsmb/common/smb_idmap.c
new file mode 100644
index 0000000000..f410fe3237
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_idmap.c
@@ -0,0 +1,382 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <strings.h>
+#include <smbsrv/libsmb.h>
+
+static idmap_handle_t *idmap_clnt_hdl = NULL;
+static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib);
+
+/*
+ * smb_idmap_start
+ *
+ * This function initializes the idmap client handle. It should be called
+ * at startup.
+ */
+int
+smb_idmap_start(void)
+{
+ idmap_stat stat;
+
+ if (idmap_clnt_hdl)
+ return (0);
+
+ stat = idmap_init(&idmap_clnt_hdl);
+ if (stat < 0) {
+ syslog(LOG_ERR, "smb_idmap_start: idmap_init failed (%s)",
+ idmap_stat2string(NULL, stat));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * smb_idmap_stop
+ *
+ * This function destroys the idmap client handle. It should be called
+ * prior to exiting the SMB daemon.
+ */
+void
+smb_idmap_stop(void)
+{
+ if (idmap_clnt_hdl) {
+ (void) idmap_fini(idmap_clnt_hdl);
+ idmap_clnt_hdl = NULL;
+ }
+}
+
+/*
+ * smb_idmap_restart
+ *
+ * This function should be called when the idmap client handle
+ * becomes invalid.
+ */
+int
+smb_idmap_restart(void)
+{
+ smb_idmap_stop();
+ if (smb_idmap_start() != 0) {
+ syslog(LOG_ERR, "smb_idmap_restart: smb_idmap_start failed");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * smb_idmap_getsid
+ *
+ * Tries to get a mapping for the given uid/gid
+ */
+idmap_stat
+smb_idmap_getsid(uid_t id, int idtype, nt_sid_t **sid)
+{
+ smb_idmap_batch_t sib;
+ idmap_stat stat;
+
+ stat = smb_idmap_batch_create(&sib, 1, SMB_IDMAP_ID2SID);
+ if (stat != IDMAP_SUCCESS)
+ return (stat);
+
+ stat = smb_idmap_batch_getsid(sib.sib_idmaph, &sib.sib_maps[0],
+ id, idtype);
+
+ if (stat != IDMAP_SUCCESS) {
+ smb_idmap_batch_destroy(&sib);
+ return (stat);
+ }
+
+ stat = smb_idmap_batch_getmappings(&sib);
+
+ if (stat != IDMAP_SUCCESS) {
+ smb_idmap_batch_destroy(&sib);
+ return (stat);
+ }
+
+ *sid = nt_sid_dup(sib.sib_maps[0].sim_sid);
+
+ smb_idmap_batch_destroy(&sib);
+
+ return (IDMAP_SUCCESS);
+}
+
+/*
+ * smb_idmap_batch_create
+ *
+ * Creates and initializes the context for batch ID mapping.
+ */
+idmap_stat
+smb_idmap_batch_create(smb_idmap_batch_t *sib, uint16_t nmap, int flags)
+{
+ idmap_stat stat;
+
+ if (!sib)
+ return (IDMAP_ERR_ARG);
+
+ bzero(sib, sizeof (smb_idmap_batch_t));
+ stat = idmap_get_create(idmap_clnt_hdl, &sib->sib_idmaph);
+ if (stat != IDMAP_SUCCESS)
+ return (stat);
+
+ sib->sib_flags = flags;
+ sib->sib_nmap = nmap;
+ sib->sib_size = nmap * sizeof (smb_idmap_t);
+ sib->sib_maps = malloc(sib->sib_size);
+ if (!sib->sib_maps)
+ return (IDMAP_ERR_MEMORY);
+
+ bzero(sib->sib_maps, sib->sib_size);
+ return (IDMAP_SUCCESS);
+}
+
+/*
+ * smb_idmap_batch_destroy
+ *
+ * Frees the batch ID mapping context.
+ */
+void
+smb_idmap_batch_destroy(smb_idmap_batch_t *sib)
+{
+ nt_sid_t *sid;
+ char *domsid;
+ int i;
+
+ if (!sib)
+ return;
+
+ if (sib->sib_idmaph) {
+ idmap_get_destroy(sib->sib_idmaph);
+ sib->sib_idmaph = NULL;
+ }
+
+ if (!sib->sib_maps)
+ return;
+
+ switch (sib->sib_flags) {
+ case SMB_IDMAP_SID2ID:
+ /*
+ * SIDs are allocated only when mapping
+ * UID/GID to SIDs
+ */
+ for (i = 0; i < sib->sib_nmap; i++) {
+ sid = sib->sib_maps[i].sim_sid;
+ if (sid)
+ free(sid);
+ }
+ break;
+ case SMB_IDMAP_ID2SID:
+ /*
+ * SID prefixes are allocated only when mapping
+ * SIDs to UID/GID
+ */
+ for (i = 0; i < sib->sib_nmap; i++) {
+ domsid = sib->sib_maps[i].sim_domsid;
+ if (domsid)
+ free(domsid);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (sib->sib_size && sib->sib_maps) {
+ free(sib->sib_maps);
+ sib->sib_maps = NULL;
+ }
+}
+
+/*
+ * smb_idmap_batch_getid
+ *
+ * Queue a request to map the given SID to a UID or GID.
+ *
+ * sim->sim_id should point to variable that's supposed to
+ * hold the returned UID/GID. This needs to be setup by caller
+ * of this function.
+ * If requested ID type is known, it's passed as 'idtype',
+ * if it's unknown it'll be returned in sim->sim_idtype.
+ */
+idmap_stat
+smb_idmap_batch_getid(idmap_get_handle_t *idmaph, smb_idmap_t *sim,
+ nt_sid_t *sid, int idtype)
+{
+ nt_sid_t *tmpsid;
+ idmap_stat stat;
+ int flag = 0;
+
+ if (!idmaph || !sim || !sid)
+ return (IDMAP_ERR_ARG);
+
+ tmpsid = nt_sid_dup(sid);
+ if (!tmpsid)
+ return (IDMAP_ERR_MEMORY);
+
+ if (nt_sid_split(tmpsid, &sim->sim_rid) != 0) {
+ free(tmpsid);
+ return (IDMAP_ERR_ARG);
+ }
+
+ sim->sim_domsid = nt_sid_format(tmpsid);
+ free(tmpsid);
+
+ switch (idtype) {
+ case SMB_IDMAP_USER:
+ stat = idmap_get_uidbysid(idmaph, sim->sim_domsid,
+ sim->sim_rid, flag, sim->sim_id, &sim->sim_stat);
+ break;
+
+ case SMB_IDMAP_GROUP:
+ stat = idmap_get_gidbysid(idmaph, sim->sim_domsid,
+ sim->sim_rid, flag, sim->sim_id, &sim->sim_stat);
+ break;
+
+ case SMB_IDMAP_UNKNOWN:
+ stat = idmap_get_pidbysid(idmaph, sim->sim_domsid,
+ sim->sim_rid, flag, sim->sim_id, &sim->sim_idtype,
+ &sim->sim_stat);
+ break;
+
+ default:
+ return (IDMAP_ERR_ARG);
+ }
+
+ return (stat);
+}
+
+/*
+ * smb_idmap_batch_getsid
+ *
+ * Queue a request to map the given UID/GID to a SID.
+ *
+ * sim->sim_domsid and sim->sim_rid will contain the mapping
+ * result upon successful process of the batched request.
+ */
+idmap_stat
+smb_idmap_batch_getsid(idmap_get_handle_t *idmaph, smb_idmap_t *sim,
+ uid_t id, int idtype)
+{
+ idmap_stat stat;
+ int flag = 0;
+
+ if (!idmaph || !sim)
+ return (IDMAP_ERR_ARG);
+
+ switch (idtype) {
+ case SMB_IDMAP_USER:
+ stat = idmap_get_sidbyuid(idmaph, id, flag,
+ &sim->sim_domsid, &sim->sim_rid, &sim->sim_stat);
+ break;
+
+ case SMB_IDMAP_GROUP:
+ stat = idmap_get_sidbygid(idmaph, id, flag,
+ &sim->sim_domsid, &sim->sim_rid, &sim->sim_stat);
+ break;
+
+ case SMB_IDMAP_EVERYONE:
+ /* Everyone S-1-1-0 */
+ sim->sim_domsid = "S-1-1";
+ sim->sim_rid = 0;
+ sim->sim_stat = IDMAP_SUCCESS;
+ stat = IDMAP_SUCCESS;
+ break;
+
+ default:
+ return (IDMAP_ERR_ARG);
+ }
+
+ return (stat);
+}
+
+/*
+ * smb_idmap_batch_getmappings
+ *
+ * trigger ID mapping service to get the mappings for queued
+ * requests.
+ *
+ * Checks the result of all the queued requests.
+ */
+idmap_stat
+smb_idmap_batch_getmappings(smb_idmap_batch_t *sib)
+{
+ idmap_stat stat = IDMAP_SUCCESS;
+ int i;
+
+ stat = idmap_get_mappings(sib->sib_idmaph);
+ if (stat != IDMAP_SUCCESS) {
+ return (stat);
+ }
+
+ /*
+ * Check the status for all the queued requests
+ */
+ for (i = 0; i < sib->sib_nmap; i++) {
+ if (sib->sib_maps[i].sim_stat != IDMAP_SUCCESS) {
+ return (sib->sib_maps[i].sim_stat);
+ }
+ }
+
+ if (smb_idmap_batch_binsid(sib) != 0) {
+ stat = IDMAP_ERR_OTHER;
+ }
+
+ return (stat);
+}
+
+/*
+ * smb_idmap_batch_binsid
+ *
+ * Convert sidrids to binary sids
+ *
+ * Returns 0 if successful and non-zero upon failure.
+ */
+static int
+smb_idmap_batch_binsid(smb_idmap_batch_t *sib)
+{
+ nt_sid_t *sid;
+ smb_idmap_t *sim;
+ int i;
+
+ if (sib->sib_flags & SMB_IDMAP_SID2ID)
+ /* This operation is not required */
+ return (0);
+
+ sim = sib->sib_maps;
+ for (i = 0; i < sib->sib_nmap; sim++, i++) {
+ if (sim->sim_domsid == NULL)
+ return (-1);
+
+ sid = nt_sid_strtosid(sim->sim_domsid);
+ if (sid == NULL)
+ return (-1);
+
+ sim->sim_sid = nt_sid_splice(sid, sim->sim_rid);
+ free(sid);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_info.c b/usr/src/lib/smbsrv/libsmb/common/smb_info.c
new file mode 100644
index 0000000000..24d7b1b3d1
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_info.c
@@ -0,0 +1,382 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <synch.h>
+#include <syslog.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <sys/sockio.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/netbios.h>
+#include <smbsrv/libsmb.h>
+
+static smb_ntdomain_t smbpdc_cache;
+static mutex_t smbpdc_mtx;
+static cond_t smbpdc_cv;
+
+extern int getdomainname(char *, int);
+
+uint32_t
+smb_get_security_mode()
+{
+ uint32_t mode;
+
+ smb_config_rdlock();
+ mode = smb_config_get_secmode();
+ smb_config_unlock();
+
+ return (mode);
+}
+
+/*
+ * smb_purge_domain_info
+ *
+ * Clean out the environment in preparation for joining a domain.
+ * This ensures that we don't have any old information lying around.
+ */
+void
+smb_purge_domain_info(void)
+{
+ smb_config_wrlock();
+ (void) smb_config_set(SMB_CI_DOMAIN_NAME, 0);
+ (void) smb_config_set(SMB_CI_DOMAIN_SID, 0);
+ (void) smb_config_set(SMB_CI_DOMAIN_MEMB, 0);
+ smb_config_unlock();
+}
+
+int
+smb_is_domain_member(void)
+{
+ int is_memb;
+
+ smb_config_rdlock();
+ is_memb = smb_config_getyorn(SMB_CI_DOMAIN_MEMB);
+ smb_config_unlock();
+
+ return (is_memb);
+}
+
+uint8_t
+smb_get_fg_flag(void)
+{
+ uint8_t run_fg;
+
+ smb_config_rdlock();
+ run_fg = smb_config_get_fg_flag();
+ smb_config_unlock();
+
+ return (run_fg);
+}
+
+void
+smb_set_domain_member(int set)
+{
+ char *member;
+
+ smb_config_wrlock();
+ member = (set) ? "true" : "false";
+ (void) smb_config_set(SMB_CI_DOMAIN_MEMB, member);
+ smb_config_unlock();
+}
+
+/*
+ * smb_getdomaininfo
+ *
+ * Returns a pointer to the cached domain data. The caller can specify
+ * whether or not he is prepared to wait if the cache is not yet valid
+ * and for how long. The specified timeout is in seconds.
+ */
+smb_ntdomain_t *
+smb_getdomaininfo(uint32_t timeout)
+{
+ timestruc_t to;
+ int err;
+
+ if (timeout != 0) {
+ (void) mutex_lock(&smbpdc_mtx);
+ while (smbpdc_cache.ipaddr == 0) {
+ to.tv_sec = timeout;
+ to.tv_nsec = 0;
+ err = cond_reltimedwait(&smbpdc_cv, &smbpdc_mtx, &to);
+ if (err == ETIME)
+ break;
+ }
+ (void) mutex_unlock(&smbpdc_mtx);
+ }
+
+ if (smbpdc_cache.ipaddr != 0)
+ return (&smbpdc_cache);
+ else
+ return (0);
+}
+
+void
+smb_logdomaininfo(smb_ntdomain_t *di)
+{
+ char ipstr[16];
+
+ (void) inet_ntop(AF_INET, (const void *)&di->ipaddr, ipstr,
+ sizeof (ipstr));
+ syslog(LOG_DEBUG, "smbd: %s (%s:%s)", di->domain, di->server, ipstr);
+}
+
+/*
+ * smb_setdomaininfo
+ *
+ * Set the information for the specified domain. If the information is
+ * non-null, the notification event is raised to wakeup any threads
+ * blocking on the cache.
+ */
+void
+smb_setdomaininfo(char *domain, char *server, uint32_t ipaddr)
+{
+ char *p;
+
+ bzero(&smbpdc_cache, sizeof (smb_ntdomain_t));
+
+ if (domain && server && ipaddr) {
+ (void) strlcpy(smbpdc_cache.domain, domain, SMB_PI_MAX_DOMAIN);
+ (void) strlcpy(smbpdc_cache.server, server, SMB_PI_MAX_DOMAIN);
+
+ /*
+ * Remove DNS domain name extension
+ * to avoid confusing NetBIOS.
+ */
+ if ((p = strchr(smbpdc_cache.domain, '.')) != 0)
+ *p = '\0';
+
+ if ((p = strchr(smbpdc_cache.server, '.')) != 0)
+ *p = '\0';
+
+ (void) mutex_lock(&smbpdc_mtx);
+ smbpdc_cache.ipaddr = ipaddr;
+ (void) cond_broadcast(&smbpdc_cv);
+ (void) mutex_unlock(&smbpdc_mtx);
+ }
+}
+
+void
+smb_load_kconfig(smb_kmod_cfg_t *kcfg)
+{
+ smb_config_rdlock();
+ bzero(kcfg, sizeof (smb_kmod_cfg_t));
+
+ kcfg->skc_maxbufsize = smb_config_getnum(SMB_CI_MAX_BUFSIZE);
+ kcfg->skc_maxworkers = smb_config_getnum(SMB_CI_MAX_WORKERS);
+ kcfg->skc_keepalive = smb_config_getnum(SMB_CI_KEEPALIVE);
+ if ((kcfg->skc_keepalive != 0) &&
+ (kcfg->skc_keepalive < SMB_PI_KEEP_ALIVE_MIN))
+ kcfg->skc_keepalive = SMB_PI_KEEP_ALIVE_MIN;
+ kcfg->skc_restrict_anon = smb_config_getyorn(SMB_CI_RESTRICT_ANON);
+
+ kcfg->skc_signing_enable = smb_config_getyorn(SMB_CI_SIGNING_ENABLE);
+ kcfg->skc_signing_required = smb_config_getyorn(SMB_CI_SIGNING_REQD);
+ kcfg->skc_signing_check = smb_config_getyorn(SMB_CI_SIGNING_CHECK);
+
+ kcfg->skc_oplock_enable = smb_config_getyorn(SMB_CI_OPLOCK_ENABLE);
+ kcfg->skc_oplock_timeout = smb_config_getnum(SMB_CI_OPLOCK_TIMEOUT);
+
+ kcfg->skc_flush_required = smb_config_getyorn(SMB_CI_FLUSH_REQUIRED);
+ kcfg->skc_sync_enable = smb_config_getyorn(SMB_CI_SYNC_ENABLE);
+ kcfg->skc_dirsymlink_enable =
+ !smb_config_getyorn(SMB_CI_DIRSYMLINK_DISABLE);
+ kcfg->skc_announce_quota = smb_config_getyorn(SMB_CI_ANNONCE_QUOTA);
+ kcfg->skc_announce_quota = smb_config_getyorn(SMB_CI_ANNONCE_QUOTA);
+
+ kcfg->skc_secmode = smb_config_get_secmode();
+ kcfg->skc_lmlevel = smb_config_getnum(SMB_CI_LM_LEVEL);
+ kcfg->skc_maxconnections = smb_config_getnum(SMB_CI_MAX_CONNECTIONS);
+
+ (void) strlcpy(kcfg->skc_resource_domain,
+ smb_config_getstr(SMB_CI_DOMAIN_NAME),
+ sizeof (kcfg->skc_resource_domain));
+
+ (void) smb_gethostname(kcfg->skc_hostname,
+ sizeof (kcfg->skc_hostname), 1);
+
+ (void) strlcpy(kcfg->skc_system_comment,
+ smb_config_getstr(SMB_CI_SYS_CMNT),
+ sizeof (kcfg->skc_system_comment));
+
+ smb_config_unlock();
+}
+
+/*
+ * Get the current system NetBIOS name. The hostname is truncated at
+ * the first `.` or 15 bytes, whichever occurs first, and converted
+ * to uppercase (by smb_gethostname). Text that appears after the
+ * first '.' is considered to be part of the NetBIOS scope.
+ *
+ * Returns 0 on success, otherwise -1 to indicate an error.
+ */
+int
+smb_getnetbiosname(char *buf, size_t buflen)
+{
+ if (smb_gethostname(buf, buflen, 1) != 0)
+ return (-1);
+
+ if (buflen >= NETBIOS_NAME_SZ)
+ buf[NETBIOS_NAME_SZ - 1] = '\0';
+
+ return (0);
+}
+
+/*
+ * Get the current system node name. The returned name is guaranteed
+ * to be null-terminated (gethostname may not null terminate the name).
+ * If the hostname has been fully-qualified for some reason, the domain
+ * part will be removed. If the caller would like the name in upper
+ * case, it is folded to uppercase.
+ *
+ * If gethostname fails, the returned buffer will contain an empty
+ * string.
+ */
+int
+smb_gethostname(char *buf, size_t buflen, int upcase)
+{
+ char *p;
+
+ if (buf == NULL || buflen == 0)
+ return (-1);
+
+ if (gethostname(buf, buflen) != 0) {
+ *buf = '\0';
+ return (-1);
+ }
+
+ buf[buflen - 1] = '\0';
+
+ if ((p = strchr(buf, '.')) != NULL)
+ *p = '\0';
+
+ if (upcase)
+ (void) utf8_strupr(buf);
+
+ return (0);
+}
+
+/*
+ * The ADS domain is often the same as the DNS domain but they can be
+ * different - one might be a sub-domain of the other.
+ *
+ * If an ADS domain name has been configured, return it. Otherwise,
+ * return the DNS domain name.
+ *
+ * If getdomainname fails, the returned buffer will contain an empty
+ * string.
+ */
+int
+smb_getdomainname(char *buf, size_t buflen)
+{
+ char *domain;
+
+ if (buf == NULL || buflen == 0)
+ return (-1);
+
+ smb_config_rdlock();
+
+ domain = smb_config_getstr(SMB_CI_ADS_DOMAIN);
+ if ((domain != NULL) && (*domain != '\0')) {
+ (void) strlcpy(buf, domain, buflen);
+ smb_config_unlock();
+ return (0);
+ }
+
+ smb_config_unlock();
+
+ if (getdomainname(buf, buflen) != 0) {
+ *buf = '\0';
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Obtain the fully-qualified name for this machine. If the
+ * hostname is fully-qualified, accept it. Otherwise, try to
+ * find an appropriate domain name to append to the hostname.
+ */
+int
+smb_getfqhostname(char *buf, size_t buflen)
+{
+ char hostname[MAXHOSTNAMELEN];
+ char domain[MAXHOSTNAMELEN];
+
+ hostname[0] = '\0';
+ domain[0] = '\0';
+
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0)
+ return (-1);
+
+ if (smb_getdomainname(domain, MAXHOSTNAMELEN) != 0)
+ return (-1);
+
+ if (hostname[0] == '\0')
+ return (-1);
+
+ if (domain[0] == '\0') {
+ (void) strlcpy(buf, hostname, buflen);
+ return (0);
+ }
+
+ (void) snprintf(buf, buflen, "%s.%s", hostname, domain);
+ return (0);
+}
+
+/*
+ * Temporary fbt for dtrace until user space sdt enabled.
+ */
+void
+smb_tracef(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[128];
+
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, 128, fmt, ap);
+ va_end(ap);
+
+ smb_trace(buf);
+}
+
+/*
+ * Temporary fbt for dtrace until user space sdt enabled.
+ */
+void
+smb_trace(const char *s)
+{
+ syslog(LOG_DEBUG, "%s", s);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_mac.c b/usr/src/lib/smbsrv/libsmb/common/smb_mac.c
new file mode 100644
index 0000000000..57fb74530c
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_mac.c
@@ -0,0 +1,207 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SMB MAC Signing support.
+ */
+
+#include <strings.h>
+#include <security/cryptoki.h>
+#include <security/pkcs11.h>
+
+#include <smbsrv/libsmb.h>
+
+#include <smbsrv/smb.h>
+
+/*
+ * smb_mac_init
+ *
+ * Calculates the MAC key using the specified user session
+ * key (NTLM or NTLMv2).
+ *
+ * Returns SMBAUTH_SUCCESS if key generation was successful,
+ * SMBAUTH_FAILURE if not.
+ */
+int
+smb_mac_init(smb_sign_ctx_t *sign_ctx, smb_auth_info_t *auth)
+{
+ unsigned char S16[SMBAUTH_SESSION_KEY_SZ];
+
+ if (smb_auth_gen_session_key(auth, S16) != SMBAUTH_SUCCESS)
+ return (SMBAUTH_FAILURE);
+ bcopy(S16, sign_ctx->ssc_mackey, SMBAUTH_SESSION_KEY_SZ);
+ bcopy(auth->cs, &(sign_ctx->ssc_mackey[SMBAUTH_SESSION_KEY_SZ]),
+ auth->cs_len);
+ sign_ctx->ssc_keylen = SMBAUTH_SESSION_KEY_SZ + auth->cs_len;
+ return (SMBAUTH_SUCCESS);
+}
+
+/*
+ * smb_mac_calc
+ *
+ * Calculates MAC signature for the given buffer and returns
+ * it in the mac_sign parameter.
+ *
+ * The MAC signature is calculated as follows:
+ *
+ * data = concat(MAC_Key, MAC_Key_Len, SMB_Msg, SMB_Msg_Len);
+ * hash = MD5(data);
+ * MAC = head(hash, 8);
+ *
+ * The tricky part is that a sequence number should be used
+ * in calculation instead of the signature field in the
+ * SMB header.
+ *
+ * Returns SMBAUTH_SUCCESS if cryptology framework use was successful,
+ * SMBAUTH_FAILURE if not.
+ */
+int
+smb_mac_calc(smb_sign_ctx_t *sign_ctx, const unsigned char *buf,
+ size_t buf_len, unsigned char *mac_sign)
+{
+ CK_RV rv;
+ CK_MECHANISM mechanism;
+ CK_SESSION_HANDLE hSession;
+ unsigned long diglen = MD_DIGEST_LEN;
+ int rc = SMBAUTH_FAILURE;
+
+ int offset_end_of_sig = (SMB_SIG_OFFS + SMB_SIG_SIZE);
+ unsigned char seq_buf[SMB_SIG_SIZE];
+ unsigned char mac[16];
+
+ /*
+ * put seq_num into the first 4 bytes and
+ * zero out the next 4 bytes
+ */
+ bcopy(&sign_ctx->ssc_seqnum, seq_buf, 4);
+ bzero(seq_buf + 4, 4);
+
+ mechanism.mechanism = CKM_MD5;
+ mechanism.pParameter = 0;
+ mechanism.ulParameterLen = 0;
+
+ rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
+ if (rv != CKR_OK)
+ return (SMBAUTH_FAILURE);
+
+ /* Initialize the digest operation in the session */
+ rv = C_DigestInit(hSession, &mechanism);
+ if (rv != CKR_OK)
+ goto smbmacdone;
+
+ /* init with the MAC key */
+ rv = C_DigestUpdate(hSession, sign_ctx->ssc_mackey,
+ sign_ctx->ssc_keylen);
+ if (rv != CKR_OK)
+ goto smbmacdone;
+
+ /* copy in SMB packet info till signature field */
+ rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)buf, SMB_SIG_OFFS);
+ if (rv != CKR_OK)
+ goto smbmacdone;
+
+ /* copy in the seq_buf instead of the signature */
+ rv = C_DigestUpdate(hSession, seq_buf, sizeof (seq_buf));
+ if (rv != CKR_OK)
+ goto smbmacdone;
+
+ /* copy in the rest of the packet, skipping the signature */
+ rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)buf + offset_end_of_sig,
+ buf_len - offset_end_of_sig);
+ if (rv != CKR_OK)
+ goto smbmacdone;
+
+ rv = C_DigestFinal(hSession, mac, &diglen);
+ if (rv != CKR_OK)
+ goto smbmacdone;
+
+ bcopy(mac, mac_sign, SMB_SIG_SIZE);
+ rc = SMBAUTH_SUCCESS;
+
+smbmacdone:
+ (void) C_CloseSession(hSession);
+ return (rc);
+}
+
+/*
+ * smb_mac_chk
+ *
+ * Calculates MAC signature for the given buffer
+ * and compares it to the signature in the given context.
+ * Return 1 if the signature are match, otherwise, return (0);
+ */
+int
+smb_mac_chk(smb_sign_ctx_t *sign_ctx,
+ const unsigned char *buf, size_t buf_len)
+{
+ unsigned char mac_sign[SMB_SIG_SIZE];
+
+ /* calculate mac signature */
+ if (smb_mac_calc(sign_ctx, buf, buf_len, mac_sign) != SMBAUTH_SUCCESS)
+ return (0);
+
+ /* compare the signatures */
+ if (memcmp(sign_ctx->ssc_sign, mac_sign, SMB_SIG_SIZE) == 0)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * smb_mac_sign
+ *
+ * Calculates MAC signature for the given buffer,
+ * and write it to the buffer's signature field.
+ *
+ * Returns SMBAUTH_SUCCESS if cryptology framework use was successful,
+ * SMBAUTH_FAILURE if not.
+ */
+int
+smb_mac_sign(smb_sign_ctx_t *sign_ctx, unsigned char *buf, size_t buf_len)
+{
+ unsigned char mac_sign[SMB_SIG_SIZE];
+
+ /* calculate mac signature */
+ if (smb_mac_calc(sign_ctx, buf, buf_len, mac_sign) != SMBAUTH_SUCCESS)
+ return (SMBAUTH_FAILURE);
+
+ /* put mac signature in the header's signature field */
+ (void) memcpy(buf + SMB_SIG_OFFS, mac_sign, SMB_SIG_SIZE);
+ return (SMBAUTH_SUCCESS);
+}
+
+void
+smb_mac_inc_seqnum(smb_sign_ctx_t *sign_ctx)
+{
+ sign_ctx->ssc_seqnum++;
+}
+
+void
+smb_mac_dec_seqnum(smb_sign_ctx_t *sign_ctx)
+{
+ sign_ctx->ssc_seqnum--;
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_privilege.c b/usr/src/lib/smbsrv/libsmb/common/smb_privilege.c
new file mode 100644
index 0000000000..ce7cef198a
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_privilege.c
@@ -0,0 +1,361 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module provides the interface to the built-in privilege names
+ * and id's. NT privileges are known on the network using strings. Each
+ * system assigns locally unique identifiers (LUID) for use within the
+ * system. Each built-in privilege also has a display-name, which is a
+ * short description of the privilege. The functions here provide an
+ * interface to map between LUIDs, names and display names.
+ */
+
+#include <string.h>
+#include <syslog.h>
+
+#include <smbsrv/string.h>
+#include <smbsrv/libsmb.h>
+#include <smbsrv/smb_privilege.h>
+
+#define SMB_PRIV_MIN 2
+#define SMB_PRIV_MAX 24
+
+static char *smb_priv_getname(uint32_t id);
+
+/*
+ * Table of built-in privilege id's, names and display strings. This
+ * table matches the response from an NT4.0 PDC LSARPC service.
+ * Requests for values 0 and 1 return STATUS_NO_SUCH_PRIVILEGE.
+ *
+ * SE_UNSOLICITED_INPUT_NAME/SeUnsolicitedInputPrivilege is defined in
+ * winnt.h but doesn't appear in the list reported by the NT4.0 LSA.
+ */
+static smb_privinfo_t priv_table[] = {
+ { 0, "", "", 0 },
+ { 1, "", "", 0 },
+ { 2, SE_CREATE_TOKEN_NAME, "Create a token object", 0 },
+ { 3, SE_ASSIGNPRIMARYTOKEN_NAME, "Replace a process level token", 0 },
+ { 4, SE_LOCK_MEMORY_NAME, "Lock pages in memory", 0 },
+ { 5, SE_INCREASE_QUOTA_NAME, "Increase quotas", 0 },
+ { 6, SE_MACHINE_ACCOUNT_NAME, "Add workstations to domain", 0 },
+ { 7, SE_TCB_NAME, "Act as part of the operating system", 0 },
+ { 8, SE_SECURITY_NAME, "Manage auditing and security log", 0 },
+ { 9, SE_TAKE_OWNERSHIP_NAME,
+ "Take ownership of files or other objects", PF_PRESENTABLE },
+ { 10, SE_LOAD_DRIVER_NAME, "Load and unload device drivers", 0 },
+ { 11, SE_SYSTEM_PROFILE_NAME, "Profile system performance", 0 },
+ { 12, SE_SYSTEMTIME_NAME, "Change the system time", 0 },
+ { 13, SE_PROF_SINGLE_PROCESS_NAME, "Profile single process", 0 },
+ { 14, SE_INC_BASE_PRIORITY_NAME, "Increase scheduling priority", 0 },
+ { 15, SE_CREATE_PAGEFILE_NAME, "Create a pagefile", 0 },
+ { 16, SE_CREATE_PERMANENT_NAME, "Create permanent shared objects", 0 },
+ { 17, SE_BACKUP_NAME, "Back up files and directories",
+ PF_PRESENTABLE },
+ { 18, SE_RESTORE_NAME, "Restore files and directories",
+ PF_PRESENTABLE },
+ { 19, SE_SHUTDOWN_NAME, "Shut down the system", 0 },
+ { 20, SE_DEBUG_NAME, "Debug programs", 0 },
+ { 21, SE_AUDIT_NAME, "Generate security audits", 0 },
+ { 22, SE_SYSTEM_ENVIRONMENT_NAME,
+ "Modify firmware environment values", 0 },
+ { 23, SE_CHANGE_NOTIFY_NAME, "Bypass traverse checking", 0 },
+ { 24, SE_REMOTE_SHUTDOWN_NAME,
+ "Force shutdown from a remote system", 0 }
+};
+
+/*
+ * smb_priv_presentable_num
+ *
+ * Returns number of presentable privileges
+ */
+int
+smb_priv_presentable_num()
+{
+ int i, num;
+
+ num = 0;
+ for (i = SMB_PRIV_MIN; i <= SMB_PRIV_MAX; i++)
+ if (priv_table[i].flags == PF_PRESENTABLE)
+ num++;
+
+ return (num);
+}
+
+/*
+ * smb_priv_presentable_ids
+ *
+ * Returns IDs of presentable privileges
+ * Returns 0 in case of invalid parameter and 1 on success.
+ */
+int
+smb_priv_presentable_ids(uint32_t *ids, int num)
+{
+ int i, j;
+
+ if (ids == NULL || num <= 0)
+ return (0);
+
+ for (i = SMB_PRIV_MIN, j = 0; i <= SMB_PRIV_MAX; i++)
+ if (priv_table[i].flags == PF_PRESENTABLE)
+ ids[j++] = priv_table[i].id;
+
+ return (1);
+}
+
+/*
+ * smb_priv_getbyvalue
+ *
+ * Return the privilege info for the specified id (low part of the LUID).
+ * Returns a null pointer if id is out-of-range.
+ */
+smb_privinfo_t *
+smb_priv_getbyvalue(uint32_t id)
+{
+ if (id < SMB_PRIV_MIN || id > SMB_PRIV_MAX)
+ return (0);
+
+ return (&priv_table[id]);
+}
+
+
+/*
+ * smb_priv_getbyname
+ *
+ * Return the privilege info for the specified name. Returns a null
+ * pointer if we can't find a matching name in the table.
+ */
+smb_privinfo_t *
+smb_priv_getbyname(char *name)
+{
+ smb_privinfo_t *entry;
+ int i;
+
+ if (name == 0)
+ return (0);
+
+ for (i = SMB_PRIV_MIN; i <= SMB_PRIV_MAX; ++i) {
+ entry = &priv_table[i];
+
+ if (utf8_strcasecmp(name, entry->name) == 0)
+ return (entry);
+ }
+
+ return (0);
+}
+
+/*
+ * smb_privset_size
+ *
+ * Returns the memory block size needed to keep a complete
+ * set of privileges in a smb_privset_t structure.
+ */
+int
+smb_privset_size()
+{
+ int pcnt = SMB_PRIV_MAX - SMB_PRIV_MIN + 1;
+
+ return (2 * sizeof (uint32_t) +
+ pcnt * sizeof (smb_luid_attrs_t));
+}
+
+/*
+ * smb_privset_validate
+ *
+ * Validates the given privilege set structure
+ * Returns 1 if the structure is Ok, otherwise returns 0.
+ */
+int
+smb_privset_validate(smb_privset_t *privset)
+{
+ int count;
+ uint32_t i;
+
+ if (privset == 0) {
+ return (0);
+ }
+
+ count = SMB_PRIV_MAX - SMB_PRIV_MIN + 1;
+
+ if (privset->priv_cnt != count) {
+ return (0);
+ }
+
+ for (i = 0; i < count; i++) {
+ if (privset->priv[i].luid.hi_part != 0) {
+ return (0);
+ }
+
+ if (privset->priv[i].luid.lo_part !=
+ i + SMB_PRIV_MIN) {
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+/*
+ * smb_privset_init
+ *
+ * initialize all privileges in disable state.
+ */
+void
+smb_privset_init(smb_privset_t *privset)
+{
+ int count;
+ uint32_t i;
+
+ if (privset == 0)
+ return;
+
+ count = SMB_PRIV_MAX - SMB_PRIV_MIN + 1;
+
+ privset->priv_cnt = count;
+ privset->control = 0;
+ for (i = 0; i < count; i++) {
+ privset->priv[i].luid.hi_part = 0;
+ privset->priv[i].luid.lo_part = i + SMB_PRIV_MIN;
+ privset->priv[i].attrs = 0;
+ }
+}
+
+/*
+ * smb_privset_new
+ *
+ * Allocate memory and initialize all privileges in disable state.
+ * Returns pointer to allocated space or NULL if there is not
+ * enough memory.
+ */
+smb_privset_t *
+smb_privset_new()
+{
+ smb_privset_t *privset;
+
+ privset = malloc(smb_privset_size());
+ if (privset == NULL)
+ return (NULL);
+
+ smb_privset_init(privset);
+
+ return (privset);
+}
+
+/*
+ * smb_privset_copy
+ *
+ * Copy privleges information specified by 'src' to the
+ * buffer specified by dst.
+ */
+void
+smb_privset_copy(smb_privset_t *dst, smb_privset_t *src)
+{
+ if (src == 0 || dst == 0)
+ return;
+
+ (void) memcpy(dst, src, smb_privset_size());
+}
+
+/*
+ * smb_privset_free
+ *
+ * This will free the memory allocated by the 'privset'.
+ */
+void
+smb_privset_free(smb_privset_t *privset)
+{
+ free(privset);
+}
+
+void
+smb_privset_enable(smb_privset_t *privset, uint32_t id)
+{
+ int i;
+
+ if (privset == NULL)
+ return;
+
+ for (i = 0; i < privset->priv_cnt; i++) {
+ if (privset->priv[i].luid.lo_part == id)
+ privset->priv[i].attrs = SE_PRIVILEGE_ENABLED;
+ }
+}
+
+void
+smb_privset_log(smb_privset_t *privset)
+{
+ smb_luid_t *luid;
+ int i, ecnt;
+
+ if (privset == NULL)
+ return;
+
+ for (i = 0, ecnt = 0; i < privset->priv_cnt; ++i) {
+ if (privset->priv[i].attrs != 0) {
+ ecnt++;
+ }
+ }
+
+ syslog(LOG_DEBUG, " Privilege Count: %d (Enable=%d)",
+ privset->priv_cnt, ecnt);
+
+ for (i = 0; i < privset->priv_cnt; ++i) {
+ if (privset->priv[i].attrs != 0) {
+ luid = &privset->priv[i].luid;
+ syslog(LOG_DEBUG, " %s",
+ smb_priv_getname(luid->lo_part));
+ }
+ }
+}
+
+int
+smb_privset_query(smb_privset_t *privset, uint32_t id)
+{
+ int i;
+
+ if (privset == NULL)
+ return (0);
+
+ for (i = 0; privset->priv_cnt; i++) {
+ if (privset->priv[i].luid.lo_part == id) {
+ if (privset->priv[i].attrs == SE_PRIVILEGE_ENABLED)
+ return (1);
+ else
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+static char *
+smb_priv_getname(uint32_t id)
+{
+ if (id < SMB_PRIV_MIN || id > SMB_PRIV_MAX)
+ return ("Unknown Privilege");
+
+ return (priv_table[id].name);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c b/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c
new file mode 100644
index 0000000000..10026d7418
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c
@@ -0,0 +1,529 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <strings.h>
+#include <synch.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <thread.h>
+#include <pwd.h>
+#include <smbsrv/libsmb.h>
+
+#define SMB_PASSWD "/var/smb/smbpasswd"
+#define SMB_OPASSWD "/var/smb/osmbpasswd"
+#define SMB_PASSTEMP "/var/smb/ptmp"
+#define SMB_PASSLCK "/var/smb/.pwd.lock"
+
+#define SMB_PWD_DISABLE "*DIS*"
+#define SMB_PWD_BUFSIZE 256
+
+#define S_WAITTIME 15
+
+typedef enum {
+ SMB_PWD_NAME = 0,
+ SMB_PWD_UID,
+ SMB_PWD_LMHASH,
+ SMB_PWD_NTHASH,
+ SMB_PWD_NARG
+} smb_pwdarg_t;
+
+static struct flock flock = {
+ 0, /* l_type */
+ 0, /* l_whence */
+ 0, /* l_start */
+ 0, /* l_len */
+ 0, /* l_sysid */
+ 0 /* l_pid */
+ };
+
+static pid_t lck_pid = 0; /* process's pid at last lock */
+static thread_t lck_tid = 0; /* thread that holds the lock */
+static int fildes = -1;
+static mutex_t lck_lock = DEFAULTMUTEX;
+
+typedef struct smb_pwbuf {
+ char *pw_name;
+ smb_passwd_t *pw_pwd;
+} smb_pwbuf_t;
+
+static int smb_pwd_lock(void);
+static int smb_pwd_unlock(void);
+static int smb_pwd_flck(void);
+static int smb_pwd_fulck(void);
+
+static smb_pwbuf_t *smb_pwd_fgetent(FILE *, smb_pwbuf_t *, char *, size_t);
+static int smb_pwd_fputent(FILE *, smb_pwbuf_t *);
+static int smb_pwd_chgpwent(smb_passwd_t *, const char *, int);
+static int smb_pwd_update(const char *, const char *, int);
+
+/*
+ * smb_pwd_get
+ *
+ * Returns a smb password structure for the given user name.
+ * smbpw is a pointer to a buffer allocated by the caller.
+ *
+ * Returns NULL upon failure.
+ */
+smb_passwd_t *
+smb_pwd_getpasswd(const char *name, smb_passwd_t *smbpw)
+{
+ char buf[SMB_PWD_BUFSIZE];
+ boolean_t found = B_FALSE;
+ smb_pwbuf_t pwbuf;
+ int err;
+ FILE *fp;
+
+ err = smb_pwd_lock();
+ if (err != SMB_PWE_SUCCESS)
+ return (NULL);
+
+ if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
+ (void) smb_pwd_unlock();
+ return (NULL);
+ }
+
+ pwbuf.pw_name = NULL;
+ pwbuf.pw_pwd = smbpw;
+
+ while (smb_pwd_fgetent(fp, &pwbuf, buf, sizeof (buf)) != NULL) {
+ if (strcmp(name, pwbuf.pw_name) == 0) {
+ if ((smbpw->pw_flags & (SMB_PWF_LM | SMB_PWF_NT)))
+ found = B_TRUE;
+ break;
+ }
+ }
+
+ (void) fclose(fp);
+ (void) smb_pwd_unlock();
+
+ if (!found) {
+ bzero(smbpw, sizeof (smb_passwd_t));
+ return (NULL);
+ }
+
+ return (smbpw);
+}
+
+/*
+ * smb_pwd_set
+ *
+ * Update/add the given user to the smbpasswd file.
+ */
+int
+smb_pwd_setpasswd(const char *name, const char *password)
+{
+ return (smb_pwd_update(name, password, 0));
+}
+
+/*
+ * smb_pwd_setcntl
+ *
+ * Change the account state. This can be making the account
+ * disable/enable or removing its LM hash.
+ */
+int
+smb_pwd_setcntl(const char *name, int control)
+{
+ if (control == 0)
+ return (SMB_PWE_SUCCESS);
+
+ return (smb_pwd_update(name, NULL, control));
+}
+
+static int
+smb_pwd_update(const char *name, const char *password, int control)
+{
+ struct stat64 stbuf;
+ FILE *src, *dst;
+ int tempfd;
+ char buf[SMB_PWD_BUFSIZE];
+ int err = SMB_PWE_SUCCESS;
+ smb_pwbuf_t pwbuf;
+ smb_passwd_t smbpw;
+ boolean_t newent = B_TRUE;
+ boolean_t user_disable = B_FALSE;
+ char uxbuf[1024];
+ struct passwd uxpw;
+ int lm_level;
+ char *lm_str;
+
+ err = smb_pwd_lock();
+ if (err != SMB_PWE_SUCCESS)
+ return (err);
+
+ if (stat64(SMB_PASSWD, &stbuf) < 0) {
+ err = SMB_PWE_STAT_FAILED;
+ goto passwd_exit;
+ }
+
+ if ((tempfd = open(SMB_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
+ err = SMB_PWE_OPEN_FAILED;
+ goto passwd_exit;
+ }
+
+ if ((dst = fdopen(tempfd, "wF")) == NULL) {
+ err = SMB_PWE_OPEN_FAILED;
+ goto passwd_exit;
+ }
+
+ if ((src = fopen(SMB_PASSWD, "rF")) == NULL) {
+ err = SMB_PWE_OPEN_FAILED;
+ (void) fclose(dst);
+ (void) unlink(SMB_PASSTEMP);
+ goto passwd_exit;
+ }
+
+ lm_str = smb_config_getenv(SMB_CI_LM_LEVEL);
+ if (lm_str) {
+ lm_level = strtoul(lm_str, 0, 10);
+ free(lm_str);
+ } else {
+ lm_level = 4;
+ }
+
+ if (lm_level >= 4)
+ control |= SMB_PWC_NOLM;
+
+ /*
+ * copy old password entries to temporary file while replacing
+ * the entry that matches "name"
+ */
+ pwbuf.pw_name = NULL;
+ pwbuf.pw_pwd = &smbpw;
+
+ while (smb_pwd_fgetent(src, &pwbuf, buf, sizeof (buf)) != NULL) {
+ if (strcmp(pwbuf.pw_name, name) == 0) {
+ err = smb_pwd_chgpwent(&smbpw, password, control);
+ if (err == SMB_PWE_USER_DISABLE)
+ user_disable = B_TRUE;
+ err = smb_pwd_fputent(dst, &pwbuf);
+ newent = B_FALSE;
+ } else {
+ err = smb_pwd_fputent(dst, &pwbuf);
+ }
+
+ if (err != SMB_PWE_SUCCESS) {
+ (void) fclose(src);
+ (void) fclose(dst);
+ goto passwd_exit;
+ }
+ }
+
+ if (newent) {
+ if (getpwnam_r(name, &uxpw, uxbuf, sizeof (uxbuf))) {
+ pwbuf.pw_name = uxpw.pw_name;
+ smbpw.pw_flags = 0;
+ smbpw.pw_uid = uxpw.pw_uid;
+ (void) smb_pwd_chgpwent(&smbpw, password, control);
+ err = smb_pwd_fputent(dst, &pwbuf);
+ } else {
+ err = SMB_PWE_USER_UNKNOWN;
+ }
+
+ if (err != SMB_PWE_SUCCESS) {
+ (void) fclose(src);
+ (void) fclose(dst);
+ goto passwd_exit;
+ }
+ }
+
+ (void) fclose(src);
+ if (fclose(dst) != 0) {
+ err = SMB_PWE_CLOSE_FAILED;
+ goto passwd_exit; /* Don't trust the temporary file */
+ }
+
+ /* Rename temp to passwd */
+ if (unlink(SMB_OPASSWD) && access(SMB_OPASSWD, 0) == 0) {
+ err = SMB_PWE_UPDATE_FAILED;
+ (void) unlink(SMB_PASSTEMP);
+ goto passwd_exit;
+ }
+
+ if (link(SMB_PASSWD, SMB_OPASSWD) == -1) {
+ err = SMB_PWE_UPDATE_FAILED;
+ (void) unlink(SMB_PASSTEMP);
+ goto passwd_exit;
+ }
+
+ if (rename(SMB_PASSTEMP, SMB_PASSWD) == -1) {
+ err = SMB_PWE_UPDATE_FAILED;
+ (void) unlink(SMB_PASSTEMP);
+ goto passwd_exit;
+ }
+
+ (void) chmod(SMB_PASSWD, 0400);
+
+passwd_exit:
+ (void) smb_pwd_unlock();
+ if ((err == SMB_PWE_SUCCESS) && user_disable)
+ err = SMB_PWE_USER_DISABLE;
+
+ return (err);
+}
+
+/*
+ * smb_getpwent
+ *
+ * Parse the buffer in the passed pwbuf and fill in the
+ * smb password structure to point to the parsed information.
+ * The entry format is:
+ *
+ * <user-name>:<user-id>:<LM hash>:<NTLM hash>
+ *
+ * Returns a pointer to the password structure on success,
+ * otherwise returns NULL.
+ */
+static smb_pwbuf_t *
+smb_pwd_fgetent(FILE *fp, smb_pwbuf_t *pwbuf, char *buf, size_t bufsize)
+{
+ char *argv[SMB_PWD_NARG];
+ smb_passwd_t *pw;
+ smb_pwdarg_t i;
+ int lm_len, nt_len;
+
+ if (fgets(buf, bufsize, fp) == NULL)
+ return (NULL);
+ (void) trim_whitespace(buf);
+
+ for (i = 0; i < SMB_PWD_NARG; ++i) {
+ if ((argv[i] = strsep((char **)&buf, ":")) == 0) {
+ return (NULL);
+ }
+ }
+
+ if ((*argv[SMB_PWD_NAME] == '\0') || (*argv[SMB_PWD_UID] == '\0'))
+ return (NULL);
+
+ pwbuf->pw_name = argv[SMB_PWD_NAME];
+ pw = pwbuf->pw_pwd;
+ bzero(pw, sizeof (smb_passwd_t));
+ pw->pw_uid = strtoul(argv[SMB_PWD_UID], 0, 10);
+
+ if (strcmp(argv[SMB_PWD_LMHASH], SMB_PWD_DISABLE) == 0) {
+ pw->pw_flags |= SMB_PWF_DISABLE;
+ (void) strcpy((char *)pw->pw_lmhash, SMB_PWD_DISABLE);
+ (void) strcpy((char *)pw->pw_nthash, SMB_PWD_DISABLE);
+ return (pwbuf);
+ }
+
+ lm_len = strlen(argv[SMB_PWD_LMHASH]);
+ if (lm_len == SMBAUTH_HEXHASH_SZ) {
+ (void) hextobin(argv[SMB_PWD_LMHASH], SMBAUTH_HEXHASH_SZ,
+ (char *)pw->pw_lmhash, SMBAUTH_HASH_SZ);
+
+ pw->pw_flags |= SMB_PWF_LM;
+ } else if (lm_len != 0) {
+ return (NULL);
+ }
+
+ nt_len = strlen(argv[SMB_PWD_NTHASH]);
+ if (nt_len == SMBAUTH_HEXHASH_SZ) {
+ (void) hextobin(argv[SMB_PWD_NTHASH], SMBAUTH_HEXHASH_SZ,
+ (char *)pw->pw_nthash, SMBAUTH_HASH_SZ);
+
+ pw->pw_flags |= SMB_PWF_NT;
+ } else if (nt_len != 0) {
+ return (NULL);
+ }
+
+ return (pwbuf);
+}
+
+static int
+smb_pwd_chgpwent(smb_passwd_t *smbpw, const char *password, int control)
+{
+ if (control & SMB_PWC_DISABLE) {
+ smbpw->pw_flags |= SMB_PWF_DISABLE;
+ (void) strcpy((char *)smbpw->pw_lmhash, SMB_PWD_DISABLE);
+ (void) strcpy((char *)smbpw->pw_nthash, SMB_PWD_DISABLE);
+ smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
+ return (SMB_PWE_SUCCESS);
+ } else if ((control & SMB_PWC_ENABLE) &&
+ (smbpw->pw_flags & SMB_PWF_DISABLE)) {
+ *smbpw->pw_lmhash = '\0';
+ *smbpw->pw_nthash = '\0';
+ smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
+ return (SMB_PWE_SUCCESS);
+ }
+
+ /* No password update if account is disabled */
+ if (smbpw->pw_flags & SMB_PWF_DISABLE)
+ return (SMB_PWE_USER_DISABLE);
+
+ if (control & SMB_PWC_NOLM) {
+ smbpw->pw_flags &= ~SMB_PWF_LM;
+ *smbpw->pw_lmhash = '\0';
+ } else {
+ smbpw->pw_flags |= SMB_PWF_LM;
+ (void) smb_auth_lm_hash((char *)password, smbpw->pw_lmhash);
+ }
+
+ smbpw->pw_flags |= SMB_PWF_NT;
+ (void) smb_auth_ntlm_hash((char *)password, smbpw->pw_nthash);
+ return (SMB_PWE_SUCCESS);
+}
+
+/*
+ * smb_putpwent
+ *
+ * Creates LM and NTLM hash from the given plain text password
+ * and write them along with user's name and Id to the smbpasswd
+ * file.
+ */
+static int
+smb_pwd_fputent(FILE *fp, smb_pwbuf_t *pwbuf)
+{
+ smb_passwd_t *pw = pwbuf->pw_pwd;
+ char hex_nthash[SMBAUTH_HEXHASH_SZ+1];
+ char hex_lmhash[SMBAUTH_HEXHASH_SZ+1];
+ int rc;
+
+ if ((pw->pw_flags & SMB_PWF_LM) == SMB_PWF_LM) {
+ (void) bintohex((char *)pw->pw_lmhash, SMBAUTH_HASH_SZ,
+ hex_lmhash, SMBAUTH_HEXHASH_SZ);
+ hex_lmhash[SMBAUTH_HEXHASH_SZ] = '\0';
+ } else {
+ (void) strcpy(hex_lmhash, (char *)pw->pw_lmhash);
+ }
+
+ if ((pw->pw_flags & SMB_PWF_NT) == SMB_PWF_NT) {
+ (void) bintohex((char *)pw->pw_nthash, SMBAUTH_HASH_SZ,
+ hex_nthash, SMBAUTH_HEXHASH_SZ);
+ hex_nthash[SMBAUTH_HEXHASH_SZ] = '\0';
+ } else {
+ (void) strcpy(hex_nthash, (char *)pw->pw_nthash);
+ }
+
+ rc = fprintf(fp, "%s:%d:%s:%s\n", pwbuf->pw_name, pw->pw_uid,
+ hex_lmhash, hex_nthash);
+
+ if (rc <= 0)
+ return (SMB_PWE_WRITE_FAILED);
+
+ return (SMB_PWE_SUCCESS);
+}
+
+static int
+smb_pwd_lock(void)
+{
+ int res;
+
+ if (smb_pwd_flck()) {
+ switch (errno) {
+ case EINTR:
+ res = SMB_PWE_BUSY;
+ break;
+ case EACCES:
+ res = SMB_PWE_DENIED;
+ break;
+ case 0:
+ res = SMB_PWE_SUCCESS;
+ break;
+ }
+ } else
+ res = SMB_PWE_SUCCESS;
+
+ return (res);
+}
+
+static int
+smb_pwd_unlock(void)
+{
+ if (smb_pwd_fulck())
+ return (SMB_PWE_SYSTEM_ERROR);
+
+ return (SMB_PWE_SUCCESS);
+}
+
+static int
+smb_pwd_flck(void)
+{
+ int seconds = 0;
+
+ (void) mutex_lock(&lck_lock);
+ for (;;) {
+ if (lck_pid != 0 && lck_pid != getpid()) {
+ /* somebody forked */
+ lck_pid = 0;
+ lck_tid = 0;
+ }
+
+ if (lck_tid == 0) {
+ if ((fildes = creat(SMB_PASSLCK, 0600)) == -1)
+ break;
+ flock.l_type = F_WRLCK;
+ if (fcntl(fildes, F_SETLK, &flock) != -1) {
+ lck_pid = getpid();
+ lck_tid = thr_self();
+ (void) mutex_unlock(&lck_lock);
+ return (0);
+ }
+ (void) close(fildes);
+ fildes = -1;
+ }
+
+ if (seconds++ >= S_WAITTIME) {
+ /*
+ * For compatibility with the past, pretend
+ * that we were interrupted by SIGALRM.
+ */
+ errno = EINTR;
+ break;
+ }
+
+ (void) mutex_unlock(&lck_lock);
+ (void) sleep(1);
+ (void) mutex_lock(&lck_lock);
+ }
+ (void) mutex_unlock(&lck_lock);
+
+ return (-1);
+}
+
+static int
+smb_pwd_fulck(void)
+{
+ (void) mutex_lock(&lck_lock);
+ if (lck_tid == thr_self() && fildes >= 0) {
+ flock.l_type = F_UNLCK;
+ (void) fcntl(fildes, F_SETLK, &flock);
+ (void) close(fildes);
+ fildes = -1;
+ lck_pid = 0;
+ lck_tid = 0;
+ (void) mutex_unlock(&lck_lock);
+ return (0);
+ }
+ (void) mutex_unlock(&lck_lock);
+ return (-1);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c b/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c
new file mode 100644
index 0000000000..1f5083f448
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c
@@ -0,0 +1,1035 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* helper functions for using libscf with CIFS */
+
+#include <libscf.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <errno.h>
+#include <libintl.h>
+#include <assert.h>
+#include <strings.h>
+
+#include <uuid/uuid.h>
+#include <sys/param.h>
+
+#include <smbsrv/alloc.h>
+
+#include <smbsrv/libsmb.h>
+
+/*
+ * smb_smf_scf_log_error(msg)
+ * Logs error messages from scf API's
+ */
+static void
+smb_smf_scf_log_error(char *msg)
+{
+ if (!msg) {
+ syslog(LOG_ERR, " SMBD SMF problem: %s\n",
+ scf_strerror(scf_error()));
+ } else { /*LINTED E_SEC_PRINTF_E_VAR_FMT*/
+ syslog(LOG_ERR, msg, scf_strerror(scf_error()));
+ }
+}
+
+/*
+ * Check if instance with given name exists for a service.
+ * Returns 0 is instance exist
+ */
+int
+smb_smf_instance_exists(smb_scfhandle_t *handle, char *inst_name)
+{
+ int ret = SMBD_SMF_OK;
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ handle->scf_instance = scf_instance_create(handle->scf_handle);
+ if (scf_service_get_instance(handle->scf_service, inst_name,
+ handle->scf_instance) != SCF_SUCCESS)
+ ret = SMBD_SMF_SYSTEM_ERR;
+
+ scf_instance_destroy(handle->scf_instance);
+ handle->scf_instance = NULL;
+ return (ret);
+}
+
+/*
+ * Create a service instance. returns 0 if successful.
+ * If instance already exists enable it.
+ */
+int
+smb_smf_instance_create(smb_scfhandle_t *handle, char *serv_prefix,
+ char *inst_name)
+{
+ char *instance;
+ int ret = SMBD_SMF_OK;
+ int sz;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ if (!serv_prefix || !inst_name)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ sz = strlen(serv_prefix) + strlen(inst_name) + 2;
+ instance = malloc(sz);
+ if (!instance)
+ return (SMBD_SMF_NO_MEMORY);
+
+ (void) snprintf(instance, sz, "%s:%s", serv_prefix, inst_name);
+ handle->scf_instance = scf_instance_create(handle->scf_handle);
+ if (scf_service_get_instance(handle->scf_service, inst_name,
+ handle->scf_instance) != SCF_SUCCESS) {
+ if (scf_service_add_instance(handle->scf_service,
+ inst_name, handle->scf_instance) == SCF_SUCCESS) {
+ if (smf_enable_instance(instance, 0))
+ ret = SMBD_SMF_SYSTEM_ERR;
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ if (smf_enable_instance(instance, 0))
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ free(instance);
+ return (ret);
+}
+
+/*
+ * Delete a specified instance. Return SMBD_SMF_OK for success.
+ */
+int
+smb_smf_instance_delete(smb_scfhandle_t *handle, char *inst_name)
+{
+ int ret = SMBD_SMF_OK;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ handle->scf_instance = scf_instance_create(handle->scf_handle);
+ if (scf_service_get_instance(handle->scf_service, inst_name,
+ handle->scf_instance) == SCF_SUCCESS) {
+ if (scf_instance_delete(handle->scf_instance) == SCF_SUCCESS) {
+ return (ret);
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ smb_smf_scf_log_error(NULL);
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ return (ret);
+}
+
+/*
+ * smb_smf_create_service_pgroup(handle, pgroup)
+ *
+ * create a new property group at service level.
+ */
+int
+smb_smf_create_service_pgroup(smb_scfhandle_t *handle, char *pgroup)
+{
+ int ret = SMBD_SMF_OK;
+ int err;
+
+ if (handle == NULL) {
+ return (SMBD_SMF_SYSTEM_ERR);
+ }
+
+ /*
+ * only create a handle if it doesn't exist. It is ok to exist
+ * since the pg handle will be set as a side effect.
+ */
+ if (handle->scf_pg == NULL)
+ handle->scf_pg = scf_pg_create(handle->scf_handle);
+
+ /*
+ * if the pgroup exists, we are done. If it doesn't, then we
+ * need to actually add one to the service instance.
+ */
+ if (scf_service_get_pg(handle->scf_service,
+ pgroup, handle->scf_pg) != 0) {
+ /* doesn't exist so create one */
+ if (scf_service_add_pg(handle->scf_service, pgroup,
+ SCF_GROUP_APPLICATION, 0, handle->scf_pg) != 0) {
+ err = scf_error();
+ if (err != SCF_ERROR_NONE)
+ smb_smf_scf_log_error(NULL);
+ switch (err) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SMBD_SMF_NO_PERMISSION;
+ break;
+ default:
+ ret = SMBD_SMF_SYSTEM_ERR;
+ break;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * smb_smf_create_instance_pgroup(handle, pgroup)
+ *
+ * create a new property group at instance level.
+ */
+int
+smb_smf_create_instance_pgroup(smb_scfhandle_t *handle, char *pgroup)
+{
+ int ret = SMBD_SMF_OK;
+ int err;
+
+ if (handle == NULL) {
+ return (SMBD_SMF_SYSTEM_ERR);
+ }
+
+ /*
+ * only create a handle if it doesn't exist. It is ok to exist
+ * since the pg handle will be set as a side effect.
+ */
+ if (handle->scf_pg == NULL)
+ handle->scf_pg = scf_pg_create(handle->scf_handle);
+
+ /*
+ * if the pgroup exists, we are done. If it doesn't, then we
+ * need to actually add one to the service instance.
+ */
+ if (scf_instance_get_pg(handle->scf_instance,
+ pgroup, handle->scf_pg) != 0) {
+ /* doesn't exist so create one */
+ if (scf_instance_add_pg(handle->scf_instance, pgroup,
+ SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) {
+ err = scf_error();
+ if (err != SCF_ERROR_NONE)
+ smb_smf_scf_log_error(NULL);
+ switch (err) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SMBD_SMF_NO_PERMISSION;
+ break;
+ default:
+ ret = SMBD_SMF_SYSTEM_ERR;
+ break;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * smb_smf_delete_service_pgroup(handle, pgroup)
+ *
+ * remove the property group from the current service.
+ * but only if it actually exists.
+ */
+int
+smb_smf_delete_service_pgroup(smb_scfhandle_t *handle, char *pgroup)
+{
+ int ret = SMBD_SMF_OK;
+ int err;
+
+ if (handle == NULL) {
+ return (SMBD_SMF_SYSTEM_ERR);
+ }
+
+ /*
+ * only create a handle if it doesn't exist. It is ok to exist
+ * since the pg handle will be set as a side effect.
+ */
+ if (handle->scf_pg == NULL)
+ handle->scf_pg = scf_pg_create(handle->scf_handle);
+
+ /*
+ * only delete if it does exist.
+ */
+ if (scf_service_get_pg(handle->scf_service,
+ pgroup, handle->scf_pg) == 0) {
+ /* does exist so delete it */
+ if (scf_pg_delete(handle->scf_pg) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ err = scf_error();
+ if (err != SCF_ERROR_NONE) {
+ smb_smf_scf_log_error("SMF delpg "
+ "problem: %s\n");
+ }
+ }
+ } else {
+ err = scf_error();
+ if (err != SCF_ERROR_NONE)
+ smb_smf_scf_log_error("SMF getpg problem: %s\n");
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (ret == SMBD_SMF_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED) {
+ ret = SMBD_SMF_NO_PERMISSION;
+ }
+ return (ret);
+}
+
+/*
+ * smb_smf_delete_instance_pgroup(handle, pgroup)
+ *
+ * remove the property group from the current instance.
+ * but only if it actually exists.
+ */
+int
+smb_smf_delete_instance_pgroup(smb_scfhandle_t *handle, char *pgroup)
+{
+ int ret = SMBD_SMF_OK;
+ int err;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ /*
+ * only create a handle if it doesn't exist. It is ok to exist
+ * since the pg handle will be set as a side effect.
+ */
+ if (handle->scf_pg == NULL)
+ handle->scf_pg = scf_pg_create(handle->scf_handle);
+
+ /*
+ * only delete if it does exist.
+ */
+ if (scf_instance_get_pg(handle->scf_instance,
+ pgroup, handle->scf_pg) == 0) {
+ /* does exist so delete it */
+ if (scf_pg_delete(handle->scf_pg) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ err = scf_error();
+ if (err != SCF_ERROR_NONE) {
+ smb_smf_scf_log_error("SMF delpg "
+ "problem: %s\n");
+ }
+ }
+ } else {
+ err = scf_error();
+ if (err != SCF_ERROR_NONE)
+ smb_smf_scf_log_error("SMF getpg problem: %s\n");
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (ret == SMBD_SMF_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED)
+ ret = SMBD_SMF_NO_PERMISSION;
+
+ return (ret);
+}
+
+/*
+ * Start transaction on current pg in handle.
+ * The pg could be service or instance level.
+ * Must be called after pg handle is obtained
+ * from create or get.
+ */
+int
+smb_smf_start_transaction(smb_scfhandle_t *handle)
+{
+ int ret = SMBD_SMF_OK;
+
+ if (!handle || (!handle->scf_pg))
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ /*
+ * lookup the property group and create it if it doesn't already
+ * exist.
+ */
+ if (handle->scf_state == SCH_STATE_INIT) {
+ if (ret == SMBD_SMF_OK) {
+ handle->scf_trans =
+ scf_transaction_create(handle->scf_handle);
+ if (handle->scf_trans != NULL) {
+ if (scf_transaction_start(handle->scf_trans,
+ handle->scf_pg) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ scf_transaction_destroy(
+ handle->scf_trans);
+ handle->scf_trans = NULL;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ }
+ }
+ if (ret == SMBD_SMF_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED)
+ ret = SMBD_SMF_NO_PERMISSION;
+
+ return (ret);
+}
+
+/*
+ * smb_smf_end_transaction(handle)
+ *
+ * Commit the changes that were added to the transaction in the
+ * handle. Do all necessary cleanup.
+ */
+int
+smb_smf_end_transaction(smb_scfhandle_t *handle)
+{
+ int ret = SMBD_SMF_OK;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ if (handle->scf_trans == NULL) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ } else {
+ if (scf_transaction_commit(handle->scf_trans) < 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ smb_smf_scf_log_error("Failed to commit "
+ "transaction: %s");
+ }
+ scf_transaction_destroy_children(handle->scf_trans);
+ scf_transaction_destroy(handle->scf_trans);
+ handle->scf_trans = NULL;
+ }
+ return (ret);
+}
+
+/*
+ * Deletes property in current pg
+ */
+int
+smb_smf_delete_property(smb_scfhandle_t *handle, char *propname)
+{
+ int ret = SMBD_SMF_OK;
+ scf_transaction_entry_t *entry = NULL;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ entry = scf_entry_create(handle->scf_handle);
+ if (entry != NULL) {
+ if (scf_transaction_property_delete(handle->scf_trans, entry,
+ propname) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (ret == SMBD_SMF_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SMBD_SMF_NO_PERMISSION;
+ break;
+ }
+ }
+
+ /*
+ * cleanup if there were any errors that didn't leave these
+ * values where they would be cleaned up later.
+ */
+ if ((ret != SMBD_SMF_OK) && (entry != NULL))
+ scf_entry_destroy(entry);
+
+ return (ret);
+}
+
+/*
+ * Sets string property in current pg
+ */
+int
+smb_smf_set_string_property(smb_scfhandle_t *handle,
+ char *propname, char *valstr)
+{
+ int ret = SMBD_SMF_OK;
+ scf_value_t *value = NULL;
+ scf_transaction_entry_t *entry = NULL;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ value = scf_value_create(handle->scf_handle);
+ entry = scf_entry_create(handle->scf_handle);
+ if (value != NULL && entry != NULL) {
+ if (scf_transaction_property_change(handle->scf_trans, entry,
+ propname, SCF_TYPE_ASTRING) == 0 ||
+ scf_transaction_property_new(handle->scf_trans, entry,
+ propname, SCF_TYPE_ASTRING) == 0) {
+ if (scf_value_set_astring(value, valstr) == 0) {
+ if (scf_entry_add_value(entry, value) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ scf_value_destroy(value);
+ }
+ /* the value is in the transaction */
+ value = NULL;
+ } else {
+ /* value couldn't be constructed */
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ /* the entry is in the transaction */
+ entry = NULL;
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (ret == SMBD_SMF_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SMBD_SMF_NO_PERMISSION;
+ break;
+ }
+ }
+
+ /*
+ * cleanup if there were any errors that didn't leave these
+ * values where they would be cleaned up later.
+ */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+ return (ret);
+}
+
+/*
+ * Gets string property value.upto sz size.
+ * Caller is responsible to have enough memory allocated.
+ */
+int
+smb_smf_get_string_property(smb_scfhandle_t *handle, char *propname,
+ char *valstr, size_t sz)
+{
+ int ret = SMBD_SMF_OK;
+ scf_value_t *value;
+ scf_property_t *prop;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ value = scf_value_create(handle->scf_handle);
+ prop = scf_property_create(handle->scf_handle);
+ if (value && prop &&
+ (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valstr, sz) < 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ return (ret);
+}
+
+/*
+ * Set integer value of property.
+ * The value is returned as int64_t value
+ * Caller ensures appropriate translation.
+ */
+int
+smb_smf_set_integer_property(smb_scfhandle_t *handle, char *propname,
+ int64_t valint)
+{
+ int ret = SMBD_SMF_OK;
+ scf_value_t *value = NULL;
+ scf_transaction_entry_t *entry = NULL;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ value = scf_value_create(handle->scf_handle);
+ entry = scf_entry_create(handle->scf_handle);
+ if (value != NULL && entry != NULL) {
+ if (scf_transaction_property_change(handle->scf_trans, entry,
+ propname, SCF_TYPE_INTEGER) == 0 ||
+ scf_transaction_property_new(handle->scf_trans, entry,
+ propname, SCF_TYPE_INTEGER) == 0) {
+ scf_value_set_integer(value, valint);
+ if (scf_entry_add_value(entry, value) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ scf_value_destroy(value);
+ }
+ /* the value is in the transaction */
+ value = NULL;
+ }
+ /* the entry is in the transaction */
+ entry = NULL;
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (ret == SMBD_SMF_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SMBD_SMF_NO_PERMISSION;
+ break;
+ }
+ }
+ /*
+ * cleanup if there were any errors that didn't leave these
+ * values where they would be cleaned up later.
+ */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+ return (ret);
+}
+
+/*
+ * Gets integer property value.
+ * Caller is responsible to have enough memory allocated.
+ */
+int
+smb_smf_get_integer_property(smb_scfhandle_t *handle, char *propname,
+ int64_t *valint)
+{
+ int ret = SMBD_SMF_OK;
+ scf_value_t *value = NULL;
+ scf_property_t *prop = NULL;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ value = scf_value_create(handle->scf_handle);
+ prop = scf_property_create(handle->scf_handle);
+ if ((prop) && (value) &&
+ (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_integer(value,
+ valint) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ return (ret);
+}
+
+/*
+ * Set boolean value of property.
+ * The value is returned as int64_t value
+ * Caller ensures appropriate translation.
+ */
+int
+smb_smf_set_boolean_property(smb_scfhandle_t *handle, char *propname,
+ uint8_t valbool)
+{
+ int ret = SMBD_SMF_OK;
+ scf_value_t *value = NULL;
+ scf_transaction_entry_t *entry = NULL;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ value = scf_value_create(handle->scf_handle);
+ entry = scf_entry_create(handle->scf_handle);
+ if (value != NULL && entry != NULL) {
+ if (scf_transaction_property_change(handle->scf_trans, entry,
+ propname, SCF_TYPE_BOOLEAN) == 0 ||
+ scf_transaction_property_new(handle->scf_trans, entry,
+ propname, SCF_TYPE_BOOLEAN) == 0) {
+ scf_value_set_boolean(value, valbool);
+ if (scf_entry_add_value(entry, value) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ scf_value_destroy(value);
+ }
+ /* the value is in the transaction */
+ value = NULL;
+ }
+ /* the entry is in the transaction */
+ entry = NULL;
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (ret == SMBD_SMF_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SMBD_SMF_NO_PERMISSION;
+ break;
+ }
+ }
+ /*
+ * cleanup if there were any errors that didn't leave these
+ * values where they would be cleaned up later.
+ */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+ return (ret);
+}
+
+/*
+ * Gets boolean property value.
+ * Caller is responsible to have enough memory allocated.
+ */
+int
+smb_smf_get_boolean_property(smb_scfhandle_t *handle, char *propname,
+ uint8_t *valbool)
+{
+ int ret = SMBD_SMF_OK;
+ scf_value_t *value = NULL;
+ scf_property_t *prop = NULL;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ value = scf_value_create(handle->scf_handle);
+ prop = scf_property_create(handle->scf_handle);
+ if ((prop) && (value) &&
+ (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_boolean(value,
+ valbool) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ return (ret);
+}
+
+/*
+ * Sets a blob property value.
+ */
+int
+smb_smf_set_opaque_property(smb_scfhandle_t *handle, char *propname,
+ void *voidval, size_t sz)
+{
+ int ret = SMBD_SMF_OK;
+ scf_value_t *value;
+ scf_transaction_entry_t *entry;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ value = scf_value_create(handle->scf_handle);
+ entry = scf_entry_create(handle->scf_handle);
+ if (value != NULL && entry != NULL) {
+ if (scf_transaction_property_change(handle->scf_trans, entry,
+ propname, SCF_TYPE_OPAQUE) == 0 ||
+ scf_transaction_property_new(handle->scf_trans, entry,
+ propname, SCF_TYPE_OPAQUE) == 0) {
+ if (scf_value_set_opaque(value, voidval, sz) == 0) {
+ if (scf_entry_add_value(entry, value) != 0) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ scf_value_destroy(value);
+ }
+ /* the value is in the transaction */
+ value = NULL;
+ } else {
+ /* value couldn't be constructed */
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ /* the entry is in the transaction */
+ entry = NULL;
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (ret == SMBD_SMF_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SMBD_SMF_NO_PERMISSION;
+ break;
+ }
+ }
+ /*
+ * cleanup if there were any errors that didn't leave these
+ * values where they would be cleaned up later.
+ */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+ return (ret);
+}
+
+/*
+ * Gets a blob property value.
+ * Caller is responsible to have enough memory allocated.
+ */
+int
+smb_smf_get_opaque_property(smb_scfhandle_t *handle, char *propname,
+ void *v, size_t sz)
+{
+ int ret = SMBD_SMF_OK;
+ scf_value_t *value = NULL;
+ scf_property_t *prop = NULL;
+
+ if (handle == NULL)
+ return (SMBD_SMF_SYSTEM_ERR);
+
+ value = scf_value_create(handle->scf_handle);
+ prop = scf_property_create(handle->scf_handle);
+ if ((prop) && (value) &&
+ (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_opaque(value, (char *)v, sz) != sz) {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ } else {
+ ret = SMBD_SMF_SYSTEM_ERR;
+ }
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ return (ret);
+}
+
+/*
+ * Get property based on property type. Returns string value of that
+ * property. Only SCF_TYPE_ASTRING, SCF_TYPE_INTEGER, SCF_TYPE_BOOLEAN
+ * supported.
+ */
+int
+smb_smf_get_property(smb_scfhandle_t *handle, int proptype, char *propname,
+ char *valstr, size_t sz)
+{
+ int64_t valint = 0;
+ uint8_t valbool = 0;
+ int ret = SMBD_SMF_OK;
+
+ switch (proptype) {
+ case SCF_TYPE_ASTRING:
+ ret = smb_smf_get_string_property(handle, propname,
+ valstr, sz);
+ break;
+ case SCF_TYPE_INTEGER:
+ if ((ret = smb_smf_get_integer_property(handle, propname,
+ &valint)) != 0)
+ return (ret);
+ (void) snprintf(valstr, sz, "%lld", valint);
+ break;
+ case SCF_TYPE_BOOLEAN:
+ if ((ret = smb_smf_get_boolean_property(handle, propname,
+ &valbool)) != 0)
+ return (ret);
+ (void) strlcpy(valstr, (valbool ? "true" : "false"), sz);
+ break;
+ default:
+ return (SMBD_SMF_SYSTEM_ERR);
+ }
+ return (ret);
+}
+
+/*
+ * Set property based on property type.
+ * Only SCF_TYPE_ASTRING, SCF_TYPE_INTEGER, SCF_TYPE_BOOLEAN supported.
+ */
+int
+smb_smf_set_property(smb_scfhandle_t *handle, int proptype,
+ char *propname, char *valstr)
+{
+ int64_t valint = 0;
+ uint8_t valbool = 0;
+ int ret = SMBD_SMF_OK;
+
+ switch (proptype) {
+ case SCF_TYPE_ASTRING:
+ ret = smb_smf_set_string_property(handle, propname,
+ valstr);
+ break;
+ case SCF_TYPE_INTEGER:
+ valint = strtol(valstr, 0, 10);
+ ret = smb_smf_set_integer_property(handle, propname,
+ valint);
+ break;
+ case SCF_TYPE_BOOLEAN:
+ if (strcasecmp(valstr, "true") == 0)
+ valbool = 1;
+ ret = smb_smf_set_boolean_property(handle, propname, valbool);
+ break;
+ default:
+ return (SMBD_SMF_SYSTEM_ERR);
+ }
+ return (ret);
+}
+
+/*
+ * Gets an instance iterator for the service specified.
+ */
+smb_scfhandle_t *
+smb_smf_get_iterator(char *svc_name)
+{
+ smb_scfhandle_t *handle = NULL;
+
+ handle = smb_smf_scf_init(svc_name);
+ if (!handle)
+ return (NULL);
+
+ handle->scf_inst_iter = scf_iter_create(handle->scf_handle);
+ if (handle->scf_inst_iter) {
+ if (scf_iter_service_instances(handle->scf_inst_iter,
+ handle->scf_service) != 0) {
+ smb_smf_scf_fini(handle);
+ handle = NULL;
+ } else {
+ handle->scf_instance = NULL;
+ }
+ } else {
+ smb_smf_scf_fini(handle);
+ handle = NULL;
+ }
+ return (handle);
+}
+
+/*
+ * smb_smf_scf_init()
+ *
+ * must be called before using any of the SCF functions.
+ * Returns smb_scfhandle_t pointer if success.
+ */
+smb_scfhandle_t *
+smb_smf_scf_init(char *svc_name)
+{
+ smb_scfhandle_t *handle;
+
+ handle = malloc(sizeof (smb_scfhandle_t));
+ if (handle != NULL) {
+ bzero((char *)handle, sizeof (smb_scfhandle_t));
+ handle->scf_state = SCH_STATE_INITIALIZING;
+ handle->scf_handle = scf_handle_create(SCF_VERSION);
+ if (handle->scf_handle != NULL) {
+ if (scf_handle_bind(handle->scf_handle) == 0) {
+ handle->scf_scope =
+ scf_scope_create(handle->scf_handle);
+ if (scf_handle_get_local_scope(
+ handle->scf_handle, handle->scf_scope) != 0)
+ goto err;
+
+ handle->scf_service =
+ scf_service_create(handle->scf_handle);
+
+ if (scf_scope_get_service(handle->scf_scope,
+ svc_name, handle->scf_service)
+ != SCF_SUCCESS) {
+ goto err;
+ }
+ handle->scf_pg =
+ scf_pg_create(handle->scf_handle);
+ handle->scf_state = SCH_STATE_INIT;
+ } else {
+ goto err;
+ }
+ } else {
+ free(handle);
+ handle = NULL;
+ smb_smf_scf_log_error("Could not access SMF "
+ "repository: %s\n");
+ }
+ }
+ return (handle);
+
+ /* error handling/unwinding */
+err:
+ (void) smb_smf_scf_fini(handle);
+ (void) smb_smf_scf_log_error("SMF initialization problem: %s\n");
+ return (NULL);
+}
+
+/*
+ * smb_smf_scf_fini(handle)
+ *
+ * must be called when done. Called with the handle allocated in
+ * smb_smf_scf_init(), it cleans up the state and frees any SCF resources
+ * still in use.
+ */
+void
+smb_smf_scf_fini(smb_scfhandle_t *handle)
+{
+ if (handle != NULL) {
+ int unbind = 0;
+ scf_iter_destroy(handle->scf_pg_iter);
+ handle->scf_pg_iter = NULL;
+
+ scf_iter_destroy(handle->scf_inst_iter);
+ handle->scf_inst_iter = NULL;
+
+ unbind = 1;
+ scf_scope_destroy(handle->scf_scope);
+ handle->scf_scope = NULL;
+
+ scf_instance_destroy(handle->scf_instance);
+ handle->scf_instance = NULL;
+
+ scf_service_destroy(handle->scf_service);
+ handle->scf_service = NULL;
+
+ scf_pg_destroy(handle->scf_pg);
+ handle->scf_pg = NULL;
+
+ handle->scf_state = SCH_STATE_UNINIT;
+ if (unbind)
+ (void) scf_handle_unbind(handle->scf_handle);
+ scf_handle_destroy(handle->scf_handle);
+ handle->scf_handle = NULL;
+
+ free(handle);
+ }
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_util.c b/usr/src/lib/smbsrv/libsmb/common/smb_util.c
new file mode 100644
index 0000000000..bce8efee8e
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_util.c
@@ -0,0 +1,336 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <syslog.h>
+#include <sys/varargs.h>
+#include <sys/types.h>
+#include <smbsrv/string.h>
+
+#define C2H(c) "0123456789ABCDEF"[(c)]
+#define H2C(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
+ ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \
+ ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
+ '\0')
+#define DEFAULT_SBOX_SIZE 256
+
+/*
+ *
+ * hexdump
+ *
+ * Simple hex dump display function. Displays nbytes of buffer in hex and
+ * printable format. Non-printing characters are shown as '.'. It is safe
+ * to pass a null pointer. Each line begins with the offset. If nbytes is
+ * 0, the line will be blank except for the offset. Example output:
+ *
+ * 00000000 54 68 69 73 20 69 73 20 61 20 70 72 6F 67 72 61 This is a progra
+ * 00000010 6D 20 74 65 73 74 2E 00 m test..
+ *
+ */
+void
+hexdump_offset(unsigned char *buffer, int nbytes, unsigned long *start, int log)
+{
+ static char *hex = "0123456789ABCDEF";
+ int i, count;
+ int offset;
+ unsigned char *p;
+ char ascbuf[64];
+ char hexbuf[64];
+ char *ap = ascbuf;
+ char *hp = hexbuf;
+
+ if ((p = buffer) == 0) {
+ if (log)
+ syslog(LOG_DEBUG, "hexdump: (null)");
+ else
+ (void) printf("hexdump: (null)\n");
+ return;
+ }
+
+ offset = *start;
+
+ *ap = '\0';
+ *hp = '\0';
+ count = 0;
+
+ for (i = 0; i < nbytes; ++i) {
+ if (i && (i % 16) == 0) {
+ if (log)
+ syslog(LOG_DEBUG,
+ "%06X %s %s", offset, hexbuf, ascbuf);
+ else
+ (void) printf("%06X %s %s\n",
+ offset, hexbuf, ascbuf);
+ ap = ascbuf;
+ hp = hexbuf;
+ count = 0;
+ offset += 16;
+ }
+
+ ap += sprintf(ap, "%c",
+ (*p >= 0x20 && *p < 0x7F) ? *p : '.');
+ hp += sprintf(hp, " %c%c",
+ hex[(*p >> 4) & 0x0F], hex[(*p & 0x0F)]);
+ ++p;
+ ++count;
+ }
+
+ if (count) {
+ if (log)
+ syslog(LOG_DEBUG,
+ "%06X %-48s %s", offset, hexbuf, ascbuf);
+ else
+ (void) printf("%06X %-48s %s\n",
+ offset, hexbuf, ascbuf);
+
+ offset += count;
+ }
+
+ *start = offset;
+}
+
+void
+hexdump(unsigned char *buffer, int nbytes)
+{
+ unsigned long start = 0;
+
+ hexdump_offset(buffer, nbytes, &start, 1);
+}
+
+/*
+ * bintohex
+ *
+ * Converts the given binary data (srcbuf) to
+ * its equivalent hex chars (hexbuf).
+ *
+ * hexlen should be at least twice as srclen.
+ * if hexbuf is not big enough returns 0.
+ * otherwise returns number of valid chars in
+ * hexbuf which is srclen * 2.
+ */
+size_t
+bintohex(const char *srcbuf, size_t srclen,
+ char *hexbuf, size_t hexlen)
+{
+ size_t outlen;
+ char c;
+
+ outlen = srclen << 1;
+
+ if (hexlen < outlen)
+ return (0);
+
+ while (srclen-- > 0) {
+ c = *srcbuf++;
+ *hexbuf++ = C2H(c & 0xF);
+ *hexbuf++ = C2H((c >> 4) & 0xF);
+ }
+
+ return (outlen);
+}
+
+/*
+ * hextobin
+ *
+ * Converts hex to binary.
+ *
+ * Assuming hexbuf only contains hex digits (chars)
+ * this function convert every two bytes of hexbuf
+ * to one byte and put it in dstbuf.
+ *
+ * hexlen should be an even number.
+ * dstlen should be at least half of hexlen.
+ *
+ * Returns 0 if sizes are not correct, otherwise
+ * returns the number of converted bytes in dstbuf
+ * which is half of hexlen.
+ */
+size_t
+hextobin(const char *hexbuf, size_t hexlen,
+ char *dstbuf, size_t dstlen)
+{
+ size_t outlen;
+
+ if ((hexlen % 2) != 0)
+ return (0);
+
+ outlen = hexlen >> 1;
+ if (dstlen < outlen)
+ return (0);
+
+ while (hexlen > 0) {
+ *dstbuf = H2C(*hexbuf) & 0x0F;
+ hexbuf++;
+ *dstbuf++ |= (H2C(*hexbuf) << 4) & 0xF0;
+ hexbuf++;
+
+ hexlen -= 2;
+ }
+
+ return (outlen);
+}
+
+/*
+ * trim_whitespace
+ *
+ * Trim leading and trailing whitespace chars (as defined by isspace)
+ * from a buffer. Example; if the input buffer contained " text ",
+ * it will contain "text", when we return. We assume that the buffer
+ * contains a null terminated string. A pointer to the buffer is
+ * returned.
+ */
+char *
+trim_whitespace(char *buf)
+{
+ char *p = buf;
+ char *q = buf;
+
+ if (buf == 0)
+ return (0);
+
+ while (*p && isspace(*p))
+ ++p;
+
+ while ((*q = *p++) != 0)
+ ++q;
+
+ if (q != buf) {
+ while ((--q, isspace(*q)) != 0)
+ *q = '\0';
+ }
+
+ return (buf);
+}
+
+/*
+ * randomize
+ *
+ * Randomize the contents of the specified buffer.
+ */
+void
+randomize(char *data, unsigned len)
+{
+ unsigned dwlen = len / 4;
+ unsigned remlen = len % 4;
+ unsigned tmp;
+ unsigned i; /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ unsigned *p = (unsigned *)data;
+
+ for (i = 0; i < dwlen; ++i)
+ *p++ = random();
+
+ if (remlen) {
+ tmp = random();
+ (void) memcpy(p, &tmp, remlen);
+ }
+}
+
+/*
+ * This is the hash mechanism used to encrypt passwords for commands like
+ * SamrSetUserInformation. It uses a 256 byte s-box.
+ */
+void
+rand_hash(
+ unsigned char *data,
+ size_t datalen,
+ unsigned char *key,
+ size_t keylen)
+{
+ unsigned char sbox[DEFAULT_SBOX_SIZE];
+ unsigned char tmp;
+ unsigned char index_i = 0;
+ unsigned char index_j = 0;
+ unsigned char j = 0;
+ int i;
+
+ for (i = 0; i < DEFAULT_SBOX_SIZE; ++i)
+ sbox[i] = (unsigned char)i;
+
+ for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) {
+ j += (sbox[i] + key[i % keylen]);
+
+ tmp = sbox[i];
+ sbox[i] = sbox[j];
+ sbox[j] = tmp;
+ }
+
+ for (i = 0; i < datalen; ++i) {
+ index_i++;
+ index_j += sbox[index_i];
+
+ tmp = sbox[index_i];
+ sbox[index_i] = sbox[index_j];
+ sbox[index_j] = tmp;
+
+ tmp = sbox[index_i] + sbox[index_j];
+ data[i] = data[i] ^ sbox[tmp];
+ }
+}
+
+/*
+ * strsep
+ *
+ * The strsep() function locates, in the string referenced by *stringp, the
+ * first occurrence of any character in the string delim (or the terminating
+ * `\0' character) and replaces it with a `\0'. The location of the next
+ * character after the delimiter character (or NULL, if the end of the
+ * string was reached) is stored in *stringp. The original value of
+ * *stringp is returned.
+ *
+ * If *stringp is initially NULL, strsep() returns NULL.
+ */
+char *
+strsep(char **stringp, const char *delim)
+{
+ char *s;
+ const char *spanp;
+ int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+
+ for (tok = s; ; ) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_wins.c b/usr/src/lib/smbsrv/libsmb/common/smb_wins.c
new file mode 100644
index 0000000000..09498abfca
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_wins.c
@@ -0,0 +1,210 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SMB WINS support functions
+ */
+
+#include <strings.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <smbsrv/smbinfo.h>
+
+#include <smbsrv/libsmb.h>
+
+/*
+ * smb_wins_iplist
+ *
+ * Get a string containing a list of comma separated IP addresses
+ * and return an array containing numeric equivalent for string IPs.
+ *
+ * Returns the number of parsed IPs.
+ * Return -1 if list is badly formatted.
+ * This routine need fix for IPv6
+ */
+int
+smb_wins_iplist(char *list, uint32_t iplist[], int max_naddr)
+{
+ char *ip, *ctx;
+ char *tmp;
+ int n = 0;
+
+ if ((list == NULL) || (*list == '\0'))
+ return (0);
+
+ if ((tmp = strdup(list)) == NULL)
+ return (0);
+
+ ip = strtok_r(tmp, ",", &ctx);
+ while (ip && (n < max_naddr)) {
+ ip = trim_whitespace(ip);
+ if (*ip != 0) {
+ if (inet_pton(AF_INET, ip, &iplist[n]) == 1) {
+ n++;
+ } else {
+ return (-1);
+ }
+ }
+ ip = strtok_r(0, ",", &ctx);
+ }
+
+ free(tmp);
+ return (n);
+}
+
+/*
+ * smb_wins_is_excluded
+ *
+ * Check to see if the given IP addr shouldn't be registered in WINS.
+ *
+ * Returns 1 if it's excluded, 0 if it's not.
+ */
+boolean_t
+smb_wins_is_excluded(in_addr_t ipaddr,
+ unsigned long *exclude_list, int nexclude)
+{
+ int i;
+
+ if (nexclude == 0)
+ return (B_FALSE);
+
+ for (i = 0; i < nexclude; i++)
+ if (ipaddr == exclude_list[i]) {
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Build a CSV list of ips to be excluded.
+ * This function needs fix for IPv6
+ */
+void
+smb_wins_build_list(char *buf, uint32_t iplist[], int max_naddr)
+{
+ char ipstr[16];
+ int i;
+
+ if (!buf)
+ return;
+
+ buf[0] = '\0';
+ for (i = 0; i < max_naddr; i++) {
+ /* XXX these will be removed */
+ /*LINTED*/
+ if (iplist[i] == -1)
+ continue;
+
+ if (inet_ntop(AF_INET, (const void *)(&iplist[i]), ipstr,
+ sizeof (ipstr)) == 0)
+ continue;
+ (void) strcat(buf, ipstr);
+ (void) strcat(buf, ",");
+ }
+ buf[strlen(buf)-1] = '\0';
+}
+
+/*
+ * This function build the new WINS exclude list from
+ * configured list + new additions to exclude list
+ * It also assumes that the buffers are of enough space.
+ */
+int
+smb_wins_exclude_list(char *config_list, char *exclude_list)
+{
+ int ccnt, ecnt, already_there;
+ int i, j;
+ uint32_t ncur_list[SMB_PI_MAX_NETWORKS];
+ uint32_t ecur_list[SMB_PI_MAX_NETWORKS];
+
+ ccnt = smb_wins_iplist(config_list, ncur_list, SMB_PI_MAX_NETWORKS);
+ if (ccnt < 0)
+ return (-1);
+
+ ecnt = smb_wins_iplist(exclude_list, ecur_list, SMB_PI_MAX_NETWORKS);
+ if (ecnt < 0)
+ return (-1);
+
+ if ((ccnt + ecnt) > SMB_PI_MAX_NETWORKS)
+ return (-1);
+
+ for (i = 0; i < ecnt; i++) {
+ already_there = 0;
+ for (j = 0; j < ccnt; j++) {
+ if (ncur_list[j] == ecur_list[i]) {
+ already_there = 1;
+ }
+ }
+ if (already_there)
+ continue;
+
+ ncur_list[ccnt++] = ecur_list[i];
+ }
+
+ smb_wins_build_list(config_list, ncur_list, ccnt);
+ return (0);
+}
+
+/*
+ * This function build the new WINS allow list from
+ * configured list - new allowed list
+ * It also assumes that the buffers are of enough space.
+ */
+int
+smb_wins_allow_list(char *config_list, char *allow_list)
+{
+ int ccnt, acnt;
+ int i, j;
+ uint32_t ncur_list[SMB_PI_MAX_NETWORKS];
+ uint32_t acur_list[SMB_PI_MAX_NETWORKS];
+
+ ccnt = smb_wins_iplist(config_list, ncur_list, SMB_PI_MAX_NETWORKS);
+ if (ccnt < 0)
+ return (-1);
+
+ acnt = smb_wins_iplist(allow_list, acur_list, SMB_PI_MAX_NETWORKS);
+ if (acnt < 0)
+ return (0);
+
+ for (i = 0; i < acnt; i++) {
+ for (j = 0; j < ccnt; j++) {
+ if (ncur_list[j] == (in_addr_t)(-1))
+ continue;
+ if (ncur_list[j] == acur_list[i]) {
+ ncur_list[j] = (in_addr_t)(-1);
+ }
+ }
+ }
+ smb_wins_build_list(config_list, ncur_list, ccnt);
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_wksids.c b/usr/src/lib/smbsrv/libsmb/common/smb_wksids.c
new file mode 100644
index 0000000000..0a40b95418
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_wksids.c
@@ -0,0 +1,350 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module provides the interface to builtin domain information.
+ * These are the predefined groups and aliases in the NT AUTHORITY or
+ * BUILTIN domains, and some other miscellaneous bits.
+ */
+
+#include <string.h>
+#include <synch.h>
+#include <smbsrv/ntsid.h>
+#include <smbsrv/string.h>
+#include <smbsrv/alloc.h>
+
+/*
+ * This table should contain all of the NT builtin domain names.
+ */
+static char *domain[] = {
+ "LOCAL",
+ "BUILTIN",
+ "NT AUTHORITY",
+ "UNKNOWN"
+};
+
+static int wk_init = 0;
+static rwlock_t wk_rwlock;
+
+/*
+ * This table should contain all of the builtin domains, groups and
+ * aliases. The order is important because we do string compares on
+ * the SIDs. For each domain, ensure that the domain SID appears
+ * before any aliases in that domain.
+ */
+static well_known_account_t wkt[] = {
+ { SidTypeWellKnownGroup, 0, "S-1-0-0", "Null",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 1, "S-1-1-0", "Everyone",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 1, "S-1-2-0", "LOCAL",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 1, "S-1-3-0", "CREATOR OWNER",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 1, "S-1-3-1", "CREATOR GROUP",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 1, "S-1-3-2", "CREATOR OWNER SERVER",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 1, "S-1-3-3", "CREATOR GROUP SERVER",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeDomain, 1, "S-1-4", "NON UNIQUE",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeDomain, 2, "S-1-5", "NT AUTHORITY",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-1", "DIALUP",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-2", "NETWORK",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-3", "BATCH",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-4", "INTERACTIVE",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-6", "SERVICE",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-7", "ANONYMOUS",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-8", "PROXY",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-9", "SERVER",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-10", "SELF",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-11", "Authenticated Users",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-12", "RESTRICTED",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-18", "SYSTEM",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeWellKnownGroup, 2, "S-1-5-21", "NON_UNIQUE",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeDomain, 2, "S-1-5-32", "BUILTIN",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeAlias, 1, "S-1-5-32-544", "Administrators",
+ 0, "Members can fully administer the computer/domain", NULL },
+ { SidTypeAlias, 1, "S-1-5-32-545", "Users",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeAlias, 1, "S-1-5-32-546", "Guests",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeAlias, 1, "S-1-5-32-547", "Power Users",
+ 0, "Members can share directories", NULL },
+ { SidTypeAlias, 1, "S-1-5-32-548", "Account Operators",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeAlias, 1, "S-1-5-32-549", "Server Operators",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeAlias, 1, "S-1-5-32-550", "Print Operators",
+ LGF_HIDDEN, 0, NULL},
+ { SidTypeAlias, 1, "S-1-5-32-551", "Backup Operators",
+ 0, "Members can bypass file security to back up files", NULL },
+ { SidTypeAlias, 1, "S-1-5-32-552", "Replicator",
+ LGF_HIDDEN, 0, NULL}
+};
+
+
+/*
+ * nt_builtin_lookup_sid
+ *
+ * Search the wkt looking for a match on the specified SID. If the
+ * SID matches a builtin entry, the associated name is returned.
+ * Otherwise a null pointer is returned.
+ */
+char *
+nt_builtin_lookup_sid(nt_sid_t *sid, WORD *sid_name_use)
+{
+ well_known_account_t *entry;
+ char *sidbuf;
+ int sidlen;
+ int i;
+
+ if ((sidbuf = nt_sid_format(sid)) == 0) {
+ return (0);
+ }
+
+ sidlen = strlen(sidbuf);
+
+ for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) {
+ entry = &wkt[i];
+
+ if (strncmp(sidbuf, entry->sid, sidlen) == 0) {
+ if (sid_name_use)
+ *sid_name_use = entry->sid_name_use;
+ free(sidbuf);
+ return (entry->name);
+ }
+ }
+
+ free(sidbuf);
+ return (0);
+}
+
+
+/*
+ * nt_builtin_lookup_name
+ *
+ * Search the wkt looking for a match on the specified name. If the
+ * name matches a builtin entry, the associated SID (which is in
+ * malloc'd memory) is returned. Otherwise a null pointer is returned.
+ */
+nt_sid_t *
+nt_builtin_lookup_name(char *name, WORD *sid_name_use)
+{
+ well_known_account_t *entry;
+ int i;
+
+ for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) {
+ entry = &wkt[i];
+
+ if (!utf8_strcasecmp(name, entry->name)) {
+ if (sid_name_use)
+ *sid_name_use = entry->sid_name_use;
+ return (nt_sid_strtosid(entry->sid));
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * nt_builtin_lookup
+ *
+ * Search the wkt looking for a match on the specified name. If the
+ * name matches a builtin entry then pointer to that entry will be
+ * returned. Otherwise 0 is returned.
+ */
+well_known_account_t *
+nt_builtin_lookup(char *name)
+{
+ well_known_account_t *entry;
+ int i;
+
+ (void) rw_rdlock(&wk_rwlock);
+ for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) {
+ entry = &wkt[i];
+
+ if (!utf8_strcasecmp(name, entry->name)) {
+ (void) rw_unlock(&wk_rwlock);
+ return (entry);
+ }
+ }
+
+ (void) rw_unlock(&wk_rwlock);
+ return (0);
+}
+
+
+/*
+ * nt_builtin_is_wellknown
+ *
+ * Search the wkt looking for a match on the specified name. If the
+ * name matches a builtin entry returns 1. Otherwise returns 0.
+ */
+int
+nt_builtin_is_wellknown(char *name)
+{
+ well_known_account_t *entry;
+ int i;
+
+ for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) {
+ entry = &wkt[i];
+
+ if (!utf8_strcasecmp(name, entry->name)) {
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * nt_builtin_lookup_domain
+ *
+ * Return the builtin domain name for the specified alias or group name.
+ */
+char *
+nt_builtin_lookup_domain(char *name)
+{
+ well_known_account_t *entry;
+ char *domain_name;
+ int i;
+
+ for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) {
+ entry = &wkt[i];
+
+ if (!utf8_strcasecmp(name, entry->name)) {
+ domain_name = domain[entry->domain_ix];
+ return (domain_name);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * nt_builtin_findfirst
+ *
+ * Returns pointer to the first entry of well known sids table.
+ */
+well_known_account_t *
+nt_builtin_findfirst(DWORD *iterator)
+{
+ *iterator = 1;
+ return (&wkt[0]);
+}
+
+/*
+ * nt_builtin_findnext
+ *
+ * Returns pointer to the entry of well known sids table specified
+ * by the iterator. Increments iterator to point to the next entry.
+ */
+well_known_account_t *
+nt_builtin_findnext(DWORD *iterator)
+{
+ if (*iterator < sizeof (wkt)/sizeof (wkt[0]))
+ return (&wkt[(*iterator)++]);
+
+ return (0);
+}
+
+/*
+ * nt_builtin_init
+ *
+ * Generate binary SIDs from the string SIDs in the table
+ * and set the proper field.
+ *
+ * Caller MUST not store the binary SID pointer anywhere that
+ * could lead to freeing it.
+ *
+ * This function should only be called once.
+ */
+int
+nt_builtin_init()
+{
+ well_known_account_t *entry;
+ int i;
+
+ (void) rw_wrlock(&wk_rwlock);
+ if (wk_init) {
+ (void) rw_unlock(&wk_rwlock);
+ return (1);
+ }
+
+ for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) {
+ entry = &wkt[i];
+ entry->binsid = nt_sid_strtosid(entry->sid);
+ if (entry->binsid == NULL) {
+ (void) rw_unlock(&wk_rwlock);
+ nt_builtin_fini();
+ return (0);
+ }
+ }
+
+ wk_init = 1;
+ (void) rw_unlock(&wk_rwlock);
+ return (1);
+}
+
+void
+nt_builtin_fini()
+{
+ int i;
+
+ (void) rw_wrlock(&wk_rwlock);
+ if (wk_init == 0) {
+ (void) rw_unlock(&wk_rwlock);
+ return;
+ }
+
+ for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) {
+ if (wkt[i].binsid) {
+ free(wkt[i].binsid);
+ wkt[i].binsid = NULL;
+ }
+ }
+
+ wk_init = 0;
+ (void) rw_unlock(&wk_rwlock);
+}
diff --git a/usr/src/lib/smbsrv/libsmb/i386/Makefile b/usr/src/lib/smbsrv/libsmb/i386/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libsmb/sparc/Makefile b/usr/src/lib/smbsrv/libsmb/sparc/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libsmb/sparcv9/Makefile b/usr/src/lib/smbsrv/libsmb/sparcv9/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/sparcv9/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libsmbns/Makefile b/usr/src/lib/smbsrv/libsmbns/Makefile
new file mode 100644
index 0000000000..495ff5688d
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+HDRS= libsmbns.h
+
+include ../Makefile.smbsrv
diff --git a/usr/src/lib/smbsrv/libsmbns/Makefile.com b/usr/src/lib/smbsrv/libsmbns/Makefile.com
new file mode 100644
index 0000000000..2166b2640d
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/Makefile.com
@@ -0,0 +1,62 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY= libsmbns.a
+VERS= .1
+
+OBJS_SHARED = \
+ smb_netbios_util.o \
+
+OBJS_COMMON= \
+ smbns_ads.o \
+ smbns_browser.o \
+ smbns_dyndns.o \
+ smbns_krb.o \
+ smbns_ksetpwd.o \
+ smbns_netbios.o \
+ smbns_netbios_cache.o \
+ smbns_netbios_datagram.o\
+ smbns_netbios_name.o \
+ smbns_netlogon.o \
+ smbns_nicconfig.o
+
+OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED)
+
+include ../../../Makefile.lib
+include ../../Makefile.lib
+
+SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
+ $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c)
+
+LDLIBS += -lsmb -lgss -lldap -lresolv -lnsl -lsocket -lc
+CPPFLAGS += -D_REENTRANT
+
+# DYNLIB libraries do not have lint libs and are not linted
+$(DYNLIB) := LDLIBS += -lkrb5
+
+include ../../Makefile.targ
+include ../../../Makefile.targ
diff --git a/usr/src/lib/smbsrv/libsmbns/amd64/Makefile b/usr/src/lib/smbsrv/libsmbns/amd64/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h b/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h
new file mode 100644
index 0000000000..a05c197c35
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h
@@ -0,0 +1,184 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBSMBNS_H
+#define _LIBSMBNS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ldap.h>
+#include <net/if.h>
+
+#include <smbsrv/libsmb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ADS typedef/data structures and functions */
+#define ADS_MAXBUFLEN 100
+
+typedef struct ads_handle_s {
+ char *user; /* admin user to create share in ADS */
+ char *pwd; /* user password */
+ char *container; /* user container in ADS */
+ char *domain; /* ADS domain */
+ char *domain_dn; /* domain in Distinquish Name format */
+ char *ip_addr; /* ip addr in string format */
+ char *hostname; /* fully qualified hostname */
+ char *site; /* local ADS site */
+ LDAP *ld; /* LDAP handle */
+} ADS_HANDLE;
+
+/*
+ * The possible return status of the adjoin routine.
+ */
+typedef enum adjoin_status {
+ ADJOIN_SUCCESS = 0,
+ ADJOIN_ERR_GET_HANDLE,
+ ADJOIN_ERR_GEN_PASSWD,
+ ADJOIN_ERR_ADD_TRUST_ACCT,
+ ADJOIN_ERR_GET_ENCTYPES,
+ ADJOIN_ERR_GET_HOST_PRINC,
+ ADJOIN_ERR_INIT_KRB_CTX,
+ ADJOIN_ERR_GET_KRB_PRINC,
+ ADJOIN_ERR_KSETPWD,
+ ADJOIN_ERR_MOD_TRUST_ACCT,
+ ADJOIN_ERR_WRITE_KEYTAB,
+ ADJOIN_ERR_IDMAP_SET_DOMAIN,
+ ADJOIN_ERR_IDMAP_SET_GC,
+ ADJOIN_ERR_IDMAP_REFRESH,
+ ADJOIN_ERR_IDMAP_CCACHE,
+
+ ADJOIN_NUM_STATUS
+} adjoin_status_t;
+
+/* ADS functions */
+extern ADS_HANDLE *ads_open(void);
+extern void ads_close(ADS_HANDLE *);
+extern int ads_publish_share(ADS_HANDLE *, const char *, const char *,
+ const char *, const char *);
+extern int ads_remove_share(ADS_HANDLE *, const char *, const char *,
+ const char *, const char *);
+extern int ads_build_unc_name(char *, int, const char *, const char *);
+extern int ads_lookup_share(ADS_HANDLE *, const char *, const char *, char *);
+extern int ads_add_share(ADS_HANDLE *, const char *, const char *,
+ const char *);
+
+extern adjoin_status_t adjoin(char *, int);
+extern char *adjoin_report_err(adjoin_status_t status);
+
+/* DYNDNS functions */
+extern int dyndns_update(void);
+extern int dyndns_clear_rev_zone(void);
+
+/* Kerberos initialization function */
+extern int smb_kinit(char *user, char *passwd);
+
+
+/* NETBIOS Functions */
+extern int msdcs_lookup_ads(void);
+extern void smb_netbios_start(void);
+extern void smb_netbios_shutdown(void);
+extern void smb_netbios_name_reconfig(void);
+
+/* Browser Configure */
+extern void smb_browser_config(void);
+
+extern void smb_netlogon_request(int, int, char *);
+
+/*
+ * NIC listing and config
+ */
+#define MAXIFS 256
+#define SIZE_IP 17
+
+typedef struct {
+ char ifname[LIFNAMSIZ];
+ uint32_t ip;
+ uint32_t mask;
+ uint32_t broadcast;
+ boolean_t exclude;
+ uint64_t flags;
+ char groupname[LIFGRNAMSIZ];
+ char **aliases;
+ int naliases;
+} net_cfg_t;
+typedef struct {
+ net_cfg_t *net_cfg_list;
+ int net_cfg_cnt;
+} net_cfg_list_t;
+
+struct if_list {
+ char name[IFNAMSIZ+1];
+ struct if_list *next;
+};
+
+struct ip_alias {
+ char name[SIZE_IP];
+ struct ip_alias *next;
+};
+
+#define GATEWAY_FILE "/etc/defaultrouter"
+
+/* NIC Config functions */
+extern void smb_resolver_init(void);
+extern void smb_resolver_close(void);
+extern int smb_get_nameservers(struct in_addr *, int);
+extern uint16_t smb_get_next_resid(void);
+extern void smb_nic_lock(void);
+extern void smb_nic_unlock(void);
+extern int smb_nic_init(void);
+extern void smb_nic_build_info(void);
+extern net_cfg_t *smb_nic_get_byind(int, net_cfg_t *);
+extern net_cfg_t *smb_nic_get_bysubnet(uint32_t, net_cfg_t *);
+extern net_cfg_t *smb_nic_get_byip(uint32_t, net_cfg_t *);
+extern int smb_nic_get_num(void);
+extern int smb_nic_get_IP(char *, uint32_t *uip);
+extern int smb_nic_get_broadcast(char *, uint32_t *uip);
+extern int smb_nic_get_netmask(char *, uint32_t *uip);
+extern int smb_nic_get_IP_aliases(char *, struct ip_alias **);
+extern int smb_nic_get_number(void);
+extern int smb_nic_get_num_physical(void);
+extern int smb_nic_get_num_logical(void);
+extern int smb_nic_get_num_aliases(char *);
+extern int smb_nic_get_default_gateway(char *, unsigned int);
+extern int smb_nic_flags(char *, uint64_t *);
+extern int smb_nic_build_if_name(char ***);
+extern int smb_nic_build_network_structures(net_cfg_t **, int *);
+extern char *smb_nic_get_ifnames(int, int);
+extern int smb_nic_validate_ip_address(char *);
+extern int smb_nic_status(char *, uint64_t);
+extern int smb_nic_get_group(char *lifname, char *grname);
+extern int smb_nic_set_group(char *lifname, char *grname);
+extern int smb_nic_clear_niclist(net_cfg_t *, int);
+extern int smb_nic_clear_name_list(char **, int);
+extern int smb_nic_clear_ip_alias(struct ip_alias *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSMBNS_H */
diff --git a/usr/src/lib/smbsrv/libsmbns/common/llib-lsmbns b/usr/src/lib/smbsrv/libsmbns/common/llib-lsmbns
new file mode 100644
index 0000000000..5420d543b5
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/llib-lsmbns
@@ -0,0 +1,31 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <smbsrv/libsmbns.h>
diff --git a/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers b/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers
new file mode 100644
index 0000000000..d20641252d
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers
@@ -0,0 +1,81 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SUNWprivate {
+ global:
+ adjoin;
+ adjoin_report_err;
+ ads_add_share;
+ ads_build_unc_name;
+ ads_close;
+ ads_lookup_share;
+ ads_open;
+ ads_publish_share;
+ ads_remove_share;
+ dyndns_clear_rev_zone;
+ dyndns_update;
+ msdcs_lookup_ads;
+ smb_browser_config;
+ smb_get_nameservers;
+ smb_get_next_resid;
+ smb_kinit;
+ smb_netbios_name_reconfig;
+ smb_netbios_start;
+ smb_netbios_shutdown;
+ smb_netlogon_request;
+ smb_nic_build_if_name;
+ smb_nic_build_network_structures;
+ smb_nic_clear_ip_alias;
+ smb_nic_clear_name_list;
+ smb_nic_clear_niclist;
+ smb_nic_free_niclist;
+ smb_nic_get_IP;
+ smb_nic_get_IP_aliases;
+ smb_nic_get_broadcast;
+ smb_nic_build_info;
+ smb_nic_get_byind;
+ smb_nic_get_byip;
+ smb_nic_get_bysubnet;
+ smb_nic_get_default_gateway;
+ smb_nic_get_group;
+ smb_nic_get_ifnames;
+ smb_nic_get_netmask;
+ smb_nic_get_num;
+ smb_nic_get_num_aliases;
+ smb_nic_get_number;
+ smb_nic_get_num_logical;
+ smb_nic_get_num_physical;
+ smb_nic_init;
+ smb_nic_lock;
+ smb_nic_status;
+ smb_nic_unlock;
+ smb_nic_validate_ip_address;
+ smb_resolver_close;
+ smb_resolver_init;
+ local:
+ *;
+};
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c
new file mode 100644
index 0000000000..c57971e455
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c
@@ -0,0 +1,2304 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <ldap.h>
+#include <stdlib.h>
+#include <gssapi/gssapi.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <sys/synch.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <smbsrv/libsmbns.h>
+#include <smbns_ads.h>
+#include <smbns_dyndns.h>
+#include <smbns_krb.h>
+
+#define ADS_DN_MAX 300
+#define ADS_MAXMSGLEN 512
+#define ADS_HOST_PREFIX "host/"
+#define ADS_COMPUTERS_CN "Computers"
+
+/* current ADS server to communicate with */
+ADS_HOST_INFO *ads_host_info = NULL;
+mutex_t ads_mtx;
+
+/*
+ * adjoin_errmsg
+ *
+ * Use the adjoin return status defined in adjoin_status_t as the index
+ * to this table.
+ */
+static char *adjoin_errmsg[] = {
+ "ADJOIN succeeded.",
+ "ADJOIN failed to get handle.",
+ "ADJOIN failed to generate machine password.",
+ "ADJOIN failed to add workstation trust account.",
+ "ADJOIN failed to get list of encryption types.",
+ "ADJOIN failed to get host principal.",
+ "ADJOIN failed to initialize kerberos context.",
+ "ADJOIN failed to get kerberos principal.",
+ "ADJOIN failed to set machine account password on AD.",
+ "ADJOIN failed to modify workstation trust account.",
+ "ADJOIN failed to write Keberos keytab file.",
+ "ADJOIN failed to configure idmap(mapping domain).",
+ "ADJOIN failed to configure idmap(global catalog).",
+ "ADJOIN failed to refresh idmap service."
+ "ADJOIN failed to remove idmap ccache."
+};
+
+static int ads_bind(ADS_HANDLE *);
+static void ads_get_computer_dn(ADS_HANDLE *, char *, size_t);
+static char *ads_get_host_principal(char *fqhost);
+static char *ads_get_host_principal_w_realm(char *princ, char *domain);
+static int ads_get_host_principals(char *fqhost, char *domain,
+ char **princ, char **princ_r);
+static int ads_add_computer(ADS_HANDLE *ah);
+static void ads_del_computer(ADS_HANDLE *ah);
+static int ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val);
+static int ads_modify_computer(ADS_HANDLE *ah, int des_only);
+static krb5_kvno ads_lookup_computer_attr_kvno(ADS_HANDLE *ah);
+static int ads_gen_machine_passwd(char *machine_passwd, int bufsz);
+static void ads_set_host_info(ADS_HOST_INFO *host);
+static ADS_HOST_INFO *ads_get_host_info(void);
+
+/*
+ * ads_build_unc_name
+ *
+ * Construct the UNC name of the share object in the format of
+ * \\hostname.domain\shareUNC
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int
+ads_build_unc_name(char *unc_name, int maxlen,
+ const char *hostname, const char *shareUNC)
+{
+ char my_domain[ADS_MAXBUFLEN];
+
+ if (smb_getdomainname(my_domain, sizeof (my_domain)) != 0)
+ return (-1);
+
+ (void) snprintf(unc_name, maxlen, "\\\\%s.%s\\%s",
+ hostname, my_domain, shareUNC);
+ return (0);
+}
+
+/*
+ * ads_skip_domain_name
+ * Skip domain name format in DNS message. The format is a sequence of
+ * ascii labels with each label having a length byte at the beginning.
+ * The domain name is terminated with a NULL character.
+ * i.e. 3sun3com0
+ * Parameters:
+ * bufptr: address of pointer of buffer that contains domain name
+ * Returns:
+ * bufptr: points to the data after the domain name label
+ */
+static void
+ads_skip_domain_name(char **bufptr)
+{
+ int i = 0;
+ unsigned char c, d;
+
+ c = (*bufptr)[i++];
+ d = c & 0xC0;
+ while (c != 0 && (d != 0xC0)) { /* do nothing */
+ c = (*bufptr)[i++];
+ d = c & 0xC0;
+ }
+
+ if (d == 0xC0)
+ /* skip 2nd byte in 2 byte ptr info */
+ i++;
+ *bufptr += i;
+}
+
+static int
+ads_is_ptr(char *buf, int len, char *offset_ptr, char **new_loc)
+{
+ uint16_t offset;
+ unsigned char c;
+
+ c = len & 0xC0;
+ if (c == 0xC0) {
+ offset_ptr = dyndns_get_nshort(offset_ptr, &offset);
+ offset &= 0x3FFF;
+ if (offset > NS_PACKETSZ) {
+ return (-1);
+ }
+ *new_loc = buf + offset;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * ads_get_domain_name
+ * Converts the domain name format in DNS message back to string format.
+ * The format is a sequence of ascii labels with each label having a length
+ * byte at the beginning. The domain name is terminated with a NULL
+ * character.
+ * i.e. 6procom3com0 -> procom.com
+ * Parameters:
+ * bufptr : address of pointer to buffer that contains domain name
+ * dname_len: length of domain name in label format
+ * Returns:
+ * NULL : error
+ * domain name: in string format using allocated memory
+ * bufptr : points to the data after the domain name label
+ */
+static char *
+ads_get_domain_name(char *buf, char **bufptr)
+{
+ char str[256], *ptr, *new_loc;
+ int i, j, k, len, ret;
+ int skip = 0;
+ i = 0;
+ k = 0;
+ ptr = *bufptr;
+
+ /* get len of first label */
+ len = ptr[i++];
+ if ((ret = ads_is_ptr(buf, len, &ptr[i-1], &new_loc)) == 1) {
+ if (skip == 0) {
+ /* skip up to first ptr */
+ skip = i;
+ }
+
+ i = 0;
+ ptr = new_loc;
+
+ /* get len of first label */
+ len = ptr[i++];
+ } else {
+ if (ret == -1) {
+ return (NULL);
+ }
+ }
+
+ while (len) {
+ if ((len > 63) || (k >= 255))
+ return (NULL);
+
+ for (j = 0; j < len; j++)
+ str[k++] = ptr[i++];
+
+ /* get len of next label */
+ len = ptr[i++];
+ if ((ret = ads_is_ptr(buf, len, &ptr[i-1], &new_loc)) == 1) {
+ if (skip == 0) {
+ /* skip up to first ptr */
+ skip = i;
+ }
+ i = 0;
+ ptr = new_loc;
+
+ /* get len of first label */
+ len = ptr[i++];
+ } else if (ret == -1) {
+ return (NULL);
+ }
+
+ if (len) {
+ /* replace label len or ptr with '.' */
+ str[k++] = '.';
+ }
+ }
+
+ str[k] = 0;
+
+ if (skip) {
+ /* skip name with ptr or just ptr */
+ *bufptr += skip + 1;
+ } else {
+ /* skip name */
+ *bufptr += i;
+ }
+
+ return (strdup(str));
+}
+
+/*
+ * ads_ping
+ * Ping IP without displaying log. This is used to ping an ADS server to see
+ * if it is still alive before connecting to it with TCP.
+ * Taken from os/service/ping.c
+ * Parameters:
+ * hostinetaddr: 4 bytes IP address in network byte order
+ * Returns:
+ * -1: error
+ * 0: successful
+ */
+/*ARGSUSED*/
+static int
+ads_ping(unsigned long hostinetaddr)
+{
+ return (0);
+}
+
+/*
+ * ads_free_host_list
+ */
+static void
+ads_free_host_list(ADS_HOST_INFO *host_list, int count)
+{
+ int i;
+ for (i = 0; i < count; i++) {
+ free(host_list[i].name);
+ }
+ free(host_list);
+}
+
+/*
+ * ads_set_host_info
+ * Cache the result of the ADS discovery if the cache is empty.
+ */
+static void
+ads_set_host_info(ADS_HOST_INFO *host)
+{
+ (void) mutex_lock(&ads_mtx);
+ if (!ads_host_info)
+ ads_host_info = host;
+ (void) mutex_unlock(&ads_mtx);
+}
+
+/*
+ * ads_get_host_info
+ * Get the cached ADS host info.
+ */
+static ADS_HOST_INFO *
+ads_get_host_info(void)
+{
+ ADS_HOST_INFO *host;
+
+ (void) mutex_lock(&ads_mtx);
+ host = ads_host_info;
+ (void) mutex_unlock(&ads_mtx);
+ return (host);
+}
+/*
+ * ads_find_host
+ * This routine builds a DNS service location message and sends it to the
+ * DNS server via UDP to query it for a list of ADS server(s). Once a reply
+ * is received, the reply message is parsed to get the hostname and IP
+ * addresses of the ADS server(s). One ADS server will be selected from the
+ * list. A ping is sent to each host at a time and the one that respond will
+ * be selected.
+ *
+ * The service location of _ldap._tcp.dc.msdcs.<ADS domain> is used to
+ * guarantee that Microsoft domain controllers are returned. Microsoft domain
+ * controllers are also ADS servers.
+ *
+ * The ADS hostnames are stored in the answer section of the DNS reply message.
+ * The IP addresses are stored in the additional section. If the additional
+ * section does not contain any IP addresses then a DNS query by hostname is
+ * sent to get the IP address of the hostname. This is very unlikely.
+ *
+ * The DNS reply message may be in compress formed. The compression is done
+ * on repeating domain name label in the message. i.e hostname.
+ * Parameters:
+ * ns: Nameserver to use to find the ADS host
+ * domain: domain of ADS host.
+ * Returns:
+ * ADS host: fully qualified hostname, ip address, ldap port
+ * port : LDAP port of ADS host
+ */
+/*ARGSUSED*/
+ADS_HOST_INFO *
+ads_find_host(char *ns, char *domain, int *port, char *service, int *go_next)
+{
+ int s;
+ uint16_t id, rid, data_len, eport;
+ int ipaddr;
+ struct hostent *h;
+ char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
+ char *bufptr, *str;
+ int i, ret;
+ int queryReq;
+ uint16_t query_cnt, ans_cnt, namser_cnt, addit_cnt;
+ int quest_type, quest_class;
+ int dns_ip, decode_ip;
+ struct in_addr addr;
+ uint16_t flags = 0;
+ int force_recurs = 0;
+ ADS_HOST_INFO *ads_hosts_list = NULL, *ads_host;
+ ADS_HOST_INFO *ads_hosts_list2 = NULL;
+
+ *go_next = 0;
+
+ /*
+ * If we have already found an ADS server, skip the ads_find_host
+ * process. Returns the ADS host from the cache.
+ */
+ ads_host = ads_get_host_info();
+ if (ads_host)
+ return (ads_host);
+
+ if (ns == NULL || *ns == 0) {
+ return (NULL);
+ }
+ dns_ip = inet_addr(ns);
+
+ if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0)
+ return (NULL);
+
+retry:
+ /* build query request */
+ queryReq = REQ_QUERY;
+ query_cnt = 1;
+ ans_cnt = 0;
+ namser_cnt = 0;
+ addit_cnt = 0;
+
+ (void) memset(buf, 0, NS_PACKETSZ);
+ bufptr = buf;
+ id = smb_get_next_resid();
+ if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), id, queryReq,
+ query_cnt, ans_cnt, namser_cnt, addit_cnt, flags) == -1) {
+ (void) close(s);
+ return (NULL);
+ }
+
+ quest_type = ns_t_srv;
+ quest_class = ns_c_in;
+
+ if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), service,
+ quest_type, quest_class) == -1) {
+ (void) close(s);
+ return (NULL);
+ }
+
+ if (dyndns_udp_send_recv(s, buf, bufptr - buf, buf2) == -1) {
+ (void) close(s);
+ syslog(LOG_ERR, "smb_ads: send/receive error");
+ *go_next = 1;
+ return (NULL);
+ }
+ (void) close(s);
+
+ (void) dyndns_get_nshort(buf2, &rid);
+ if (id != rid)
+ return (NULL);
+
+ /*
+ * check if query is successful by checking error
+ * field in UDP
+ */
+ ret = buf2[3] & 0xf;
+ if (ret != NOERROR) {
+ syslog(LOG_ERR, "smb_ads: DNS query for ADS host error: %d: ",
+ ret);
+ dyndns_msg_err(ret);
+ *go_next = 1;
+ return (NULL);
+ }
+
+ bufptr = buf2;
+ bufptr += 2; /* Skip ID section */
+ bufptr = dyndns_get_nshort(bufptr, &flags);
+ bufptr = dyndns_get_nshort(bufptr, &query_cnt);
+ bufptr = dyndns_get_nshort(bufptr, &ans_cnt);
+ bufptr = dyndns_get_nshort(bufptr, &namser_cnt);
+ bufptr = dyndns_get_nshort(bufptr, &addit_cnt);
+
+ if (ans_cnt == 0) {
+ /* Check if the server supports recursive queries */
+ if (force_recurs++ == 0 && (flags & DNSF_RECUR_SUPP) != 0) {
+ flags = DNSF_RECUR_QRY;
+ goto retry;
+ }
+
+ syslog(LOG_DEBUG, "smb_ads: No ADS host found: "
+ "No answer section\n");
+ return (NULL);
+ }
+
+ /* skip question section */
+ if (query_cnt == 1) {
+ ads_skip_domain_name(&bufptr);
+ bufptr += 4;
+ } else {
+ syslog(LOG_ERR, "smb_ads: No ADS host found, malformed "
+ "question section, query_cnt: %d???\n", query_cnt);
+ return (NULL);
+ }
+
+ ads_hosts_list = (ADS_HOST_INFO *)
+ malloc(sizeof (ADS_HOST_INFO)*ans_cnt);
+ if (ads_hosts_list == NULL)
+ return (NULL);
+
+ bzero(ads_hosts_list, sizeof (ADS_HOST_INFO) * ans_cnt);
+
+ /* check answer section */
+ for (i = 0; i < ans_cnt; i++) {
+ ads_skip_domain_name(&bufptr);
+
+ /* skip type, class, ttl */
+ bufptr += 8;
+
+ /* len of data after this point */
+ bufptr = dyndns_get_nshort(bufptr, &data_len);
+
+ /* skip priority, weight */
+ bufptr += 4;
+ bufptr = dyndns_get_nshort(bufptr, &eport);
+ ads_hosts_list[i].port = eport;
+
+ if ((str = ads_get_domain_name(buf2, &bufptr)) == NULL) {
+ syslog(LOG_ERR, "smb_ads: No ADS host found, "
+ "error decoding DNS answer section\n");
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+ return (NULL);
+ }
+ ads_hosts_list[i].name = str;
+ }
+
+ /* check authority section */
+ for (i = 0; i < namser_cnt; i++) {
+ ads_skip_domain_name(&bufptr);
+
+ /* skip type, class, ttl */
+ bufptr += 8;
+
+ /* get len of data */
+ bufptr = dyndns_get_nshort(bufptr, &data_len);
+
+ /* skip data */
+ bufptr += data_len;
+ }
+
+ /* check additional section to get IP address of ads host */
+ decode_ip = 1;
+ smb_config_rdlock();
+ if (smb_config_getyorn(SMB_CI_ADS_IPLOOKUP) == 1)
+ decode_ip = 0;
+ smb_config_unlock();
+
+ if (decode_ip && (addit_cnt > 0)) {
+ int j;
+
+ ads_hosts_list2 = (ADS_HOST_INFO *)
+ malloc(sizeof (ADS_HOST_INFO) * addit_cnt);
+ if (ads_hosts_list2 == NULL) {
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+ return (NULL);
+ }
+
+ bzero(ads_hosts_list2, sizeof (ADS_HOST_INFO) * addit_cnt);
+
+ for (i = 0; i < addit_cnt; i++) {
+
+ if ((str = ads_get_domain_name(buf2,
+ &bufptr)) == NULL) {
+ syslog(LOG_ERR, "smb_ads: No ADS host found, "
+ "error decoding DNS additional section\n");
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+ ads_free_host_list(ads_hosts_list2, addit_cnt);
+ return (NULL);
+ }
+
+ ads_hosts_list2[i].name = str;
+ bufptr += 10;
+ bufptr = dyndns_get_int(bufptr, &ipaddr);
+ ads_hosts_list2[i].ip_addr = ipaddr;
+ }
+
+ /* pick a host that is up */
+ for (i = 0; i < addit_cnt; i++) {
+ if (ads_ping(ads_hosts_list2[i].ip_addr) != 0) {
+ continue;
+ }
+ for (j = 0; j < ans_cnt; j++)
+ if (strcmp(ads_hosts_list2[i].name,
+ ads_hosts_list[j].name) == 0)
+ break;
+ if (j == ans_cnt) {
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+ ads_free_host_list(ads_hosts_list2, addit_cnt);
+ return (NULL);
+ }
+ ads_host = (ADS_HOST_INFO *)
+ malloc(sizeof (ADS_HOST_INFO));
+ if (ads_host == NULL) {
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+ ads_free_host_list(ads_hosts_list2, addit_cnt);
+ return (NULL);
+ }
+ bzero(ads_host, sizeof (ADS_HOST_INFO));
+ ads_host->name = strdup(ads_hosts_list[j].name);
+ if (ads_host->name == NULL) {
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+ ads_free_host_list(ads_hosts_list2, addit_cnt);
+ return (NULL);
+ }
+ ads_host->ip_addr = ads_hosts_list2[i].ip_addr;
+ ads_host->port = ads_hosts_list[j].port;
+ *port = ads_host->port;
+ addr.s_addr = ads_host->ip_addr;
+ syslog(LOG_DEBUG, "smb_ads: Found ADS server: %s (%s)"
+ " from %s\n", ads_host->name, inet_ntoa(addr), ns);
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+ ads_free_host_list(ads_hosts_list2, addit_cnt);
+ ads_set_host_info(ads_host);
+ return (ads_host);
+ }
+ ads_free_host_list(ads_hosts_list2, addit_cnt);
+ } else {
+ /* use DNS to get IP address of ads host */
+ /*
+ * Shouldn't get here unless entries exist in
+ * DNS but DNS server did
+ * not put them in additional section of DNS reply packet.
+ */
+ for (i = 0; i < ans_cnt; i++) {
+ h = gethostbyname(ads_hosts_list[i].name);
+ if (h == NULL)
+ continue;
+ if (h->h_addr == NULL)
+ continue;
+ (void) memcpy(&ads_hosts_list[i].ip_addr,
+ h->h_addr, sizeof (addr.s_addr));
+ if (ads_ping(ads_hosts_list[i].ip_addr) == 0) {
+ ads_host = (ADS_HOST_INFO *)
+ malloc(sizeof (ADS_HOST_INFO));
+ if (ads_host == NULL) {
+ ads_free_host_list(ads_hosts_list,
+ ans_cnt);
+ return (NULL);
+ }
+ bzero(ads_host, sizeof (ADS_HOST_INFO));
+ ads_host->name = strdup(ads_hosts_list[i].name);
+ if (ads_host->name == NULL) {
+ ads_free_host_list(ads_hosts_list,
+ ans_cnt);
+ return (NULL);
+ }
+ ads_host->ip_addr = ads_hosts_list[i].ip_addr;
+ ads_host->port = ads_hosts_list[i].port;
+ *port = ads_host->port;
+ addr.s_addr = ads_host->ip_addr;
+ syslog(LOG_DEBUG, "smb_ads: Found ADS server"
+ " using DNS: %s (%s) port %d",
+ ads_host->name, inet_ntoa(addr),
+ ads_host->port);
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+ ads_set_host_info(ads_host);
+ return (ads_host);
+ }
+ }
+ }
+ syslog(LOG_ERR, "smb_ads: Can't get IP for "
+ "ADS host or ADS host is down.\n");
+ ads_free_host_list(ads_hosts_list, ans_cnt);
+
+ *go_next = 1;
+ return (NULL);
+}
+
+/*
+ * ads_convert_domain
+ * Converts a domain string into its distinguished name i.e. a unique
+ * name for an entry in the Directory Service.
+ * Memory is allocated
+ * for the new string.
+ * i.e. procom.com -> dc=procom,dc=com
+ * Parameters:
+ * s: fully qualified DNS domain string
+ * Returns:
+ * NULL if error
+ * DNS domain in LDAP DN string format
+ */
+static char *
+ads_convert_domain(char *s)
+{
+ char *t, *s2, *t2;
+ int len, cnt;
+
+ if (s == NULL || *s == 0)
+ return (NULL);
+
+ cnt = 0;
+ t = s;
+ while (*t) {
+ if (*t++ == '.') {
+ cnt++;
+ }
+ }
+
+ len = 3 + strlen(s) + cnt*3 + 1;
+
+ s2 = (char *)malloc(len);
+ if (s2 == NULL)
+ return (NULL);
+
+ bzero(s2, len);
+
+ t = s2;
+ (void) strncpy(t, "dc=", 3);
+ t += 3;
+ t2 = s;
+ while (*s) {
+ if (*s == '.') {
+ if (t + 3 >= s2 + len - 1) {
+ syslog(LOG_ERR, "[ads_convert_domain] "
+ "buffer overrun for string "
+ "conversion of %s: tot buf "
+ "sz alloc: %d, last "
+ "written buf offset: %d\n",
+ t2, len, t+3-s2);
+ free(s2);
+ return (NULL);
+ }
+ (void) strncpy(t, ",dc=", 4);
+ t += 4;
+ s++;
+ } else {
+ if (t >= s2 + len - 1) {
+ syslog(LOG_ERR, "[ads_convert_domain] "
+ "buffer overrun for string "
+ "conversion of %s: tot buf "
+ "sz alloc: %d, last "
+ "written buf offset: %d\n",
+ t2, len, t-s2);
+ free(s2);
+ return (NULL);
+ }
+ *t++ = *s++;
+ }
+ }
+ *t = '\0';
+ return (s2);
+}
+
+/*
+ * ads_free_host_info
+ * Free the memory use by the global ads_host_info and set it to NULL.
+ */
+void
+ads_free_host_info(void)
+{
+ (void) mutex_lock(&ads_mtx);
+ if (ads_host_info) {
+ free(ads_host_info->name);
+ free(ads_host_info);
+ ads_host_info = NULL;
+ }
+ (void) mutex_unlock(&ads_mtx);
+}
+
+/*
+ * ads_open
+ * Open a LDAP connection to an ADS server.
+ * If ADS is enabled and the administrative username, password, container, and
+ * ADS domain are defined then query DNS to find an ADS server if this is the
+ * very first call to this routine. After an ADS server is found then this
+ * server will be used everytime this routine is called until the system is
+ * rebooted or the ADS server becomes unavailable then an ADS server will
+ * be queried again. The ADS server is always ping before an LDAP connection
+ * is made to it. If the pings fail then DNS is used once more to find an
+ * available ADS server. If the ping is successful then an LDAP connection
+ * is made to the ADS server. After the connection is made then an ADS handle
+ * is created to be returned.
+ *
+ * After the LDAP connection, the LDAP version will be set to 3 using
+ * ldap_set_option().
+ *
+ * The ads_bind() routine is also called before the ADS handle is returned.
+ * Parameters:
+ * None
+ * Returns:
+ * NULL : can't connect to ADS server or other errors
+ * ADS_HANDLE* : handle to ADS server
+ */
+ADS_HANDLE *
+ads_open(void)
+{
+ ADS_HANDLE *ah;
+ LDAP *ld;
+ int version = 3, ads_port, find_ads_retry;
+ char *adminUser, *password, *container;
+ char domain[MAXHOSTNAMELEN];
+ int enable;
+ ADS_HOST_INFO *ads_host = NULL;
+ struct in_addr addr;
+ char *site, *service = NULL, *site_service = NULL;
+ int service_sz;
+ struct in_addr ns_list[MAXNS];
+ int i, cnt, go_next;
+
+ if (smb_getdomainname(domain, MAXHOSTNAMELEN) != 0)
+ return (NULL);
+
+ smb_config_rdlock();
+ enable = smb_config_getyorn(SMB_CI_ADS_ENABLE);
+ if (!enable) {
+ smb_config_unlock();
+ return (NULL);
+ }
+ adminUser = smb_config_getstr(SMB_CI_ADS_USER);
+ if (adminUser == NULL || *adminUser == 0) {
+ syslog(LOG_ERR, "smb_ads: admin user is not set");
+ smb_config_unlock();
+ return (NULL);
+ }
+ password = smb_config_getstr(SMB_CI_ADS_PASSWD);
+ if (password == NULL || *password == 0) {
+ syslog(LOG_ERR, "smb_ads: admin user password is not set");
+ smb_config_unlock();
+ return (NULL);
+ }
+ container = smb_config_getstr(SMB_CI_ADS_USER_CONTAINER);
+ if (container == NULL || *container == 0)
+ container = "cn=Users";
+
+ site = smb_config_getstr(SMB_CI_ADS_SITE);
+ smb_config_unlock();
+
+
+
+ find_ads_retry = 0;
+find_ads_host:
+
+ ads_host = ads_get_host_info();
+ if (!ads_host) {
+ if (site && *site != 0) {
+ service_sz = strlen("_ldap._tcp.._sites.dc._msdcs.") +
+ strlen(site) + strlen(domain) + 1;
+ site_service = (char *)malloc(service_sz);
+ if (site_service == NULL) {
+ syslog(LOG_ERR, "smb_ads: No ADS host found"
+ " malloc failed...");
+ return (NULL);
+ }
+ (void) snprintf(site_service, service_sz,
+ "_ldap._tcp.%s._sites.dc._msdcs.%s", site, domain);
+ }
+ service_sz = strlen("_ldap._tcp.dc._msdcs.") + strlen(domain)
+ + 1;
+ service = (char *)malloc(service_sz);
+ if (service == NULL) {
+ syslog(LOG_ERR, "smb_ads: No ADS host found malloc"
+ " failed...");
+ if (site_service != NULL)
+ (void) free(site_service);
+ return (NULL);
+ }
+ (void) snprintf(service, service_sz, "_ldap._tcp.dc._msdcs.%s",
+ domain);
+
+ cnt = smb_get_nameservers(ns_list, MAXNS);
+
+ ads_host = NULL;
+ go_next = 0;
+ for (i = 0; i < cnt; i++) {
+ if (site_service != NULL) {
+ ads_host = ads_find_host(inet_ntoa(ns_list[i]),
+ domain, &ads_port, site_service, &go_next);
+ }
+ if (ads_host == NULL) {
+ ads_host = ads_find_host(inet_ntoa(ns_list[i]),
+ domain, &ads_port, service, &go_next);
+ }
+ if (ads_host != NULL)
+ break;
+ if (go_next == 0)
+ break;
+ }
+ }
+
+ if (site_service)
+ (void) free(site_service);
+ if (service)
+ (void) free(service);
+
+ if (ads_host == NULL) {
+ syslog(LOG_ERR, "smb_ads: No ADS host found from "
+ "configured nameservers");
+ return (NULL);
+ }
+
+ if (ads_ping(ads_host->ip_addr) != 0) {
+ ads_free_host_info();
+ ads_host = NULL;
+ if (find_ads_retry == 0) {
+ find_ads_retry = 1;
+ goto find_ads_host;
+ }
+ return (NULL);
+ }
+
+ ah = (ADS_HANDLE *)malloc(sizeof (ADS_HANDLE));
+ if (ah == NULL) {
+ return (NULL);
+ }
+ (void) memset(ah, 0, sizeof (ADS_HANDLE));
+
+ addr.s_addr = ads_host->ip_addr;
+ if ((ld = ldap_init((char *)inet_ntoa(addr), ads_host->port)) == NULL) {
+ syslog(LOG_ERR, "smb_ads: Could not open connection "
+ "to host: %s\n", ads_host->name);
+ ads_free_host_info();
+ ads_host = NULL;
+ free(ah);
+ if (find_ads_retry == 0) {
+ find_ads_retry = 1;
+ goto find_ads_host;
+ }
+ return (NULL);
+ }
+
+ if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version)
+ != LDAP_SUCCESS) {
+ syslog(LOG_ERR, "smb_ads: Could not set "
+ "LDAP_OPT_PROTOCOL_VERSION %d\n", version);
+ ads_free_host_info();
+ free(ah);
+ (void) ldap_unbind(ld);
+ return (NULL);
+ }
+
+ ah->ld = ld;
+ ah->user = strdup(adminUser);
+ ah->pwd = strdup(password);
+ ah->container = strdup(container);
+ ah->domain = strdup(domain);
+
+ if ((ah->user == NULL) || (ah->pwd == NULL) ||
+ (ah->container == NULL) || (ah->domain == NULL)) {
+ ads_close(ah);
+ return (NULL);
+ }
+
+ ah->domain_dn = ads_convert_domain(domain);
+ if (ah->domain_dn == NULL) {
+ ads_close(ah);
+ return (NULL);
+ }
+
+ ah->hostname = strdup(ads_host->name);
+ if (ah->hostname == NULL) {
+ ads_close(ah);
+ return (NULL);
+ }
+ if (site) {
+ ah->site = strdup(site);
+ if (ah->site == NULL) {
+ ads_close(ah);
+ return (NULL);
+ }
+ } else {
+ ah->site = NULL;
+ }
+
+ if (ads_bind(ah) == -1) {
+ ads_close(ah);
+ return (NULL);
+ }
+
+ return (ah);
+}
+
+/*
+ * ads_close
+ * Close connection to ADS server and free memory allocated for ADS handle.
+ * LDAP unbind is called here.
+ * Parameters:
+ * ah: handle to ADS server
+ * Returns:
+ * void
+ */
+void
+ads_close(ADS_HANDLE *ah)
+{
+ int len;
+
+ if (ah == NULL)
+ return;
+ /* close and free connection resources */
+ if (ah->ld)
+ (void) ldap_unbind(ah->ld);
+
+ free(ah->user);
+ if (ah->pwd) {
+ len = strlen(ah->pwd);
+ /* zero out the memory that contains user's password */
+ if (len > 0)
+ bzero(ah->pwd, len);
+ free(ah->pwd);
+ }
+ free(ah->container);
+ free(ah->domain);
+ free(ah->domain_dn);
+ free(ah->hostname);
+ free(ah->site);
+ free(ah);
+}
+
+/*
+ * ads_display_stat
+ * Display error message for GSS-API routines.
+ * Parameters:
+ * maj: GSS major status
+ * min: GSS minor status
+ * Returns:
+ * None
+ */
+static void
+ads_display_stat(OM_uint32 maj, OM_uint32 min)
+{
+ gss_buffer_desc msg;
+ OM_uint32 msg_ctx = 0;
+ OM_uint32 min2;
+ (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ syslog(LOG_ERR, "smb_ads: major status error: %s\n", (char *)msg.value);
+ (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ syslog(LOG_ERR, "smb_ads: minor status error: %s\n", (char *)msg.value);
+}
+
+/*
+ * free_attr
+ * Free memory allocated when publishing a share.
+ * Parameters:
+ * addattrs: an array of LDAPMod pointers
+ * Returns:
+ * None
+ */
+static void
+free_attr(LDAPMod *addattrs[])
+{
+ int i;
+ for (i = 0; addattrs[i]; i++) {
+ free(addattrs[i]);
+ }
+}
+
+/*
+ * ads_acquire_cred
+ * Called by ads_bind() to get a handle to administrative user's credential
+ * stored locally on the system. The credential is the TGT. If the attempt at
+ * getting handle fails then a second attempt will be made after getting a
+ * new TGT.
+ * Please look at ads_bind() for more information.
+ *
+ * Paramters:
+ * ah : handle to ADS server
+ * kinit_retry: if 0 then a second attempt will be made to get handle to the
+ * credential if the first attempt fails
+ * Returns:
+ * cred_handle: handle to the administrative user's credential (TGT)
+ * oid : contains Kerberos 5 object identifier
+ * kinit_retry: A 1 indicates that a second attempt has been made to get
+ * handle to the credential and no further attempts can be made
+ * -1 : error
+ * 0 : success
+ */
+static int
+ads_acquire_cred(ADS_HANDLE *ah, gss_cred_id_t *cred_handle, gss_OID *oid,
+ int *kinit_retry)
+{
+ return (krb5_acquire_cred_kinit(ah->user, ah->pwd, cred_handle, oid,
+ kinit_retry, "ads"));
+}
+
+/*
+ * ads_establish_sec_context
+ * Called by ads_bind() to establish a security context to an LDAP service on
+ * an ADS server. If the attempt at establishing the security context fails
+ * then a second attempt will be made by ads_bind() if a new TGT has not been
+ * already obtained in ads_acquire_cred. The second attempt, if allowed, will
+ * obtained a new TGT here and a new handle to the credential will also be
+ * obtained in ads_acquire_cred. LDAP SASL bind is used to send and receive
+ * the GSS tokens to and from the ADS server.
+ * Please look at ads_bind for more information.
+ * Paramters:
+ * ah : handle to ADS server
+ * cred_handle : handle to administrative user's credential (TGT)
+ * oid : Kerberos 5 object identifier
+ * kinit_retry : if 0 then a second attempt can be made to establish a
+ * security context with ADS server if first attempt fails
+ * Returns:
+ * gss_context : security context to ADS server
+ * sercred : encrypted ADS server's supported security layers
+ * do_acquire_cred: if 1 then a second attempt will be made to establish a
+ * security context with ADS server after getting a new
+ * handle to the user's credential
+ * kinit_retry : if 1 then a second attempt will be made to establish a
+ * a security context and no further attempts can be made
+ * -1 : error
+ * 0 : success
+ */
+static int
+ads_establish_sec_context(ADS_HANDLE *ah, gss_ctx_id_t *gss_context,
+ gss_cred_id_t cred_handle, gss_OID oid, struct berval **sercred,
+ int *kinit_retry, int *do_acquire_cred)
+{
+ OM_uint32 maj, min, time_rec;
+ char service_name[ADS_MAXBUFLEN], *user_dn;
+ gss_buffer_desc send_tok, service_buf;
+ gss_name_t target_name;
+ gss_buffer_desc input;
+ gss_buffer_desc *inputptr;
+ struct berval cred;
+ OM_uint32 ret_flags;
+ int stat, len;
+ int gss_flags;
+
+ /*
+ * 6 additional bytes for the "cn=,, " and the null terminator
+ */
+ len = strlen(ah->user) + strlen(ah->container) +
+ strlen(ah->domain_dn) + 6;
+
+ if ((user_dn = (char *)malloc(len)) == NULL)
+ return (-1);
+
+ (void) snprintf(user_dn, len, "cn=%s,%s,%s", ah->user, ah->container,
+ ah->domain_dn);
+
+ (void) snprintf(service_name, ADS_MAXBUFLEN, "ldap@%s", ah->hostname);
+ service_buf.value = service_name;
+ service_buf.length = strlen(service_name)+1;
+ if ((maj = gss_import_name(&min, &service_buf,
+ (gss_OID) gss_nt_service_name,
+ &target_name)) != GSS_S_COMPLETE) {
+ ads_display_stat(maj, min);
+ (void) gss_release_oid(&min, &oid);
+ free(user_dn);
+ return (-1);
+ }
+
+ *gss_context = GSS_C_NO_CONTEXT;
+ *sercred = NULL;
+ inputptr = GSS_C_NO_BUFFER;
+ gss_flags = GSS_C_MUTUAL_FLAG;
+ do {
+ if (krb5_establish_sec_ctx_kinit(ah->user, ah->pwd,
+ cred_handle, gss_context, target_name, oid,
+ gss_flags, inputptr, &send_tok,
+ &ret_flags, &time_rec, kinit_retry,
+ do_acquire_cred, &maj, "ads") == -1) {
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ free(user_dn);
+ return (-1);
+ }
+
+ cred.bv_val = send_tok.value;
+ cred.bv_len = send_tok.length;
+ if (*sercred) {
+ ber_bvfree(*sercred);
+ *sercred = NULL;
+ }
+ stat = ldap_sasl_bind_s(ah->ld, user_dn, "GSSAPI",
+ &cred, NULL, NULL, sercred);
+ if (stat != LDAP_SUCCESS &&
+ stat != LDAP_SASL_BIND_IN_PROGRESS) {
+ /* LINTED - E_SEC_PRINTF_VAR_FMT */
+ syslog(LOG_ERR, ldap_err2string(stat));
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ (void) gss_release_buffer(&min, &send_tok);
+ free(user_dn);
+ return (-1);
+ }
+ input.value = (*sercred)->bv_val;
+ input.length = (*sercred)->bv_len;
+ inputptr = &input;
+ if (send_tok.length > 0)
+ (void) gss_release_buffer(&min, &send_tok);
+ } while (maj != GSS_S_COMPLETE);
+
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ free(user_dn);
+
+ return (0);
+}
+
+/*
+ * ads_negotiate_sec_layer
+ * Call by ads_bind() to negotiate additional security layer for further
+ * communication after security context establishment. No additional security
+ * is needed so a "no security layer" is negotiated. The security layer is
+ * described in the SASL RFC 2478 and this step is needed for secure LDAP
+ * binding. LDAP SASL bind is used to send and receive the GSS tokens to and
+ * from the ADS server.
+ * Please look at ads_bind for more information.
+ *
+ * Paramters:
+ * ah : handle to ADS server
+ * gss_context: security context to ADS server
+ * sercred : encrypted ADS server's supported security layers
+ * Returns:
+ * -1 : error
+ * 0 : success
+ */
+static int
+ads_negotiate_sec_layer(ADS_HANDLE *ah, gss_ctx_id_t gss_context,
+ struct berval *sercred)
+{
+ OM_uint32 maj, min;
+ gss_buffer_desc unwrap_inbuf, unwrap_outbuf;
+ gss_buffer_desc wrap_inbuf, wrap_outbuf;
+ int conf_state, sec_layer;
+ char auth_id[5];
+ struct berval cred;
+ int stat;
+ gss_qop_t qt;
+
+ /* check for server supported security layer */
+ unwrap_inbuf.value = sercred->bv_val;
+ unwrap_inbuf.length = sercred->bv_len;
+ if ((maj = gss_unwrap(&min, gss_context,
+ &unwrap_inbuf, &unwrap_outbuf,
+ &conf_state, &qt)) != GSS_S_COMPLETE) {
+ ads_display_stat(maj, min);
+ if (sercred)
+ ber_bvfree(sercred);
+ return (-1);
+ }
+ sec_layer = *((char *)unwrap_outbuf.value);
+ (void) gss_release_buffer(&min, &unwrap_outbuf);
+ if (!(sec_layer & 1)) {
+ syslog(LOG_ERR, "smb_ads: ADS server does not support "
+ "no security layer!\n");
+ if (sercred) ber_bvfree(sercred);
+ return (-1);
+ }
+ if (sercred) ber_bvfree(sercred);
+
+ /* no security layer needed after successful binding */
+ auth_id[0] = 0x01;
+
+ /* byte 2-4: max client recv size in network byte order */
+ auth_id[1] = 0x00;
+ auth_id[2] = 0x40;
+ auth_id[3] = 0x00;
+ wrap_inbuf.value = auth_id;
+ wrap_inbuf.length = 4;
+ conf_state = 0;
+ if ((maj = gss_wrap(&min, gss_context, conf_state, 0, &wrap_inbuf,
+ &conf_state, &wrap_outbuf)) != GSS_S_COMPLETE) {
+ ads_display_stat(maj, min);
+ return (-1);
+ }
+
+ cred.bv_val = wrap_outbuf.value;
+ cred.bv_len = wrap_outbuf.length;
+ sercred = NULL;
+ stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", &cred, NULL, NULL,
+ &sercred);
+ if (stat != LDAP_SUCCESS && stat != LDAP_SASL_BIND_IN_PROGRESS) {
+ /* LINTED - E_SEC_PRINTF_VAR_FMT */
+ syslog(LOG_ERR, ldap_err2string(stat));
+ (void) gss_release_buffer(&min, &wrap_outbuf);
+ return (-1);
+ }
+
+ (void) gss_release_buffer(&min, &wrap_outbuf);
+ if (sercred)
+ ber_bvfree(sercred);
+
+ return (0);
+}
+
+/*
+ * ads_bind
+ * Use secure binding to bind to ADS server.
+ * Use GSS-API with Kerberos 5 as the security mechanism and LDAP SASL with
+ * Kerberos 5 as the security mechanisn to authenticate, obtain a security
+ * context, and securely bind an administrative user so that other LDAP
+ * commands can be used, i.e. add and delete.
+ *
+ * To obtain the security context, a Kerberos ticket-granting ticket (TGT)
+ * for the user is needed to obtain a ticket for the LDAP service. To get
+ * a TGT for the user, the username and password is needed. Once a TGT is
+ * obtained then it will be stored locally and used until it is expired.
+ * This routine will automatically obtained a TGT for the first time or when
+ * it expired. LDAP SASL bind is then finally used to send GSS tokens to
+ * obtain a security context for the LDAP service on the ADS server. If
+ * there is any problem getting the security context then a new TGT will be
+ * obtain to try getting the security context once more.
+ *
+ * After the security context is obtain and established, the LDAP SASL bind
+ * is used to negotiate an additional security layer. No further security is
+ * needed so a "no security layer" is negotiated. After this the security
+ * context can be deleted and further LDAP commands can be sent to the ADS
+ * server until a LDAP unbind command is issued to the ADS server.
+ * Paramaters:
+ * ah: handle to ADS server
+ * Returns:
+ * -1: error
+ * 0: success
+ */
+static int
+ads_bind(ADS_HANDLE *ah)
+{
+ OM_uint32 min;
+ gss_cred_id_t cred_handle;
+ gss_ctx_id_t gss_context;
+ OM_uint32 maj;
+ gss_OID oid;
+ struct berval *sercred;
+ int kinit_retry, do_acquire_cred;
+
+ kinit_retry = 0;
+ do_acquire_cred = 0;
+ acquire_cred:
+
+ if (ads_acquire_cred(ah, &cred_handle, &oid, &kinit_retry))
+ return (-1);
+
+ if (ads_establish_sec_context(ah, &gss_context, cred_handle,
+ oid, &sercred, &kinit_retry, &do_acquire_cred)) {
+ (void) gss_release_cred(&min, &cred_handle);
+ if (do_acquire_cred) {
+ do_acquire_cred = 0;
+ goto acquire_cred;
+ }
+ return (-1);
+ }
+
+ if (ads_negotiate_sec_layer(ah, gss_context, sercred)) {
+ (void) gss_release_cred(&min, &cred_handle);
+ (void) gss_delete_sec_context(&min, &gss_context, NULL);
+ return (-1);
+ }
+
+ if ((maj = gss_release_cred(&min, &cred_handle))
+ != GSS_S_COMPLETE) {
+ syslog(LOG_ERR, "smb_ads: Can't release credential handle\n");
+ ads_display_stat(maj, min);
+ (void) gss_delete_sec_context(&min, &gss_context, NULL);
+ return (-1);
+ }
+
+ if ((maj = gss_delete_sec_context(&min, &gss_context, NULL))
+ != GSS_S_COMPLETE) {
+ syslog(LOG_ERR, "smb_ads: Can't delete security context\n");
+ ads_display_stat(maj, min);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * ads_add_share
+ * Call by ads_publish_share to create share object in ADS.
+ * This routine specifies the attributes of an ADS LDAP share object. The first
+ * attribute and values define the type of ADS object, the share object. The
+ * second attribute and value define the UNC of the share data for the share
+ * object. The LDAP synchronous add command is used to add the object into ADS.
+ * The container location to add the object needs to specified.
+ * Parameters:
+ * ah : handle to ADS server
+ * adsShareName: name of share object to be created in ADS
+ * shareUNC : share name on NetForce
+ * adsContainer: location in ADS to create share object
+ *
+ * Returns:
+ * -1 : error
+ * 0 : success
+ */
+int
+ads_add_share(ADS_HANDLE *ah, const char *adsShareName,
+ const char *unc_name, const char *adsContainer)
+{
+ LDAPMod *addattrs[3];
+ char *tmp1[5], *tmp2[5];
+ int j = -1;
+ char *share_dn;
+ char buf[ADS_MAXMSGLEN];
+
+ int len, ret;
+
+ len = 5 + strlen(adsShareName) + strlen(adsContainer) +
+ strlen(ah->domain_dn) + 1;
+
+ share_dn = (char *)malloc(len);
+ if (share_dn == NULL)
+ return (-1);
+
+ (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName,
+ adsContainer, ah->domain_dn);
+
+ addattrs[++j] = (LDAPMod *)malloc(sizeof (LDAPMod));
+ addattrs[j]->mod_op = LDAP_MOD_ADD;
+ addattrs[j]->mod_type = "objectClass";
+ tmp1[0] = "top";
+ tmp1[1] = "leaf";
+ tmp1[2] = "connectionPoint";
+ tmp1[3] = "volume";
+ tmp1[4] = 0;
+ addattrs[j]->mod_values = tmp1;
+
+ addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
+ addattrs[j]->mod_op = LDAP_MOD_ADD;
+ addattrs[j]->mod_type = "uNCName";
+
+ tmp2[0] = (char *)unc_name;
+ tmp2[1] = 0;
+ addattrs[j]->mod_values = tmp2;
+
+ addattrs[++j] = 0;
+
+ if ((ret = ldap_add_s(ah->ld, share_dn, addattrs)) != LDAP_SUCCESS) {
+ (void) snprintf(buf, ADS_MAXMSGLEN,
+ "ads_add_share: %s:", share_dn);
+ /* LINTED - E_SEC_PRINTF_VAR_FMT */
+ syslog(LOG_ERR, ldap_err2string(ret));
+ free_attr(addattrs);
+ free(share_dn);
+ return (ret);
+ }
+ free(share_dn);
+ free_attr(addattrs);
+
+ (void) snprintf(buf, ADS_MAXMSGLEN,
+ "Share %s has been added to ADS container: %s.\n", adsShareName,
+ adsContainer);
+ syslog(LOG_DEBUG, "smb_ads: %s", buf);
+
+ return (0);
+}
+
+/*
+ * ads_del_share
+ * Call by ads_remove_share to remove share object from ADS. The container
+ * location to remove the object needs to specified. The LDAP synchronous
+ * delete command is used.
+ * Parameters:
+ * ah : handle to ADS server
+ * adsShareName: name of share object in ADS to be removed
+ * adsContainer: location of share object in ADS
+ * Returns:
+ * -1 : error
+ * 0 : success
+ */
+static int
+ads_del_share(ADS_HANDLE *ah, const char *adsShareName,
+ const char *adsContainer)
+{
+ char *share_dn, buf[ADS_MAXMSGLEN];
+ int len, ret;
+
+ len = 5 + strlen(adsShareName) + strlen(adsContainer) +
+ strlen(ah->domain_dn) + 1;
+
+ share_dn = (char *)malloc(len);
+ if (share_dn == NULL)
+ return (-1);
+
+ (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName,
+ adsContainer, ah->domain_dn);
+ if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) {
+ /* LINTED - E_SEC_PRINTF_VAR_FMT */
+ syslog(LOG_ERR, ldap_err2string(ret));
+ free(share_dn);
+ return (-1);
+ }
+ free(share_dn);
+
+ (void) snprintf(buf, ADS_MAXMSGLEN,
+ "Share %s has been removed from ADS container: %s.\n",
+ adsShareName, adsContainer);
+ syslog(LOG_DEBUG, "smb_ads: %s", buf);
+
+ return (0);
+}
+
+
+/*
+ * ads_escape_search_filter_chars
+ *
+ * This routine will escape the special characters found in a string
+ * that will later be passed to the ldap search filter.
+ *
+ * RFC 1960 - A String Representation of LDAP Search Filters
+ * 3. String Search Filter Definition
+ * If a value must contain one of the characters '*' OR '(' OR ')',
+ * these characters
+ * should be escaped by preceding them with the backslash '\' character.
+ *
+ * RFC 2252 - LDAP Attribute Syntax Definitions
+ * a backslash quoting mechanism is used to escape
+ * the following separator symbol character (such as "'", "$" or "#") if
+ * it should occur in that string.
+ */
+static int
+ads_escape_search_filter_chars(const char *src, char *dst)
+{
+ int avail = ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */
+
+ if (src == NULL || dst == NULL)
+ return (-1);
+
+ while (*src) {
+ if (!avail) {
+ *dst = 0;
+ return (-1);
+ }
+
+ switch (*src) {
+ case '\\':
+ case '\'':
+ case '$':
+ case '#':
+ case '*':
+ case '(':
+ case ')':
+ *dst++ = '\\';
+ avail--;
+ /* fall through */
+
+ default:
+ *dst++ = *src++;
+ avail--;
+ }
+ }
+
+ *dst = 0;
+
+ return (0);
+}
+
+/*
+ * ads_lookup_share
+ * The search filter is set to search for a specific share name in the
+ * specified ADS container. The LDSAP synchronous search command is used.
+ * Parameters:
+ * ah : handle to ADS server
+ * adsShareName: name of share object in ADS to be searched
+ * adsContainer: location of share object in ADS
+ * Returns:
+ * -1 : error
+ * 0 : not found
+ * 1 : found
+ */
+int
+ads_lookup_share(ADS_HANDLE *ah, const char *adsShareName,
+ const char *adsContainer, char *unc_name)
+{
+ char *attrs[4], filter[ADS_MAXBUFLEN];
+ char *share_dn;
+ int len, ret;
+ LDAPMessage *res;
+ char tmpbuf[ADS_MAXBUFLEN];
+
+ if (adsShareName == NULL || adsContainer == NULL)
+ return (-1);
+
+ len = 5 + strlen(adsShareName) + strlen(adsContainer) +
+ strlen(ah->domain_dn) + 1;
+
+ share_dn = (char *)malloc(len);
+ if (share_dn == NULL)
+ return (-1);
+
+ (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName,
+ adsContainer, ah->domain_dn);
+
+ res = NULL;
+ attrs[0] = "cn";
+ attrs[1] = "objectClass";
+ attrs[2] = "uNCName";
+ attrs[3] = NULL;
+
+ if (ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) {
+ free(share_dn);
+ return (-1);
+ }
+
+ (void) snprintf(filter, sizeof (filter),
+ "(&(objectClass=volume)(uNCName=%s))", tmpbuf);
+
+ if ((ret = ldap_search_s(ah->ld, share_dn,
+ LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) {
+ /* LINTED - E_SEC_PRINTF_VAR_FMT */
+ syslog(LOG_ERR, ldap_err2string(ret));
+ (void) ldap_msgfree(res);
+ free(share_dn);
+ return (0);
+ }
+
+ (void) free(share_dn);
+
+ /* no match is found */
+ if (ldap_count_entries(ah->ld, res) == 0) {
+ (void) ldap_msgfree(res);
+ return (0);
+ }
+
+ /* free the search results */
+ (void) ldap_msgfree(res);
+
+ return (1);
+}
+
+/*
+ * ads_convert_directory
+ * Convert relative share directory to UNC to be appended to hostname.
+ * i.e. cvol/a/b -> cvol\a\b
+ */
+char *
+ads_convert_directory(char *rel_dir)
+{
+ char *t, *s2;
+ int len;
+
+ if (rel_dir == NULL)
+ return (NULL);
+
+ len = strlen(rel_dir) + 1;
+ s2 = (char *)malloc(len);
+ if (s2 == NULL)
+ return (NULL);
+
+ t = s2;
+ while (*rel_dir) {
+ if (*rel_dir == '/') {
+ *t++ = '\\';
+ rel_dir++;
+ } else {
+ *t++ = *rel_dir++;
+ }
+ }
+ *t = '\0';
+ return (s2);
+}
+
+/*
+ * ads_publish_share
+ * Publish share into ADS. If a share name already exist in ADS in the same
+ * container then the existing share object is removed before adding the new
+ * share object.
+ * Parameters:
+ * ah : handle return from ads_open
+ * adsShareName: name of share to be added to ADS directory
+ * shareUNC : name of share on client, can be NULL to use the same name
+ * as adsShareName
+ * adsContainer: location for share to be added in ADS directory, ie
+ * ou=share_folder
+ * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR
+ * to use host ip addr for UNC.
+ * Returns:
+ * -1 : error
+ * 0 : success
+ */
+int
+ads_publish_share(ADS_HANDLE *ah, const char *adsShareName,
+ const char *shareUNC, const char *adsContainer, const char *hostname)
+{
+ int ret;
+ char unc_name[ADS_MAXBUFLEN];
+
+ if (adsShareName == NULL || adsContainer == NULL)
+ return (-1);
+
+ if (shareUNC == 0 || *shareUNC == 0)
+ shareUNC = adsShareName;
+
+ if (ads_build_unc_name(unc_name, sizeof (unc_name),
+ hostname, shareUNC) < 0) {
+ syslog(LOG_DEBUG, "smb_ads: Cannot publish share '%s' "
+ "[missing UNC name]", shareUNC);
+ return (-1);
+ }
+
+ ret = ads_lookup_share(ah, adsShareName, adsContainer, unc_name);
+
+ switch (ret) {
+ case 1:
+ (void) ads_del_share(ah, adsShareName, adsContainer);
+ ret = ads_add_share(ah, adsShareName, unc_name, adsContainer);
+ break;
+
+ case 0:
+ ret = ads_add_share(ah, adsShareName, unc_name, adsContainer);
+ if (ret == LDAP_ALREADY_EXISTS) {
+ syslog(LOG_DEBUG, "smb_ads: Cannot publish share '%s' "
+ "[name is already in use]", adsShareName);
+ ret = -1;
+ }
+ break;
+
+ case -1:
+ default:
+ /* return with error code */
+ ret = -1;
+ }
+
+ return (ret);
+}
+
+/*
+ * ads_remove_share
+ * Remove share from ADS. A search is done first before explicitly removing
+ * the share.
+ * Parameters:
+ * ah : handle return from ads_open
+ * adsShareName: name of share to be removed from ADS directory
+ * adsContainer: location for share to be removed from ADS directory, ie
+ * ou=share_folder
+ * Returns:
+ * -1 : error
+ * 0 : success
+ */
+int
+ads_remove_share(ADS_HANDLE *ah, const char *adsShareName, const char *shareUNC,
+ const char *adsContainer, const char *hostname)
+{
+ int ret;
+ char unc_name[ADS_MAXBUFLEN];
+
+ if (adsShareName == NULL || adsContainer == NULL)
+ return (-1);
+ if (shareUNC == 0 || *shareUNC == 0)
+ shareUNC = adsShareName;
+
+ if (ads_build_unc_name(unc_name, sizeof (unc_name),
+ hostname, shareUNC) < 0) {
+ syslog(LOG_DEBUG, "smb_ads: Unable to remove share '%s' from "
+ "ADS [missing UNC name]", shareUNC);
+ return (-1);
+ }
+
+ ret = ads_lookup_share(ah, adsShareName, adsContainer, unc_name);
+ if (ret == 0)
+ return (0);
+ if (ret == -1)
+ return (-1);
+
+ return (ads_del_share(ah, adsShareName, adsContainer));
+}
+
+/*
+ * ads_get_computer_dn
+ *
+ * Build the distinguish name for this system.
+ */
+static void
+ads_get_computer_dn(ADS_HANDLE *ah, char *buf, size_t buflen)
+{
+ char hostname[MAXHOSTNAMELEN];
+
+ (void) smb_gethostname(hostname, MAXHOSTNAMELEN, 0);
+ (void) snprintf(buf, buflen, "cn=%s,cn=%s,%s",
+ hostname, ADS_COMPUTERS_CN, ah->domain_dn);
+}
+
+static char *
+ads_get_host_principal(char *fqhost)
+{
+ int len;
+ char *princ;
+
+ if (!fqhost)
+ return (NULL);
+
+ len = strlen(ADS_HOST_PREFIX) + strlen(fqhost) + 1;
+ princ = (char *)malloc(len);
+
+ if (!princ) {
+ syslog(LOG_ERR, "ads_get_host_principal: resource shortage");
+ return (NULL);
+ }
+ (void) snprintf(princ, len, "%s%s", ADS_HOST_PREFIX,
+ fqhost);
+
+ return (princ);
+}
+
+static char *
+ads_get_host_principal_w_realm(char *princ, char *domain)
+{
+ int len;
+ char *realm;
+ char *princ_r;
+
+ if (!princ || !domain)
+ return (NULL);
+
+ realm = strdup(domain);
+ if (!realm)
+ return (NULL);
+
+ (void) utf8_strupr(realm);
+
+ len = strlen(princ) + 1 + strlen(realm) + 1;
+ princ_r = (char *)malloc(len);
+ if (!princ_r) {
+ syslog(LOG_ERR, "ads_get_host_principal_w_realm: resource"
+ " shortage");
+ free(realm);
+ return (NULL);
+ }
+
+ (void) snprintf(princ_r, len, "%s@%s", princ, realm);
+ free(realm);
+
+ return (princ_r);
+}
+
+/*
+ * ads_get_host_principals
+ *
+ * If fqhost is NULL, this function will attempt to obtain fully qualified
+ * hostname prior to generating the host principals.
+ */
+static int
+ads_get_host_principals(char *fqhost, char *domain, char **princ,
+ char **princ_r)
+{
+ char hostname[MAXHOSTNAMELEN];
+
+ *princ = *princ_r = NULL;
+
+ if (fqhost) {
+ (void) strlcpy(hostname, fqhost, MAXHOSTNAMELEN);
+ } else {
+ if (smb_getfqhostname(hostname, MAXHOSTNAMELEN) != 0)
+ return (-1);
+ }
+
+ if ((*princ = ads_get_host_principal(hostname)) == NULL) {
+ return (-1);
+ }
+
+ *princ_r = ads_get_host_principal_w_realm(*princ, domain);
+ if (*princ_r == NULL) {
+ free(*princ);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * ads_add_computer
+ *
+ * Returns 0 upon success. Otherwise, returns -1.
+ */
+static int
+ads_add_computer(ADS_HANDLE *ah)
+{
+ LDAPMod *addattrs[7];
+ char *oc_vals[6], *sam_val[2], *usr_val[2];
+ char *svc_val[2], *ctl_val[2], *fqh_val[2];
+ int j = -1;
+ int ret, usrctl_flags = 0;
+ char sam_acct[MAXHOSTNAMELEN + 1];
+ char fqhost[MAXHOSTNAMELEN];
+ char dn[ADS_DN_MAX];
+ char *user_principal, *svc_principal;
+ char usrctl_buf[16];
+
+ if (smb_getfqhostname(fqhost, MAXHOSTNAMELEN) != 0)
+ return (-1);
+
+ if (smb_gethostname(sam_acct, MAXHOSTNAMELEN, 0) != 0)
+ return (-1);
+
+ (void) strlcat(sam_acct, "$", MAXHOSTNAMELEN + 1);
+
+ if (ads_get_host_principals(fqhost, ah->domain, &svc_principal,
+ &user_principal) == -1) {
+ syslog(LOG_ERR,
+ "ads_add_computer: unable to get host principal");
+ return (-1);
+ }
+
+ ads_get_computer_dn(ah, dn, ADS_DN_MAX);
+
+ addattrs[++j] = (LDAPMod *)malloc(sizeof (LDAPMod));
+ addattrs[j]->mod_op = LDAP_MOD_ADD;
+ addattrs[j]->mod_type = "objectClass";
+ oc_vals[0] = "top";
+ oc_vals[1] = "person";
+ oc_vals[2] = "organizationalPerson";
+ oc_vals[3] = "user";
+ oc_vals[4] = "computer";
+ oc_vals[5] = 0;
+ addattrs[j]->mod_values = oc_vals;
+
+ addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
+ addattrs[j]->mod_op = LDAP_MOD_ADD;
+ addattrs[j]->mod_type = "sAMAccountName";
+
+ sam_val[0] = sam_acct;
+ sam_val[1] = 0;
+ addattrs[j]->mod_values = sam_val;
+
+
+ addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
+ addattrs[j]->mod_op = LDAP_MOD_ADD;
+ addattrs[j]->mod_type = "userPrincipalName";
+
+ usr_val[0] = user_principal;
+ usr_val[1] = 0;
+ addattrs[j]->mod_values = usr_val;
+
+ addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
+ addattrs[j]->mod_op = LDAP_MOD_ADD;
+ addattrs[j]->mod_type = "servicePrincipalName";
+
+ svc_val[0] = svc_principal;
+ svc_val[1] = 0;
+ addattrs[j]->mod_values = svc_val;
+
+ addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
+ addattrs[j]->mod_op = LDAP_MOD_ADD;
+ addattrs[j]->mod_type = "userAccountControl";
+
+ usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
+ ADS_USER_ACCT_CTL_PASSWD_NOTREQD |
+ ADS_USER_ACCT_CTL_ACCOUNTDISABLE);
+
+ (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags);
+ ctl_val[0] = usrctl_buf;
+ ctl_val[1] = 0;
+ addattrs[j]->mod_values = ctl_val;
+ addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
+ addattrs[j]->mod_op = LDAP_MOD_ADD;
+ addattrs[j]->mod_type = "dNSHostName";
+
+ fqh_val[0] = fqhost;
+ fqh_val[1] = 0;
+ addattrs[j]->mod_values = fqh_val;
+ addattrs[++j] = 0;
+
+ if ((ret = ldap_add_s(ah->ld, dn, addattrs)) != LDAP_SUCCESS) {
+ syslog(LOG_ERR, "ads_add_computer: %s", ldap_err2string(ret));
+ ret = -1;
+ }
+
+ free_attr(addattrs);
+ free(user_principal);
+ free(svc_principal);
+
+ return (ret);
+}
+
+/*
+ * Delete an ADS computer account.
+ */
+static void
+ads_del_computer(ADS_HANDLE *ah)
+{
+ char dn[ADS_DN_MAX];
+ int rc;
+
+ ads_get_computer_dn(ah, dn, ADS_DN_MAX);
+
+ if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) {
+ syslog(LOG_DEBUG, "ads_del_computer: %s",
+ ldap_err2string(rc));
+ }
+}
+
+/*
+ * ads_lookup_computer_n_attr
+ *
+ * Lookup the value of the specified attribute on the computer
+ * object. If the specified attribute can be found, its value is returned
+ * via 'val' parameter.
+ *
+ * 'attr' parameter can be set to NULL if you only attempt to
+ * see whether the computer object exists on AD or not.
+ *
+ * Return:
+ * 1 if both the computer and the specified attribute is found.
+ * 0 if either the computer or the specified attribute is not found.
+ * -1 on error.
+ */
+static int
+ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val)
+{
+ char *attrs[2], filter[ADS_MAXBUFLEN];
+ LDAPMessage *res, *entry;
+ char **vals;
+ char tmpbuf[ADS_MAXBUFLEN];
+ char my_hostname[MAXHOSTNAMELEN], sam_acct[MAXHOSTNAMELEN + 1];
+ char dn[ADS_DN_MAX];
+
+ if (smb_gethostname(my_hostname, MAXHOSTNAMELEN, 0) != 0)
+ return (-1);
+
+ (void) snprintf(sam_acct, sizeof (sam_acct), "%s$", my_hostname);
+ ads_get_computer_dn(ah, dn, ADS_DN_MAX);
+
+ res = NULL;
+ attrs[0] = attr;
+ attrs[1] = NULL;
+
+ if (ads_escape_search_filter_chars(sam_acct, tmpbuf) != 0) {
+ return (-1);
+ }
+
+ (void) snprintf(filter, sizeof (filter),
+ "(&(objectClass=computer)(sAMAccountName=%s))",
+ tmpbuf);
+
+ if (ldap_search_s(ah->ld, dn, LDAP_SCOPE_BASE, filter, attrs, 0,
+ &res) != LDAP_SUCCESS) {
+ (void) ldap_msgfree(res);
+ return (0);
+ }
+
+ if (attr) {
+ /* no match for the specified attribute is found */
+ if (ldap_count_entries(ah->ld, res) == 0) {
+ if (val)
+ *val = NULL;
+
+ (void) ldap_msgfree(res);
+ return (0);
+ }
+
+ entry = ldap_first_entry(ah->ld, res);
+ if (entry) {
+ vals = ldap_get_values(ah->ld, entry, attr);
+ if (!vals && val) {
+ *val = NULL;
+ (void) ldap_msgfree(res);
+ return (0);
+ }
+
+ if (vals[0] != NULL && val)
+ *val = strdup(vals[0]);
+ }
+ }
+
+ /* free the search results */
+ (void) ldap_msgfree(res);
+ return (1);
+}
+
+/*
+ * ads_find_computer
+ *
+ * Return:
+ * 1 if found.
+ * 0 if not found or encounters error.
+ */
+static int
+ads_find_computer(ADS_HANDLE *ah)
+{
+ return (ads_lookup_computer_n_attr(ah, NULL, NULL) == 1);
+}
+
+/*
+ * ads_modify_computer
+ *
+ * Modify the user account control attribute of an existing computer
+ * object on AD.
+ *
+ * Returns 0 on success. Otherwise, returns -1.
+ */
+static int
+ads_modify_computer(ADS_HANDLE *ah, int des_only)
+{
+ LDAPMod *attrs[6];
+ char *ctl_val[2];
+ int j = -1;
+ int ret, usrctl_flags = 0;
+ char dn[ADS_DN_MAX];
+ char usrctl_buf[16];
+
+ ads_get_computer_dn(ah, dn, ADS_DN_MAX);
+
+ attrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod));
+ attrs[j]->mod_op = LDAP_MOD_REPLACE;
+ attrs[j]->mod_type = "userAccountControl";
+
+ usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
+ ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION |
+ ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD);
+
+ if (des_only)
+ usrctl_flags |= ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY;
+
+ (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags);
+ ctl_val[0] = usrctl_buf;
+ ctl_val[1] = 0;
+ attrs[j]->mod_values = ctl_val;
+
+ attrs[++j] = 0;
+
+ if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) {
+ syslog(LOG_ERR, "ads_modify_computer: %s",
+ ldap_err2string(ret));
+ ret = -1;
+ }
+
+ free_attr(attrs);
+ return (ret);
+}
+
+/*
+ * ads_lookup_computer_attr_kvno
+ *
+ * Lookup the value of the Kerberos version number attribute of the computer
+ * account.
+ */
+static krb5_kvno
+ads_lookup_computer_attr_kvno(ADS_HANDLE *ah)
+{
+ char *val = NULL;
+ int kvno = 1;
+
+ if (ads_lookup_computer_n_attr(ah, "ms-DS-KeyVersionNumber",
+ &val) == 1) {
+ if (val) {
+ kvno = atoi(val);
+ free(val);
+ }
+ }
+
+ return (kvno);
+}
+
+/*
+ * ads_gen_machine_passwd
+ *
+ * Returned a null-terminated machine password generated randomly
+ * from [0-9a-zA-Z] character set. In order to pass the password
+ * quality check (three character classes), an uppercase letter is
+ * used as the first character of the machine password.
+ */
+static int
+ads_gen_machine_passwd(char *machine_passwd, int bufsz)
+{
+ char *data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK"
+ "LMNOPQRSTUVWXYZ";
+ int datalen = strlen(data);
+ int i, data_idx;
+
+ if (!machine_passwd || bufsz == 0)
+ return (-1);
+
+ /*
+ * The decimal value of upper case 'A' is 65. Randomly pick
+ * an upper-case letter from the ascii table.
+ */
+ machine_passwd[0] = (random() % 26) + 65;
+ for (i = 1; i < bufsz - 1; i++) {
+ data_idx = random() % datalen;
+ machine_passwd[i] = data[data_idx];
+ }
+
+ machine_passwd[bufsz - 1] = 0;
+ return (0);
+}
+
+/*
+ * adjoin
+ *
+ * Besides the NT-4 style domain join (using MS-RPC), CIFS server also
+ * provides the domain join using Kerberos Authentication, Keberos
+ * Change & Set password, and LDAP protocols. Basically, adjoin
+ * operation would require the following tickets to be acquired for the
+ * the user account that is provided for the domain join.
+ *
+ * 1) a Keberos TGT ticket,
+ * 2) a ldap service ticket, and
+ * 3) kadmin/changpw service ticket
+ *
+ * The ADS client first sends a ldap search request to find out whether
+ * or not the workstation trust account already exists in the Active Directory.
+ * The existing computer object for this workstation will be removed and
+ * a new one will be added. The machine account password is randomly
+ * generated and set for the newly created computer object using KPASSD
+ * protocol (See RFC 3244). Once the password is set, our ADS client
+ * finalizes the machine account by modifying the user acount control
+ * attribute of the computer object. Kerberos keys derived from the machine
+ * account password will be stored locally in /etc/krb5/krb5.keytab file.
+ * That would be needed while acquiring Kerberos TGT ticket for the host
+ * principal after the domain join operation.
+ */
+adjoin_status_t
+adjoin(char *machine_passwd, int len)
+{
+ ADS_HANDLE *ah = NULL;
+ krb5_enctype enctypes[10];
+ krb5_context ctx = NULL;
+ krb5_principal krb5princ;
+ krb5_kvno kvno;
+ char *princ, *princ_r;
+ int des_only, delete = 1, fini_krbctx = 1;
+ adjoin_status_t rc = ADJOIN_SUCCESS;
+ struct stat fstat;
+
+ char *idmap_ccache = "/var/run/idmap/ccache";
+
+ if ((ah = ads_open()) == NULL)
+ return (ADJOIN_ERR_GET_HANDLE);
+
+ if (ads_gen_machine_passwd(machine_passwd, len) != 0) {
+ ads_close(ah);
+ return (ADJOIN_ERR_GEN_PASSWD);
+ }
+
+ if (ads_find_computer(ah))
+ ads_del_computer(ah);
+
+ if (ads_add_computer(ah) != 0) {
+ ads_close(ah);
+ return (ADJOIN_ERR_ADD_TRUST_ACCT);
+ }
+
+ /*
+ * Call library functions that can be used to get
+ * the list of encryption algorithms available on the system.
+ * (similar to what 'encrypt -l' CLI does). For now,
+ * unless someone has modified the configuration of the
+ * cryptographic framework (very unlikely), the following is the
+ * list of algorithms available on any system running Nevada
+ * by default.
+ */
+ enctypes[0] = ENCTYPE_DES_CBC_CRC;
+ enctypes[1] = ENCTYPE_DES_CBC_MD5;
+ enctypes[2] = ENCTYPE_ARCFOUR_HMAC;
+ enctypes[3] = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
+
+ des_only = 0;
+
+ /*
+ * If we are talking to a Longhorn server, we need to set up
+ * the msDS-SupportedEncryptionTypes attribute of the computer
+ * object accordingly
+ *
+ * The code to modify the msDS-SupportedEncryptionTypes can be
+ * added once we figure out why the Longhorn server rejects the
+ * SmbSessionSetup request sent by SMB redirector.
+ */
+
+ if (ads_get_host_principals(NULL, ah->domain, &princ, &princ_r) == -1) {
+ ads_del_computer(ah);
+ ads_close(ah);
+ return (ADJOIN_ERR_GET_HOST_PRINC);
+ }
+
+ if (smb_krb5_ctx_init(&ctx) != 0) {
+ fini_krbctx = 0;
+ rc = ADJOIN_ERR_INIT_KRB_CTX;
+ goto adjoin_cleanup;
+ }
+
+ if (smb_krb5_get_principal(ctx, princ_r, &krb5princ) != 0) {
+ rc = ADJOIN_ERR_GET_KRB_PRINC;
+ goto adjoin_cleanup;
+ }
+
+ if (smb_krb5_setpwd(ctx, krb5princ, machine_passwd) != 0) {
+ rc = ADJOIN_ERR_KSETPWD;
+ goto adjoin_cleanup;
+ }
+
+ kvno = ads_lookup_computer_attr_kvno(ah);
+ if (ads_modify_computer(ah, des_only) != 0) {
+ rc = ADJOIN_ERR_MOD_TRUST_ACCT;
+ goto adjoin_cleanup;
+ }
+ if (smb_krb5_write_keytab(ctx, krb5princ, "/etc/krb5/krb5.keytab", kvno,
+ machine_passwd, enctypes, 4) != 0) {
+ rc = ADJOIN_ERR_WRITE_KEYTAB;
+ goto adjoin_cleanup;
+ }
+
+ /* Set IDMAP config */
+ if (smb_config_set_idmap_domain(ah->domain) != 0) {
+ rc = ADJOIN_ERR_IDMAP_SET_DOMAIN;
+ goto adjoin_cleanup;
+ }
+
+ if (smb_config_set_idmap_gc(ah->hostname) != 0) {
+ rc = ADJOIN_ERR_IDMAP_SET_GC;
+ goto adjoin_cleanup;
+ }
+
+ /* Refresh IDMAP service */
+ if (smb_config_refresh_idmap() != 0) {
+ rc = ADJOIN_ERR_IDMAP_REFRESH;
+ goto adjoin_cleanup;
+ }
+
+ /* Remove the idmap ccache */
+ if (stat(idmap_ccache, &fstat) == 0) {
+ if (remove(idmap_ccache) != 0) {
+ rc = ADJOIN_ERR_IDMAP_CCACHE;
+ goto adjoin_cleanup;
+ }
+ }
+
+ delete = 0;
+adjoin_cleanup:
+ if (delete)
+ ads_del_computer(ah);
+
+ if (fini_krbctx)
+ smb_krb5_ctx_fini(ctx);
+
+ ads_close(ah);
+ free(princ);
+ free(princ_r);
+
+ return (rc);
+}
+
+/*
+ * adjoin_report_err
+ *
+ * Display error message for the specific adjoin error code.
+ */
+char *
+adjoin_report_err(adjoin_status_t status)
+{
+ if (status < 0 || status >= ADJOIN_NUM_STATUS)
+ return ("ADJOIN: unknown status");
+
+ return (adjoin_errmsg[status]);
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.h
new file mode 100644
index 0000000000..af52f0ebfc
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.h
@@ -0,0 +1,87 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMBSRV_ADS_H
+#define _SMBSRV_ADS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <smbsrv/libsmbns.h>
+#include <smbsrv/string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * UserAccountControl flags: manipulate user account properties.
+ *
+ * The hexadecimal value of the following property flags are based on MSDN
+ * article # 305144.
+ */
+#define ADS_USER_ACCT_CTL_SCRIPT 0x00000001
+#define ADS_USER_ACCT_CTL_ACCOUNTDISABLE 0x00000002
+#define ADS_USER_ACCT_CTL_HOMEDIR_REQUIRED 0x00000008
+#define ADS_USER_ACCT_CTL_LOCKOUT 0x00000010
+#define ADS_USER_ACCT_CTL_PASSWD_NOTREQD 0x00000020
+#define ADS_USER_ACCT_CTL_PASSWD_CANT_CHANGE 0x00000040
+#define ADS_USER_ACCT_CTL_ENCRYPTED_TEXT_PWD_ALLOWED 0x00000080
+#define ADS_USER_ACCT_CTL_TMP_DUP_ACCT 0x00000100
+#define ADS_USER_ACCT_CTL_NORMAL_ACCT 0x00000200
+#define ADS_USER_ACCT_CTL_INTERDOMAIN_TRUST_ACCT 0x00000800
+#define ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT 0x00001000
+#define ADS_USER_ACCT_CTL_SRV_TRUST_ACCT 0x00002000
+#define ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD 0x00010000
+#define ADS_USER_ACCT_CTL_MNS_LOGON_ACCT 0x00020000
+#define ADS_USER_ACCT_CTL_SMARTCARD_REQUIRED 0x00040000
+#define ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION 0x00080000
+#define ADS_USER_ACCT_CTL_NOT_DELEGATED 0x00100000
+#define ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY 0x00200000
+#define ADS_USER_ACCT_CTL_DONT_REQ_PREAUTH 0x00400000
+#define ADS_USER_ACCT_CTL_PASSWD_EXPIRED 0x00800000
+#define ADS_USER_ACCT_CTL_TRUSTED_TO_AUTH_FOR_DELEGATION 0x01000000
+
+typedef struct ads_host_info_s {
+ char *name; /* fully qualified hostname */
+ int port; /* ldap port */
+ in_addr_t ip_addr; /* network byte order */
+} ADS_HOST_INFO;
+
+#define UNC_HOSTADDR 0 /* use ip addr in UNC */
+#define UNC_HOSTNAME 1 /* use hostname in UNC */
+#define ADS_PATH_SCRN_LEN 60
+
+ADS_HOST_INFO *ads_find_host(char *, char *, int *, char *, int *);
+char *ads_convert_directory(char *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMBSRV_ADS_H */
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.c
new file mode 100644
index 0000000000..853d1ff814
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.c
@@ -0,0 +1,1410 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <synch.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <smbsrv/libsmbns.h>
+
+#include <smbsrv/cifs.h>
+#include <smbsrv/mailslot.h>
+
+#include <smbns_browser.h>
+#include <smbns_netbios.h>
+
+#define SMB_SERVER_SIGNATURE 0xaa550415
+
+/*
+ * Macro definitions:
+ */
+static char *lanman = MAILSLOT_LANMAN;
+static char *browse = MAILSLOT_BROWSE;
+
+typedef struct server_info {
+ uint32_t type;
+ uint32_t signature;
+ char major;
+ char minor;
+ char hostname[NETBIOS_NAME_SZ];
+ char comment[SMB_PI_MAX_COMMENT];
+ char update_count;
+ struct name_entry name;
+} server_info_t;
+
+#define BROWSER_NF_INVALID 0x00
+#define BROWSER_NF_VALID 0x01
+
+typedef struct browser_netinfo {
+ uint32_t flags;
+ int next_announce;
+ int reps;
+ int interval;
+ server_info_t server;
+ mutex_t mtx;
+} browser_netinfo_t;
+
+/*
+ * Local Data Definitions:
+ */
+static struct browser_netinfo smb_browser_info[SMB_PI_MAX_NETWORKS];
+
+static void smb_browser_init(void);
+
+static inline browser_netinfo_t *
+smb_browser_getnet(int net)
+{
+ browser_netinfo_t *subnet;
+
+ if (net < smb_nic_get_num()) {
+ subnet = &smb_browser_info[net];
+ (void) mutex_lock(&subnet->mtx);
+ if (subnet->flags & BROWSER_NF_VALID)
+ return (subnet);
+ }
+
+ return (0);
+}
+
+static inline void
+smb_browser_putnet(browser_netinfo_t *netinfo)
+{
+ if (netinfo)
+ (void) mutex_unlock(&netinfo->mtx);
+}
+
+/*
+ * 3. Browser Overview
+ *
+ * Hosts involved in the browsing process can be separated into two
+ * distinct groups, browser clients and browser servers (often referred to
+ * simply as "browsers").
+ *
+ * A browser is a server which maintains information about servers -
+ * primarily the domain they are in and the services that they are running
+ * -- and about domains. Browsers may assume several different roles in
+ * their lifetimes, and dynamically switch between them.
+ *
+ * Browser clients are of two types: workstations and (non-browser)
+ * servers. In the context of browsing, workstations query browsers for the
+ * information they contain; servers supply browsers the information by
+ * registering with them. Note that, at times, browsers may themselves
+ * behave as browser clients and query other browsers.
+ *
+ * For the purposes of this specification, a domain is simply a name with
+ * which to associate a group of resources such as computers, servers and
+ * users. Domains allow a convenient means for browser clients to restrict
+ * the scope of a search when they query browser servers. Every domain has
+ * a "master" server called the Primary Domain Controller (PDC) that
+ * manages various activities within the domain.
+ *
+ * One browser for each domain on a subnet is designated the Local Master
+ * Browser for that domain. Servers in its domain on the subnet register
+ * with it, as do the Local Master Browsers for other domains on the
+ * subnet. It uses these registrations to maintain authoritative
+ * information about its domain on its subnet. If there are other subnets
+ * in the network, it also knows the name of the server running the
+ * domain's Domain Master Browser; it registers with it, and uses it to
+ * obtain information about the rest of the network (see below).
+ *
+ * Clients on a subnet query browsers designated as the Backup Browsers for
+ * the subnet (not the Master Browser). Backup Browsers maintain a copy of
+ * the information on the Local Master Browser; they get it by periodically
+ * querying the Local Master Browser for all of its information. Clients
+ * find the Backup Browsers by asking the Local Master Browser. Clients are
+ * expected to spread their queries evenly across Backup Browsers to
+ * balance the load.
+ *
+ * The Local Master Browser is dynamically elected automatically. Multiple
+ * Backup Browser Servers may exist per subnet; they are selected from
+ * among the potential browser servers by the Local Master Browser, which
+ * is configured to select enough to handle the expected query load.
+ *
+ * When there are multiple subnets, a Domain Master Browser is assigned
+ * the task of keeping the multiple subnets in synchronization. The Primary
+ * Domain Controller (PDC) always acts as the Domain Master Browser. The
+ * Domain Master Browser periodically acts as a client and queries all the
+ * Local Master Browsers for its domain, asking them for a list containing
+ * all the domains and all the servers in their domain known within their
+ * subnets; it merges all the replies into a single master list. This
+ * allows a Domain Master Browser server to act as a collection point for
+ * inter-subnet browsing information. Local Master Browsers periodically
+ * query the Domain Master Browser to retrieve the network-wide information
+ * it maintains.
+ *
+ * When a domain spans only a single subnet, there will not be any distinct
+ * Local Master Browser; this role will be handled by the Domain Master
+ * Browser. Similarly, the Domain Master Browser is always the Local Master
+ * Browser for the subnet it is on.
+ *
+ * When a browser client suspects that the Local Master Browser has failed,
+ * the client will instigate an election in which the browser servers
+ * participate, and some browser servers may change roles.
+ *
+ * Some characteristics of a good browsing mechanism include:
+ * . minimal network traffic
+ * . minimum server discovery time
+ * . minimum change discovery latency
+ * . immunity to machine failures
+ *
+ * Historically, Browser implementations had been very closely tied to
+ * NETBIOS and datagrams. The early implementations caused a lot of
+ * broadcast traffic. See Appendix D for an overview that presents how the
+ * Browser specification evolved.
+ *
+ * 4. Browsing Protocol Architecture
+ *
+ * This section first describes the how the browsing protocol is layered,
+ * then describes the roles of clients, servers, and browsers in the
+ * browsing subsystem.
+ *
+ * 4.1 Layering of Browsing Protocol Requests
+ *
+ * Most of the browser functionality is implemented using mailslots.
+ * Mailslots provide a mechanism for fast, unreliable unidirectional data
+ * transfer; they are named via ASCII "mailslot (path) name". Mailslots are
+ * implemented using the CIFS Transact SMB which is encapsulated in a
+ * NETBIOS datagram. Browser protocol requests are sent to browser specific
+ * mailslots using some browser-specific NETBIOS names. These datagrams can
+ * either be unicast or broadcast, depending on whether the NETBIOS name is
+ * a "unique name" or a "group name". Various data structures, which are
+ * detailed subsequently within this document, flow as the data portion of
+ * the Transact SMB.
+ *
+ * Here is an example of a generic browser SMB, showing how a browser
+ * request is encapsulated in a TRANSACT SMB request. Note that the PID,
+ * TID, MID, UID, and Flags are all 0 in mailslot requests.
+ *
+ * SMB: C transact, File = \MAILSLOT\BROWSE
+ * SMB: SMB Status = Error Success
+ * SMB: Error class = No Error
+ * SMB: Error code = No Error
+ * SMB: Header: PID = 0x0000 TID = 0x0000 MID = 0x0000 UID = 0x0000
+ * SMB: Tree ID (TID) = 0 (0x0)
+ * SMB: Process ID (PID) = 0 (0x0)
+ * SMB: User ID (UID) = 0 (0x0)
+ * SMB: Multiplex ID (MID) = 0 (0x0)
+ * SMB: Flags Summary = 0 (0x0)
+ * SMB: Command = C transact
+ * SMB: Word count = 17
+ * SMB: Word parameters
+ * SMB: Total parm bytes = 0
+ * SMB: Total data bytes = 33
+ * SMB: Max parm bytes = 0
+ * SMB: Max data bytes = 0
+ * SMB: Max setup words = 0
+ * SMB: Transact Flags Summary = 0 (0x0)
+ * SMB: ...............0 = Leave session intact
+ * SMB: ..............0. = Response required
+ * SMB: Transact timeout = 0 (0x0)
+ * SMB: Parameter bytes = 0 (0x0)
+ * SMB: Parameter offset = 0 (0x0)
+ * SMB: Data bytes = 33 (0x21)
+ * SMB: Data offset = 86 (0x56)
+ * SMB: Setup word count = 3
+ * SMB: Setup words
+ * SMB: Mailslot opcode = Write mailslot
+ * SMB: Transaction priority = 1
+ * SMB: Mailslot class = Unreliable (broadcast)
+ * SMB: Byte count = 50
+ * SMB: Byte parameters
+ * SMB: Path name = \MAILSLOT\BROWSE
+ * SMB: Transaction data
+ * SMB: Data: Number of data bytes remaining = 33 (0x0021)
+ *
+ * Note the SMB command is Transact, the opcode within the Transact SMB is
+ * Mailslot Write, and the browser data structure is carried as the
+ * Transact data.
+ * The Transaction data begins with an opcode, that signifies the operation
+ * and determines the size and structure of data that follows. This opcode
+ * is named as per one of the below:
+ *
+ * HostAnnouncement 1
+ * AnnouncementRequest 2
+ * RequestElection 8
+ * GetBackupListReq 9
+ * GetBackupListResp 10
+ * BecomeBackup 11
+ * DomainAnnouncment 12
+ * MasterAnnouncement 13
+ * LocalMasterAnnouncement 15
+ *
+ * Browser datagrams are often referred to as simply browser frames. The
+ * frames are in particular, referred to by the name of the opcode within
+ * the Transaction data e.g. a GetBackupListReq browser frame, a
+ * RequestElection browser frame, etc.
+ *
+ * The structures that are sent as the data portion of the Transact SMB are
+ * described in section(s) 6.2 through 6.12 in this document. These
+ * structures are tightly packed, i.e. there are no intervening pad bytes
+ * in the structure, unless they are explicitly described as being there.
+ * All quantities are sent in native Intel format and multi-byte values are
+ * transmitted least significant byte first.
+ *
+ * Besides mailslots and Transaction SMBs, the other important piece of the
+ * browser architecture is the NetServerEnum2 request. This request that
+ * allows an application to interrogate a Browser Server and obtain a
+ * complete list of resources (servers, domains, etc) known to that Browser
+ * server. Details of the NetServerEnum2 request are presented in section
+ * 6.4. Some examples of the NetServerEnum2 request being used are when a
+ * Local Master Browser sends a NetServerEnum2 request to the Domain Master
+ * Browser and vice versa. Another example is when a browser client sends a
+ * NetServerEnum2 request to a Backup Browser server.
+ *
+ * 4.3 Non-Browser Server
+ *
+ * A non-browser server is a server that has some resource(s) or service(s)
+ * it wishes to advertise as being available using the browsing protocol.
+ * Examples of non-browser servers would be an SQL server, print server,
+ * etc.
+ *
+ * A non-browser server MUST periodically send a HostAnnouncement browser
+ * frame, specifying the type of resources or services it is advertising.
+ * Details are in section 6.5.
+ *
+ * A non-browser server SHOULD announce itself relatively frequently when
+ * it first starts up in order to make its presence quickly known to the
+ * browsers and thence to potential clients. The frequency of the
+ * announcements SHOULD then be gradually stretched, so as to minimize
+ * network traffic. Typically, non-browser servers announce themselves
+ * once every minute upon start up and then gradually adjust the frequency
+ * of the announcements to once every 12 minutes.
+ *
+ * A non-browser server SHOULD send a HostAnnouncement browser frame
+ * specifying a type of 0 just prior to shutting down, to allow it to
+ * quickly be removed from the list of available servers.
+ *
+ * A non-browser server MUST receive and process AnnouncementRequest frames
+ * from the Local Master Browser, and MUST respond with a HostAnnouncement
+ * frame, after a delay chosen randomly from the interval [0,30] seconds.
+ * AnnouncementRequests typically happen when a Local Master Browser starts
+ * up with an empty list of servers for the domain, and wants to fill it
+ * quickly. The 30 second range for responses prevents the Master Browser
+ * from becoming overloaded and losing replies, as well as preventing the
+ * network from being flooded with responses.
+ *
+ * 4.4 Browser Servers
+ *
+ * The following sections describe the roles of the various types of
+ * browser servers.
+ *
+ * 4.4.1 Potential Browser Server
+ *
+ * A Potential Browser server is a browser server that is capable of being
+ * a Backup Browser server or Master Browser server, but is not currently
+ * fulfilling either of those roles.
+ *
+ * A Potential Browser MUST set type SV_TYPE_POTENTIAL_BROWSER (see section
+ * 6.4.1) in its HostAnnouncement until it is ready to shut down. In its
+ * last HostAnnouncement frame before it shuts down, it SHOULD specify a
+ * type of 0.
+ *
+ * A Potential Browser server MUST receive and process BecomeBackup frames
+ * (see section 6.9) and become a backup browser upon their receipt.
+ *
+ * A Potential Browser MUST participate in browser elections (see section
+ * 6.8).
+ *
+ * 4.4.2 Backup Browser
+ *
+ * Backup Browser servers are a subset of the Potential Browsers that have
+ * been chosen by the Master Browser on their subnet to be the Backup
+ * Browsers for the subnet.
+ *
+ * A Backup Browser MUST set type SV_TYPE_BACKUP_BROWSER (see section
+ * 6.4.1) in its HostAnnouncement until it is ready to shut down. In its
+ * last HostAnnouncement frame before it shuts down, it SHOULD specify a
+ * type of 0.
+ *
+ * A Backup Browser MUST listen for a LocalMasterAnnouncement frame (see
+ * section 6.10) from the Local Master Browser, and use it to set the name
+ * of the Master Browser it queries for the server and domain lists.
+ *
+ * A Backup Browsers MUST periodically make a NetServerEnum2 request of
+ * the Master Browser on its subnet for its domain to get a list of servers
+ * in that domain, as well as a list of domains. The period is a
+ * configuration option balancing currency of the information with network
+ * traffic costs - a typical value is 15 minutes.
+ *
+ * A Backup Browser SHOULD force an election by sending a RequestElection
+ * frame (see section 6.7) if it does not get a response to its periodic
+ * NetServeEnum2 request to the Master Browser.
+ *
+ * A Backup Browser MUST receive and process NetServerEnum2 requests from
+ * browser clients, for its own domain and others. If the request is for a
+ * list of servers in its domain, or for a list of domains, it can answer
+ * from its internal lists. If the request is for a list of servers in a
+ * domain different than the one it serves, it sends a NetServerEnum2
+ * request to the Domain Master Browser for that domain (which it can in
+ * find in its list of domains and their Domain Master Browsers).
+ *
+ * A Backup Browser MUST participate in browser elections (see section
+ * 6.8).
+ *
+ * 4.4.3 Master Browser
+ *
+ * Master Browsers are responsible for:
+ * . indicating it is a Master Browser
+ * . receiving server announcements and building a list of such servers
+ * and keeping it reasonably up-to-date.
+ * . returning lists of Backup Browsers to browser clients.
+ * . ensuring an appropriate number of Backup Browsers are available.
+ * . announcing their existence to other Master Browsers on their subnet,
+ * to the Domain Master Browser for their domain, and to all browsers in
+ * their domain on their subnet
+ * . forwarding requests for lists of servers on other domains to the
+ * Master Browser for that domain
+ * . keeping a list of domains in its subnet
+ * . synchronizing with the Domain Master Browser (if any) for its domain
+ * . participating in browser elections
+ * . ensuring that there is only one Master Browser on its subnet
+ *
+ * A Master Browser MUST set type SV_TYPE_MASTER_BROWSER (see section
+ * 6.4.1) in its HostAnnouncement until it is ready to shut down. In its
+ * last HostAnnouncement frame before it shuts down, it SHOULD specify a
+ * type of 0.
+ *
+ * A Master Browser MUST receive and process HostAnnouncement frames from
+ * servers, adding the server name and other information to its servers
+ * list; it must mark them as "local" entries. Periodically, it MUST check
+ * all local server entries to see if a server's HostAnnouncement has timed
+ * out (no HostAnnouncement received for three times the periodicity the
+ * server gave in the last received HostAnnouncement) and remove timed-out
+ * servers from its list.
+ *
+ * A Master Browser MUST receive and process DomainAnnouncement frames (see
+ * section 6.12) and maintain the domain names and their associated (Local)
+ * Master Browsers in its internal domain list until they time out; it must
+ * mark these as "local" entries. Periodically, it MUST check all local
+ * domain entries to see if a server's DomainAnnouncement has timed out (no
+ * DomainAnnouncement received for three times the periodicity the server
+ * gave in the last received DomainAnnouncement) and remove timed-out
+ * servers from its list.
+ *
+ * A Master Browser MUST receive and process GetBackupListRequest frames
+ * from clients, returning GetBackupListResponse frames containing a list
+ * of the Backup Servers for its domain.
+ *
+ * A Master Browser MUST eventually send BecomeBackup frames (see section
+ * 6.9) to one or more Potential Browser servers to increase the number of
+ * Backup Browsers if there are not enough Backup Browsers to handle the
+ * anticipated query load. Note: possible good times for checking for
+ * sufficient backup browsers are after being elected, when timing out
+ * server HostAnnouncements, and when receiving a server's HostAnnouncement
+ * for the first time.
+ *
+ * A Master Browser MUST periodically announce itself and the domain it
+ * serves to other (Local) Master Browsers on its subnet, by sending a
+ * DomainAnnouncement frame (see section 6.12) to its subnet.
+ *
+ * A Master Browser MUST send a MasterAnnouncement frame (see section 6.11)
+ * to the Domain Master Browser after it is first elected, and periodically
+ * thereafter. This informs the Domain Master Browser of the presence of
+ * all the Master Browsers.
+ *
+ * A Master Browser MUST periodically announce itself to all browsers for
+ * its domain on its subnet by sending a LocalMasterAnnouncement frame (see
+ * section 6.10).
+ *
+ * A Master Browser MUST receive and process NetServerEnum2 requests from
+ * browser clients, for its own domain and others. If the request is for a
+ * list of servers in its domain, or for a list of domains, it can answer
+ * from its internal lists. Entries in its list marked "local" MUST have
+ * the SV_TYPE_LOCAL_LIST_ONLY bit set in the returned results; it must be
+ * clear for all other entries. If the request is for a list of servers in
+ * a domain different than the one it serves, it sends a NetServerEnum2
+ * request to the Domain Master Browser for that domain (which it can in
+ * find in its list of domains and their Domain Master Browsers).
+ *
+ * Note: The list of servers that the Master Browser maintains and
+ * returns to the Backup Browsers, is limited in size to 64K of
+ * data. This will limit the number of systems that can be in a
+ * browse list in a single workgroup or domain to approximately two
+ * thousand systems.
+ *
+ * A Master Browser SHOULD request all servers to register with it by
+ * sending an AnnouncementRequest frame, if, on becoming the Master Browser
+ * by winning an election, its server list is empty. Otherwise, clients
+ * might get an incomplete list of servers until the servers' periodic
+ * registrations fill the server list.
+ *
+ * If the Master Browser on a subnet is not the Primary Domain Controller
+ * (PDC), then it is a Local Master Browser.
+ *
+ * A Local Master Browser MUST periodically synchronize with the Domain
+ * Master Browser (which is the PDC). This synchronization is performed by
+ * making a NetServerEnum2 request to the Domain Master Browser and merging
+ * the results with its list of servers and domains. An entry from the
+ * Domain Master Browser should be marked "non-local", and must not
+ * overwrite an entry with the same name marked "local". The Domain Master
+ * Browser is located as specified in Appendix B.
+ *
+ * A Master Browser MUST participate in browser elections (see section
+ * 6.8).
+ *
+ * A Master Browser MUST, if it receives a HostAnnouncement,
+ * DomainAnnouncement, or LocalMasterAnnouncement frame another system that
+ * claims to be the Master Browser for its domain, demote itself from
+ * Master Browser and force an election. This ensures that there is only
+ * ever one Master Browser in each workgroup or domain.
+ *
+ * A Master Browser SHOULD, if it loses an election, become a Backup
+ * Browser (without being told to do so by the new Master Browser). Since
+ * it has more up-to-date information in its lists than a Potential
+ * Browser, it is more efficient to have it be a Backup Browser than to
+ * promote a Potential Browser.
+ *
+ * 4.4.3.1 Preferred Master Browser
+ *
+ * A Preferred Master Browser supports exactly the same protocol elements
+ * as a Potential Browser, except as follows.
+ *
+ * A Preferred Master Browser MUST always force an election when it starts
+ * up.
+ *
+ * A Preferred Master Browser MUST participate in browser elections (see
+ * section 6.8).
+ *
+ * A Preferred Master Browser MUST set the Preferred Master bit in the
+ * RequestElection frame (see section 6.7) to bias the election in its
+ * favor.
+ *
+ * A Preferred Master Browser SHOULD, if it loses an election,
+ * automatically become a Backup Browser, without being told to do so by
+ * the Master Browser.
+ *
+ * 4.4.4 Domain Master Browser
+ *
+ * Since the Domain Master Browser always runs on the PDC, it must
+ * implement all the protocols required of a PDC in addition to the
+ * browsing protocol, and that is way beyond the scope of this
+ * specification.
+ *
+ * 5. Mailslot Protocol Specification
+ *
+ * The only transaction allowed to a mailslot is a mailslot write. Mailslot
+ * writes requests are encapsulated in TRANSACT SMBs. The following table
+ * shows the interpretation of the TRANSACT SMB parameters for a mailslot
+ * transaction:
+ *
+ * Name Value Description
+ * Command SMB_COM_TRANSACTION
+ * Name <name> STRING name of mail slot to write;
+ * must start with "\\MAILSLOT\\"
+ * SetupCount 3 Always 3 for mailslot writes
+ * Setup[0] 1 Command code == write mailslot
+ * Setup[1] Ignored
+ * Setup[2] Ignored
+ * TotalDataCount n Size of data in bytes to write to
+ * the mailslot
+ * Data[ n ] The data to write to the mailslot
+ *
+ */
+
+/*
+ * SMB: C transact, File = \MAILSLOT\BROWSE
+ * SMB: SMB Status = Error Success
+ * SMB: Error class = No Error
+ * SMB: Error code = No Error
+ * SMB: Header: PID = 0x0000 TID = 0x0000 MID = 0x0000 UID = 0x0000
+ * SMB: Tree ID (TID) = 0 (0x0)
+ * SMB: Process ID (PID) = 0 (0x0)
+ * SMB: User ID (UID) = 0 (0x0)
+ * SMB: Multiplex ID (MID) = 0 (0x0)
+ * SMB: Flags Summary = 0 (0x0)
+ * SMB: Command = C transact
+ * SMB: Word count = 17
+ * SMB: Word parameters
+ * SMB: Total parm bytes = 0
+ * SMB: Total data bytes = 33
+ * SMB: Max parm bytes = 0
+ * SMB: Max data bytes = 0
+ * SMB: Max setup words = 0
+ * SMB: Transact Flags Summary = 0 (0x0)
+ * SMB: ...............0 = Leave session intact
+ * SMB: ..............0. = Response required
+ * SMB: Transact timeout = 0 (0x0)
+ * SMB: Parameter bytes = 0 (0x0)
+ * SMB: Parameter offset = 0 (0x0)
+ * SMB: Data bytes = 33 (0x21)
+ * SMB: Data offset = 86 (0x56)
+ * SMB: Setup word count = 3
+ * SMB: Setup words
+ * SMB: Mailslot opcode = Write mailslot
+ * SMB: Transaction priority = 1
+ * SMB: Mailslot class = Unreliable (broadcast)
+ * SMB: Byte count = 50
+ * SMB: Byte parameters
+ * SMB: Path name = \MAILSLOT\BROWSE
+ * SMB: Transaction data
+ * SMB: Data: Number of data bytes remaining = 33 (0x0021)
+ *
+ * 5. Mailslot Protocol Specification
+ *
+ * The only transaction allowed to a mailslot is a mailslot write. Mailslot
+ * writes requests are encapsulated in TRANSACT SMBs. The following table
+ * shows the interpretation of the TRANSACT SMB parameters for a mailslot
+ * transaction:
+ *
+ * Name Value Description
+ * Command SMB_COM_TRANSACTION
+ * Name <name> STRING name of mail slot to write;
+ * must start with "\MAILSLOT\"
+ * SetupCount 3 Always 3 for mailslot writes
+ * Setup[0] 1 Command code == write mailslot
+ * Setup[1] Ignored
+ * Setup[2] Ignored
+ * TotalDataCount n Size of data in bytes to write to
+ * the mailslot
+ * Data[ n ] The data to write to the mailslot
+ *
+ * Magic 0xFF 'S' 'M' 'B'
+ * smb_com a byte, the "first" command
+ * Error a 4-byte union, ignored in a request
+ * smb_flg a one byte set of eight flags
+ * smb_flg2 a two byte set of 16 flags
+ * . twelve reserved bytes, have a role
+ * in connectionless transports (IPX, UDP?)
+ * smb_tid a 16-bit tree ID, a mount point sorta,
+ * 0xFFFF is this command does not have
+ * or require a tree context
+ * smb_pid a 16-bit process ID
+ * smb_uid a 16-bit user ID, specific to this "session"
+ * and mapped to a system (bona-fide) UID
+ * smb_mid a 16-bit multiplex ID, used to differentiate
+ * multiple simultaneous requests from the same
+ * process (pid) (ref RPC "xid")
+ */
+
+int
+smb_browser_load_transact_header(unsigned char *buffer, int maxcnt,
+ int data_count, int reply, char *mailbox)
+{
+ smb_msgbuf_t mb;
+ int mailboxlen;
+ char *fmt;
+ int result;
+ short class = (reply == ONE_WAY_TRANSACTION) ? 2 : 0;
+
+ /*
+ * If the mailboxlen is an even number we need to pad the
+ * header so that the data starts on a word boundary.
+ */
+ fmt = "Mb4.bw20.bwwwwb.wl2.wwwwb.wwwws";
+ mailboxlen = strlen(mailbox) + 1;
+
+ if ((mailboxlen & 0x01) == 0) {
+ ++mailboxlen;
+ fmt = "Mb4.bw20.bwwwwb.wl2.wwwwb.wwwws.";
+ }
+
+ bzero(buffer, maxcnt);
+ smb_msgbuf_init(&mb, buffer, maxcnt, 0);
+
+ result = smb_msgbuf_encode(&mb, fmt,
+ SMB_COM_TRANSACTION, /* Command */
+ 0x18,
+ 0x3,
+ 17, /* Count of parameter words */
+ 0, /* Total Parameter words sent */
+ data_count, /* Total Data bytes sent */
+ 2, /* Max Parameters to return */
+ 0, /* Max data bytes to return */
+ 0, /* Max setup bytes to return */
+ reply, /* No reply */
+ 0xffffffff, /* Timeout */
+ 0, /* Parameter bytes sent */
+ 0, /* Parameter offset */
+ data_count, /* Data bytes sent */
+ 69 + mailboxlen, /* Data offset */
+ 3, /* Setup word count */
+ 1, /* Setup word[0] */
+ 0, /* Setup word[1] */
+ class, /* Setup word[2] */
+ mailboxlen + data_count, /* Total request bytes */
+ mailbox); /* Mailbox address */
+
+ smb_msgbuf_term(&mb);
+ return (result);
+}
+
+/*
+ * smb_net_id
+ *
+ * Lookup for the given IP in the NICs info table.
+ * If it finds a matching entry it'll return the index,
+ * otherwise returns -1.
+ *
+ * SMB network table and SMB browser info table share
+ * the same index.
+ */
+int
+smb_net_id(uint32_t ipaddr)
+{
+ uint32_t myaddr, mask;
+ int net, smb_nc_cnt;
+
+ smb_nc_cnt = smb_nic_get_num();
+ for (net = 0; net < smb_nc_cnt; net++) {
+ net_cfg_t cfg;
+ if (smb_nic_get_byind(net, &cfg) == NULL)
+ break;
+ mask = cfg.mask;
+ myaddr = cfg.ip;
+ if ((ipaddr & mask) == (myaddr & mask))
+ return (net);
+ }
+
+ return (-1);
+}
+
+/*
+ * smb_browser_get_srvname
+ *
+ */
+struct name_entry *
+smb_browser_get_srvname(unsigned short netid)
+{
+ if (netid < smb_nic_get_num())
+ return (&(smb_browser_info[netid].server.name));
+
+ return (NULL);
+}
+
+static int
+smb_browser_addr_of_subnet(struct name_entry *name, int subnet,
+ struct name_entry *result)
+{
+ uint32_t ipaddr, mask, saddr;
+ struct addr_entry *addr;
+ int smb_nc_cnt;
+ net_cfg_t cfg;
+
+ smb_nc_cnt = smb_nic_get_num();
+ if ((name == 0) || subnet >= smb_nc_cnt)
+ return (-1);
+
+ if (smb_nic_get_byind(subnet, &cfg) == NULL)
+ return (-1);
+ ipaddr = cfg.ip;
+ mask = cfg.mask;
+
+ *result = *name;
+ addr = &name->addr_list;
+ do {
+ saddr = addr->sin.sin_addr.s_addr;
+ if ((saddr & mask) == (ipaddr & mask)) {
+ *result = *name;
+ result->addr_list = *addr;
+ result->addr_list.forw = result->addr_list.back =
+ &result->addr_list;
+ return (0);
+ }
+ addr = addr->forw;
+ } while (addr != &name->addr_list);
+
+ return (-1);
+}
+
+
+static int
+smb_browser_bcast_addr_of_subnet(struct name_entry *name, int net,
+ struct name_entry *result)
+{
+ uint32_t broadcast;
+ int smb_nc_cnt;
+ net_cfg_t cfg;
+
+ smb_nc_cnt = smb_nic_get_num();
+ if (net >= smb_nc_cnt)
+ return (-1);
+
+ if (name != 0 && name != result)
+ *result = *name;
+
+ if (smb_nic_get_byind(net, &cfg) == NULL)
+ return (-1);
+
+ broadcast = cfg.broadcast;
+ result->addr_list.sin.sin_family = AF_INET;
+ result->addr_list.sinlen = sizeof (result->addr_list.sin);
+ result->addr_list.sin.sin_addr.s_addr = broadcast;
+ result->addr_list.sin.sin_port = htons(DGM_SRVC_UDP_PORT);
+ result->addr_list.forw = result->addr_list.back = &result->addr_list;
+ return (0);
+}
+
+/*
+ * 6.5 HostAnnouncement Browser Frame
+ *
+ * To advertise its presence, i.e. to publish itself as being available, a
+ * non-browser server sends a HostAnnouncement browser frame. If the server
+ * is a member of domain "D", this frame is sent to the NETBIOS unique name
+ * D(1d) and mailslot "\\MAILSLOT\\BROWSE". The definition of the
+ * HostAnnouncement frame is:
+ *
+ * struct {
+ * unsigned short Opcode;
+ * unsigned char UpdateCount;
+ * uint32_t Periodicity;
+ * unsigned char ServerName[];
+ * unsigned char VersionMajor;
+ * unsigned char VersionMinor;
+ * uint32_t Type;
+ * uint32_t Signature;
+ * unsigned char Comment[];
+ * }
+ *
+ * where:
+ * Opcode - Identifies this structure as a browser server
+ * announcement and is defined as HostAnnouncement with a
+ * value of decimal 1.
+ *
+ * UpdateCount - must be sent as zero and ignored on receipt.
+ *
+ * Periodicity - The announcement frequency of the server (in
+ * seconds). The server will be removed from the browse list
+ * if it has not been heard from in 3X its announcement
+ * frequency. In no case will the server be removed from the
+ * browse list before the period 3X has elapsed. Actual
+ * implementations may take more than 3X to actually remove
+ * the server from the browse list.
+ *
+ * ServerName - Null terminated ASCII server name (up to 16 bytes
+ * in length).
+ *
+ * VersionMajor - The major version number of the OS the server
+ * is running. it will be returned by NetServerEnum2.
+ *
+ * VersionMinor - The minor version number of the OS the server
+ * is running. This is entirely informational and does not
+ * have any significance for the browsing protocol.
+ *
+ * Type - Specifies the type of the server. The server type bits
+ * are specified in the NetServerEnum2 section.
+ *
+ * Signature - The browser protocol minor version number in the
+ * low 8 bits, the browser protocol major version number in
+ * the next higher 8 bits and the signature 0xaa55 in the
+ * high 16 bits of this field. Thus, for this version of the
+ * browser protocol (1.15) this field has the value
+ * 0xaa55010f. This may used to isolate browser servers that
+ * are running out of revision browser software; otherwise,
+ * it is ignored.
+ *
+ * Comment - Null terminated ASCII comment for the server.
+ * Limited to 43 bytes.
+ *
+ * When a non-browser server starts up, it announces itself in the manner
+ * described once every minute. The frequency of these statements is
+ * gradually stretched to once every 12 minutes.
+ *
+ * Note: older non-browser servers in a domain "D" sent HostAnnouncement
+ * frames to the NETBIOS group name D(00). Non-Browser servers supporting
+ * version 1.15 of the browsing protocol SHOULD NOT use this NETBIOS name,
+ * but for backwards compatibility Master Browsers MAY receive and process
+ * HostAnnouncement frames on this name as described above for D(1d).
+ */
+
+void
+smb_browser_send_HostAnnouncement(int net, int32_t next_announcement,
+ struct addr_entry *addr, char suffix)
+{
+ smb_msgbuf_t mb;
+ int offset, announce_len, data_length;
+ struct name_entry dest_name;
+ struct name_entry server_name;
+ struct browser_netinfo *subnet;
+ server_info_t *server;
+ unsigned char *buffer;
+ uint32_t type;
+ char resource_domain[SMB_PI_MAX_DOMAIN];
+
+ syslog(LOG_DEBUG, "smb_browse: send_HostAnnouncement(%d)", net);
+
+ smb_config_rdlock();
+ (void) strlcpy(resource_domain,
+ smb_config_getstr(SMB_CI_DOMAIN_NAME), SMB_PI_MAX_DOMAIN);
+ (void) utf8_strupr(resource_domain);
+ smb_config_unlock();
+
+ if (addr == 0) {
+ /* Local master Browser */
+ smb_init_name_struct(
+ (unsigned char *)resource_domain, suffix,
+ 0, 0, 0, 0, 0, &dest_name);
+ if (smb_browser_bcast_addr_of_subnet(0, net, &dest_name) < 0)
+ return;
+ } else {
+ smb_init_name_struct(
+ (unsigned char *)resource_domain, suffix,
+ 0, 0, 0, 0, 0, &dest_name);
+ dest_name.addr_list = *addr;
+ dest_name.addr_list.forw = dest_name.addr_list.back =
+ &dest_name.addr_list;
+ }
+
+ /* give some extra room */
+ buffer = (unsigned char *)malloc(MAX_DATAGRAM_LENGTH * 2);
+ if (buffer == 0) {
+ syslog(LOG_ERR, "HostAnnouncement: resource shortage");
+ return;
+ }
+
+ subnet = smb_browser_getnet(net);
+ if (subnet == 0) {
+ free(buffer);
+ return;
+ }
+
+ server = &subnet->server;
+
+ data_length = 1 + 1 + 4 + 16 + 1 + 1 + 4 + 4 +
+ strlen(server->comment) + 1;
+
+ if ((offset = smb_browser_load_transact_header(buffer,
+ MAX_DATAGRAM_LENGTH, data_length, ONE_WAY_TRANSACTION,
+ browse)) < 0) {
+
+ smb_browser_putnet(subnet);
+ free(buffer);
+ return;
+ }
+
+ /*
+ * A non-browser server SHOULD send a HostAnnouncement browser frame
+ * specifying a type of 0 just prior to shutting down, to allow it to
+ * quickly be removed from the list of available servers.
+ */
+ type = (nb_status.state & NETBIOS_SHUTTING_DOWN) ? 0 : server->type;
+
+ smb_msgbuf_init(&mb, buffer + offset, MAX_DATAGRAM_LENGTH - offset, 0);
+ announce_len = smb_msgbuf_encode(&mb, "bbl16cbblls",
+ (char)HOST_ANNOUNCEMENT, /* Announcement opcode */
+ (char)++subnet->server.update_count,
+ next_announcement * 60000, /* Periodicity in MilliSeconds */
+ server->hostname, /* Server name */
+ server->major, /* our major version */
+ server->minor, /* our minor version */
+ type, /* server type */
+ server->signature, /* Signature */
+ server->comment); /* Let 'em know */
+
+ server_name = server->name;
+ smb_browser_putnet(subnet);
+
+ if (announce_len > 0)
+ (void) smb_netbios_datagram_send(&server_name, &dest_name,
+ buffer, offset + announce_len);
+
+ free(buffer);
+ smb_msgbuf_term(&mb);
+}
+
+void
+smb_browser_process_AnnouncementRequest(struct datagram *datagram,
+ char *mailbox)
+{
+ struct browser_netinfo *subnet;
+ unsigned int next_announcement;
+ uint32_t delay = random() % 29; /* in seconds */
+ int net;
+
+ if (strcmp(mailbox, lanman) != 0) {
+ syslog(LOG_DEBUG, "smb_browse: Wrong Mailbox (%s)", mailbox);
+ return;
+ }
+
+ net = smb_net_id(datagram->src.addr_list.sin.sin_addr.s_addr);
+ if (net < 0) {
+ /* We don't know who this is so ignore it... */
+ return;
+ }
+
+ (void) sleep(delay);
+
+ subnet = smb_browser_getnet(net);
+ if (subnet) {
+ next_announcement = subnet->next_announce * 60 * 1000;
+ smb_browser_putnet(subnet);
+ smb_browser_send_HostAnnouncement(net, next_announcement,
+ &datagram->src.addr_list, 0x1D);
+ }
+}
+
+void *
+smb_browser_dispatch(void *arg)
+{
+ struct datagram *datagram = (struct datagram *)arg;
+ smb_msgbuf_t mb;
+ int rc;
+ unsigned char command;
+ unsigned char parameter_words;
+ unsigned short total_parameter_words;
+ unsigned short total_data_count;
+ unsigned short max_parameters_to_return;
+ unsigned short max_data_to_return;
+ unsigned char max_setup_bytes_to_return;
+ unsigned short reply;
+ unsigned short parameter_bytes_sent;
+ unsigned short parameter_offset;
+ unsigned short data_bytes_sent;
+ unsigned short data_offset;
+ unsigned char setup_word_count;
+ unsigned short setup_word_0;
+ unsigned short setup_word_1;
+ unsigned short setup_word_2;
+ unsigned short total_request_bytes;
+ char *mailbox;
+ unsigned char message_type;
+ unsigned char *data;
+ int datalen;
+
+ syslog(LOG_DEBUG, "smb_browse: packet_received");
+
+ smb_msgbuf_init(&mb, datagram->data, datagram->data_length, 0);
+ rc = smb_msgbuf_decode(&mb, "Mb27.bwwwwb.w6.wwwwb.wwwws",
+ &command, /* Command */
+ &parameter_words, /* Count of parameter words */
+ &total_parameter_words, /* Total Parameter words sent */
+ &total_data_count, /* Total Data bytes sent */
+ &max_parameters_to_return, /* Max Parameters to return */
+ &max_data_to_return, /* Max data bytes to return */
+ &max_setup_bytes_to_return, /* Max setup bytes to return */
+ &reply, /* No reply */
+ &parameter_bytes_sent, /* Parameter bytes sent */
+ &parameter_offset, /* Parameter offset */
+ &data_bytes_sent, /* Data bytes sent */
+ &data_offset, /* Data offset */
+ &setup_word_count, /* Setup word count */
+ &setup_word_0, /* Setup word[0] */
+ &setup_word_1, /* Setup word[1] */
+ &setup_word_2, /* Setup word[2] */
+ &total_request_bytes, /* Total request bytes */
+ &mailbox); /* Mailbox address */
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "smb_browser_dispatch: decode error");
+ smb_msgbuf_term(&mb);
+ free(datagram);
+ return (0);
+ }
+
+ data = &datagram->data[data_offset];
+ datalen = datagram->data_length - data_offset;
+
+ /*
+ * The PDC location protocol, i.e. anything on the \\NET
+ * mailslot, is handled by the smb_netlogon module.
+ */
+ if (strncasecmp("\\MAILSLOT\\NET\\", mailbox, 14) == 0) {
+ smb_netlogon_receive(datagram, mailbox, data, datalen);
+ smb_msgbuf_term(&mb);
+ free(datagram);
+ return (0);
+ }
+
+ /*
+ * If it's not a netlogon message, assume it's a browser request.
+ * This is not the most elegant way to extract the command byte
+ * but at least we no longer use it to get the netlogon opcode.
+ */
+ message_type = datagram->data[data_offset];
+
+ switch (message_type) {
+ case ANNOUNCEMENT_REQUEST :
+ smb_browser_process_AnnouncementRequest(datagram, mailbox);
+ break;
+
+ default:
+ syslog(LOG_DEBUG, "smb_browse: invalid message_type(%d, %x)",
+ message_type, message_type);
+ break;
+ }
+
+ smb_msgbuf_term(&mb);
+ free(datagram);
+ return (0);
+}
+
+
+/*
+ * 11.1 Registered unique names
+ *
+ * <COMPUTER>(00)
+ * This name is used by all servers and clients to receive second
+ * class mailslot messages. A system must add this name in order to
+ * receive mailslot messages. The only browser requests that should
+ * appear on this name are BecomeBackup, GetBackupListResp,
+ * MasterAnnouncement, and LocalMasterAnnouncement frames. All other
+ * datagrams (other than the expected non-browser datagrams) may be
+ * ignored and an error logged.
+ *
+ * <DOMAIN>(1d)
+ * This name is used to identify a master browser server for domain
+ * "DOMAIN" on a subnet. A master browser server adds this name as a
+ * unique NETBIOS name when it becomes master browser. If the attempt
+ * to add the name fails, the master browser server assumes that there
+ * is another master in the domain and will fail to come up. It may
+ * log an error if the failure occurs more than 3 times in a row (this
+ * either indicates some form of network misconfiguration or a
+ * software error). The only requests that should appear on this name
+ * are GetBackupListRequest and HostAnnouncement requests. All other
+ * datagrams on this name may be ignored (and an error logged). If
+ * running a NETBIOS name service (NBNS, such as WINS), this name
+ * should not be registered with the NBNS.
+ *
+ * <DOMAIN>(1b)
+ * This name is used to identify the Domain Master Browser for domain
+ * "DOMAIN" (which is also the primary domain controller). It is a
+ * unique name added only by the primary domain controller. The
+ * primary domain controller will respond to GetBackupListRequest on
+ * this name just as it responds to these requests on the <DOMAIN>(1d)
+ * name.
+ *
+ * 11.2 Registered group names
+ *
+ * (01)(02)__MSBROWSE__(02)(01)
+ * This name is used by Master Browsers to announce themselves to the
+ * other Master Browsers on a subnet. It is added as a group name by
+ * all Master Browser servers. The only broadcasts that should appear
+ * on this name is DomainAnnouncement requests. All other datagrams
+ * can be ignored.
+ *
+ * <DOMAIN>(00)
+ * This name is used by clients and servers in domain "DOMAIN" to
+ * process server announcements. The only requests that should appear
+ * on this name that the browser is interested in are
+ * AnnouncementRequest and NETLOGON_QUERY (to locate the PDC) packets.
+ * All other unidentifiable requests may be ignored (and an error
+ * logged).
+ *
+ * <DOMAIN>(1E)
+ * This name is used for announcements to browsers for domain "DOMAIN"
+ * on a subnet. This name is registered by all the browser servers in
+ * the domain. The only requests that should appear on this name are
+ * RequestElection and AnnouncementRequest packets. All other
+ * datagrams may be ignored (and an error logged).
+ *
+ * <DOMAIN>(1C)
+ * This name is registered by Primary Domain Controllers.
+ */
+
+void
+smb_browser_config(void)
+{
+ struct name_entry name;
+ struct name_entry master;
+ struct name_entry dest;
+ struct name_entry *entry;
+ int smb_nc_cnt;
+ net_cfg_t cfg;
+ int net;
+ char resource_domain[SMB_PI_MAX_DOMAIN];
+
+ syslog(LOG_DEBUG, "smb_browse: reconfigure");
+
+ smb_browser_init();
+
+ smb_config_rdlock();
+ (void) strlcpy(resource_domain,
+ smb_config_getstr(SMB_CI_DOMAIN_NAME), SMB_PI_MAX_DOMAIN);
+ (void) utf8_strupr(resource_domain);
+ smb_config_unlock();
+
+ /* domain<00> */
+ smb_init_name_struct((unsigned char *)resource_domain, 0x00,
+ 0, 0, 0, 0, 0, &name);
+ entry = smb_name_find_name(&name);
+ smb_name_unlock_name(entry);
+
+ smb_nc_cnt = smb_nic_get_num();
+ for (net = 0; net < smb_nc_cnt; net++) {
+ if (smb_nic_get_byind(net, &cfg) == NULL)
+ break;
+ if (cfg.exclude)
+ continue;
+ smb_init_name_struct(
+ (unsigned char *)resource_domain, 0x00, 0,
+ cfg.ip, htons(DGM_SRVC_UDP_PORT),
+ NAME_ATTR_GROUP, NAME_ATTR_LOCAL, &name);
+ (void) smb_name_add_name(&name);
+ }
+
+ /* All our local master browsers */
+ smb_init_name_struct((unsigned char *)resource_domain, 0x1D,
+ 0, 0, 0, 0, 0, &dest);
+ entry = smb_name_find_name(&dest);
+
+ if (entry) {
+ for (net = 0; net < smb_nc_cnt; net++) {
+ if (smb_browser_addr_of_subnet(entry, net, &master)
+ == 0) {
+ syslog(LOG_DEBUG,
+ "smbd: Master browser found at %s",
+ inet_ntoa(master.addr_list.sin.sin_addr));
+ }
+ }
+ smb_name_unlock_name(entry);
+ }
+ smb_init_name_struct((unsigned char *)resource_domain,
+ 0x1B, 0, 0, 0, 0, 0, &dest);
+
+ if ((entry = smb_name_find_name(&dest)) != 0) {
+ syslog(LOG_DEBUG, "smbd: Domain Master browser for %s is %s",
+ resource_domain,
+ inet_ntoa(entry->addr_list.sin.sin_addr));
+ smb_name_unlock_name(entry);
+ }
+}
+
+static void
+smb_browser_init()
+{
+ struct browser_netinfo *subnet;
+ struct server_info *server;
+ char cmnt[SMB_PI_MAX_COMMENT], hostname[MAXHOSTNAMELEN];
+ int i, j;
+ int smb_nc_cnt;
+ net_cfg_t cfg;
+
+ (void) smb_gethostname(hostname, MAXHOSTNAMELEN, 1);
+
+ smb_config_rdlock();
+ (void) strlcpy(cmnt, smb_config_getstr(SMB_CI_SYS_CMNT),
+ sizeof (cmnt));
+ smb_config_unlock();
+
+ smb_nc_cnt = smb_nic_get_num();
+ for (i = 0; i < smb_nc_cnt; i++) {
+ if (smb_nic_get_byind(i, &cfg) == NULL)
+ break;
+ if (cfg.exclude)
+ continue;
+
+ subnet = &smb_browser_info[i];
+ (void) mutex_lock(&subnet->mtx);
+
+ /* One Minute announcements for first five */
+ subnet->flags = BROWSER_NF_VALID;
+ subnet->next_announce = 1;
+ subnet->interval = 1;
+ subnet->reps = 5;
+
+ server = &subnet->server;
+ bzero(server, sizeof (struct server_info));
+
+ server->type = MY_SERVER_TYPE;
+ server->major = SMB_VERSION_MAJOR;
+ server->minor = SMB_VERSION_MINOR;
+ server->signature = SMB_SERVER_SIGNATURE;
+ (void) strlcpy(server->comment, cmnt, SMB_PI_MAX_COMMENT);
+
+ (void) snprintf(server->hostname, NETBIOS_NAME_SZ, "%.15s",
+ hostname);
+
+ /*
+ * 00 is workstation service.
+ * 20 is file server service.
+ */
+ smb_init_name_struct((unsigned char *)server->hostname, 0x20, 0,
+ cfg.ip, htons(DGM_SRVC_UDP_PORT),
+ NAME_ATTR_UNIQUE, NAME_ATTR_LOCAL, &server->name);
+
+ (void) mutex_unlock(&subnet->mtx);
+ }
+
+ /* Invalidate unconfigured NICs */
+ for (j = i; j < SMB_PI_MAX_NETWORKS; j++) {
+ subnet = &smb_browser_info[j];
+ (void) mutex_lock(&subnet->mtx);
+ subnet->flags = BROWSER_NF_INVALID;
+ (void) mutex_unlock(&subnet->mtx);
+ }
+}
+
+/*
+ * smb_browser_non_master_duties
+ *
+ * To advertise its presence, i.e. to publish itself as being available, a
+ * non-browser server sends a HostAnnouncement browser frame. If the server
+ * is a member of domain "D", this frame is sent to the NETBIOS unique name
+ * D(1d) and mailslot "\\MAILSLOT\\BROWSE".
+ */
+void
+smb_browser_non_master_duties(int net)
+{
+ struct browser_netinfo *subnet;
+ struct name_entry name;
+ struct name_entry *dest;
+ struct addr_entry addr;
+ int interval;
+ char resource_domain[SMB_PI_MAX_DOMAIN];
+
+ subnet = smb_browser_getnet(net);
+ if (subnet == 0)
+ return;
+
+ interval = subnet->interval;
+ smb_browser_putnet(subnet);
+
+ smb_browser_send_HostAnnouncement(net, interval, 0, 0x1D);
+
+ smb_config_rdlock();
+ (void) strlcpy(resource_domain,
+ smb_config_getstr(SMB_CI_DOMAIN_NAME), SMB_PI_MAX_DOMAIN);
+ (void) utf8_strupr(resource_domain);
+ smb_config_unlock();
+
+ smb_init_name_struct((unsigned char *)resource_domain, 0x1D,
+ 0, 0, 0, 0, 0, &name);
+
+ if ((dest = smb_name_find_name(&name))) {
+ addr = dest->addr_list;
+ addr.forw = addr.back = &addr;
+ smb_name_unlock_name(dest);
+ smb_browser_send_HostAnnouncement(net, interval, &addr, 0x1D);
+ } else {
+ smb_init_name_struct(
+ (unsigned char *)resource_domain, 0x1B,
+ 0, 0, 0, 0, 0, &name);
+ if ((dest = smb_name_find_name(&name))) {
+ addr = dest->addr_list;
+ addr.forw = addr.back = &addr;
+ smb_name_unlock_name(dest);
+ smb_browser_send_HostAnnouncement(net, interval,
+ &addr, 0x1B);
+ }
+ }
+
+ subnet = smb_browser_getnet(net);
+ /*
+ * One Minute announcements for first five
+ * minutes, one munute longer each round
+ * until 12 minutes and every 12 minutes
+ * thereafter.
+ */
+ if (--subnet->reps == 0) {
+ if (subnet->interval < 12)
+ subnet->interval++;
+
+ subnet->reps = 1;
+ }
+
+ subnet->next_announce = subnet->interval;
+ smb_browser_putnet(subnet);
+}
+
+
+/*
+ * browser_sleep
+ *
+ * Put browser in 1 minute sleep if netbios services are not
+ * shutting down and both name and datagram services are still
+ * running. It'll wake up after 1 minute or if one of the above
+ * conditions go false. It checks the conditions again and return
+ * 1 if everything is ok or 0 if browser shouldn't continue
+ * running.
+ */
+static int
+browser_sleep()
+{
+ int slept = 0;
+ timestruc_t to;
+
+ (void) mutex_lock(&nb_status.mtx);
+ while (((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) &&
+ (nb_status.state & NETBIOS_NAME_SVC_RUNNING) &&
+ (nb_status.state & NETBIOS_DATAGRAM_SVC_RUNNING)) {
+
+ if (slept) {
+ (void) mutex_unlock(&nb_status.mtx);
+ return (1);
+ }
+
+ to.tv_sec = 60; /* 1 minute */
+ to.tv_nsec = 0;
+ (void) cond_reltimedwait(&nb_status.cv, &nb_status.mtx, &to);
+ slept = 1;
+ }
+ (void) mutex_unlock(&nb_status.mtx);
+
+ return (0);
+}
+
+/*
+ * smb_browser_start
+ *
+ * Smb Netbios browser daemon.
+ */
+/*ARGSUSED*/
+void *
+smb_browser_daemon(void *arg)
+{
+ int net;
+ int next_announce;
+ struct browser_netinfo *subnet;
+ int run = 1;
+ int smb_nc_cnt;
+ net_cfg_t cfg;
+
+ smb_browser_config();
+
+ nb_status.state |= NETBIOS_BROWSER_RUNNING;
+
+ while (run) {
+ smb_nc_cnt = smb_nic_get_num();
+ for (net = 0; net < smb_nc_cnt; net++) {
+ if (smb_nic_get_byind(net, &cfg) == NULL)
+ break;
+ if (cfg.exclude)
+ continue;
+
+ subnet = smb_browser_getnet(net);
+ next_announce = --subnet->next_announce;
+ smb_browser_putnet(subnet);
+
+ if (next_announce > 0 || cfg.broadcast == 0)
+ continue;
+
+ smb_browser_non_master_duties(net);
+ }
+
+ run = browser_sleep();
+ }
+
+ smb_netbios_chg_status(NETBIOS_BROWSER_RUNNING, 0);
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.h
new file mode 100644
index 0000000000..17f3fdf4a9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.h
@@ -0,0 +1,190 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _BROWSER_H_
+#define _BROWSER_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * NetBIOS name types describe the functionality of the registration.
+ * A following list of NetBIOS suffixes (16th Character of the NetBIOS
+ * Name) is detailed in Microsoft knowledge base article Q163409.
+ *
+ * Name Number(h) Type Usage
+ * --------------------------------------------------------------------------
+ * <computername> 00 U Workstation Service
+ * <computername> 01 U Messenger Service
+ * <\\--__MSBROWSE__> 01 G Master Browser
+ * <computername> 03 U Messenger Service
+ * <computername> 06 U RAS Server Service
+ * <computername> 1F U NetDDE Service
+ * <computername> 20 U File Server Service
+ * <computername> 21 U RAS Client Service
+ * <computername> 22 U Microsoft Exchange Interchange(MSMail
+ * Connector)
+ * <computername> 23 U Microsoft Exchange Store
+ * <computername> 24 U Microsoft Exchange Directory
+ * <computername> 30 U Modem Sharing Server Service
+ * <computername> 31 U Modem Sharing Client Service
+ * <computername> 43 U SMS Clients Remote Control
+ * <computername> 44 U SMS Administrators Remote Control
+ * Tool
+ * <computername> 45 U SMS Clients Remote Chat
+ * <computername> 46 U SMS Clients Remote Transfer
+ * <computername> 4C U DEC Pathworks TCPIP service on
+ * Windows NT
+ * <computername> 52 U DEC Pathworks TCPIP service on
+ * Windows NT
+ * <computername> 87 U Microsoft Exchange Message Transfer
+ * Agent
+ * <computername> 6A U Microsoft Exchange IMC
+ * <computername> BE U Network Monitor Agent
+ * <computername> BF U Network Monitor Application
+ * <username> 03 U Messenger Service
+ * <domain> 00 G Domain Name
+ * <domain> 1B U Domain Master Browser
+ * <domain> 1C G Domain Controllers
+ * <domain> 1D U Master Browser
+ * <domain> 1E G Browser Service Elections
+ * <INet~Services> 1C G IIS
+ * <IS~computer name> 00 U IIS
+ * <computername> [2B] U Lotus Notes Server Service
+ * IRISMULTICAST [2F] G Lotus Notes
+ * IRISNAMESERVER [33] G Lotus Notes
+ * Forte_$ND800ZA [20] U DCA IrmaLan Gateway Server Service
+ *
+ * Unique (U): The name may have only one IP address assigned to it. On
+ * a network device multiple occurrences of a single name may appear to
+ * be registered. The suffix may be the only unique character in the name.
+ *
+ * Group (G): A normal group; the single name may exist with many IP
+ * addresses. WINS responds to a name query on a group name with the
+ * limited broadcast address (255.255.255.255). Because routers block
+ * the transmission of these addresses, the Internet Group was designed
+ * to service communications between subnets.
+ *
+ * Multihomed (M): The name is unique, but due to multiple network
+ * interfaces on the same computer this configuration is necessary to
+ * permit the registration. The maximum number of addresses is 25.
+ *
+ * Internet Group (I): This is a special configuration of the group name
+ * used to manage Windows NT Domain names.
+ *
+ * Domain Name (D): New in Windows NT 4.0.
+ */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Message flags used when building the SMB transact headers.
+ */
+#define TWO_WAY_TRANSACTION 0x00
+#define END_SESSION_TRANSACTION 0x01
+#define ONE_WAY_TRANSACTION 0x02
+
+
+/*
+ * Browser commands associated with the BROWSE and MSBROWSE mailslots.
+ */
+#define HOST_ANNOUNCEMENT 0x01
+#define ANNOUNCEMENT_REQUEST 0x02
+#define REQUEST_ELECTION 0x08
+#define GET_BACKUP_LIST_REQ 0x09
+#define GET_BACKUP_LIST_RESP 0x0A
+#define BECOME_BACKUP 0x0B
+#define DOMAIN_ANNOUNCEMENT 0x0C
+#define MASTER_ANNOUNCEMENT 0x0D
+#define LOCAL_MASTER_ANNOUNCEMENT 0x0F
+
+
+/*
+ * Opcodes associated with NETLOGON or NTLOGON mailslots (KB 109626).
+ * LOGON_REQUEST LM1.0/2.0 LOGON Request from client
+ * LOGON_RESPONSE LM1.0 Response to LOGON_REQUEST
+ * LOGON_CENTRAL_QUERY LM1.0 QUERY for centralized init
+ * LOGON_DISTRIB_QUERY LM1.0 QUERY for non-centralized init
+ * LOGON_CENTRAL_RESPONSE LM1.0 response to LOGON_CENTRAL_QUERY
+ * LOGON_DISTRIB_RESPONSE LM1.0 resp to LOGON_DISTRIB_QUERY
+ * LOGON_RESPONSE2 LM2.0 Response to LOGON_REQUEST
+ * LOGON_PRIMARY_QUERY QUERY for Primary DC
+ * LOGON_START_PRIMARY announce startup of Primary DC
+ * LOGON_FAIL_PRIMARY announce failed Primary DC
+ * LOGON_UAS_CHANGE announce change to UAS or SAM
+ * LOGON_NO_USER announce no user on machine
+ * LOGON_PRIMARY_RESPONSE response to LOGON_PRIMARY_QUERY
+ * LOGON_RELOGON_RESPONSE LM1.0/2.0 resp to relogon request
+ * LOGON_WKSTINFO_RESPONSE LM1.0/2.0 resp to interrogate request
+ * LOGON_PAUSE_RESPONSE LM2.0 resp when NETLOGON is paused
+ * LOGON_USER_UNKNOWN LM2.0 response when user is unknown
+ * LOGON_UPDATE_ACCOUNT LM2.1 announce account updates
+ * LOGON_SAM_LOGON_REQUEST SAM LOGON request from client
+ * LOGON_SAM_LOGON_RESPONSE SAM Response to SAM logon request
+ * LOGON_SAM_PAUSE_RESPONSE SAM response when NETLOGON is paused
+ * LOGON_SAM_USER_UNKNOWN SAM response when user is unknown
+ * LOGON_SAM_WKSTINFO_RESPONSE SAM response to interrogate request
+ */
+#define LOGON_REQUEST 0
+#define LOGON_RESPONSE 1
+#define LOGON_CENTRAL_QUERY 2
+#define LOGON_DISTRIB_QUERY 3
+#define LOGON_CENTRAL_RESPONSE 4
+#define LOGON_DISTRIB_RESPONSE 5
+#define LOGON_RESPONSE2 6
+#define LOGON_PRIMARY_QUERY 7
+#define LOGON_START_PRIMARY 8
+#define LOGON_FAIL_PRIMARY 9
+#define LOGON_UAS_CHANGE 10
+#define LOGON_NO_USER 11
+#define LOGON_PRIMARY_RESPONSE 12
+#define LOGON_RELOGON_RESPONSE 13
+#define LOGON_WKSTINFO_RESPONSE 14
+#define LOGON_PAUSE_RESPONSE 15
+#define LOGON_USER_UNKNOWN 16
+#define LOGON_UPDATE_ACCOUNT 17
+#define LOGON_SAM_LOGON_REQUEST 18
+#define LOGON_SAM_LOGON_RESPONSE 19
+#define LOGON_SAM_PAUSE_RESPONSE 20
+#define LOGON_SAM_USER_UNKNOWN 21
+#define LOGON_SAM_WKSTINFO_RESPONSE 22
+
+
+/*
+ * Local protocol flags used to indicate which version of the
+ * netlogon protocol to use when attempting to find the PDC.
+ */
+#define NETLOGON_PROTO_NETLOGON 0x01
+#define NETLOGON_PROTO_SAMLOGON 0x02
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _BROWSER_H_ */
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c
new file mode 100644
index 0000000000..474adca09b
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c
@@ -0,0 +1,1894 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <gssapi/gssapi.h>
+#include <kerberosv5/krb5.h>
+#include <net/if.h>
+
+#include <smbns_dyndns.h>
+#include <smbns_krb.h>
+
+/* internal use, in dyndns_add_entry */
+#define DEL_NONE 2
+/* Maximum retires if not authoritative */
+#define MAX_AUTH_RETRIES 3
+
+
+static int
+dyndns_enabled(void)
+{
+ int enabled;
+
+ smb_config_rdlock();
+ enabled = smb_config_getyorn(SMB_CI_DYNDNS_ENABLE);
+ smb_config_unlock();
+
+ return ((enabled) ? 1 : 0);
+}
+
+/*
+ * XXX The following should be removed once head/arpa/nameser_compat.h
+ * defines BADSIG, BADKEY, BADTIME macros
+ */
+#ifndef BADSIG
+#define BADSIG ns_r_badsig
+#endif /* BADSIG */
+
+#ifndef BADKEY
+#define BADKEY ns_r_badkey
+#endif /* BADKEY */
+
+#ifndef BADTIME
+#define BADTIME ns_r_badtime
+#endif /* BADTIME */
+
+/*
+ * dyndns_msg_err
+ * Display error message for DNS error code found in the DNS header in
+ * reply message.
+ * Parameters:
+ * err: DNS errer code
+ * Returns:
+ * None
+ */
+void
+dyndns_msg_err(int err)
+{
+ switch (err) {
+ case NOERROR:
+ break;
+ case FORMERR:
+ syslog(LOG_ERR, "DNS message format error\n");
+ break;
+ case SERVFAIL:
+ syslog(LOG_ERR, "DNS server internal error\n");
+ break;
+ case NXDOMAIN:
+ syslog(LOG_ERR, "DNS entry should exist but does not exist\n");
+ break;
+ case NOTIMP:
+ syslog(LOG_ERR, "DNS opcode not supported\n");
+ break;
+ case REFUSED:
+ syslog(LOG_ERR, "DNS operation refused\n");
+ break;
+ case YXDOMAIN:
+ syslog(LOG_ERR, "DNS entry shouldn't exist but does exist\n");
+ break;
+ case YXRRSET:
+ syslog(LOG_ERR, "DNS RRSet shouldn't exist but does exist\n");
+ break;
+ case NXRRSET:
+ syslog(LOG_ERR, "DNS RRSet should exist but does not exist\n");
+ break;
+ case NOTAUTH:
+ syslog(LOG_ERR, "DNS server is not authoritative "
+ "for specified zone\n");
+ break;
+ case NOTZONE:
+ syslog(LOG_ERR, "Name in Prereq or Update section not "
+ "within specified zone\n");
+ break;
+ case BADSIG:
+ syslog(LOG_ERR, "Bad transaction signature (TSIG)");
+ break;
+ case BADKEY:
+ syslog(LOG_ERR, "Bad transaction key (TKEY)");
+ break;
+ case BADTIME:
+ syslog(LOG_ERR, "Time not synchronized");
+ break;
+
+ default:
+ syslog(LOG_ERR, "Unknown DNS error\n");
+ }
+}
+
+/*
+ * display_stat
+ * Display GSS error message from error code. This routine is used to display
+ * the mechanism independent and mechanism specific error messages for GSS
+ * routines. The major status error code is the mechanism independent error
+ * code and the minor status error code is the mechanism specific error code.
+ * Parameters:
+ * maj: GSS major status
+ * min: GSS minor status
+ * Returns:
+ * None
+ */
+static void
+display_stat(OM_uint32 maj, OM_uint32 min)
+{
+ gss_buffer_desc msg;
+ OM_uint32 msg_ctx = 0;
+ OM_uint32 min2;
+ (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ syslog(LOG_ERR, "dyndns: GSS major status error: %s\n",
+ (char *)msg.value);
+ (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ syslog(LOG_ERR, "dyndns: GSS minor status error: %s\n",
+ (char *)msg.value);
+}
+
+static char *
+dyndns_put_nshort(char *buf, uint16_t val)
+{
+ uint16_t nval;
+
+ nval = htons(val);
+ (void) memcpy(buf, &nval, sizeof (uint16_t));
+ buf += sizeof (uint16_t);
+ return (buf);
+}
+
+char *
+dyndns_get_nshort(char *buf, uint16_t *val)
+{
+ uint16_t nval;
+
+ (void) memcpy(&nval, buf, sizeof (uint16_t));
+ *val = ntohs(nval);
+ buf += sizeof (uint16_t);
+ return (buf);
+}
+
+static char *
+dyndns_put_nlong(char *buf, uint32_t val)
+{
+ uint32_t lval;
+
+ lval = htonl(val);
+ (void) memcpy(buf, &lval, sizeof (uint32_t));
+ buf += sizeof (uint32_t);
+ return (buf);
+}
+
+static char *
+dyndns_put_byte(char *buf, char val)
+{
+ *buf = val;
+ buf++;
+ return (buf);
+}
+
+static char *
+dyndns_put_int(char *buf, int val)
+{
+ (void) memcpy(buf, &val, sizeof (int));
+ buf += sizeof (int);
+ return (buf);
+}
+
+char *
+dyndns_get_int(char *buf, int *val)
+{
+ (void) memcpy(val, buf, sizeof (int));
+ buf += sizeof (int);
+ return (buf);
+}
+
+
+/*
+ * dyndns_stuff_str
+ * Converts a domain string by removing periods and replacing with a byte value
+ * of how many characters following period. A byte value is placed in front
+ * to indicate how many characters before first period. A NULL character is
+ * placed at the end. i.e. host.procom.com -> 4host5procom3com0
+ * Buffer space checking is done by caller.
+ * Parameters:
+ * ptr : address of pointer to buffer to store converted string
+ * zone: domain name string
+ * Returns:
+ * ptr: address of pointer to next available buffer space
+ * -1 : error
+ * 0 : success
+ */
+static int
+dyndns_stuff_str(char **ptr, char *zone)
+{
+ int len;
+ char *lenPtr, *zonePtr;
+
+ for (zonePtr = zone; *zonePtr; ) {
+ lenPtr = *ptr;
+ *ptr = *ptr + 1;
+ len = 0;
+ while (*zonePtr != '.' && *zonePtr != 0) {
+ *ptr = dyndns_put_byte(*ptr, *zonePtr);
+ zonePtr++;
+ len++;
+ }
+ *lenPtr = len;
+ if (*zonePtr == '.')
+ zonePtr++;
+ }
+ *ptr = dyndns_put_byte(*ptr, 0);
+ return (0);
+}
+
+/*
+ * dyndns_build_header
+ * Build the header for DNS query and DNS update request message.
+ * Parameters:
+ * ptr : address of pointer to buffer to store header
+ * buf_len : buffer length
+ * msg_id : message id
+ * query_req : use REQ_QUERY for query message or REQ_UPDATE for
+ * update message
+ * quest_zone_cnt : number of question record for query message or
+ * number of zone record for update message
+ * ans_prereq_cnt : number of answer record for query message or
+ * number of prerequisite record for update message
+ * nameser_update_cnt: number of name server for query message or
+ * number of update record for update message
+ * addit_cnt : number of additional record
+ * flags : query flags word
+ * Returns:
+ * ptr: address of pointer to next available buffer space
+ * -1 : error
+ * 0 : success
+ */
+int
+dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req,
+ uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt,
+ uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags)
+{
+ uint16_t opcode;
+
+ if (buf_len < 12) {
+ syslog(LOG_ERR, "dyndns: no more buf for header section\n");
+ return (-1);
+ }
+
+ *ptr = dyndns_put_nshort(*ptr, msg_id); /* mesg ID */
+ if (query_req == REQ_QUERY)
+ opcode = ns_o_query; /* query msg */
+ else
+ opcode = ns_o_update << 11; /* update msg */
+ opcode |= flags;
+ /* mesg opcode */
+ *ptr = dyndns_put_nshort(*ptr, opcode);
+ /* zone record count */
+ *ptr = dyndns_put_nshort(*ptr, quest_zone_cnt);
+ /* prerequiste record count */
+ *ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt);
+ /* update record count */
+ *ptr = dyndns_put_nshort(*ptr, nameser_update_cnt);
+ /* additional record count */
+ *ptr = dyndns_put_nshort(*ptr, addit_cnt);
+
+ return (0);
+}
+
+/*
+ * dyndns_build_quest_zone
+ * Build the question section for query message or zone section for
+ * update message.
+ * Parameters:
+ * ptr : address of pointer to buffer to store question or zone section
+ * buf_len: buffer length
+ * name : question or zone name
+ * type : type of question or zone
+ * class : class of question or zone
+ * Returns:
+ * ptr: address of pointer to next available buffer space
+ * -1 : error
+ * 0 : success
+ */
+int
+dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type,
+ int class)
+{
+ char *zonePtr;
+
+ if ((strlen(name) + 6) > buf_len) {
+ syslog(LOG_ERR, "dyndns: no more buf "
+ "for question/zone section\n");
+ return (-1);
+ }
+
+ zonePtr = *ptr;
+ (void) dyndns_stuff_str(&zonePtr, name);
+ *ptr = zonePtr;
+ *ptr = dyndns_put_nshort(*ptr, type);
+ *ptr = dyndns_put_nshort(*ptr, class);
+ return (0);
+}
+
+/*
+ * dyndns_build_update
+ * Build update section of update message for adding and removing a record.
+ * If the ttl value is 0 then this message is for record deletion.
+ *
+ * Parameters:
+ * ptr : address of pointer to buffer to store update section
+ * buf_len : buffer length
+ * name : resource name of this record
+ * type : type of this record
+ * class : class of this record
+ * ttl : time-to-live, cached time of this entry by others and not
+ * within DNS database, a zero value for record(s) deletion
+ * data : data of this resource record
+ * forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
+ * add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone
+ * del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all
+ * entries of the same resource name. Only valid for UPDATE_DEL.
+ * Returns:
+ * ptr: address of pointer to next available buffer space
+ * -1 : error
+ * 0 : success
+ */
+static int
+dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class,
+ uint32_t ttl, char *data, int forw_rev, int add_del, int del_type)
+{
+ char *namePtr;
+ int rec_len, data_len;
+
+ rec_len = strlen(name) + 10;
+ if (add_del == UPDATE_ADD) {
+ if (forw_rev == UPDATE_FORW)
+ data_len = 4;
+ else
+ data_len = strlen(data) + 2;
+ } else {
+ if (del_type == DEL_ALL)
+ data_len = 0;
+ else if (forw_rev == UPDATE_FORW)
+ data_len = 4;
+ else
+ data_len = strlen(data) + 2;
+ }
+
+ if (rec_len + data_len > buf_len) {
+ syslog(LOG_ERR, "dyndns: no more buf for update section\n");
+ return (-1);
+ }
+
+ namePtr = *ptr;
+ (void) dyndns_stuff_str(&namePtr, name);
+ *ptr = namePtr;
+ *ptr = dyndns_put_nshort(*ptr, type);
+ *ptr = dyndns_put_nshort(*ptr, class);
+ *ptr = dyndns_put_nlong(*ptr, ttl);
+
+ if (add_del == UPDATE_DEL && del_type == DEL_ALL) {
+ *ptr = dyndns_put_nshort(*ptr, 0);
+ return (0);
+ }
+
+ if (forw_rev == UPDATE_FORW) {
+ *ptr = dyndns_put_nshort(*ptr, 4);
+ *ptr = dyndns_put_int(*ptr, inet_addr(data)); /* ip address */
+ } else {
+ *ptr = dyndns_put_nshort(*ptr, strlen(data)+2);
+ namePtr = *ptr;
+ (void) dyndns_stuff_str(&namePtr, data); /* hostname */
+ *ptr = namePtr;
+ }
+ return (0);
+}
+
+/*
+ * dyndns_build_tkey
+ * Build TKEY section to establish security context for secure dynamic DNS
+ * update. DNS header and question sections need to be build before this
+ * section. The TKEY data are the tokens generated during security context
+ * establishment and the TKEY message is used to transmit those tokens, one
+ * at a time, to the DNS server.
+ * Parameters:
+ * ptr : address of pointer to buffer to store TKEY
+ * buf_len : buffer length
+ * name : key name, must be unique and same as for TSIG record
+ * key_expire: expiration time of this key in second
+ * data : TKEY data
+ * data_size : data size
+ * Returns:
+ * ptr: address of the pointer to the next available buffer space
+ * -1 : error
+ * 0 : success
+ */
+static int
+dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire,
+ char *data, int data_size)
+{
+ char *namePtr;
+ struct timeval tp;
+
+ if (strlen(name)+2 + 45 + data_size > buf_len) {
+ syslog(LOG_ERR, "dyndns: no more buf for TKEY record\n");
+ return (-1);
+ }
+
+ namePtr = *ptr;
+ (void) dyndns_stuff_str(&namePtr, name); /* unique global name */
+ *ptr = namePtr;
+ *ptr = dyndns_put_nshort(*ptr, ns_t_tkey);
+ *ptr = dyndns_put_nshort(*ptr, ns_c_any);
+ *ptr = dyndns_put_nlong(*ptr, 0);
+ /* 19 + 14 + data_size + 2 */
+ *ptr = dyndns_put_nshort(*ptr, 35 + data_size);
+ namePtr = *ptr;
+ (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
+ *ptr = namePtr;
+ (void) gettimeofday(&tp, 0);
+ *ptr = dyndns_put_nlong(*ptr, tp.tv_sec); /* inception */
+ /* expiration, 86400 */
+ *ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire);
+ *ptr = dyndns_put_nshort(*ptr, MODE_GSS_API); /* mode: gss-api */
+ *ptr = dyndns_put_nshort(*ptr, 0); /* error */
+ *ptr = dyndns_put_nshort(*ptr, data_size); /* key size */
+ (void) memcpy(*ptr, data, data_size); /* key data */
+ *ptr += data_size;
+ *ptr = dyndns_put_nshort(*ptr, 0); /* other */
+ return (0);
+}
+
+/*
+ * dyndns_build_tsig
+ * Build TSIG section for secure dynamic DNS update. This routine will be
+ * called twice. First called with TSIG_UNSIGNED, and second with TSIG_SIGNED.
+ * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request
+ * message encrypted for TSIG_SIGNED. The message id must be the same id as the
+ * one in the update request before it is encrypted.
+ * Parameters:
+ * ptr : address of pointer to buffer to store TSIG
+ * buf_len : buffer length
+ * msg_id : message id
+ * name : key name, must be the same as in TKEY record
+ * fudge_time : amount of error time allow in seconds
+ * data : TSIG data if TSIG_SIGNED, otherwise NULL
+ * data_size : size of data, otherwise 0 if data is NULL
+ * data_signed: TSIG_SIGNED to indicate data is signed and encrypted,
+ * otherwise TSIG_UNSIGNED
+ * Returns:
+ * ptr: address of pointer to next available buffer space
+ * -1 : error
+ * 0 : success
+ */
+static int
+dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name,
+ int fudge_time, char *data, int data_size, int data_signed)
+{
+ char *namePtr;
+ struct timeval tp;
+ int signtime, fudge, rec_len;
+
+ if (data_signed == TSIG_UNSIGNED)
+ rec_len = strlen(name)+2 + 37;
+ else
+ rec_len = strlen(name)+2 + 45 + data_size;
+
+ if (rec_len > buf_len) {
+ syslog(LOG_ERR, "dyndns: no more buf for TSIG record\n");
+ return (-1);
+ }
+
+ namePtr = *ptr;
+ (void) dyndns_stuff_str(&namePtr, name); /* unique global name */
+ *ptr = namePtr;
+ if (data_signed == TSIG_SIGNED)
+ *ptr = dyndns_put_nshort(*ptr, ns_t_tsig);
+ *ptr = dyndns_put_nshort(*ptr, ns_c_any);
+ *ptr = dyndns_put_nlong(*ptr, 0);
+ if (data_signed == TSIG_SIGNED) {
+ /* 19 + 10 + data_size + 6 */
+ *ptr = dyndns_put_nshort(*ptr, 35 + data_size);
+ }
+ namePtr = *ptr;
+ (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
+ *ptr = namePtr;
+ (void) gettimeofday(&tp, 0);
+ signtime = tp.tv_sec >> 16;
+ *ptr = dyndns_put_nlong(*ptr, signtime); /* sign time */
+ fudge = tp.tv_sec << 16;
+ fudge |= fudge_time;
+ *ptr = dyndns_put_nlong(*ptr, fudge); /* fudge time */
+ if (data_signed == TSIG_SIGNED) {
+ /* signed data size */
+ *ptr = dyndns_put_nshort(*ptr, data_size);
+ (void) memcpy(*ptr, data, data_size); /* signed data */
+ *ptr += data_size;
+ *ptr = dyndns_put_nshort(*ptr, msg_id); /* original id */
+ }
+ *ptr = dyndns_put_nshort(*ptr, 0); /* error */
+ *ptr = dyndns_put_nshort(*ptr, 0); /* other */
+ return (0);
+}
+
+/*
+ * dyndns_open_init_socket
+ * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it
+ * by doing bind() and setting linger option to off.
+ *
+ * Parameters:
+ * sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP
+ * dest_addr: destination address in network byte order
+ * port : destination port number
+ * Returns:
+ * descriptor: descriptor referencing the created socket
+ * -1 : error
+ */
+int
+dyndns_open_init_socket(int sock_type, unsigned long dest_addr, int port)
+{
+ int s;
+ struct sockaddr_in my_addr;
+ struct linger l;
+ struct sockaddr_in serv_addr;
+
+ if ((s = socket(AF_INET, sock_type, 0)) == -1) {
+ syslog(LOG_ERR, "dyndns: socket err\n");
+ return (-1);
+ }
+
+ l.l_onoff = 0;
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER,
+ (char *)&l, sizeof (l)) == -1) {
+ syslog(LOG_ERR, "dyndns: setsocket err\n");
+ (void) close(s);
+ return (-1);
+ }
+
+ bzero(&my_addr, sizeof (my_addr));
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(0);
+ my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(s, (struct sockaddr *)&my_addr, sizeof (my_addr)) < 0) {
+ syslog(LOG_ERR, "dyndns: client bind err\n");
+ (void) close(s);
+ return (-1);
+ }
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(port);
+ serv_addr.sin_addr.s_addr = dest_addr;
+
+ if (connect(s, (struct sockaddr *)&serv_addr,
+ sizeof (struct sockaddr_in)) < 0) {
+ syslog(LOG_ERR, "dyndns: client connect err (%s)\n",
+ strerror(errno));
+ (void) close(s);
+ return (-1);
+ }
+
+ return (s);
+}
+
+/*
+ * dyndns_acquire_cred
+ * This routine is used to acquire a GSS credential handle to a user's Kerberos
+ * ticket-granting ticket (TGT) stored locally on the system. If getting a
+ * handle fails, then a new TGT will be obtained again before trying to get a
+ * handle once more.
+ * The user's password is taken from the environment variable
+ * lookup.dns.dynamic.passwd and is encrypted.
+ * Paramaters:
+ * kinit_retry: if 0 then a new TGT can be obtained before second attempt to
+ * get a handle to TGT if first attempt fails
+ * Returns:
+ * user_name : name of user to get credential handle from
+ * credHandle : handle to user's credential (TGT)
+ * oid : contains Kerberos 5 object identifier
+ * kinit_retry: 1 if a new TGT has been acquired in this routine, otherwise 0
+ * -1 : error
+ */
+static int
+dyndns_acquire_cred(gss_cred_id_t *credHandle, char *user_name,
+ gss_OID *oid, int *kinit_retry)
+{
+ char *p, pwd[100];
+
+ smb_config_rdlock();
+ p = smb_config_getstr(SMB_CI_ADS_USER);
+ if (p == NULL || *p == 0) {
+ syslog(LOG_ERR, "No user configured for "
+ "secure dynamic DNS update.\n");
+ smb_config_unlock();
+ return (-1);
+ }
+ (void) strcpy(user_name, p);
+
+ p = smb_config_getstr(SMB_CI_ADS_PASSWD);
+ if (p == NULL || *p == 0) {
+ syslog(LOG_ERR, "No password configured for "
+ "secure dynamic DNS update.\n");
+ smb_config_unlock();
+ return (-1);
+ }
+ smb_config_unlock();
+
+ (void) strcpy(pwd, p);
+
+ return krb5_acquire_cred_kinit(user_name, pwd, credHandle,
+ oid, kinit_retry, "dyndns");
+}
+
+/*
+ * dyndns_build_tkey_msg
+ * This routine is used to build the TKEY message to transmit GSS tokens
+ * during GSS security context establishment for secure DNS update. The
+ * TKEY message format uses the DNS query message format. The TKEY section
+ * is the answer section of the query message format.
+ * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
+ * Parameters:
+ * buf : buffer to build and store TKEY message
+ * key_name: a unique key name, this same key name must be also be used in
+ * the TSIG message
+ * out_tok : TKEY message data (GSS tokens)
+ * Returns:
+ * id : message id of this TKEY message
+ * message size: the size of the TKEY message
+ * -1 : error
+ */
+static int
+dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id,
+ gss_buffer_desc *out_tok)
+{
+ int queryReq, zoneCount, preqCount, updateCount, additionalCount;
+ int zoneType, zoneClass;
+ char *bufptr;
+
+ queryReq = REQ_QUERY;
+ /* query section of query request */
+ zoneCount = 1;
+ /* answer section of query request */
+ preqCount = 1;
+ updateCount = 0;
+ additionalCount = 0;
+
+ (void) memset(buf, 0, MAX_TCP_SIZE);
+ bufptr = buf;
+ *id = smb_get_next_resid();
+
+ /* add TCP length info that follows this field */
+ bufptr = dyndns_put_nshort(bufptr,
+ 26 + (strlen(key_name)+2)*2 + 35 + out_tok->length);
+
+ if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq,
+ zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
+ return (-1);
+ }
+
+ zoneType = ns_t_tkey;
+ zoneClass = ns_c_in;
+ if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
+ zoneType, zoneClass) == -1) {
+ return (-1);
+ }
+
+ if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
+ 86400, out_tok->value, out_tok->length) == -1) {
+ return (-1);
+ }
+
+ return (bufptr - buf);
+}
+
+/*
+ * dyndns_establish_sec_ctx
+ * This routine is used to establish a security context with the DNS server
+ * by building TKEY messages and sending them to the DNS server. TKEY messages
+ * are also received from the DNS server for processing. The security context
+ * establishment is done with the GSS client on the system producing a token
+ * and sending the token within the TKEY message to the GSS server on the DNS
+ * server. The GSS server then processes the token and then send a TKEY reply
+ * message with a new token to be processed by the GSS client. The GSS client
+ * processes the new token and then generates a new token to be sent to the
+ * GSS server. This cycle is continued until the security establishment is
+ * done. TCP is used to send and receive TKEY messages.
+ * If gss_init_sec_context fails then a new TGT will be acquired so that
+ * security establishment can be retry once more by the caller after getting
+ * a handle to the new TGT (credential).
+ * Parameters:
+ * credHandle : handle to credential
+ * s : socket descriptor to DNS server
+ * key_name : TKEY key name
+ * dns_hostname: fully qualified DNS hostname
+ * oid : contains Kerberos 5 object identifier
+ * user_name : name of user to perform DNS update
+ * kinit_retry : if 0 and gss_init_sec_context fails then get new TGT so
+ * the caller can restart doing security context establishment
+ * Returns:
+ * gss_context : handle to security context
+ * kinit_retry : 1 if a new TGT has been acquired in this routine,
+ * otherwise 0
+ * do_acquire_cred: if 1 then caller will restart security context
+ * establishment
+ * -1 : error
+ */
+static int
+dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t credHandle,
+ int s, char *key_name, char *dns_hostname, gss_OID oid, char *user_name,
+ int *kinit_retry, int *do_acquire_cred)
+{
+ uint16_t id, rid, rsz;
+ char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE];
+ int ret;
+ char *service_name, *tmpptr;
+ int service_sz;
+ OM_uint32 min, maj, time_rec;
+ gss_buffer_desc service_buf, in_tok, out_tok;
+ gss_name_t target_name;
+ gss_buffer_desc *inputptr;
+ int gss_flags;
+ OM_uint32 ret_flags;
+ int buf_sz;
+ char *p, pwd[100];
+
+ smb_config_rdlock();
+ p = smb_config_getstr(SMB_CI_ADS_PASSWD);
+ if (p == NULL || *p == 0) {
+ syslog(LOG_ERR, "No password configured for "
+ "secure dynamic DNS update.\n");
+ smb_config_unlock();
+ return (-1);
+ }
+ smb_config_unlock();
+ (void) strcpy(pwd, p);
+
+ service_sz = strlen(dns_hostname) + 5;
+ service_name = (char *)malloc(sizeof (char) * service_sz);
+ if (service_name == NULL) {
+ syslog(LOG_ERR, "Malloc failed for %d bytes ", service_sz);
+ smb_config_unlock();
+ return (-1);
+ }
+ (void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname);
+ service_buf.value = service_name;
+ service_buf.length = strlen(service_name)+1;
+ if ((maj = gss_import_name(&min, &service_buf,
+ (gss_OID) gss_nt_service_name,
+ &target_name)) != GSS_S_COMPLETE) {
+ display_stat(maj, min);
+ (void) gss_release_oid(&min, &oid);
+ (void) free(service_name);
+ return (-1);
+ }
+ (void) free(service_name);
+
+ inputptr = GSS_C_NO_BUFFER;
+ *gss_context = GSS_C_NO_CONTEXT;
+ gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG |
+ GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
+ do {
+ if (krb5_establish_sec_ctx_kinit(user_name, pwd, credHandle,
+ gss_context, target_name, oid, gss_flags, inputptr,
+ &out_tok, &ret_flags, &time_rec, kinit_retry,
+ do_acquire_cred, &maj, "dyndns") == -1) {
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ return (-1);
+ }
+
+ if ((maj == GSS_S_COMPLETE) &&
+ !(ret_flags & GSS_C_REPLAY_FLAG)) {
+ syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG\n");
+ if (out_tok.length > 0)
+ (void) gss_release_buffer(&min, &out_tok);
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ return (-1);
+ }
+
+ if ((maj == GSS_S_COMPLETE) &&
+ !(ret_flags & GSS_C_MUTUAL_FLAG)) {
+ syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG\n");
+ if (out_tok.length > 0)
+ (void) gss_release_buffer(&min, &out_tok);
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ return (-1);
+ }
+
+ if (out_tok.length > 0) {
+ if ((buf_sz = dyndns_build_tkey_msg(buf, key_name,
+ &id, &out_tok)) <= 0) {
+ (void) gss_release_buffer(&min, &out_tok);
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ return (-1);
+ }
+
+ (void) gss_release_buffer(&min, &out_tok);
+
+ if (send(s, buf, buf_sz, 0) == -1) {
+ syslog(LOG_ERR, "dyndns: TKEY send error\n");
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ return (-1);
+ }
+
+ bzero(buf2, MAX_TCP_SIZE);
+ if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) {
+ syslog(LOG_ERR, "dyndns: TKEY "
+ "reply recv error\n");
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ return (-1);
+ }
+
+ ret = buf2[5] & 0xf; /* error field in TCP */
+ if (ret != NOERROR) {
+ syslog(LOG_ERR, "dyndns: Error in "
+ "TKEY reply: %d: ", ret);
+ dyndns_msg_err(ret);
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ return (-1);
+ }
+
+ tmpptr = &buf2[2];
+ (void) dyndns_get_nshort(tmpptr, &rid);
+ if (id != rid) {
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+ return (-1);
+ }
+
+ tmpptr = &buf2[59+(strlen(key_name)+2)*2];
+ (void) dyndns_get_nshort(tmpptr, &rsz);
+ in_tok.length = rsz;
+
+ /* bsd38 -> 2*7=14 */
+ in_tok.value = &buf2[61+(strlen(key_name)+2)*2];
+ inputptr = &in_tok;
+ }
+
+ } while (maj != GSS_S_COMPLETE);
+
+ (void) gss_release_oid(&min, &oid);
+ (void) gss_release_name(&min, &target_name);
+
+ return (0);
+}
+
+/*
+ * dyndns_get_sec_context
+ * Get security context for secure dynamic DNS update. This routine opens
+ * a TCP socket to the DNS server and calls routines to get a handle to a
+ * locally cached user's credential and establish a security context with
+ * the DNS server to perform secure dynamic DNS update. If getting security
+ * context fails then a retry may be done after reobtaining new credential and
+ * getting a new credential handle. If obtaining new credential has been
+ * done earlier during getting a handle to credential then there is no need to
+ * do a retry for security context.
+ * Parameters:
+ * hostname: fully qualified hostname
+ * dns_ip : ip address of hostname in network byte order
+ * Returns:
+ * gss_handle: gss credential handle
+ * gss_context: gss security context
+ * -1: error
+ * 0: success
+ */
+static gss_ctx_id_t
+dyndns_get_sec_context(const char *hostname, int dns_ip)
+{
+ int s;
+ gss_cred_id_t credHandle;
+ gss_ctx_id_t gss_context;
+ gss_OID oid;
+ OM_uint32 min;
+ struct hostent *hentry;
+ int kinit_retry, do_acquire_cred;
+ char *key_name, dns_hostname[255], user_name[50];
+
+ key_name = (char *)hostname;
+
+ hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET);
+ if (hentry == NULL) {
+ syslog(LOG_ERR, "dyndns: Can't get DNS "
+ "hostname from DNS ip.\n");
+ return (NULL);
+ }
+ (void) strcpy(dns_hostname, hentry->h_name);
+
+ if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) {
+ return (NULL);
+ }
+
+ kinit_retry = 0;
+ do_acquire_cred = 0;
+ acquire_cred:
+
+ if (dyndns_acquire_cred(&credHandle, user_name, &oid, &kinit_retry)) {
+ (void) close(s);
+ return (NULL);
+ }
+
+ if (dyndns_establish_sec_ctx(&gss_context, credHandle, s, key_name,
+ dns_hostname, oid, user_name, &kinit_retry, &do_acquire_cred)) {
+ (void) gss_release_cred(&min, &credHandle);
+ if (do_acquire_cred) {
+ do_acquire_cred = 0;
+ goto acquire_cred;
+ }
+ (void) close(s);
+ return (NULL);
+ }
+
+ (void) close(s);
+
+ (void) gss_release_cred(&min, &credHandle);
+ return (gss_context);
+}
+
+/*
+ * dyndns_build_add_remove_msg
+ * This routine builds the update request message for adding and removing DNS
+ * entries which is used for non-secure and secure DNS update.
+ * This routine builds an UDP message.
+ * Parameters:
+ * buf : buffer to build message
+ * update_zone: the type of zone to update, use UPDATE_FORW for forward
+ * lookup zone, use UPDATE_REV for reverse lookup zone
+ * hostname : fully qualified hostname to update DNS with
+ * ip_addr : IP address of hostname
+ * life_time : cached time of this entry by others and not within DNS
+ * database
+ * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
+ * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
+ * entries of the same resource name. Only valid for UPDATE_DEL.
+ * addit_cnt : Indicate how many record is in the additional section of
+ * the DNS message. A value of zero is always used with
+ * non-secure update message. For secure update message,
+ * the value will be one because the signed TSIG message
+ * is added as the additional record of the DNS update message.
+ * id : DNS message ID. If a positive value then this ID value is
+ * used, otherwise the next incremented value is used
+ * level : This is the domain level which we send the request to, level
+ * zero is the default level, it can go upto 2 in reverse zone
+ * and virtually to any level in forward zone.
+ * Returns:
+ * buf : buffer containing update message
+ * id : DNS message ID
+ * int : size of update message
+ * -1 : error
+ *
+ * This function is changed to handle dynamic DNS update retires to higher
+ * authoritative domains.
+ */
+static int
+dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname,
+ const char *ip_addr, int life_time, int update_type, int del_type,
+ int addit_cnt, uint16_t *id, int level)
+{
+ int a, b, c, d;
+ char *bufptr;
+ int queryReq, zoneCount, preqCount, updateCount, additionalCount;
+ char *zone, *resource, *data, zone_buf[100], resrc_buf[100];
+ int zoneType, zoneClass, type, class, ttl;
+ char *p;
+
+ queryReq = REQ_UPDATE;
+ zoneCount = 1;
+ preqCount = 0;
+ updateCount = 1;
+ additionalCount = addit_cnt;
+
+ (void) memset(buf, 0, NS_PACKETSZ);
+ bufptr = buf;
+
+ if (*id == 0)
+ *id = smb_get_next_resid();
+
+ if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq,
+ zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
+ return (-1);
+ }
+
+ zoneType = ns_t_soa;
+ zoneClass = ns_c_in;
+
+ if (update_zone == UPDATE_FORW) {
+ p = (char *)hostname;
+
+ /* Try higher domains according to the level requested */
+ do {
+ /* domain */
+ if ((zone = (char *)strchr(p, '.')) == NULL)
+ return (-1);
+ zone += 1;
+ p = zone;
+ } while (--level >= 0);
+ resource = (char *)hostname;
+ data = (char *)ip_addr;
+ } else {
+ (void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d);
+ (void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa", c, b, a);
+ zone = p = zone_buf;
+
+ /* Try higher domains according to the level requested */
+ while (--level >= 0) {
+ /* domain */
+ if ((zone = (char *)strchr(p, '.')) == NULL) {
+ return (-1);
+ }
+ zone += 1;
+ p = zone;
+ }
+
+ (void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa",
+ d, c, b, a);
+ resource = resrc_buf; /* ip info */
+ data = (char *)hostname;
+ }
+
+ if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone,
+ zoneType, zoneClass) == -1) {
+ return (-1);
+ }
+
+ if (update_zone == UPDATE_FORW)
+ type = ns_t_a;
+ else
+ type = ns_t_ptr;
+
+ if (update_type == UPDATE_ADD) {
+ class = ns_c_in;
+ ttl = life_time;
+ } else {
+ if (del_type == DEL_ONE)
+ class = ns_c_none; /* remove one */
+ else
+ class = ns_c_any; /* remove all */
+ ttl = 0;
+ }
+ if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf),
+ resource, type, class, ttl, data, update_zone,
+ update_type, del_type) == -1) {
+ return (-1);
+ }
+
+ return (bufptr - buf);
+}
+
+/*
+ * dyndns_build_unsigned_tsig_msg
+ * This routine is used to build the unsigned TSIG message for signing. The
+ * unsigned TSIG message contains the update request message with certain TSIG
+ * fields included. An error time of 300 seconds is used for fudge time. This
+ * is the number used by Microsoft clients.
+ * This routine builds a UDP message.
+ * Parameters:
+ * buf : buffer to build message
+ * update_zone: the type of zone to update, use UPDATE_FORW for forward
+ * lookup zone, use UPDATE_REV for reverse lookup zone
+ * hostname : fully qualified hostname to update DNS with
+ * ip_addr : IP address of hostname
+ * life_time : cached time of this entry by others and not within DNS
+ * database
+ * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
+ * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
+ * entries of the same resource name. Only valid for UPDATE_DEL.
+ * key_name : same key name used in TKEY message
+ * id : DNS message ID. If a positive value then this ID value is
+ * used, otherwise the next incremented value is used
+ * level : This is the domain level which we send the request to, level
+ * zero is the default level, it can go upto 2 in reverse zone
+ * and virtually to any level in forward zone.
+ * Returns:
+ * buf : buffer containing update message
+ * id : DNS message ID
+ * int : size of update message
+ * -1 : error
+ */
+static int
+dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname,
+ const char *ip_addr, int life_time, int update_type, int del_type,
+ char *key_name, uint16_t *id, int level)
+{
+ char *bufptr;
+ int buf_sz;
+
+ if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
+ ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) {
+ return (-1);
+ }
+
+ bufptr = buf + buf_sz;
+
+ if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0,
+ key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) {
+ return (-1);
+ }
+
+ return (bufptr - buf);
+}
+
+/*
+ * dyndns_build_signed_tsig_msg
+ * This routine build the signed TSIG message which contains the update
+ * request message encrypted. An error time of 300 seconds is used for fudge
+ * time. This is the number used by Microsoft clients.
+ * This routine builds a UDP message.
+ * Parameters:
+ * buf : buffer to build message
+ * update_zone: the type of zone to update, use UPDATE_FORW for forward
+ * lookup zone, use UPDATE_REV for reverse lookup zone
+ * hostname : fully qualified hostname to update DNS with
+ * ip_addr : IP address of hostname
+ * life_time : cached time of this entry by others and not within DNS
+ * database
+ * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
+ * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
+ * entries of the same resource name. Only valid for UPDATE_DEL.
+ * key_name : same key name used in TKEY message
+ * id : DNS message ID. If a positive value then this ID value is
+ * used, otherwise the next incremented value is used
+ * in_mic : the update request message encrypted
+ * level : This is the domain level which we send the request to, level
+ * zero is the default level, it can go upto 2 in reverse zone
+ * and virtually to any level in forward zone.
+ *
+ * Returns:
+ * buf : buffer containing update message
+ * id : DNS message ID
+ * int : size of update message
+ * -1 : error
+ */
+static int
+dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname,
+ const char *ip_addr, int life_time, int update_type, int del_type,
+ char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level)
+{
+ char *bufptr;
+ int buf_sz;
+
+ if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
+ ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) {
+ return (-1);
+ }
+
+ bufptr = buf + buf_sz;
+
+ if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf),
+ *id, key_name, 300, in_mic->value,
+ in_mic->length, TSIG_SIGNED) == -1) {
+ return (-1);
+ }
+
+ return (bufptr - buf);
+}
+
+/*
+ * dyndns_udp_send_recv
+ * This routine sends and receives UDP DNS request and reply messages. Time
+ * out value and retry count is indicated by two environment variables:
+ * lookup_dns_retry_cnt
+ * lookup_dns_retry_sec
+ * If either of these two variables are undefined or their value exceed the
+ * value of 10 then a default value of 3 retry and/or a default value of 3
+ * secs are used.
+ *
+ * Pre-condition: Caller must call dyndns_open_init_socket() before calling
+ * this function.
+ *
+ * Parameters:
+ * s : socket descriptor
+ * buf : buffer containing data to send
+ * buf_sz : size of data to send
+ * Returns:
+ * -1 : error
+ * rec_buf: reply dat
+ * 0 : success
+ */
+int
+dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf)
+{
+ int i, retval, addr_len, max_retries;
+ struct timeval tv, timeout;
+ fd_set rfds;
+ struct sockaddr_in from_addr;
+ char *p;
+
+ smb_config_rdlock();
+ p = smb_config_getstr(SMB_CI_DYNDNS_RETRY_COUNT);
+ if (p == NULL || *p == 0) {
+ max_retries = 3;
+ } else {
+ max_retries = atoi(p);
+ if (max_retries < 1 || max_retries > 10)
+ max_retries = 3;
+ }
+
+ p = smb_config_getstr(SMB_CI_DYNDNS_RETRY_SEC);
+ timeout.tv_usec = 0;
+ if (p == NULL || *p == 0) {
+ timeout.tv_sec = 3;
+ } else {
+ timeout.tv_sec = atoi(p);
+ if (timeout.tv_sec < 1 || timeout.tv_sec > 10)
+ timeout.tv_sec = 3;
+ }
+ smb_config_unlock();
+
+ for (i = 0; i < max_retries + 1; i++) {
+ if (send(s, buf, buf_sz, 0) == -1) {
+ syslog(LOG_ERR, "dyndns: UDP send error (%s)\n",
+ strerror(errno));
+ return (-1);
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(s, &rfds);
+
+ tv = timeout;
+
+ retval = select(s+1, &rfds, NULL, NULL, &tv);
+
+ if (retval == -1) {
+ return (-1);
+ } else if (retval > 0) {
+ bzero(rec_buf, NS_PACKETSZ);
+ /* required by recvfrom */
+ addr_len = sizeof (struct sockaddr_in);
+ if (recvfrom(s, rec_buf, NS_PACKETSZ, 0,
+ (struct sockaddr *)&from_addr, &addr_len) == -1) {
+ syslog(LOG_ERR, "dyndns: UDP recv err\n");
+ return (-1);
+ }
+ break;
+ }
+ }
+
+ if (i == (max_retries + 1)) { /* did not receive anything */
+ syslog(LOG_ERR, "dyndns: max retries for UDP recv reached\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * dyndns_sec_add_remove_entry
+ * Perform secure dynamic DNS update after getting security context.
+ * This routine opens a UDP socket to the DNS sever, gets the security context,
+ * builds the unsigned TSIG message and signed TSIG message. The signed TSIG
+ * message containing the encrypted update request message is sent to the DNS
+ * server. The response is received and check for error. If there is no
+ * error then credential handle and security context are released and the local
+ * NSS cached is purged.
+ * Parameters:
+ * update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
+ * hostname : fully qualified hostname
+ * ip_addr : ip address of hostname in string format
+ * life_time : cached time of this entry by others and not within DNS
+ * database
+ * max_retries : maximum retries for sending DNS update request
+ * recv_timeout: receive timeout
+ * update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
+ * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
+ * entries of the same resource name. Only valid for UPDATE_DEL
+ * dns_str : DNS IP address in string format
+ * Returns:
+ * -1: error
+ * 0: success
+ *
+ * This function is enhanced to handle the case of NOTAUTH error when DNS server
+ * is not authoritative for specified zone. In this case we need to resend the
+ * same request to the higher authoritative domains.
+ * This is true for both secure and unsecure dynamic DNS updates.
+ */
+static int
+dyndns_sec_add_remove_entry(int update_zone, const char *hostname,
+ const char *ip_addr, int life_time, int update_type, int del_type,
+ char *dns_str)
+{
+ int s2;
+ uint16_t id, rid;
+ char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
+ int ret;
+ OM_uint32 min, maj;
+ gss_buffer_desc in_mic, out_mic;
+ gss_ctx_id_t gss_context;
+ int dns_ip;
+ char *key_name;
+ int buf_sz;
+ int level = 0;
+
+ assert(dns_str);
+ assert(*dns_str);
+
+ dns_ip = inet_addr(dns_str);
+
+sec_retry_higher:
+
+ if ((gss_context = dyndns_get_sec_context(hostname,
+ dns_ip)) == NULL) {
+ return (-1);
+ }
+
+ key_name = (char *)hostname;
+
+ if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) {
+ (void) gss_delete_sec_context(&min, &gss_context, NULL);
+ return (-1);
+ }
+
+ id = 0;
+ if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname,
+ ip_addr, life_time, update_type, del_type,
+ key_name, &id, level)) <= 0) {
+ (void) close(s2);
+ (void) gss_delete_sec_context(&min, &gss_context, NULL);
+ return (-1);
+ }
+
+ in_mic.length = buf_sz;
+ in_mic.value = buf;
+
+ /* sign update message */
+ if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) !=
+ GSS_S_COMPLETE) {
+ display_stat(maj, min);
+ (void) close(s2);
+ (void) gss_delete_sec_context(&min, &gss_context, NULL);
+ return (-1);
+ }
+
+ if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname,
+ ip_addr, life_time, update_type, del_type, key_name, &id,
+ &out_mic, level)) <= 0) {
+ (void) close(s2);
+ (void) gss_release_buffer(&min, &out_mic);
+ (void) gss_delete_sec_context(&min, &gss_context, NULL);
+ return (-1);
+ }
+
+ (void) gss_release_buffer(&min, &out_mic);
+
+ if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) {
+ (void) close(s2);
+ (void) gss_delete_sec_context(&min, &gss_context, NULL);
+ return (-1);
+ }
+
+ (void) close(s2);
+
+ (void) gss_delete_sec_context(&min, &gss_context, NULL);
+
+ ret = buf2[3] & 0xf; /* error field in UDP */
+
+ /*
+ * If it is a NOTAUTH error we should retry with higher domains
+ * until we get a successful reply or the maximum retries is met.
+ */
+ if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
+ goto sec_retry_higher;
+
+ /* check here for update request is successful */
+ if (ret != NOERROR) {
+ syslog(LOG_ERR, "dyndns: Error in TSIG reply: %d: ", ret);
+ dyndns_msg_err(ret);
+ return (-1);
+ }
+
+ (void) dyndns_get_nshort(buf2, &rid);
+ if (id != rid)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * dyndns_seach_entry
+ * Query DNS server for entry. This routine can indicate if an entry exist
+ * or not during forward or reverse lookup. Also can indicate if the data
+ * of the entry matched. For example, for forward lookup, the entry is
+ * searched using the hostname and the data is the IP address. For reverse
+ * lookup, the entry is searched using the IP address and the data is the
+ * hostname.
+ * Parameters:
+ * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
+ * hostname : fully qualified hostname
+ * ip_addr : ip address of hostname in string format
+ * update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
+ * Returns:
+ * time_out: no use
+ * is_match: is 1 for found matching entry, otherwise 0
+ * 1 : an entry exist but not necessarily match
+ * 0 : an entry does not exist
+ */
+/*ARGSUSED*/
+static int
+dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr,
+ int update_type, struct timeval *time_out, int *is_match)
+{
+ struct hostent *hentry;
+ struct in_addr in;
+ in_addr_t ip;
+ int i;
+
+ *is_match = 0;
+ if (update_zone == UPDATE_FORW) {
+ hentry = gethostbyname(hostname);
+ if (hentry) {
+ ip = inet_addr(ip_addr);
+ for (i = 0; hentry->h_addr_list[i]; i++) {
+ (void) memcpy(&in.s_addr,
+ hentry->h_addr_list[i], sizeof (in.s_addr));
+ if (ip == in.s_addr) {
+ *is_match = 1;
+ break;
+ }
+ }
+ return (1);
+ }
+ } else {
+ int dns_ip = inet_addr(ip_addr);
+ hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET);
+ if (hentry) {
+ if (strncasecmp(hentry->h_name, hostname,
+ strlen(hostname)) == 0) {
+ *is_match = 1;
+ }
+ return (1);
+ }
+ }
+
+ /* entry does not exist */
+ return (0);
+}
+
+/*
+ * dyndns_add_remove_entry
+ * Perform non-secure dynamic DNS update. If fail then tries secure update.
+ * This routine opens a UDP socket to the DNS sever, build the update request
+ * message, and sends the message to the DNS server. The response is received
+ * and check for error. If there is no error then the local NSS cached is
+ * purged. DNS may be used to check to see if an entry already exist before
+ * adding or to see if an entry does exist before removing it. Adding
+ * duplicate entries or removing non-existing entries does not cause any
+ * problems. DNS is not check when doing a delete all.
+ * Parameters:
+ * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
+ * hostname : fully qualified hostname
+ * ip_addr : ip address of hostname in string format
+ * life_time : cached time of this entry by others and not within DNS
+ * database
+ * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
+ * do_check : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
+ * checking before update
+ * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
+ * entries of the same resource name. Only valid for UPDATE_DEL.
+ * dns_str : DNS IP address in string format
+ * Returns:
+ * -1: error
+ * 0: success
+ *
+ * This function is enhanced to handle the case of NOTAUTH error when DNS server
+ * is not authoritative for specified zone. In this case we need to resend the
+ * same request to the higher authoritative domains.
+ * This is true for both secure and unsecure dynamic DNS updates.
+ */
+static int
+dyndns_add_remove_entry(int update_zone, const char *hostname,
+ const char *ip_addr, int life_time, int update_type,
+ int do_check, int del_type, char *dns_str)
+{
+ int s;
+ uint16_t id, rid;
+ char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
+ int ret, dns_ip;
+ int is_exist, is_match;
+ struct timeval timeout;
+ int buf_sz;
+ int level = 0;
+
+ assert(dns_str);
+ assert(*dns_str);
+
+ dns_ip = inet_addr(dns_str);
+
+ if (do_check == DNS_CHECK && del_type != DEL_ALL) {
+ is_exist = dyndns_search_entry(update_zone, hostname, ip_addr,
+ update_type, &timeout, &is_match);
+
+ if (update_type == UPDATE_ADD && is_exist && is_match) {
+ return (0);
+ } else if (update_type == UPDATE_DEL && !is_exist) {
+ return (0);
+ }
+ }
+
+retry_higher:
+ if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) {
+ return (-1);
+ }
+
+ id = 0;
+ if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
+ ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) {
+ (void) close(s);
+ return (-1);
+ }
+
+ if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) {
+ (void) close(s);
+ return (-1);
+ }
+
+ (void) close(s);
+
+ ret = buf2[3] & 0xf; /* error field in UDP */
+
+ /*
+ * If it is a NOTAUTH error we should retry with higher domains
+ * until we get a successful reply
+ */
+ if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
+ goto retry_higher;
+
+ /* check here for update request is successful */
+ if (ret == NOERROR) {
+ (void) dyndns_get_nshort(buf2, &rid);
+ if (id != rid)
+ return (-1);
+ return (0);
+ }
+
+ if (ret == NOTIMP) {
+ syslog(LOG_ERR, "dyndns: DNS does not "
+ "support dynamic update\n");
+ return (-1);
+ } else if (ret == NOTAUTH) {
+ syslog(LOG_ERR, "dyndns: DNS is not authoritative for "
+ "zone name in zone section\n");
+ return (-1);
+ }
+
+ ret = dyndns_sec_add_remove_entry(update_zone, hostname,
+ ip_addr, life_time, update_type, del_type, dns_str);
+
+ return (ret);
+}
+
+/*
+ * dyndns_add_entry
+ * Main routine to add an entry into DNS. The attempt will be made on the
+ * the servers returned by smb_get_nameserver(). Upon a successful
+ * attempt on any one of the server, the function will exit with 0.
+ * Otherwise, -1 is retuned to indicate the update attempt on all the
+ * nameservers has failed.
+ *
+ * Parameters:
+ * update_zone: the type of zone to update, use UPDATE_FORW for forward
+ * lookup zone, use UPDATE_REV for reverse lookup zone
+ * hostname : fully qualified hostname
+ * ip_addr : ip address of hostname in string format
+ * life_time : cached time of this entry by others and not within DNS
+ * database
+ * Returns:
+ * -1: error
+ * 0: success
+ */
+static int
+dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr,
+ int life_time)
+{
+ char *dns_str;
+ struct in_addr ns_list[MAXNS];
+ int i, cnt;
+ int addr, rc = 0;
+
+ if (hostname == NULL || ip_addr == NULL) {
+ return (-1);
+ }
+
+ addr = (int)inet_addr(ip_addr);
+ if ((addr == -1) || (addr == 0)) {
+ return (-1);
+ }
+
+ cnt = smb_get_nameservers(ns_list, MAXNS);
+
+ for (i = 0; i < cnt; i++) {
+ dns_str = inet_ntoa(ns_list[i]);
+ if ((dns_str == NULL) ||
+ (strcmp(dns_str, "0.0.0.0") == 0)) {
+ continue;
+ }
+
+ if (update_zone == UPDATE_FORW) {
+ syslog(LOG_DEBUG, "Dynamic update on forward lookup "
+ "zone for %s (%s)...\n", hostname, ip_addr);
+ } else {
+ syslog(LOG_DEBUG, "Dynamic update on reverse lookup "
+ "zone for %s (%s)...\n", hostname, ip_addr);
+ }
+ if (dyndns_add_remove_entry(update_zone, hostname,
+ ip_addr, life_time,
+ UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_str) != -1) {
+ rc = 1;
+ break;
+ }
+ }
+
+ return (rc ? 0 : -1);
+}
+
+/*
+ * dyndns_remove_entry
+ * Main routine to remove an entry or all entries of the same resource name
+ * from DNS. The update attempt will be made on the primary DNS server. If
+ * there is a failure then another attempt will be made on the secondary DNS
+ * server.
+ * Parameters:
+ * update_zone: the type of zone to update, use UPDATE_FORW for forward
+ * lookup zone, use UPDATE_REV for reverse lookup zone
+ * hostname : fully qualified hostname
+ * ip_addr : ip address of hostname in string format
+ * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all
+ * entries of the same resource name. Only valid for UPDATE_DEL
+ * Returns:
+ * -1: error
+ * 0: success
+ */
+static int
+dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr,
+ int del_type)
+{
+ char *dns_str;
+ struct in_addr ns_list[MAXNS];
+ int i, cnt, scnt;
+ int addr;
+
+ if ((hostname == NULL || ip_addr == NULL)) {
+ return (-1);
+ }
+
+ addr = (int)inet_addr(ip_addr);
+ if ((addr == -1) || (addr == 0)) {
+ return (-1);
+ }
+
+ cnt = smb_get_nameservers(ns_list, MAXNS);
+ scnt = 0;
+
+ for (i = 0; i < cnt; i++) {
+ dns_str = inet_ntoa(ns_list[i]);
+ if ((dns_str == NULL) ||
+ (strcmp(dns_str, "0.0.0.0") == 0)) {
+ continue;
+ }
+
+ if (update_zone == UPDATE_FORW) {
+ if (del_type == DEL_ONE) {
+ syslog(LOG_DEBUG, "Dynamic update "
+ "on forward lookup "
+ "zone for %s (%s)...\n", hostname, ip_addr);
+ } else {
+ syslog(LOG_DEBUG, "Removing all "
+ "entries of %s "
+ "in forward lookup zone...\n", hostname);
+ }
+ } else {
+ if (del_type == DEL_ONE) {
+ syslog(LOG_DEBUG, "Dynamic update "
+ "on reverse lookup "
+ "zone for %s (%s)...\n", hostname, ip_addr);
+ } else {
+ syslog(LOG_DEBUG, "Removing all "
+ "entries of %s "
+ "in reverse lookup zone...\n", ip_addr);
+ }
+ }
+ if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0,
+ UPDATE_DEL, DNS_NOCHECK, del_type, dns_str) != -1) {
+ scnt++;
+ break;
+ }
+ }
+ if (scnt)
+ return (0);
+ return (-1);
+}
+
+/*
+ * dyndns_update
+ * Perform dynamic update on both forward and reverse lookup zone using
+ * current hostname and IP addresses. Before updating DNS, existing host
+ * entries with the same hostname in the forward lookup zone are removed
+ * and existing pointer entries with the same IP addresses in the reverse
+ * lookup zone are removed. After DNS update, host entries for current
+ * hostname will show current IP addresses and pointer entries for current
+ * IP addresses will show current hostname.
+ * Parameters:
+ * None
+ * Returns:
+ * -1: some dynamic DNS updates errors
+ * 0: successful
+ */
+int
+dyndns_update(void)
+{
+ int i, forw_update_ok, error;
+ char fqdn[MAXHOSTNAMELEN];
+ char *my_ip;
+ int nc_cnt;
+ struct in_addr addr;
+ int rc;
+
+ if (!dyndns_enabled())
+ return (-1);
+
+ if (smb_getfqhostname(fqdn, MAXHOSTNAMELEN) != 0)
+ return (-1);
+
+ nc_cnt = smb_nic_get_num();
+
+ error = 0;
+ forw_update_ok = 0;
+
+ /*
+ * Dummy IP is okay since we are removing all using the hostname.
+ */
+ if (dyndns_remove_entry(UPDATE_FORW, fqdn, "1.1.1.1", DEL_ALL) == 0) {
+ forw_update_ok = 1;
+ } else {
+ error++;
+ }
+
+ for (i = 0; i < nc_cnt; i++) {
+ net_cfg_t cfg;
+ if (smb_nic_get_byind(i, &cfg) == NULL)
+ break;
+ addr.s_addr = cfg.ip;
+ if (addr.s_addr == 0)
+ continue;
+ if (smb_nic_status(cfg.ifname, IFF_STANDBY) ||
+ smb_nic_status(cfg.ifname, IFF_PRIVATE))
+ continue;
+
+ my_ip = (char *)strdup(inet_ntoa(addr));
+ if (my_ip == NULL) {
+ error++;
+ continue;
+ }
+
+ if (forw_update_ok) {
+ rc = dyndns_add_entry(UPDATE_FORW, fqdn, my_ip,
+ DDNS_TTL);
+
+ if (rc == -1)
+ error++;
+ }
+
+ rc = dyndns_remove_entry(UPDATE_REV, fqdn, my_ip, DEL_ALL);
+ if (rc == 0) {
+ rc = dyndns_add_entry(UPDATE_REV, fqdn, my_ip,
+ DDNS_TTL);
+ }
+
+ if (rc == -1)
+ error++;
+
+ (void) free(my_ip);
+ }
+
+ return ((error == 0) ? 0 : -1);
+}
+
+/*
+ * dyndns_clear_rev_zone
+ * Clear the rev zone records. Must be called to clear the OLD if list
+ * of down records prior to updating the list with new information.
+ *
+ * Parameters:
+ * None
+ * Returns:
+ * -1: some dynamic DNS updates errors
+ * 0: successful
+ */
+int
+dyndns_clear_rev_zone(void)
+{
+ int i, error;
+ char fqdn[MAXHOSTNAMELEN];
+ char *my_ip;
+ int nc_cnt;
+ struct in_addr addr;
+ int rc;
+
+ if (!dyndns_enabled())
+ return (-1);
+
+ if (smb_getfqhostname(fqdn, MAXHOSTNAMELEN) != 0)
+ return (-1);
+
+ nc_cnt = smb_nic_get_num();
+
+ error = 0;
+
+ for (i = 0; i < nc_cnt; i++) {
+ net_cfg_t cfg;
+ if (smb_nic_get_byind(i, &cfg) == NULL)
+ break;
+ addr.s_addr = cfg.ip;
+ if (addr.s_addr == 0)
+ continue;
+ if (smb_nic_status(cfg.ifname, IFF_STANDBY) ||
+ smb_nic_status(cfg.ifname, IFF_PRIVATE))
+ continue;
+
+ my_ip = (char *)strdup(inet_ntoa(addr));
+ if (my_ip == NULL) {
+ error++;
+ continue;
+ }
+
+ rc = dyndns_remove_entry(UPDATE_REV, fqdn, my_ip, DEL_ALL);
+ if (rc != 0)
+ error++;
+
+ (void) free(my_ip);
+ }
+
+ return ((error == 0) ? 0 : -1);
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.h
new file mode 100644
index 0000000000..7140eb4d59
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.h
@@ -0,0 +1,205 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMBSRV_DYNDNS_H
+#define _SMBSRV_DYNDNS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <smbsrv/libsmbns.h>
+
+/*
+ * Header section format:
+ *
+ * The header contains the following fields:
+ *
+ * 1 1 1 1 1 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | ID |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | QDCOUNT |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | ANCOUNT |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | NSCOUNT |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | ARCOUNT |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ * where:
+ *
+ * ID A 16 bit identifier assigned by the program that
+ * generates any kind of query. This identifier is copied
+ * the corresponding reply and can be used by the requester
+ * to match up replies to outstanding queries.
+ *
+ * QR A one bit field that specifies whether this message is a
+ * query (0), or a response (1).
+ *
+ * OPCODE A four bit field that specifies kind of query in this
+ * message. This value is set by the originator of a query
+ * and copied into the response. The values are:
+ *
+ * 0 a standard query (QUERY)
+ *
+ * 1 an inverse query (IQUERY)
+ *
+ * 2 a server status request (STATUS)
+ *
+ * 3-15 reserved for future use
+ *
+ * AA Authoritative Answer - this bit is valid in responses,
+ * and specifies that the responding name server is an
+ * authority for the domain name in question section.
+ *
+ * Note that the contents of the answer section may have
+ * multiple owner names because of aliases. The AA bit
+ *
+ * corresponds to the name which matches the query name, or
+ * the first owner name in the answer section.
+ *
+ * TC TrunCation - specifies that this message was truncated
+ * due to length greater than that permitted on the
+ * transmission channel.
+ *
+ * RD Recursion Desired - this bit may be set in a query and
+ * is copied into the response. If RD is set, it directs
+ * the name server to pursue the query recursively.
+ * Recursive query support is optional.
+ *
+ * RA Recursion Available - this be is set or cleared in a
+ * response, and denotes whether recursive query support is
+ * available in the name server.
+ *
+ * Z Reserved for future use. Must be zero in all queries
+ * and responses.
+ *
+ * RCODE Response code - this 4 bit field is set as part of
+ * responses. The values have the following
+ * interpretation:
+ *
+ * 0 No error condition
+ *
+ * 1 Format error - The name server was
+ * unable to interpret the query.
+ *
+ * 2 Server failure - The name server was
+ * unable to process this query due to a
+ * problem with the name server.
+ *
+ * 3 Name Error - Meaningful only for
+ * responses from an authoritative name
+ * server, this code signifies that the
+ * domain name referenced in the query does
+ * not exist.
+ *
+ * 4 Not Implemented - The name server does
+ * not support the requested kind of query.
+ *
+ * 5 Refused - The name server refuses to
+ * perform the specified operation for
+ * policy reasons. For example, a name
+ * server may not wish to provide the
+ * information to the particular requester,
+ * or a name server may not wish to perform
+ * a particular operation (e.g., zone
+ *
+ * transfer) for particular data.
+ *
+ * 6-15 Reserved for future use.
+ *
+ * QDCOUNT an unsigned 16 bit integer specifying the number of
+ * entries in the question section.
+ *
+ * ANCOUNT an unsigned 16 bit integer specifying the number of
+ * resource records in the answer section.
+ *
+ * NSCOUNT an unsigned 16 bit integer specifying the number of name
+ * server resource records in the authority records
+ * section.
+ *
+ * ARCOUNT an unsigned 16 bit integer specifying the number of
+ * resource records in the additional records section.
+ */
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Other definitions: */
+#define REQ_QUERY 1 /* DNS query request */
+#define REQ_UPDATE 0 /* DNS update request */
+#define UPDATE_FORW 1 /* Update forward lookup zone */
+#define UPDATE_REV 0 /* Update reverse lookup zone */
+#define UPDATE_ADD 1 /* Update add request */
+#define UPDATE_DEL 0 /* Update remove request */
+#define MODE_GSS_API 3 /* Key negotiation mode */
+
+/* Max buffer size for send and receive buffer */
+#define MAX_BUF_SIZE 2000
+#define MAX_RETRIES 3 /* Max number of send retries if no response */
+#define TSIG_SIGNED 1 /* TSIG contains signed data */
+#define TSIG_UNSIGNED 0 /* TSIG does not conain signed data */
+#define DNS_CHECK 1 /* Check DNS for entry */
+#define DNS_NOCHECK 0 /* Don't check DNS for entry */
+#define MAX_TCP_SIZE 2000 /* max tcp DNS message size */
+
+/* Delete 1 entry */
+#define DEL_ONE 1
+/* Delete all entries of the same resource name */
+#define DEL_ALL 0
+
+#define DNSF_RECUR_SUPP 0x80 /* Server can do recursive queries */
+#define DNSF_RECUR_QRY 0x100 /* Query is recursive */
+
+#define BUFLEN_TCP(x, y) (MAX_TCP_SIZE-(x-y))
+#define BUFLEN_UDP(x, y) (NS_PACKETSZ-(x-y))
+
+extern char *dyndns_get_nshort(char *, uint16_t *);
+extern char *dyndns_get_int(char *, int *);
+extern int dyndns_build_header(char **, int, uint16_t, int,
+ uint16_t, uint16_t, uint16_t, uint16_t, int);
+extern int dyndns_build_quest_zone(char **, int, char *, int, int);
+extern int dyndns_open_init_socket(int sock_type, unsigned long dest_addr,
+ int port);
+extern int dyndns_udp_send_recv(int, char *, int, char *);
+extern void dyndns_msg_err(int);
+
+/*
+ * DDNS_TTL is the time to live in DNS caches. Note that this
+ * does not affect the entry in the authoritative DNS database.
+ */
+#define DDNS_TTL 1200
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMBSRV_DYNDNS_H */
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c
new file mode 100644
index 0000000000..c1a2de7913
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c
@@ -0,0 +1,543 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 1990 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Initialize a credentials cache.
+ */
+#include <kerberosv5/krb5.h>
+#include <kerberosv5/com_err.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <locale.h>
+#include <strings.h>
+#include <sys/synch.h>
+#include <gssapi/gssapi.h>
+
+#include <smbsrv/libsmbns.h>
+
+#include <smbns_krb.h>
+
+static int krb5_acquire_cred_kinit_main();
+
+typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
+
+struct k_opts {
+ /* in seconds */
+ krb5_deltat starttime;
+ krb5_deltat lifetime;
+ krb5_deltat rlife;
+
+ int forwardable;
+ int proxiable;
+ int addresses;
+
+ int not_forwardable;
+ int not_proxiable;
+ int no_addresses;
+
+ int verbose;
+
+ char *principal_name;
+ char *principal_passwd;
+ char *service_name;
+ char *keytab_name;
+ char *k5_cache_name;
+ char *k4_cache_name;
+
+ action_type action;
+};
+
+struct k5_data {
+ krb5_context ctx;
+ krb5_ccache cc;
+ krb5_principal me;
+ char *name;
+};
+
+static int
+k5_begin(struct k_opts *opts, struct k5_data *k5)
+{
+ int code;
+ code = krb5_init_context(&k5->ctx);
+ if (code) {
+ return (code);
+ }
+
+ if ((code = krb5_cc_default(k5->ctx, &k5->cc))) {
+ return (code);
+ }
+
+ /* Use specified name */
+ if ((code = krb5_parse_name(k5->ctx, opts->principal_name, &k5->me))) {
+ return (code);
+ }
+
+ code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
+ if (code) {
+ return (code);
+ }
+ opts->principal_name = k5->name;
+
+ return (0);
+}
+
+static void
+k5_end(struct k5_data *k5)
+{
+ if (k5->name)
+ krb5_free_unparsed_name(k5->ctx, k5->name);
+ if (k5->me)
+ krb5_free_principal(k5->ctx, k5->me);
+ if (k5->cc)
+ krb5_cc_close(k5->ctx, k5->cc);
+ if (k5->ctx)
+ krb5_free_context(k5->ctx);
+ (void) memset(k5, 0, sizeof (*k5));
+}
+
+static int
+k5_kinit(struct k_opts *opts, struct k5_data *k5)
+{
+ int notix = 1;
+ krb5_keytab keytab = 0;
+ krb5_creds my_creds;
+ krb5_error_code code = 0;
+ krb5_get_init_creds_opt options;
+ const char *errmsg;
+
+ krb5_get_init_creds_opt_init(&options);
+ (void) memset(&my_creds, 0, sizeof (my_creds));
+
+ /*
+ * From this point on, we can goto cleanup because my_creds is
+ * initialized.
+ */
+ if (opts->lifetime)
+ krb5_get_init_creds_opt_set_tkt_life(&options, opts->lifetime);
+ if (opts->rlife)
+ krb5_get_init_creds_opt_set_renew_life(&options, opts->rlife);
+ if (opts->forwardable)
+ krb5_get_init_creds_opt_set_forwardable(&options, 1);
+ if (opts->not_forwardable)
+ krb5_get_init_creds_opt_set_forwardable(&options, 0);
+ if (opts->proxiable)
+ krb5_get_init_creds_opt_set_proxiable(&options, 1);
+ if (opts->not_proxiable)
+ krb5_get_init_creds_opt_set_proxiable(&options, 0);
+ if (opts->addresses) {
+ krb5_address **addresses = NULL;
+ code = krb5_os_localaddr(k5->ctx, &addresses);
+ if (code != 0) {
+ errmsg = error_message(code);
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "k5_kinit: "
+ "getting local addresses (%s)"), errmsg);
+ goto cleanup;
+ }
+ krb5_get_init_creds_opt_set_address_list(&options, addresses);
+ }
+ if (opts->no_addresses)
+ krb5_get_init_creds_opt_set_address_list(&options, NULL);
+
+ if ((opts->action == INIT_KT) && opts->keytab_name) {
+ code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab);
+ if (code != 0) {
+ errmsg = error_message(code);
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "k5_kinit: "
+ "resolving keytab %s (%s)"), errmsg,
+ opts->keytab_name);
+ goto cleanup;
+ }
+ }
+
+ switch (opts->action) {
+ case INIT_PW:
+ code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me,
+ opts->principal_passwd, NULL, 0, opts->starttime,
+ opts->service_name, &options);
+ break;
+ case INIT_KT:
+ code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me,
+ keytab, opts->starttime, opts->service_name, &options);
+ break;
+ case VALIDATE:
+ code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me,
+ k5->cc, opts->service_name);
+ break;
+ case RENEW:
+ code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me,
+ k5->cc, opts->service_name);
+ break;
+ }
+
+ if (code) {
+ char *doing = 0;
+ switch (opts->action) {
+ case INIT_PW:
+ case INIT_KT:
+ doing = dgettext(TEXT_DOMAIN, "k5_kinit: "
+ "getting initial credentials");
+ break;
+ case VALIDATE:
+ doing = dgettext(TEXT_DOMAIN, "k5_kinit: "
+ "validating credentials");
+ break;
+ case RENEW:
+ doing = dgettext(TEXT_DOMAIN, "k5_kinit: "
+ "renewing credentials");
+ break;
+ }
+
+ /*
+ * If got code == KRB5_AP_ERR_V4_REPLY && got_k4, we should
+ * let the user know that maybe he/she wants -4.
+ */
+ if (code == KRB5KRB_AP_ERR_V4_REPLY) {
+ syslog(LOG_ERR, "%s\n"
+ "The KDC doesn't support v5. "
+ "You may want the -4 option in the future", doing);
+ return (1);
+ } else if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "%s "
+ "(Password incorrect)"), doing);
+ } else {
+ errmsg = error_message(code);
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "%s (%s)"),
+ doing, errmsg);
+ }
+ goto cleanup;
+ }
+
+ if (!opts->lifetime) {
+ /* We need to figure out what lifetime to use for Kerberos 4. */
+ opts->lifetime = my_creds.times.endtime -
+ my_creds.times.authtime;
+ }
+
+ code = krb5_cc_initialize(k5->ctx, k5->cc, k5->me);
+ if (code) {
+ errmsg = error_message(code);
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "k5_kinit: "
+ "initializing cache %s (%s)"),
+ opts->k5_cache_name?opts->k5_cache_name:"", errmsg);
+ goto cleanup;
+ }
+
+ code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds);
+ if (code) {
+ errmsg = error_message(code);
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "k5_kinit: "
+ "storing credentials (%s)"), errmsg);
+ goto cleanup;
+ }
+
+ notix = 0;
+
+ cleanup:
+ if (my_creds.client == k5->me) {
+ my_creds.client = 0;
+ }
+ krb5_free_cred_contents(k5->ctx, &my_creds);
+ if (keytab)
+ krb5_kt_close(k5->ctx, keytab);
+ return (notix?0:1);
+}
+
+int
+smb_kinit(char *user, char *passwd)
+{
+ struct k_opts opts;
+ struct k5_data k5;
+ int authed_k5 = 0;
+
+ (void) memset(&opts, 0, sizeof (opts));
+ opts.action = INIT_PW;
+ opts.principal_name = strdup(user);
+ opts.principal_passwd = strdup(passwd);
+
+ (void) memset(&k5, 0, sizeof (k5));
+
+ if (k5_begin(&opts, &k5) != 0) {
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "smb_kinit: "
+ "NOT Authenticated to Kerberos v5 k5_begin failed\n"));
+ return (0);
+ }
+
+ authed_k5 = k5_kinit(&opts, &k5);
+ if (authed_k5) {
+ syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, "smb_kinit: "
+ "Authenticated to Kerberos v5\n"));
+ } else {
+ syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, "smb_kinit: "
+ "NOT Authenticated to Kerberos v5\n"));
+ }
+
+ k5_end(&k5);
+
+ return (authed_k5);
+}
+
+/*
+ * krb5_display_stat
+ * Display error message for GSS-API routines.
+ * Parameters:
+ * maj : GSS major status
+ * min : GSS minor status
+ * caller_mod: module name that calls this routine so that the module name
+ * can be displayed with the error messages
+ * Returns:
+ * None
+ */
+static void
+krb5_display_stat(OM_uint32 maj, OM_uint32 min, char *caller_mod)
+{
+ gss_buffer_desc msg;
+ OM_uint32 msg_ctx = 0;
+ OM_uint32 min2;
+ (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ syslog(LOG_ERR, "%s: major status error: %s\n",
+ caller_mod, (char *)msg.value);
+ (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ syslog(LOG_ERR, "%s: minor status error: %s\n",
+ caller_mod, (char *)msg.value);
+}
+
+/*
+ * krb5_acquire_cred_kinit
+ *
+ * Wrapper for krb5_acquire_cred_kinit_main with mutex to protect credential
+ * cache file when calling krb5_acquire_cred or kinit.
+ */
+
+int
+krb5_acquire_cred_kinit(char *user, char *pwd, gss_cred_id_t *cred_handle,
+ gss_OID *oid, int *kinit_retry, char *caller_mod)
+{
+ int ret;
+
+ ret = krb5_acquire_cred_kinit_main(user, pwd,
+ cred_handle, oid, kinit_retry, caller_mod);
+ return (ret);
+}
+
+/*
+ * krb5_acquire_cred_kinit_main
+ *
+ * This routine is called both by ADS and Dyn DNS modules to get a handle to
+ * administrative user's credential stored locally on the system. The
+ * credential is the TGT. If the attempt at getting handle fails then a second
+ * attempt will be made after getting a new TGT.
+ *
+ * Paramters:
+ * user : username to retrieve a handle to its credential
+ * pwd : password of username in case obtaining a new TGT is needed
+ * kinit_retry: if 0 then a second attempt will be made to get handle to the
+ * credential if the first attempt fails
+ * caller_mod : name of module that call this routine so that the module name
+ * can be included with error messages
+ * Returns:
+ * cred_handle: handle to the administrative user's credential (TGT)
+ * oid : contains Kerberos 5 object identifier
+ * kinit_retry: A 1 indicates that a second attempt has been made to get
+ * handle to the credential and no further attempts can be made
+ * -1 : error
+ * 0 : success
+ */
+static int
+krb5_acquire_cred_kinit_main(char *user, char *pwd, gss_cred_id_t *cred_handle,
+ gss_OID *oid, int *kinit_retry, char *caller_mod)
+{
+ OM_uint32 maj, min;
+ gss_name_t desired_name;
+ gss_OID_set desired_mechs;
+ gss_buffer_desc oidstr, name_buf;
+ char str[50], user_name[50];
+
+ acquire_cred:
+
+ /* Object Identifier for Kerberos 5 */
+ (void) strcpy(str, "{ 1 2 840 113554 1 2 2 }");
+ oidstr.value = str;
+ oidstr.length = strlen(str);
+ if ((maj = gss_str_to_oid(&min, &oidstr, oid)) != GSS_S_COMPLETE) {
+ krb5_display_stat(maj, min, caller_mod);
+ return (-1);
+ }
+ if ((maj = gss_create_empty_oid_set(&min, &desired_mechs))
+ != GSS_S_COMPLETE) {
+ krb5_display_stat(maj, min, caller_mod);
+ (void) gss_release_oid(&min, oid);
+ return (-1);
+ }
+ if ((maj = gss_add_oid_set_member(&min, *oid, &desired_mechs))
+ != GSS_S_COMPLETE) {
+ krb5_display_stat(maj, min, caller_mod);
+ (void) gss_release_oid(&min, oid);
+ (void) gss_release_oid_set(&min, &desired_mechs);
+ return (-1);
+ }
+
+ (void) strcpy(user_name, user);
+ name_buf.value = user_name;
+ name_buf.length = strlen(user_name)+1;
+ if ((maj = gss_import_name(&min, &name_buf, GSS_C_NT_USER_NAME,
+ &desired_name)) != GSS_S_COMPLETE) {
+ krb5_display_stat(maj, min, caller_mod);
+ (void) gss_release_oid(&min, oid);
+ (void) gss_release_oid_set(&min, &desired_mechs);
+ return (-1);
+ }
+
+ if ((maj = gss_acquire_cred(&min, desired_name, 0, desired_mechs,
+ GSS_C_INITIATE, cred_handle, NULL, NULL)) != GSS_S_COMPLETE) {
+ if (!*kinit_retry) {
+ (void) gss_release_oid(&min, oid);
+ (void) gss_release_oid_set(&min, &desired_mechs);
+ (void) gss_release_name(&min, &desired_name);
+ syslog(LOG_ERR, "%s: Retry kinit to "
+ "acquire credential.\n", caller_mod);
+ (void) smb_kinit(user, pwd);
+ *kinit_retry = 1;
+ goto acquire_cred;
+ } else {
+ krb5_display_stat(maj, min, caller_mod);
+ (void) gss_release_oid(&min, oid);
+ (void) gss_release_oid_set(&min, &desired_mechs);
+ (void) gss_release_name(&min, &desired_name);
+ return (-1);
+ }
+ }
+ (void) gss_release_oid_set(&min, &desired_mechs);
+ (void) gss_release_name(&min, &desired_name);
+
+ return (0);
+}
+
+/*
+ * krb5_establish_sec_ctx_kinit
+ *
+ * This routine is called by both the ADS and Dyn DNS modules to establish a
+ * security context before ADS or Dyn DNS updates are allowed. If establishing
+ * a security context fails for any reason, a second attempt will be made after
+ * a new TGT is obtained. This routine is called many time as needed until
+ * a security context is established.
+ *
+ * The resources use for the security context must be released if security
+ * context establishment process fails.
+ * Parameters:
+ * user : user used in establishing a security context for. Is used for
+ * obtaining a new TGT for a second attempt at establishing
+ * security context
+ * pwd : password of above user
+ * cred_handle: a handle to the user credential (TGT) stored locally
+ * gss_context: initially set to GSS_C_NO_CONTEXT but will contain a handle
+ * to a security context
+ * target_name: contains service name to establish a security context with,
+ * ie ldap or dns
+ * gss_flags : flags used in establishing security context
+ * inputptr : initially set to GSS_C_NO_BUFFER but will be token data
+ * received from service's server to be processed to generate
+ * further token to be sent back to service's server during
+ * security context establishment
+ * kinit_retry: if 0 then a second attempt will be made to get handle to the
+ * credential if the first attempt fails
+ * caller_mod : name of module that call this routine so that the module name
+ * can be included with error messages
+ * Returns:
+ * gss_context : a handle to a security context
+ * out_tok : token data to be sent to service's server to establish
+ * security context
+ * ret_flags : return flags
+ * time_rec : valid time for security context, not currently used
+ * kinit_retry : A 1 indicates that a second attempt has been made to get
+ * handle to the credential and no further attempts can be
+ * made
+ * do_acquire_cred: A 1 indicates that a new handle to the local credential
+ * is needed for second attempt at security context
+ * establishment
+ * maj : major status code used if determining is security context
+ * establishment is successful
+ */
+int
+krb5_establish_sec_ctx_kinit(char *user, char *pwd,
+ gss_cred_id_t cred_handle, gss_ctx_id_t *gss_context,
+ gss_name_t target_name, gss_OID oid, int gss_flags,
+ gss_buffer_desc *inputptr, gss_buffer_desc* out_tok,
+ OM_uint32 *ret_flags, OM_uint32 *time_rec,
+ int *kinit_retry, int *do_acquire_cred,
+ OM_uint32 *maj, char *caller_mod)
+{
+ OM_uint32 min;
+
+ *maj = gss_init_sec_context(&min, cred_handle, gss_context,
+ target_name, oid, gss_flags, 0, NULL, inputptr, NULL,
+ out_tok, ret_flags, time_rec);
+ if (*maj != GSS_S_COMPLETE && *maj != GSS_S_CONTINUE_NEEDED) {
+ if (*gss_context != NULL)
+ (void) gss_delete_sec_context(&min, gss_context, NULL);
+
+ if (!*kinit_retry) {
+ syslog(LOG_ERR, "%s: Retry kinit to establish "
+ "security context.\n", caller_mod);
+ (void) smb_kinit(user, pwd);
+ *kinit_retry = 1;
+ *do_acquire_cred = 1;
+ return (-1);
+ } else {
+ krb5_display_stat(*maj, min, caller_mod);
+ return (-1);
+ }
+ }
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h
new file mode 100644
index 0000000000..22028fcdb5
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h
@@ -0,0 +1,64 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMBSRV_SMB_KRB5_H
+#define _SMBSRV_SMB_KRB5_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <gssapi/gssapi.h>
+#include <kerberosv5/krb5.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern gss_OID gss_nt_user_name;
+extern gss_OID gss_nt_machine_uid_name;
+extern gss_OID gss_nt_string_uid_name;
+extern gss_OID gss_nt_service_name;
+extern gss_OID gss_nt_exported_name;
+extern gss_OID gss_nt_service_name_v2;
+
+int krb5_acquire_cred_kinit(char *, char *, gss_cred_id_t *,
+ gss_OID *, int *, char *);
+int krb5_establish_sec_ctx_kinit(char *, char *, gss_cred_id_t,
+ gss_ctx_id_t *, gss_name_t, gss_OID, int, gss_buffer_desc *,
+ gss_buffer_desc *, OM_uint32 *, OM_uint32 *, int *,
+ int *, OM_uint32 *, char *);
+int smb_krb5_ctx_init(krb5_context *ctx);
+void smb_krb5_ctx_fini(krb5_context ctx);
+int smb_krb5_get_principal(krb5_context ctx, char *princ_str,
+ krb5_principal *princ);
+int smb_krb5_setpwd(krb5_context ctx, krb5_principal princ, char *passwd);
+int smb_krb5_write_keytab(krb5_context ctx, krb5_principal princ,
+ char *fname, krb5_kvno kvno, char *passwd, krb5_enctype *enctypes,
+ int enctype_count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMBSRV_SMB_KRB5_H */
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c
new file mode 100644
index 0000000000..a6c99799d1
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c
@@ -0,0 +1,242 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <kerberosv5/krb5.h>
+
+static int smb_krb5_ktadd(krb5_context ctx, krb5_keytab kt,
+ const krb5_principal princ, krb5_enctype enctype, krb5_kvno kvno,
+ const char *pw);
+
+/*
+ * smb_krb5_ctx_init
+ *
+ * Initialize the kerberos context.
+ * Return 0 on success. Otherwise, return -1.
+ */
+int
+smb_krb5_ctx_init(krb5_context *ctx)
+{
+ if (krb5_init_context(ctx) != 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * smb_krb5_get_principal
+ *
+ * Setup the krb5_principal given the host principal in string format.
+ * Return 0 on success. Otherwise, return -1.
+ */
+int
+smb_krb5_get_principal(krb5_context ctx, char *princ_str, krb5_principal *princ)
+{
+ if (krb5_parse_name(ctx, princ_str, princ) != 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * smb_krb5_ctx_fini
+ *
+ * Free the kerberos context.
+ */
+void
+smb_krb5_ctx_fini(krb5_context ctx)
+{
+ krb5_free_context(ctx);
+}
+
+/*
+ * smb_ksetpw
+ *
+ * Set the workstation trust account password.
+ * Returns 0 on success. Otherwise, returns non-zero value.
+ */
+int
+smb_krb5_setpwd(krb5_context ctx, krb5_principal princ, char *passwd)
+{
+ krb5_error_code code;
+ krb5_ccache cc = NULL;
+ int result_code;
+ krb5_data result_code_string, result_string;
+
+ (void) memset(&result_code_string, 0, sizeof (result_code_string));
+ (void) memset(&result_string, 0, sizeof (result_string));
+
+ if ((code = krb5_cc_default(ctx, &cc)) != 0) {
+ syslog(LOG_ERR, "smb_krb5_setpwd: failed to find a ccache\n");
+ return (-1);
+ }
+
+ code = krb5_set_password_using_ccache(ctx, cc, passwd, princ,
+ &result_code, &result_code_string, &result_string);
+
+ krb5_cc_close(ctx, cc);
+
+ if (code != 0)
+ (void) syslog(LOG_ERR,
+ "smb_krb5_setpwd: Result: %.*s (%d) %.*s\n",
+ result_code == 0 ?
+ strlen("success") : result_code_string.length,
+ result_code == 0 ? "success" : result_code_string.data,
+ result_code, result_string.length, result_string.data);
+ return (code);
+}
+
+/*
+ * smb_krb5_write_keytab
+ *
+ * Write all the Kerberos keys to the keytab file.
+ * Returns 0 on success. Otherwise, returns -1.
+ */
+int
+smb_krb5_write_keytab(krb5_context ctx, krb5_principal princ, char *fname,
+ krb5_kvno kvno, char *passwd, krb5_enctype *enctypes, int enctype_count)
+{
+ krb5_keytab kt = NULL;
+ char *ktname;
+ int i, len;
+ int rc = 0;
+ struct stat fstat;
+
+ if (stat(fname, &fstat) == 0) {
+ if (remove(fname) != 0) {
+ syslog(LOG_ERR, "smb_krb5_write_keytab: cannot remove"
+ " existing keytab");
+ return (-1);
+ }
+ }
+
+ len = snprintf(NULL, 0, "WRFILE:%s", fname) + 1;
+ if ((ktname = malloc(len)) == NULL) {
+ syslog(LOG_ERR, "smb_krb5_write_keytab: resource shortage");
+ return (-1);
+ }
+
+ (void) snprintf(ktname, len, "WRFILE:%s", fname);
+
+ if (krb5_kt_resolve(ctx, ktname, &kt) != 0) {
+ syslog(LOG_ERR, "smb_krb5_write_keytab: failed to open/create "
+ "keytab %s\n", fname);
+ free(ktname);
+ return (-1);
+ }
+
+ free(ktname);
+
+ for (i = 0; i < enctype_count; i++) {
+ if (smb_krb5_ktadd(ctx, kt, princ, enctypes[i], kvno, passwd)
+ != 0) {
+ rc = -1;
+ break;
+ }
+
+ }
+
+ if (kt != NULL)
+ krb5_kt_close(ctx, kt);
+
+ return (rc);
+}
+
+/*
+ * smb_krb5_ktadd
+ *
+ * Add a Keberos key to the keytab file.
+ * Returns 0 on success. Otherwise, returns -1.
+ */
+static int
+smb_krb5_ktadd(krb5_context ctx, krb5_keytab kt, const krb5_principal princ,
+ krb5_enctype enctype, krb5_kvno kvno, const char *pw)
+{
+ krb5_keytab_entry *entry;
+ krb5_data password, salt;
+ krb5_keyblock key;
+ krb5_error_code code;
+ char buf[100];
+ int rc = 0;
+
+ if ((code = krb5_enctype_to_string(enctype, buf, sizeof (buf)))) {
+ syslog(LOG_ERR, "smb_krb5_ktadd[%d]: unknown enctype",
+ enctype);
+ return (-1);
+ }
+
+ if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) {
+ syslog(LOG_ERR, "smb_krb5_ktadd[%d]: resource shortage",
+ enctype);
+ return (-1);
+ }
+
+ (void) memset((char *)entry, 0, sizeof (*entry));
+
+ password.length = strlen(pw);
+ password.data = (char *)pw;
+
+ if ((code = krb5_principal2salt(ctx, princ, &salt)) != 0) {
+ syslog(LOG_ERR, "smb_krb5_ktadd[%d]: failed to compute salt",
+ enctype);
+ free(entry);
+ return (-1);
+ }
+
+ code = krb5_c_string_to_key(ctx, enctype, &password, &salt, &key);
+ krb5_xfree(salt.data);
+ if (code != 0) {
+ syslog(LOG_ERR, "smb_krb5_ktadd[%d]: failed to generate key",
+ enctype);
+ free(entry);
+ return (-1);
+ }
+
+ (void) memcpy(&entry->key, &key, sizeof (krb5_keyblock));
+ entry->vno = kvno;
+ entry->principal = princ;
+
+ if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) {
+ syslog(LOG_ERR, "smb_krb5_ktadd[%d] failed to add entry to "
+ "keytab (%d)", enctype, code);
+ rc = -1;
+ }
+
+ free(entry);
+ return (rc);
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.c
new file mode 100644
index 0000000000..7b165e3b44
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.c
@@ -0,0 +1,300 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Main startup code for SMB/NETBIOS and some utility routines
+ * for the NETBIOS layer.
+ */
+
+#include <synch.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+
+#include <smbns_netbios.h>
+
+netbios_status_t nb_status;
+
+static pthread_t smb_nbns_thr; /* name service */
+static pthread_t smb_nbds_thr; /* dgram service */
+static pthread_t smb_nbts_thr; /* timer */
+static pthread_t smb_nbbs_thr; /* browser */
+
+static void *smb_netbios_timer(void *);
+
+void
+smb_netbios_chg_status(uint32_t status, int set)
+{
+ (void) mutex_lock(&nb_status.mtx);
+ if (set)
+ nb_status.state |= status;
+ else
+ nb_status.state &= ~status;
+ (void) cond_broadcast(&nb_status.cv);
+ (void) mutex_unlock(&nb_status.mtx);
+}
+
+void
+smb_netbios_shutdown(void)
+{
+ smb_netbios_chg_status(NETBIOS_SHUTTING_DOWN, 1);
+
+ (void) pthread_join(smb_nbts_thr, 0);
+ (void) pthread_join(smb_nbbs_thr, 0);
+ (void) pthread_join(smb_nbns_thr, 0);
+ (void) pthread_join(smb_nbds_thr, 0);
+
+ nb_status.state = NETBIOS_SHUT_DOWN;
+}
+
+void
+smb_netbios_start()
+{
+ int rc;
+ mutex_t *mp;
+ cond_t *cvp;
+
+ /* Startup Netbios named; port 137 */
+ rc = pthread_create(&smb_nbns_thr, 0,
+ smb_netbios_name_service_daemon, 0);
+ if (rc)
+ return;
+
+ mp = &nb_status.mtx;
+ cvp = &nb_status.cv;
+
+ (void) mutex_lock(mp);
+
+ while (!(nb_status.state & (NETBIOS_NAME_SVC_RUNNING |
+ NETBIOS_NAME_SVC_FAILED))) {
+ (void) cond_wait(cvp, mp);
+ }
+
+ if (nb_status.state & NETBIOS_NAME_SVC_FAILED) {
+ (void) mutex_unlock(mp);
+ (void) fprintf(stderr,
+ "smbd: Netbios Name service startup failed!");
+ smb_netbios_shutdown();
+ return;
+ }
+ (void) mutex_unlock(mp);
+
+ (void) fprintf(stderr, "smbd: Netbios Name service started.");
+ smb_netbios_name_config();
+
+ /* Startup Netbios datagram service; port 138 */
+ rc = pthread_create(&smb_nbds_thr, 0,
+ smb_netbios_datagram_service_daemon, 0);
+ if (rc == 0) {
+ (void) mutex_lock(mp);
+ while (!(nb_status.state & (NETBIOS_DATAGRAM_SVC_RUNNING |
+ NETBIOS_DATAGRAM_SVC_FAILED))) {
+ (void) cond_wait(cvp, mp);
+ }
+
+ if (nb_status.state & NETBIOS_DATAGRAM_SVC_FAILED) {
+ (void) mutex_unlock(mp);
+ (void) fprintf(stderr, "smbd: Netbios Datagram service "
+ "startup failed!");
+ smb_netbios_shutdown();
+ return;
+ }
+ (void) mutex_unlock(mp);
+ } else {
+ smb_netbios_shutdown();
+ return;
+ }
+
+ (void) fprintf(stderr, "smbd: Netbios Datagram service started.");
+
+ /* Startup Netbios browser service */
+ rc = pthread_create(&smb_nbbs_thr, 0, smb_browser_daemon, 0);
+ if (rc) {
+ smb_netbios_shutdown();
+ return;
+ }
+
+ (void) fprintf(stderr, "smbd: Netbios Browser client started.");
+
+ /* Startup Our internal, 1 second resolution, timer */
+ rc = pthread_create(&smb_nbts_thr, 0, smb_netbios_timer, 0);
+ if (rc == 0) {
+ (void) mutex_lock(mp);
+ while (!(nb_status.state & (NETBIOS_TIMER_RUNNING |
+ NETBIOS_TIMER_FAILED))) {
+ (void) cond_wait(cvp, mp);
+ }
+
+ if (nb_status.state & NETBIOS_TIMER_FAILED) {
+ (void) mutex_unlock(mp);
+ smb_netbios_shutdown();
+ return;
+ }
+ (void) mutex_unlock(mp);
+ } else {
+ smb_netbios_shutdown();
+ return;
+ }
+
+ (void) fprintf(stderr, "smbd: Netbios Timer service started.");
+}
+
+/*ARGSUSED*/
+static void *
+smb_netbios_timer(void *arg)
+{
+ static unsigned int ticks;
+
+ smb_netbios_chg_status(NETBIOS_TIMER_RUNNING, 1);
+
+ while ((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) {
+ (void) sleep(1);
+
+ if (nb_status.state & NETBIOS_DATAGRAM_SVC_RUNNING)
+ smb_netbios_datagram_tick();
+ else
+ break;
+
+ if (nb_status.state & NETBIOS_NAME_SVC_RUNNING) {
+ smb_netbios_name_tick();
+
+ /* every 10 minutes */
+ if ((ticks % 600) == 0)
+ smb_netbios_cache_clean();
+ }
+ else
+ break;
+ }
+
+ nb_status.state &= ~NETBIOS_TIMER_RUNNING;
+ if ((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) {
+ /* either name or datagram service has failed */
+ smb_netbios_shutdown();
+ }
+
+ return (0);
+}
+
+int
+smb_first_level_name_encode(struct name_entry *name,
+ unsigned char *out, int max_out)
+{
+ return (netbios_first_level_name_encode(name->name, name->scope,
+ out, max_out));
+}
+
+int
+smb_first_level_name_decode(unsigned char *in, struct name_entry *name)
+{
+ return (netbios_first_level_name_decode((char *)in, (char *)name->name,
+ (char *)name->scope));
+}
+
+/*
+ * smb_encode_netbios_name
+ *
+ * Set up the name and scope fields in the destination name_entry structure.
+ * The name is padded with spaces to 15 bytes. The suffix is copied into the
+ * last byte, i.e. "netbiosname <suffix>". The scope is copied and folded
+ * to uppercase.
+ */
+void
+smb_encode_netbios_name(unsigned char *name, char suffix, unsigned char *scope,
+ struct name_entry *dest)
+{
+ char tmp_name[NETBIOS_NAME_SZ];
+ mts_wchar_t wtmp_name[NETBIOS_NAME_SZ];
+ unsigned int cpid;
+ int len;
+ size_t rc;
+
+ len = 0;
+ rc = mts_mbstowcs(wtmp_name, (const char *)name, NETBIOS_NAME_SZ);
+
+ if (rc != (size_t)-1) {
+ wtmp_name[NETBIOS_NAME_SZ - 1] = 0;
+ cpid = oem_get_smb_cpid();
+ rc = unicodestooems(tmp_name, wtmp_name, NETBIOS_NAME_SZ, cpid);
+ if (rc > 0)
+ len = strlen(tmp_name);
+ }
+
+ (void) memset(dest->name, ' ', NETBIOS_NAME_SZ - 1);
+ if (len) {
+ (void) utf8_strupr(tmp_name);
+ (void) memcpy(dest->name, tmp_name, len);
+ }
+ dest->name[NETBIOS_NAME_SZ - 1] = suffix;
+
+ if (scope == NULL) {
+ smb_config_rdlock();
+ (void) strlcpy((char *)dest->scope,
+ smb_config_getstr(SMB_CI_NBSCOPE), NETBIOS_DOMAIN_NAME_MAX);
+ smb_config_unlock();
+ } else {
+ (void) strlcpy((char *)dest->scope, (const char *)scope,
+ NETBIOS_DOMAIN_NAME_MAX);
+ }
+ (void) utf8_strupr((char *)dest->scope);
+}
+
+void
+smb_init_name_struct(unsigned char *name, char suffix, unsigned char *scope,
+ uint32_t ipaddr, unsigned short port, uint32_t attr,
+ uint32_t addr_attr, struct name_entry *dest)
+{
+ bzero(dest, sizeof (struct name_entry));
+ smb_encode_netbios_name(name, suffix, scope, dest);
+
+ switch (smb_node_type) {
+ case 'H':
+ dest->attributes = attr | NAME_ATTR_OWNER_TYPE_HNODE;
+ break;
+ case 'M':
+ dest->attributes = attr | NAME_ATTR_OWNER_TYPE_MNODE;
+ break;
+ case 'P':
+ dest->attributes = attr | NAME_ATTR_OWNER_TYPE_PNODE;
+ break;
+ case 'B':
+ default:
+ dest->attributes = attr | NAME_ATTR_OWNER_TYPE_BNODE;
+ break;
+ }
+
+ dest->addr_list.refresh_ttl = dest->addr_list.ttl =
+ TO_SECONDS(DEFAULT_TTL);
+
+ dest->addr_list.sin.sin_family = AF_INET;
+ dest->addr_list.sinlen = sizeof (dest->addr_list.sin);
+ dest->addr_list.sin.sin_addr.s_addr = ipaddr;
+ dest->addr_list.sin.sin_port = port;
+ dest->addr_list.attributes = addr_attr;
+ dest->addr_list.forw = dest->addr_list.back = &dest->addr_list;
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.h
new file mode 100644
index 0000000000..54ac5cf2ab
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.h
@@ -0,0 +1,1617 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMB_NETBIOS_H_
+#define _SMB_NETBIOS_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * 4.2. NAME SERVICE PACKETS
+ *
+ * 4.2.1. GENERAL FORMAT OF NAME SERVICE PACKETS
+ *
+ * The NetBIOS Name Service packets follow the packet structure defined
+ * in the Domain Name Service (DNS) RFC 883 [7 (pg 26-31)]. The
+ * structures are compatible with the existing DNS packet formats,
+ * however, additional types and codes have been added to work with
+ * NetBIOS.
+ *
+ * If Name Service packets are sent over a TCP connection they are
+ * preceded by a 16 bit unsigned integer representing the length of the
+ * Name Service packet.
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + ------ ------- +
+ * | HEADER |
+ * + ------ ------- +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION ENTRIES /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / ANSWER RESOURCE RECORDS /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / AUTHORITY RESOURCE RECORDS /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / ADDITIONAL RESOURCE RECORDS /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.1.1 HEADER
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID | OPCODE | NM_FLAGS | RCODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | QDCOUNT | ANCOUNT |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NSCOUNT | ARCOUNT |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Field Description
+ *
+ * NAME_TRN_ID Transaction ID for Name Service Transaction.
+ * Requester places a unique value for each active
+ * transaction. Responder puts NAME_TRN_ID value
+ * from request packet in response packet.
+ *
+ * OPCODE Packet type code, see table below.
+ *
+ * NM_FLAGS Flags for operation, see table below.
+ *
+ * RCODE Result codes of request. Table of RCODE values
+ * for each response packet below.
+ *
+ * QDCOUNT Unsigned 16 bit integer specifying the number of
+ * entries in the question section of a Name
+ * Service packet. Always zero (0) for responses.
+ * Must be non-zero for all NetBIOS Name requests.
+ *
+ * ANCOUNT Unsigned 16 bit integer specifying the number of
+ * resource records in the answer section of a Name
+ * Service packet.
+ *
+ * NSCOUNT Unsigned 16 bit integer specifying the number of
+ * resource records in the authority section of a
+ * Name Service packet.
+ *
+ * ARCOUNT Unsigned 16 bit integer specifying the number of
+ * resource records in the additional records
+ * section of a Name Service packet.
+ */
+
+
+/*
+ * The OPCODE field is defined as:
+ *
+ * 0 1 2 3 4
+ * +---+---+---+---+---+
+ * | R | OPCODE |
+ * +---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description
+ *
+ * OPCODE 1-4 Operation specifier:
+ * 0 = query
+ * 5 = registration
+ * 6 = release
+ * 7 = WACK
+ * 8 = refresh
+ *
+ * R 0 RESPONSE flag:
+ * if bit == 0 then request packet
+ * if bit == 1 then response packet.
+ */
+
+/*
+ * The NM_FLAGS field is defined as:
+ *
+ *
+ * 0 1 2 3 4 5 6
+ * +---+---+---+---+---+---+---+
+ * |AA |TC |RD |RA | 0 | 0 | B |
+ * +---+---+---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description
+ *
+ * B 6 Broadcast Flag.
+ * = 1: packet was broadcast or multicast
+ * = 0: unicast
+ *
+ * RA 3 Recursion Available Flag.
+ *
+ * Only valid in responses from a NetBIOS Name
+ * Server -- must be zero in all other
+ * responses.
+ *
+ * If one (1) then the NBNS supports recursive
+ * query, registration, and release.
+ *
+ * If zero (0) then the end-node must iterate
+ * for query and challenge for registration.
+ *
+ * RD 2 Recursion Desired Flag.
+ *
+ * May only be set on a request to a NetBIOS
+ * Name Server.
+ *
+ * The NBNS will copy its state into the
+ * response packet.
+ *
+ * If one (1) the NBNS will iterate on the
+ * query, registration, or release.
+ *
+ * TC 1 Truncation Flag.
+ *
+ * Set if this message was truncated because the
+ * datagram carrying it would be greater than
+ * 576 bytes in length. Use TCP to get the
+ * information from the NetBIOS Name Server.
+ *
+ * AA 0 Authoritative Answer flag.
+ *
+ * Must be zero (0) if R flag of OPCODE is zero
+ * (0).
+ *
+ * If R flag is one (1) then if AA is one (1)
+ * then the node responding is an authority for
+ * the domain name.
+ *
+ * End nodes responding to queries always set
+ * this bit in responses.
+ */
+
+/*
+ * 4.2.1.2 QUESTION SECTION
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | QUESTION_TYPE | QUESTION_CLASS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Field Description
+ *
+ * QUESTION_NAME The compressed name representation of the
+ * NetBIOS name for the request.
+ *
+ * QUESTION_TYPE The type of request. The values for this field
+ * are specified for each request.
+ *
+ * QUESTION_CLASS The class of the request. The values for this
+ * field are specified for each request.
+ *
+ * QUESTION_TYPE is defined as:
+ *
+ * Symbol Value Description:
+ *
+ * NB 0x0020 NetBIOS general Name Service Resource Record
+ * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE
+ * STATUS REQUEST)
+ *
+ * QUESTION_CLASS is defined as:
+ *
+ * Symbol Value Description:
+ *
+ * IN 0x0001 Internet class
+ */
+
+/*
+ * 4.2.1.3 RESOURCE RECORD
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RR_TYPE | RR_CLASS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RDLENGTH | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * / /
+ * / RDATA /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Field Description
+ *
+ * RR_NAME The compressed name representation of the
+ * NetBIOS name corresponding to this resource
+ * record.
+ *
+ * RR_TYPE Resource record type code
+ *
+ * RR_CLASS Resource record class code
+ *
+ * TTL The Time To Live of a the resource record's
+ * name.
+ *
+ * RDLENGTH Unsigned 16 bit integer that specifies the
+ * number of bytes in the RDATA field.
+ *
+ * RDATA RR_CLASS and RR_TYPE dependent field. Contains
+ * the resource information for the NetBIOS name.
+ *
+ * RESOURCE RECORD RR_TYPE field definitions:
+ *
+ * Symbol Value Description:
+ *
+ * A 0x0001 IP address Resource Record (See REDIRECT NAME
+ * QUERY RESPONSE)
+ * NS 0x0002 Name Server Resource Record (See REDIRECT
+ * NAME QUERY RESPONSE)
+ * NULL 0x000A NULL Resource Record (See WAIT FOR
+ * ACKNOWLEDGEMENT RESPONSE)
+ * NB 0x0020 NetBIOS general Name Service Resource Record
+ * (See NB_FLAGS and NB_ADDRESS, below)
+ * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE
+ * STATUS RESPONSE)
+ *
+ * RESOURCE RECORD RR_CLASS field definitions:
+ *
+ * Symbol Value Description:
+ *
+ * IN 0x0001 Internet class
+ *
+ * NB_FLAGS field of the RESOURCE RECORD RDATA field for RR_TYPE of
+ * "NB":
+ *
+ * 1 1 1 1 1 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | G | ONT | RESERVED |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description:
+ *
+ * RESERVED 3-15 Reserved for future use. Must be zero (0).
+ * ONT 1,2 Owner Node Type:
+ * 00 = B node
+ * 01 = P node
+ * 10 = M node
+ * 11 = Reserved for future use
+ * For registration requests this is the
+ * claimant's type.
+ * For responses this is the actual owner's
+ * type.
+ *
+ * G 0 Group Name Flag.
+ * If one (1) then the RR_NAME is a GROUP
+ * NetBIOS name.
+ * If zero (0) then the RR_NAME is a UNIQUE
+ * NetBIOS name.
+ *
+ * The NB_ADDRESS field of the RESOURCE RECORD RDATA field for
+ * RR_TYPE of "NB" is the IP address of the name's owner.
+ */
+
+/*
+ * 4.2.2. NAME REGISTRATION REQUEST
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x5 |0|0|1|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Since the RR_NAME is the same name as the QUESTION_NAME, the
+ * RR_NAME representation must use pointers to the QUESTION_NAME
+ * name's labels to guarantee the length of the datagram is less
+ * than the maximum 576 bytes. See section above on name formats
+ * and also page 31 and 32 of RFC 883, Domain Names - Implementation
+ * and Specification, for a complete description of compressed name
+ * label pointers.
+ */
+
+/*
+ * 4.2.3 NAME OVERWRITE REQUEST & DEMAND
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x5 |0|0|0|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.4 NAME REFRESH REQUEST
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x9 |0|0|0|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.5 POSITIVE NAME REGISTRATION RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.6 NEGATIVE NAME REGISTRATION RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| RCODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * RCODE field values:
+ *
+ * Symbol Value Description:
+ *
+ * FMT_ERR 0x1 Format Error. Request was invalidly
+ * formatted.
+ * SRV_ERR 0x2 Server failure. Problem with NBNS, cannot
+ * process name.
+ * IMP_ERR 0x4 Unsupported request error. Allowable only
+ * for challenging NBNS when gets an Update type
+ * registration request.
+ * RFS_ERR 0x5 Refused error. For policy reasons server
+ * will not register this name from this host.
+ * ACT_ERR 0x6 Active error. Name is owned by another node.
+ * CFT_ERR 0x7 Name in conflict error. A UNIQUE name is
+ * owned by more than one node.
+ */
+
+/*
+ * 4.2.7 END-NODE CHALLENGE REGISTRATION RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x5 |1|0|1|0|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.8 NAME CONFLICT DEMAND
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| 0x7 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x00000000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 |0|ONT|0| 0x000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x00000000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * This packet is identical to a NEGATIVE NAME REGISTRATION RESPONSE
+ * with RCODE = CFT_ERR.
+ */
+
+/*
+ * 4.2.9 NAME RELEASE REQUEST & DEMAND
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x6 |0|0|0|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x00000000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Since the RR_NAME is the same name as the QUESTION_NAME, the
+ * RR_NAME representation must use label string pointers to the
+ * QUESTION_NAME labels to guarantee the length of the datagram is
+ * less than the maximum 576 bytes. This is the same condition as
+ * with the NAME REGISTRATION REQUEST.
+ */
+
+/*
+ * 4.2.10 POSITIVE NAME RELEASE RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.11 NEGATIVE NAME RELEASE RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| RCODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * RCODE field values:
+ *
+ * Symbol Value Description:
+ *
+ * FMT_ERR 0x1 Format Error. Request was invalidly
+ * formatted.
+ *
+ * SRV_ERR 0x2 Server failure. Problem with NBNS, cannot
+ * process name.
+ *
+ * RFS_ERR 0x5 Refused error. For policy reasons server
+ * will not release this name from this host.
+ *
+ * ACT_ERR 0x6 Active error. Name is owned by another node.
+ * Only that node may release it. A NetBIOS
+ * Name Server can optionally allow a node to
+ * release a name it does not own. This would
+ * facilitate detection of inactive names for
+ * nodes that went down silently.
+ */
+
+/*
+ * 4.2.12 NAME QUERY REQUEST
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x0 |0|0|1|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.13 POSITIVE NAME QUERY RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x0 |1|T|1|?|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RDLENGTH | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | |
+ * / ADDR_ENTRY ARRAY /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * The ADDR_ENTRY ARRAY a sequence of zero or more ADDR_ENTRY
+ * records. Each ADDR_ENTRY record represents an owner of a name.
+ * For group names there may be multiple entries. However, the list
+ * may be incomplete due to packet size limitations. Bit 22, "T",
+ * will be set to indicate truncated data.
+ *
+ * Each ADDR_ENTRY has the following format:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_FLAGS | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS (continued) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.14 NEGATIVE NAME QUERY RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x0 |1|0|1|?|0 0|0| RCODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NULL (0x000A) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x00000000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * RCODE field values:
+ *
+ * Symbol Value Description
+ *
+ * FMT_ERR 0x1 Format Error. Request was invalidly
+ * formatted.
+ * SRV_ERR 0x2 Server failure. Problem with NBNS, cannot
+ * process name.
+ * NAM_ERR 0x3 Name Error. The name requested does not
+ * exist.
+ * IMP_ERR 0x4 Unsupported request error. Allowable only
+ * for challenging NBNS when gets an Update type
+ * registration request.
+ * RFS_ERR 0x5 Refused error. For policy reasons server
+ * will not register this name from this host.
+ */
+
+/*
+ * 4.2.15 REDIRECT NAME QUERY RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x0 |0|0|1|0|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NS (0x0002) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RDLENGTH | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | |
+ * / NSD_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | A (0x0001) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0004 | NSD_IP_ADDR |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NSD_IP_ADDR, continued |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * An end node responding to a NAME QUERY REQUEST always responds
+ * with the AA and RA bits set for both the NEGATIVE and POSITIVE
+ * NAME QUERY RESPONSE packets. An end node never sends a REDIRECT
+ * NAME QUERY RESPONSE packet.
+ *
+ * When the requestor receives the REDIRECT NAME QUERY RESPONSE it
+ * must reiterate the NAME QUERY REQUEST to the NBNS specified by
+ * the NSD_IP_ADDR field of the A type RESOURCE RECORD in the
+ * ADDITIONAL section of the response packet. This is an optional
+ * packet for the NBNS.
+ *
+ * The NSD_NAME and the RR_NAME in the ADDITIONAL section of the
+ * response packet are the same name. Space can be optimized if
+ * label string pointers are used in the RR_NAME which point to the
+ * labels in the NSD_NAME.
+ *
+ * The RR_NAME in the AUTHORITY section is the name of the domain
+ * the NBNS called by NSD_NAME has authority over.
+ */
+
+/*
+ * 4.2.16 WAIT FOR ACKNOWLEDGEMENT (WACK) RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x7 |1|0|0|0|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NULL (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0002 | OPCODE | NM_FLAGS | 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * The NAME_TRN_ID of the WACK RESPONSE packet is the same
+ * NAME_TRN_ID of the request that the NBNS is telling the requestor
+ * to wait longer to complete. The RR_NAME is the name from the
+ * request, if any. If no name is available from the request then
+ * it is a null name, single byte of zero.
+ *
+ * The TTL field of the ResourceRecord is the new time to wait, in
+ * seconds, for the request to complete. The RDATA field contains
+ * the OPCODE and NM_FLAGS of the request.
+ *
+ * A TTL value of 0 means that the NBNS can not estimate the time it
+ * may take to complete a response.
+ */
+
+/*
+ * 4.2.17 NODE STATUS REQUEST
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x0 |0|0|0|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NBSTAT (0x0021) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * 4.2.18 NODE STATUS RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x0 |1|0|0|0|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NBSTAT (0x0021) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x00000000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RDLENGTH | NUM_NAMES | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | |
+ * + +
+ * / NODE_NAME ARRAY /
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * / STATISTICS /
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * The NODE_NAME ARRAY is an array of zero or more NUM_NAMES entries
+ * of NODE_NAME records. Each NODE_NAME entry represents an active
+ * name in the same NetBIOS scope as the requesting name in the
+ * local name table of the responder. RR_NAME is the requesting
+ * name.
+ */
+
+#include <stdio.h>
+#include <synch.h>
+#include <pthread.h>
+#include <strings.h>
+#include <netinet/in.h>
+
+#include <smbsrv/libsmbns.h>
+
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/netbios.h>
+
+#define QUEUE_INSERT_TAIL(q, e) \
+ ((e)->back) = (void *)((q)->back); \
+ ((e)->forw) = (void *)(q); \
+ ((q)->back->forw) = (void *)(e); \
+ ((q)->back) = (void *)(e);
+
+#define QUEUE_CLIP(e) \
+ (e)->forw->back = (e)->back; \
+ (e)->back->forw = (e)->forw; \
+ (e)->forw = 0; \
+ (e)->back = 0;
+
+#define NETBIOS_NAME_SVC_LAUNCHED 0x00001
+#define NETBIOS_NAME_SVC_RUNNING 0x00002
+#define NETBIOS_NAME_SVC_FAILED 0x00004
+
+#define NETBIOS_DATAGRAM_SVC_LAUNCHED 0x00010
+#define NETBIOS_DATAGRAM_SVC_RUNNING 0x00020
+#define NETBIOS_DATAGRAM_SVC_FAILED 0x00040
+
+#define NETBIOS_TIMER_LAUNCHED 0x00100
+#define NETBIOS_TIMER_RUNNING 0x00200
+#define NETBIOS_TIMER_FAILED 0x00400
+
+#define NETBIOS_BROWSER_LAUNCHED 0x01000
+#define NETBIOS_BROWSER_RUNNING 0x02000
+#define NETBIOS_BROWSER_FAILED 0x04000
+
+#define NETBIOS_SHUTTING_DOWN 0x10000
+#define NETBIOS_SHUT_DOWN 0x20000
+
+char smb_node_type;
+
+typedef struct {
+ mutex_t mtx;
+ cond_t cv;
+ uint32_t state;
+} netbios_status_t;
+extern netbios_status_t nb_status;
+
+/*
+ * NAME service definitions
+ */
+#define ADDR_FLAG_INVALID 0x0000
+#define ADDR_FLAG_VALID 0x0001
+
+typedef struct addr_entry {
+ struct addr_entry *forw;
+ struct addr_entry *back;
+ uint32_t attributes;
+ uint32_t conflict_timer;
+ uint32_t refresh_ttl;
+ uint32_t ttl;
+ struct sockaddr_in sin;
+ int sinlen;
+ uint32_t flags;
+} addr_entry_t;
+
+/*
+ * The NODE_NAME ARRAY is an array of zero or more NUM_NAMES entries
+ * of NODE_NAME records. Each NODE_NAME entry represents an active
+ * name in the same NetBIOS scope as the requesting name in the
+ * local name table of the responder. RR_NAME is the requesting
+ * name.
+ *
+ * NODE_NAME Entry:
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +--- ---+
+ * | |
+ * +--- NETBIOS FORMAT NAME ---+
+ * | |
+ * +--- ---+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * The NAME_FLAGS field:
+ *
+ * 1 1 1 1 1 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | G | ONT |DRG|CNF|ACT|PRM| RESERVED |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * The NAME_FLAGS field is defined as:
+ *
+ * Symbol Bit(s) Description:
+ *
+ * RESERVED 7-15 Reserved for future use. Must be zero (0).
+ * PRM 6 Permanent Name Flag. If one (1) then entry
+ * is for the permanent node name. Flag is zero
+ * (0) for all other names.
+ * ACT 5 Active Name Flag. All entries have this flag
+ * set to one (1).
+ * CNF 4 Conflict Flag. If one (1) then name on this
+ * node is in conflict.
+ * DRG 3 Deregister Flag. If one (1) then this name
+ * is in the process of being deleted.
+ * ONT 1,2 Owner Node Type:
+ * 00 = B node
+ * 01 = P node
+ * 10 = M node
+ * 11 = Reserved for future use
+ * G 0 Group Name Flag.
+ * name.
+ * If zero (0) then it is a UNIQUE NetBIOS name.
+ */
+
+typedef struct name_entry {
+ struct name_entry *forw;
+ struct name_entry *back;
+ unsigned char name[NETBIOS_NAME_SZ];
+ unsigned char scope[NETBIOS_DOMAIN_NAME_MAX];
+ unsigned short attributes;
+ struct addr_entry addr_list;
+ mutex_t mtx;
+} name_entry;
+
+struct name_question {
+ struct name_entry *name;
+ unsigned question_type;
+ unsigned question_class;
+};
+
+struct resource_record {
+ /*
+ * These two flags and address are contained within RDATA
+ * when rr_type==0x0020 (NB - NetBIOS general Name Service)
+ * and rr_class==0x01 (IN - Internet Class).
+ */
+
+ struct name_entry *name;
+ unsigned short rr_type;
+ unsigned short rr_class;
+ uint32_t ttl;
+ unsigned short rdlength;
+ unsigned char *rdata;
+};
+
+struct name_packet {
+ unsigned short name_trn_id;
+ unsigned short info;
+
+ unsigned qdcount; /* question entries */
+ unsigned ancount; /* answer recs */
+ unsigned nscount; /* authority recs */
+ unsigned arcount; /* additional recs */
+
+ struct name_question *question;
+ struct resource_record *answer;
+ struct resource_record *authority;
+ struct resource_record *additional;
+
+ unsigned char block_data[4]; /* begining of space */
+};
+
+#define NAME_OPCODE_R 0x8000 /* RESPONSE flag: 1 bit */
+#define NAME_OPCODE_OPCODE_MASK 0x7800 /* OPCODE Field: 4 bits */
+#define NAME_OPCODE_QUERY 0x0000
+#define NAME_OPCODE_REGISTRATION 0x2800
+#define NAME_OPCODE_RELEASE 0x3000
+#define NAME_OPCODE_WACK 0x3800
+#define NAME_OPCODE_REFRESH 0x4000
+#define NAME_OPCODE_MULTIHOME 0x7800
+#define NAME_NM_FLAGS_AA 0x0400 /* Authoritative Answer:1 bit */
+#define NAME_NM_FLAGS_TC 0x0200 /* Truncation: 1 bit */
+#define NAME_NM_FLAGS_RD 0x0100 /* Recursion desired: 1 bit */
+#define NAME_NM_FLAGS_RA 0x0080 /* Recursion available: 1 bit */
+#define NAME_NM_FLAGS_x2 0x0040 /* reserved, mbz: 1 bit */
+#define NAME_NM_FLAGS_x1 0x0020 /* reserved, mbz: 1 bit */
+#define NAME_NM_FLAGS_B 0x0010 /* Broadcast: 1 bit */
+#define NAME_RCODE_MASK 0x000f /* RCODE Field: 4 bits */
+#define RCODE_FMT_ERR 0x0001
+#define RCODE_SRV_ERR 0x0002
+#define RCODE_NAM_ERR 0x0003
+#define RCODE_IMP_ERR 0x0004
+#define RCODE_RFS_ERR 0x0005
+#define RCODE_ACT_ERR 0x0006
+#define RCODE_CFT_ERR 0x0007
+
+#define NM_FLAGS_UNICAST 0
+#define NM_FLAGS_BROADCAST NAME_NM_FLAGS_B
+
+#define PACKET_TYPE(x) ((x) & (NAME_OPCODE_R | NAME_OPCODE_OPCODE_MASK | \
+ NAME_NM_FLAGS_AA | NAME_NM_FLAGS_RD))
+
+#define RCODE(x) ((x) & NAME_RCODE_MASK)
+#define POSITIVE_RESPONSE(x) (RCODE(x) == 0)
+#define NEGATIVE_RESPONSE(x) (RCODE(x) != 0)
+
+#define END_NODE_CHALLENGE_REGISTRATION_REQUEST \
+ (NAME_OPCODE_REGISTRATION | NAME_NM_FLAGS_AA | NAME_NM_FLAGS_RD)
+#define END_NODE_CHALLENGE_NAME_REGISTRATION_RESPONSE \
+ (NAME_OPCODE_R | END_NODE_CHALLENGE_REGISTRATION_REQUEST)
+
+#define NAME_QUERY_REQUEST \
+ (NAME_OPCODE_QUERY | NAME_NM_FLAGS_RD)
+#define NAME_QUERY_RESPONSE \
+ (NAME_OPCODE_R | NAME_QUERY_REQUEST | \
+ NAME_NM_FLAGS_AA | NAME_NM_FLAGS_RD)
+
+#define NODE_STATUS_REQUEST \
+ (NAME_OPCODE_QUERY)
+#define NODE_STATUS_RESPONSE \
+ (NAME_OPCODE_R | NODE_STATUS_REQUEST | NAME_NM_FLAGS_AA)
+
+#define REDIRECT_NAME_QUERY_RESPONSE \
+ (NAME_OPCODE_R | NAME_QUERY_REQUEST | NAME_NM_FLAGS_RD)
+
+#define NAME_REFRESH_REQUEST \
+ (NAME_OPCODE_REFRESH)
+#define NAME_REGISTRATION_REQUEST \
+ (NAME_OPCODE_REGISTRATION | NAME_NM_FLAGS_RD)
+#define NAME_MULTIHOME_REGISTRATION_REQUEST \
+ (NAME_OPCODE_MULTIHOME | NAME_NM_FLAGS_RD)
+#define NAME_REGISTRATION_RESPONSE \
+ (NAME_OPCODE_R | NAME_REGISTRATION_REQUEST | NAME_NM_FLAGS_AA)
+
+#define NAME_RELEASE_REQUEST \
+ (NAME_OPCODE_RELEASE)
+#define NAME_RELEASE_RESPONSE \
+ (NAME_OPCODE_R | NAME_RELEASE_REQUEST | NAME_NM_FLAGS_AA)
+
+#define WACK_RESPONSE \
+ (NAME_OPCODE_R | NAME_OPCODE_WACK | NAME_NM_FLAGS_AA)
+
+#define NAME_QUESTION_TYPE_NB 0x0020
+#define NAME_QUESTION_TYPE_NBSTAT 0x0021
+#define NAME_QUESTION_CLASS_IN 0x0001
+
+
+#define NAME_RR_TYPE_A 0x0001 /* IP Address */
+#define NAME_RR_TYPE_NS 0x0002 /* Name Server */
+#define NAME_RR_TYPE_NULL 0x000A /* NULL */
+#define NAME_RR_TYPE_NB 0x0020 /* NetBIOS Name Service */
+#define NAME_RR_TYPE_NBSTAT 0x0021 /* NetBIOS Node Status */
+
+#define NAME_RR_CLASS_IN 0x0001 /* NetBIOS Node Status */
+
+#define NAME_NB_FLAGS_ONT_MASK (3<<13)
+#define NAME_NB_FLAGS_ONT_B (0<<13) /* B-node (broadcast) */
+#define NAME_NB_FLAGS_ONT_P (1<<13) /* P-node (point-to-point) */
+#define NAME_NB_FLAGS_ONT_M (2<<13) /* M-node (multicast) */
+#define NAME_NB_FLAGS_ONT_resv (3<<13)
+#define NAME_NB_FLAGS_G (1<<15) /* Group Name */
+
+#define UNICAST 0
+#define BROADCAST 1
+#define POINTCAST 2
+
+#define NAME_ATTR_UNIQUE 0x0000
+#define NAME_ATTR_GROUP 0x8000
+#define NAME_ATTR_OWNER_NODE_TYPE 0x6000
+#define NAME_ATTR_OWNER_TYPE_BNODE 0x0000
+#define NAME_ATTR_OWNER_TYPE_PNODE 0x2000
+#define NAME_ATTR_OWNER_TYPE_MNODE 0x4000
+#define NAME_ATTR_OWNER_TYPE_HNODE 0x6000
+#define NAME_ATTR_DEREGISTER 0x1000
+#define NAME_ATTR_CONFLICT 0x0800
+#define NAME_ATTR_ACTIVE_NAME 0x0400
+#define NAME_ATTR_PERMANENT 0x0200
+#define NAME_ATTR_RESERVED 0x01FF
+#define NAME_ATTR_LOCAL 0x0001
+
+#define NODE_TYPE(x) ((x) & NAME_ATTR_OWNER_NODE_TYPE))
+#define IS_BNODE(x) (NODE_TYPE(x) == NAME_ATTR_OWNER_TYPE_BNODE)
+#define IS_PNODE(x) (NODE_TYPE(x) == NAME_ATTR_OWNER_TYPE_PNODE)
+#define IS_MNODE(x) (NODE_TYPE(x) == NAME_ATTR_OWNER_TYPE_MNODE)
+#define IS_HNODE(x) (NODE_TYPE(x) == NAME_ATTR_OWNER_TYPE_HNODE)
+
+#define IS_UNIQUE(x) (((x) & NAME_ATTR_GROUP) == 0)
+#define IS_GROUP(x) (((x) & NAME_ATTR_GROUP) != 0)
+#define IS_PERMANENT(x) (((x) & NAME_ATTR_PERMANENT) != 0)
+#define IS_CONFLICTING(x) (((x) & NAME_ATTR_CONFLICT) != 0)
+#define IS_ACTIVE(x) (((x) & NAME_ATTR_ACTIVE) != 0)
+#define IS_DEGREGISTERED(x) (((x) & NAME_ATTR_ACTIVE) != 0)
+
+#define IS_LOCAL(x) (((x) & NAME_ATTR_LOCAL) != 0)
+#define IS_PUBLIC(x) (((x) & NAME_ATTR_LOCAL) == 0)
+#define PUBLIC_BITS(x) ((x) & ~NAME_ATTR_RESERVED)
+
+#define SAME_SCOPE(scope, e) (strcmp((scope), ((e)->scope)) == 0)
+
+/*
+ * STATISTICS Field of the NODE STATUS RESPONSE:
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | UNIT_ID (Unique unit ID) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | UNIT_ID,continued | JUMPERS | TEST_RESULT |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VERSION_NUMBER | PERIOD_OF_STATISTICS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_OF_CRCs | NUMBER_ALIGNMENT_ERRORS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_OF_COLLISIONS | NUMBER_SEND_ABORTS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_GOOD_SENDS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_GOOD_RECEIVES |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_RETRANSMITS | NUMBER_NO_RESOURCE_CONDITIONS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_FREE_COMMAND_BLOCKS | TOTAL_NUMBER_COMMAND_BLOCKS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |MAX_TOTAL_NUMBER_COMMAND_BLOCKS| NUMBER_PENDING_SESSIONS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MAX_NUMBER_PENDING_SESSIONS | MAX_TOTAL_SESSIONS_POSSIBLE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SESSION_DATA_PACKET_SIZE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+typedef struct {
+ unsigned char unit_id[6];
+ unsigned char jumpers;
+ unsigned char test_result;
+ unsigned short version_number;
+ unsigned short statistical_period;
+ unsigned short crc_errors;
+ unsigned short alignment_errors;
+ unsigned short collisions;
+ unsigned short send_aborts;
+ unsigned int good_sends;
+ unsigned int good_receives;
+ unsigned short retransmits;
+ unsigned short no_resource_conditions;
+ unsigned short free_command_blocks;
+ unsigned short total_command_blocks;
+ unsigned short max_total_command_blocks;
+ unsigned short pending_sessions;
+ unsigned short max_pending_sessions;
+ unsigned short total_possible_sessions;
+ unsigned short session_data_packet_size;
+} node_status_response;
+
+/*
+ * 4.4.1. NetBIOS DATAGRAM HEADER
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MSG_TYPE | FLAGS | DGM_ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_IP |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_PORT | DGM_LENGTH |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | PACKET_OFFSET |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+typedef struct {
+ unsigned char msg_type;
+ unsigned char flags;
+ unsigned short dgm_id;
+ uint32_t source_ip;
+ unsigned short source_port;
+ unsigned short dgm_length;
+ unsigned short packet_offset;
+} datagram_header;
+
+/*
+ * MSG_TYPE values (in hexidecimal):
+ *
+ * 10 - DIRECT_UNIQUE DATAGRAM
+ * 11 - DIRECT_GROUP DATAGRAM
+ * 12 - BROADCAST DATAGRAM
+ * 13 - DATAGRAM ERROR
+ * 14 - DATAGRAM QUERY REQUEST
+ * 15 - DATAGRAM POSITIVE QUERY RESPONSE
+ * 16 - DATAGRAM NEGATIVE QUERY RESPONSE
+ */
+#define DATAGRAM_TYPE_DIRECT_UNIQUE 0x10
+#define DATAGRAM_TYPE_DIRECT_GROUP 0x11
+#define DATAGRAM_TYPE_BROADCAST 0x12
+#define DATAGRAM_TYPE_ERROR_DATAGRAM 0x13
+#define DATAGRAM_TYPE_QUERY_REQUEST 0x14
+#define DATAGRAM_TYPE_POSITIVE_RESPONSE 0x15
+#define DATAGRAM_TYPE_NEGATIVE_RESPONSE 0x16
+
+
+/*
+ * Bit definitions of the FLAGS field:
+ *
+ * 0 1 2 3 4 5 6 7
+ * +---+---+---+---+---+---+---+---+
+ * | 0 | 0 | 0 | 0 | SNT | F | M |
+ * +---+---+---+---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description
+ *
+ * M 7 MORE flag, If set then more NetBIOS datagram
+ * fragments follow.
+ *
+ * F 6 FIRST packet flag, If set then this is first
+ * (and possibly only) fragment of NetBIOS
+ * datagram
+ *
+ * SNT 4,5 Source End-Node type:
+ * 00 = B node
+ * 01 = P node
+ * 10 = M node
+ * 11 = H node
+ * RESERVED 0-3 Reserved, must be zero (0)
+ */
+#define DATAGRAM_FLAGS_MORE 0x01
+#define DATAGRAM_FLAGS_FIRST 0x02
+#define DATAGRAM_FLAGS_SRC_TYPE 0x0c
+#define DATAGRAM_FLAGS_B_NODE 0x00
+#define DATAGRAM_FLAGS_P_NODE 0x04
+#define DATAGRAM_FLAGS_M_NODE 0x08
+#define DATAGRAM_FLAGS_H_NODE 0x0C
+#define DATAGRAM_FLAGS_NBDD 0x0c
+#define DATAGRAM_FLAGS_RESERVED 0xf0
+
+/*
+ * 4.4.2. DIRECT_UNIQUE, DIRECT_GROUP, & BROADCAST DATAGRAM
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MSG_TYPE | FLAGS | DGM_ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_IP |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_PORT | DGM_LENGTH |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | PACKET_OFFSET | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | |
+ * / SOURCE_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / DESTINATION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / USER_DATA /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+typedef struct {
+ datagram_header header;
+ unsigned char *source_name;
+ unsigned char *destination_name;
+ unsigned char *user_data;
+} datagram_packet;
+
+
+/*
+ * 4.4.3. DATAGRAM ERROR PACKET
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MSG_TYPE | FLAGS | DGM_ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_IP |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_PORT | ERROR_CODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * ERROR_CODE values (in hexidecimal):
+ *
+ * 82 - DESTINATION NAME NOT PRESENT
+ * 83 - INVALID SOURCE NAME FORMAT
+ * 84 - INVALID DESTINATION NAME FORMAT
+ */
+
+typedef struct {
+ unsigned char msg_type;
+ unsigned char flags;
+ unsigned short dgm_id;
+ uint32_t source_ip;
+ unsigned short source_port;
+ unsigned char error;
+} datagram_error_packet;
+
+/*
+ * 4.4.4. DATAGRAM QUERY REQUEST
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MSG_TYPE | FLAGS | DGM_ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_IP |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_PORT | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | |
+ * / DESTINATION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 4.4.5. DATAGRAM POSITIVE AND NEGATIVE QUERY RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MSG_TYPE | FLAGS | DGM_ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_IP |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_PORT | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | |
+ * / DESTINATION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+typedef struct datagram_query_packet {
+ unsigned char msg_type;
+ unsigned char flags;
+ unsigned short dgm_id;
+ uint32_t source_ip;
+ unsigned short source_port;
+ unsigned char destination_name[MAX_NAME_LENGTH];
+} datagram_query_packet;
+
+
+typedef struct datagram {
+ struct datagram *forw;
+ struct datagram *back;
+ struct addr_entry inaddr;
+ int discard_timer;
+ unsigned char packet_type;
+ unsigned char flags;
+ unsigned short datagram_id;
+ struct name_entry src;
+ struct name_entry dest;
+ unsigned short offset;
+ unsigned short data_length;
+ unsigned char *data;
+ unsigned int rawbytes;
+ unsigned char rawbuf[MAX_DATAGRAM_LENGTH];
+} datagram;
+
+typedef struct datagram_queue {
+ struct datagram *forw;
+ struct datagram *back;
+} datagram_queue;
+
+typedef struct name_queue {
+ struct name_entry head;
+ mutex_t mtx;
+} name_queue_t;
+
+#define NETBIOS_EMPTY_NAME (unsigned char *)""
+
+#define NETBIOS_NAME_IS_STAR(name) \
+ (bcmp(name, "*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", NETBIOS_NAME_SZ) == 0)
+
+void smb_netbios_chg_status(uint32_t status, int set);
+
+/*
+ * Name Cache Functions
+ */
+int smb_netbios_cache_init(void);
+void smb_netbios_cache_fini(void);
+void smb_netbios_cache_dump(void);
+void smb_netbios_cache_print(void);
+void smb_netbios_cache_diag(char ** pbuf);
+int smb_netbios_cache_count(void);
+void smb_netbios_cache_clean(void);
+void smb_netbios_cache_reset_ttl(void);
+void smb_netbios_cache_delete_locals(name_queue_t *delq);
+void smb_netbios_cache_refresh(name_queue_t *refq);
+
+int smb_netbios_cache_insert(struct name_entry *name);
+int smb_netbios_cache_insert_list(struct name_entry *name);
+void smb_netbios_cache_delete(struct name_entry *name);
+int smb_netbios_cache_delete_addr(struct name_entry *name);
+struct name_entry *smb_netbios_cache_lookup(struct name_entry *name);
+struct name_entry *smb_netbios_cache_lookup_addr(struct name_entry *name);
+void smb_netbios_cache_update_entry(struct name_entry *entry,
+ struct name_entry *name);
+void smb_netbios_cache_unlock_entry(struct name_entry *name);
+unsigned char *smb_netbios_cache_status(unsigned char *buf, int bufsize,
+ unsigned char *scope);
+
+void smb_netbios_name_dump(struct name_entry *entry);
+void smb_netbios_name_logf(struct name_entry *entry);
+void smb_netbios_name_freeaddrs(struct name_entry *entry);
+struct name_entry *smb_netbios_name_dup(struct name_entry *entry,
+ int alladdr);
+
+/* Name service functions */
+void *smb_netbios_name_service_daemon(void *);
+void smb_init_name_struct(unsigned char *, char,
+ unsigned char *, uint32_t, unsigned short,
+ uint32_t, uint32_t, struct name_entry *);
+
+struct name_entry *smb_name_find_name(struct name_entry *name);
+int smb_name_add_name(struct name_entry *name);
+int smb_name_delete_name(struct name_entry *name);
+void smb_name_unlock_name(struct name_entry *name);
+
+void smb_netbios_name_config(void);
+void smb_netbios_name_unconfig(void);
+void smb_netbios_name_tick(void);
+
+int smb_first_level_name_encode(struct name_entry *name,
+ unsigned char *out, int max_out);
+int smb_first_level_name_decode(unsigned char *in,
+ struct name_entry *name);
+void smb_encode_netbios_name(unsigned char *name,
+ char suffix, unsigned char *scope,
+ struct name_entry *dest);
+
+/* Datagram service functions */
+void *smb_netbios_datagram_service_daemon(void *);
+int smb_netbios_datagram_send(struct name_entry *,
+ struct name_entry *, unsigned char *, int);
+void smb_netbios_datagram_tick(void);
+
+
+/* browser functions */
+void smb_browser_config(void);
+void *smb_browser_dispatch(void *arg);
+void *smb_browser_daemon(void *);
+int smb_net_id(uint32_t ipaddr);
+struct name_entry *smb_browser_get_srvname(unsigned short netid);
+int smb_browser_load_transact_header(unsigned char *buffer,
+ int maxcnt, int data_count, int reply, char *mailbox);
+
+/* Netlogon function */
+/*
+ * smb_netlogon_receive
+ *
+ * This is where we handle all incoming NetLogon messages. Currently, we
+ * ignore requests from anyone else. We are only interested in responses
+ * to our own requests. The NetLogonResponse provides the name of the PDC.
+ * If we don't already have a controller name, we use the name provided
+ * in the message. Otherwise we use the name already in the environment.
+ */
+void smb_netlogon_receive(struct datagram *datagram, char *mailbox,
+ unsigned char *data, int datalen);
+
+#endif /* _SMB_NETBIOS_H_ */
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_cache.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_cache.c
new file mode 100644
index 0000000000..1e47658700
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_cache.c
@@ -0,0 +1,776 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <synch.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <smbsrv/libsmbns.h>
+#include <smbns_netbios.h>
+
+#define NETBIOS_HTAB_SZ 128
+#define NETBIOS_HKEY_SZ (NETBIOS_NAME_SZ + NETBIOS_DOMAIN_NAME_MAX)
+
+#define NETBIOS_NAMEBUF char namebuf[20]
+
+#define NETBIOS_SAME_IP(addr1, addr2) \
+ ((addr1)->sin.sin_addr.s_addr == (addr2)->sin.sin_addr.s_addr)
+
+int smb_netbios_name_debug = 0;
+
+typedef char nb_key_t[NETBIOS_HKEY_SZ];
+static HT_HANDLE *smb_netbios_cache = 0;
+static rwlock_t nb_cache_lock;
+
+static char *smb_strname(struct name_entry *name, char *buf, int bufsize);
+static void hash_callback(HT_ITEM *item);
+static int smb_netbios_match(const char *key1, const char *key2, size_t n);
+static void smb_netbios_cache_key(char *key, unsigned char *name,
+ unsigned char *scope);
+
+int
+smb_netbios_cache_init()
+{
+ (void) rw_wrlock(&nb_cache_lock);
+ if (smb_netbios_cache == 0) {
+ smb_netbios_cache = ht_create_table(NETBIOS_HTAB_SZ,
+ NETBIOS_HKEY_SZ, HTHF_FIXED_KEY);
+ if (smb_netbios_cache == 0) {
+ syslog(LOG_ERR,
+ "smbd: cannot create NetBIOS name cache");
+ (void) rw_unlock(&nb_cache_lock);
+ return (0);
+ }
+ (void) ht_register_callback(smb_netbios_cache, hash_callback);
+ ht_set_cmpfn(smb_netbios_cache, smb_netbios_match);
+ }
+ (void) rw_unlock(&nb_cache_lock);
+
+ return (1);
+}
+
+void
+smb_netbios_cache_fini()
+{
+ (void) rw_wrlock(&nb_cache_lock);
+ ht_destroy_table(smb_netbios_cache);
+ smb_netbios_cache = 0;
+ (void) rw_unlock(&nb_cache_lock);
+}
+
+void
+smb_netbios_cache_clean()
+{
+ (void) rw_wrlock(&nb_cache_lock);
+ (void) ht_clean_table(smb_netbios_cache);
+ (void) rw_unlock(&nb_cache_lock);
+}
+
+/*
+ * smb_netbios_cache_lookup
+ *
+ * Searches the name cache for the given entry, if found
+ * the entry will be locked before returning to caller
+ * so caller MUST unlock the entry after it's done with it.
+ */
+struct name_entry *
+smb_netbios_cache_lookup(struct name_entry *name)
+{
+ HT_ITEM *item;
+ nb_key_t key;
+ struct name_entry *entry = NULL;
+ unsigned char scope[SMB_PI_MAX_SCOPE];
+ unsigned char hostname[MAXHOSTNAMELEN];
+
+ if (NETBIOS_NAME_IS_STAR(name->name)) {
+ /* Return our address */
+ smb_config_rdlock();
+ (void) strlcpy((char *)scope,
+ smb_config_getstr(SMB_CI_NBSCOPE), sizeof (scope));
+ (void) utf8_strupr((char *)scope);
+ smb_config_unlock();
+
+ if (smb_getnetbiosname((char *)hostname, MAXHOSTNAMELEN) != 0)
+ return (NULL);
+
+ smb_encode_netbios_name(hostname, 0x00, scope, name);
+ }
+
+ (void) rw_rdlock(&nb_cache_lock);
+
+ smb_netbios_cache_key(key, name->name, name->scope);
+ item = ht_find_item(smb_netbios_cache, key);
+ if (item) {
+ entry = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&entry->mtx);
+ if ((entry->attributes & NAME_ATTR_CONFLICT) != 0) {
+ (void) mutex_unlock(&entry->mtx);
+ entry = NULL;
+ }
+ }
+
+ (void) rw_unlock(&nb_cache_lock);
+ return (entry);
+}
+
+void
+smb_netbios_cache_unlock_entry(struct name_entry *name)
+{
+ if (name)
+ (void) mutex_unlock(&name->mtx);
+}
+
+/*
+ * smb_netbios_cache_lookup_addr
+ *
+ * lookup the given 'name' in the cache and then checks
+ * if the address also matches with the found entry.
+ * 'name' is supposed to contain only one address.
+ *
+ * The found entry will be locked before returning to caller
+ * so caller MUST unlock the entry after it's done with it.
+ */
+struct name_entry *
+smb_netbios_cache_lookup_addr(struct name_entry *name)
+{
+ struct name_entry *entry = 0;
+ struct addr_entry *addr;
+ struct addr_entry *name_addr;
+ HT_ITEM *item;
+ nb_key_t key;
+
+ (void) rw_rdlock(&nb_cache_lock);
+ smb_netbios_cache_key(key, name->name, name->scope);
+ item = ht_find_item(smb_netbios_cache, key);
+
+ if (item && item->hi_data) {
+ name_addr = &name->addr_list;
+ entry = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&entry->mtx);
+ addr = &entry->addr_list;
+ do {
+ if (NETBIOS_SAME_IP(addr, name_addr)) {
+ /* note that entry lock isn't released here */
+ (void) rw_unlock(&nb_cache_lock);
+ return (entry);
+ }
+ addr = addr->forw;
+ } while (addr != &entry->addr_list);
+ (void) mutex_unlock(&entry->mtx);
+ }
+
+ (void) rw_unlock(&nb_cache_lock);
+ return (0);
+}
+
+int
+smb_netbios_cache_insert(struct name_entry *name)
+{
+ struct name_entry *entry;
+ struct addr_entry *addr;
+ struct addr_entry *name_addr;
+ HT_ITEM *item;
+ nb_key_t key;
+
+ /* No point in adding a name with IP address 255.255.255.255 */
+ if (name->addr_list.sin.sin_addr.s_addr == 0xffffffff)
+ return (0);
+
+ (void) rw_wrlock(&nb_cache_lock);
+ smb_netbios_cache_key(key, name->name, name->scope);
+ item = ht_find_item(smb_netbios_cache, key);
+
+ if (item && item->hi_data) {
+ /* Name already exists */
+ entry = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&entry->mtx);
+
+ name_addr = &name->addr_list;
+ addr = &entry->addr_list;
+ if (NETBIOS_SAME_IP(addr, name_addr) &&
+ (addr->sin.sin_port == name_addr->sin.sin_port)) {
+ entry->attributes |=
+ name_addr->attributes & NAME_ATTR_LOCAL;
+ syslog(LOG_DEBUG, "cache_insert: exists");
+ (void) mutex_unlock(&entry->mtx);
+ (void) rw_unlock(&nb_cache_lock);
+ return (0); /* exists */
+ }
+
+ /* Was not primary: looks for others */
+ for (addr = entry->addr_list.forw;
+ addr != &entry->addr_list; addr = addr->forw) {
+ if (NETBIOS_SAME_IP(addr, name_addr) &&
+ (addr->sin.sin_port == name_addr->sin.sin_port)) {
+ syslog(LOG_DEBUG, "cache_insert: dup");
+ (void) mutex_unlock(&entry->mtx);
+ (void) rw_unlock(&nb_cache_lock);
+ return (0); /* exists */
+ }
+ }
+
+ addr = (struct addr_entry *)malloc(sizeof (struct addr_entry));
+ if (addr == 0) {
+ (void) mutex_unlock(&entry->mtx);
+ (void) rw_unlock(&nb_cache_lock);
+ return (-1);
+ }
+ *addr = name->addr_list;
+ entry->attributes |= addr->attributes;
+ QUEUE_INSERT_TAIL(&entry->addr_list, addr);
+ (void) mutex_unlock(&entry->mtx);
+ (void) rw_unlock(&nb_cache_lock);
+ return (0);
+ }
+
+ entry = (struct name_entry *)malloc(sizeof (struct name_entry));
+ if (entry == 0) {
+ (void) rw_unlock(&nb_cache_lock);
+ return (-1);
+ }
+ *entry = *name;
+ entry->addr_list.forw = entry->addr_list.back = &entry->addr_list;
+ entry->attributes |= entry->addr_list.attributes;
+ (void) mutex_init(&entry->mtx, 0, 0);
+ if (ht_replace_item(smb_netbios_cache, key, entry) == 0) {
+ free(entry);
+ (void) rw_unlock(&nb_cache_lock);
+ return (-1);
+ }
+
+ (void) rw_unlock(&nb_cache_lock);
+ return (0);
+}
+
+
+void
+smb_netbios_cache_delete(struct name_entry *name)
+{
+ nb_key_t key;
+ HT_ITEM *item;
+ struct name_entry *entry;
+
+ (void) rw_wrlock(&nb_cache_lock);
+ smb_netbios_cache_key(key, name->name, name->scope);
+ item = ht_find_item(smb_netbios_cache, key);
+ if (item && item->hi_data) {
+ entry = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&entry->mtx);
+ ht_mark_delete(smb_netbios_cache, item);
+ (void) mutex_unlock(&entry->mtx);
+ }
+ (void) rw_unlock(&nb_cache_lock);
+}
+
+/*
+ * smb_netbios_cache_insert_list
+ *
+ * Insert a name with multiple addresses
+ */
+int
+smb_netbios_cache_insert_list(struct name_entry *name)
+{
+ struct name_entry entry;
+ struct addr_entry *addr;
+
+ addr = &name->addr_list;
+ do {
+ smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, name->scope,
+ addr->sin.sin_addr.s_addr,
+ addr->sin.sin_port,
+ name->attributes,
+ addr->attributes,
+ &entry);
+ (void) memcpy(entry.name, name->name, NETBIOS_NAME_SZ);
+ entry.addr_list.refresh_ttl = entry.addr_list.ttl =
+ addr->refresh_ttl;
+ (void) smb_netbios_cache_insert(&entry);
+ addr = addr->forw;
+ } while (addr != &name->addr_list);
+
+ return (0);
+}
+
+void
+smb_netbios_cache_update_entry(struct name_entry *entry,
+ struct name_entry *name)
+{
+ struct addr_entry *addr;
+ struct addr_entry *name_addr;
+
+ addr = &entry->addr_list;
+ name_addr = &name->addr_list;
+
+ if (IS_UNIQUE(entry->attributes)) {
+ do {
+ addr->ttl = name_addr->ttl;
+ addr = addr->forw;
+ } while (addr != &entry->addr_list);
+
+ } else {
+ do {
+ if (NETBIOS_SAME_IP(addr, name_addr) &&
+ (addr->sin.sin_port == name_addr->sin.sin_port)) {
+ addr->ttl = name_addr->ttl;
+ return;
+ }
+ addr = addr->forw;
+ } while (addr != &entry->addr_list);
+ }
+}
+
+/*
+ * smb_netbios_cache_status
+ *
+ * Scan the name cache and gather status for
+ * Node Status response for names in the given scope
+ */
+unsigned char *
+smb_netbios_cache_status(unsigned char *buf, int bufsize, unsigned char *scope)
+{
+ HT_ITERATOR hti;
+ HT_ITEM *item;
+ struct name_entry *name;
+ unsigned char *numnames;
+ unsigned char *scan;
+ unsigned char *scan_end;
+
+ scan = buf;
+ scan_end = scan + bufsize;
+
+ numnames = scan++;
+ *numnames = 0;
+
+ (void) rw_rdlock(&nb_cache_lock);
+ item = ht_findfirst(smb_netbios_cache, &hti);
+ do {
+ if (item == 0)
+ break;
+
+ if (item->hi_data == 0)
+ continue;
+
+ if ((scan + NETBIOS_NAME_SZ + 2) >= scan_end)
+ /* no room for adding next entry */
+ break;
+
+ name = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&name->mtx);
+
+ if (IS_LOCAL(name->attributes) &&
+ (strcasecmp((char *)scope, (char *)name->scope) == 0)) {
+ bcopy(name->name, scan, NETBIOS_NAME_SZ);
+ scan += NETBIOS_NAME_SZ;
+ *scan++ = PUBLIC_BITS(name->attributes) >> 8;
+ *scan++ = PUBLIC_BITS(name->attributes);
+ (*numnames)++;
+ }
+
+ (void) mutex_unlock(&name->mtx);
+ } while ((item = ht_findnext(&hti)) != 0);
+ (void) rw_unlock(&nb_cache_lock);
+
+ return (scan);
+}
+
+void
+smb_netbios_cache_reset_ttl()
+{
+ struct addr_entry *addr;
+ struct name_entry *name;
+ HT_ITERATOR hti;
+ HT_ITEM *item;
+
+ (void) rw_rdlock(&nb_cache_lock);
+ item = ht_findfirst(smb_netbios_cache, &hti);
+ do {
+ if (item == 0)
+ break;
+
+ if (item->hi_data == 0)
+ continue;
+
+ name = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&name->mtx);
+
+ addr = &name->addr_list;
+ do {
+ if (addr->ttl < 1) {
+ if (addr->refresh_ttl)
+ addr->ttl = addr->refresh_ttl;
+ else
+ addr->refresh_ttl = addr->ttl =
+ TO_SECONDS(DEFAULT_TTL);
+ }
+ addr = addr->forw;
+ } while (addr != &name->addr_list);
+
+ (void) mutex_unlock(&name->mtx);
+ } while ((item = ht_findnext(&hti)) != 0);
+ (void) rw_unlock(&nb_cache_lock);
+}
+
+/*
+ * Returns TRUE when given name is added to the refresh queue
+ * FALSE if not.
+ */
+static boolean_t
+smb_netbios_cache_insrefq(name_queue_t *refq, HT_ITEM *item)
+{
+ struct name_entry *name;
+ struct name_entry *refent;
+
+ name = (struct name_entry *)item->hi_data;
+
+ if (IS_LOCAL(name->attributes)) {
+ if (IS_UNIQUE(name->attributes)) {
+ refent = smb_netbios_name_dup(name, 1);
+ if (refent)
+ QUEUE_INSERT_TAIL(&refq->head, refent)
+
+ /* next name */
+ return (B_TRUE);
+ }
+ } else {
+ ht_mark_delete(smb_netbios_cache, item);
+ refent = smb_netbios_name_dup(name, 0);
+ if (refent)
+ QUEUE_INSERT_TAIL(&refq->head, refent)
+
+ /* next name */
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * smb_netbios_cache_refresh
+ *
+ * Scans the name cache and add all local unique names
+ * and non-local names the passed refresh queue. Non-
+ * local names will also be marked as deleted.
+ *
+ * NOTE that the caller MUST protect the queue using
+ * its mutex
+ */
+void
+smb_netbios_cache_refresh(name_queue_t *refq)
+{
+ struct name_entry *name;
+ struct addr_entry *addr;
+ HT_ITERATOR hti;
+ HT_ITEM *item;
+
+ bzero(&refq->head, sizeof (refq->head));
+ refq->head.forw = refq->head.back = &refq->head;
+
+ (void) rw_rdlock(&nb_cache_lock);
+ item = ht_findfirst(smb_netbios_cache, &hti);
+ do { /* name loop */
+ if (item == 0)
+ break;
+
+ if (item->hi_data == 0)
+ continue;
+
+ name = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&name->mtx);
+
+ addr = &name->addr_list;
+ do { /* address loop */
+ if (addr->ttl > 0) {
+ addr->ttl--;
+ if (addr->ttl == 0) {
+ if (smb_netbios_cache_insrefq(refq,
+ item))
+ break;
+ }
+ }
+ addr = addr->forw;
+ } while (addr != &name->addr_list);
+
+ (void) mutex_unlock(&name->mtx);
+ } while ((item = ht_findnext(&hti)) != 0);
+ (void) rw_unlock(&nb_cache_lock);
+}
+
+/*
+ * smb_netbios_cache_delete_locals
+ *
+ * Scans the name cache and add all local names to
+ * the passed delete queue.
+ *
+ * NOTE that the caller MUST protect the queue using
+ * its mutex
+ */
+void
+smb_netbios_cache_delete_locals(name_queue_t *delq)
+{
+ struct name_entry *entry;
+ struct name_entry *delent;
+ HT_ITERATOR hti;
+ HT_ITEM *item;
+
+ bzero(&delq->head, sizeof (delq->head));
+ delq->head.forw = delq->head.back = &delq->head;
+
+ (void) rw_wrlock(&nb_cache_lock);
+ item = ht_findfirst(smb_netbios_cache, &hti);
+ do {
+ if (item == 0)
+ break;
+
+ if (item->hi_data == 0)
+ continue;
+
+ entry = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&entry->mtx);
+
+ if (IS_LOCAL(entry->attributes)) {
+ ht_mark_delete(smb_netbios_cache, item);
+ delent = smb_netbios_name_dup(entry, 1);
+ if (delent)
+ QUEUE_INSERT_TAIL(&delq->head, delent)
+ }
+
+ (void) mutex_unlock(&entry->mtx);
+ } while ((item = ht_findnext(&hti)) != 0);
+ (void) rw_unlock(&nb_cache_lock);
+}
+
+void
+smb_netbios_name_freeaddrs(struct name_entry *entry)
+{
+ struct addr_entry *addr;
+
+ if (entry == 0)
+ return;
+
+ while ((addr = entry->addr_list.forw) != &entry->addr_list) {
+ QUEUE_CLIP(addr);
+ free(addr);
+ }
+}
+
+/*
+ * smb_netbios_cache_count
+ *
+ * Returns the number of names in the cache
+ */
+int
+smb_netbios_cache_count()
+{
+ int cnt;
+
+ (void) rw_rdlock(&nb_cache_lock);
+ cnt = ht_get_total_items(smb_netbios_cache);
+ (void) rw_unlock(&nb_cache_lock);
+
+ return (cnt);
+}
+
+void
+smb_netbios_cache_dump(void)
+{
+ struct name_entry *name;
+ HT_ITERATOR hti;
+ HT_ITEM *item;
+
+ (void) rw_rdlock(&nb_cache_lock);
+ item = ht_findfirst(smb_netbios_cache, &hti);
+ while (item) {
+ if (item->hi_data) {
+ name = (struct name_entry *)item->hi_data;
+ (void) mutex_lock(&name->mtx);
+ smb_netbios_name_dump(name);
+ (void) mutex_unlock(&name->mtx);
+ }
+ item = ht_findnext(&hti);
+ }
+ (void) rw_unlock(&nb_cache_lock);
+}
+
+void
+smb_netbios_name_dump(struct name_entry *entry)
+{
+ struct addr_entry *addr;
+ int count = 0;
+
+ if (smb_netbios_name_debug == 0)
+ return;
+
+ syslog(LOG_DEBUG, "name='%15.15s<%02X>' scope='%s' attr=0x%x",
+ entry->name, entry->name[15],
+ entry->scope, entry->attributes);
+ addr = &entry->addr_list;
+ do {
+ syslog(LOG_DEBUG, "addr_list[%d]:", count++);
+ syslog(LOG_DEBUG, " attributes = 0x%x", addr->attributes);
+ syslog(LOG_DEBUG, " conflict_timer = %d",
+ addr->conflict_timer);
+ syslog(LOG_DEBUG, " refresh_ttl = %d", addr->refresh_ttl);
+ syslog(LOG_DEBUG, " ttl = %d", addr->ttl);
+ syslog(LOG_DEBUG, " sin.sin_addr = %s",
+ inet_ntoa(addr->sin.sin_addr));
+ syslog(LOG_DEBUG, " sin.sin_port = %d", addr->sin.sin_port);
+ syslog(LOG_DEBUG, " sin.sinlen = %d", addr->sinlen);
+ addr = addr->forw;
+ } while (addr != &entry->addr_list);
+}
+
+void
+smb_netbios_name_logf(struct name_entry *entry)
+{
+ struct addr_entry *addr;
+ NETBIOS_NAMEBUF;
+
+ (void) smb_strname(entry, namebuf, sizeof (namebuf));
+ syslog(LOG_DEBUG, "%s flags=0x%x\n", namebuf, entry->attributes);
+ addr = &entry->addr_list;
+ do {
+ syslog(LOG_DEBUG, " %s ttl=%d flags=0x%x",
+ inet_ntoa(addr->sin.sin_addr),
+ addr->ttl, addr->attributes);
+ addr = addr->forw;
+ } while (addr && (addr != &entry->addr_list));
+}
+
+/*
+ * smb_netbios_name_dup
+ *
+ * Duplicate the given name entry. If 'alladdr' is 0 only
+ * copy the primary address otherwise duplicate all the
+ * addresses. NOTE that the duplicate structure is not
+ * like a regular cache entry i.e. it's a contiguous block
+ * of memory and each addr structure doesn't have it's own
+ * allocated memory. So, the returned structure can be freed
+ * by one free call.
+ */
+struct name_entry *
+smb_netbios_name_dup(struct name_entry *entry, int alladdr)
+{
+ struct addr_entry *addr;
+ struct addr_entry *dup_addr;
+ struct name_entry *dup;
+ int addr_cnt = 0;
+ int size = 0;
+
+ if (alladdr) {
+ addr = entry->addr_list.forw;
+ while (addr && (addr != &entry->addr_list)) {
+ addr_cnt++;
+ addr = addr->forw;
+ }
+ }
+
+ size = sizeof (struct name_entry) +
+ (addr_cnt * sizeof (struct addr_entry));
+ dup = (struct name_entry *)malloc(size);
+ if (dup == 0)
+ return (0);
+
+ bzero(dup, size);
+
+ dup->forw = dup->back = dup;
+ dup->attributes = entry->attributes;
+ (void) memcpy(dup->name, entry->name, NETBIOS_NAME_SZ);
+ (void) strlcpy((char *)dup->scope, (char *)entry->scope,
+ NETBIOS_DOMAIN_NAME_MAX);
+ dup->addr_list = entry->addr_list;
+ dup->addr_list.forw = dup->addr_list.back = &dup->addr_list;
+
+ if (alladdr == 0)
+ return (dup);
+
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ dup_addr = (struct addr_entry *)((unsigned char *)dup +
+ sizeof (struct name_entry));
+
+ addr = entry->addr_list.forw;
+ while (addr && (addr != &entry->addr_list)) {
+ *dup_addr = *addr;
+ QUEUE_INSERT_TAIL(&dup->addr_list, dup_addr);
+ addr = addr->forw;
+ dup_addr++;
+ }
+
+ return (dup);
+}
+
+static char *
+smb_strname(struct name_entry *name, char *buf, int bufsize)
+{
+ char *p;
+
+ (void) snprintf(buf, bufsize, "%15.15s", name->name);
+ p = strchr(buf, ' ');
+ if (p)
+ (void) snprintf(p, 5, "<%02X>", name->name[15]);
+
+ return (buf);
+}
+
+static void
+hash_callback(HT_ITEM *item)
+{
+ struct name_entry *entry;
+
+ if (item && item->hi_data) {
+ entry = (struct name_entry *)item->hi_data;
+ smb_netbios_name_freeaddrs(entry);
+ free(entry);
+ }
+}
+
+
+/*ARGSUSED*/
+static int
+smb_netbios_match(const char *key1, const char *key2, size_t n)
+{
+ int res;
+
+ res = bcmp(key1, key2, NETBIOS_NAME_SZ);
+ if (res == 0) {
+ /* Names are the same, compare scopes */
+ res = strcmp(key1 + NETBIOS_NAME_SZ, key2 + NETBIOS_NAME_SZ);
+ }
+
+ return (res);
+}
+
+static void
+smb_netbios_cache_key(char *key, unsigned char *name, unsigned char *scope)
+{
+ bzero(key, NETBIOS_HKEY_SZ);
+ (void) memcpy(key, name, NETBIOS_NAME_SZ);
+ (void) memcpy(key + NETBIOS_NAME_SZ, scope,
+ strlen((const char *)scope));
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_datagram.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_datagram.c
new file mode 100644
index 0000000000..2775ccfe7a
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_datagram.c
@@ -0,0 +1,1061 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Description:
+ *
+ * Contains base code for netbios datagram service.
+ *
+ * Relavent sections from RFC1002:
+ *
+ * 5.3. NetBIOS DATAGRAM SERVICE PROTOCOLS
+ *
+ * The following are GLOBAL variables and should be NetBIOS user
+ * configurable:
+ *
+ * - SCOPE_ID: the non-leaf section of the domain name preceded by a
+ * '.' which represents the domain of the NetBIOS scope for the
+ * NetBIOS name. The following protocol description only supports
+ * single scope operation.
+ *
+ * - MAX_DATAGRAM_LENGTH: the maximum length of an IP datagram. The
+ * minimal maximum length defined in for IP is 576 bytes. This
+ * value is used when determining whether to fragment a NetBIOS
+ * datagram. Implementations are expected to be capable of
+ * receiving unfragmented NetBIOS datagrams up to their maximum
+ * size.
+ *
+ * - BROADCAST_ADDRESS: the IP address B-nodes use to send datagrams
+ * with group name destinations and broadcast datagrams. The
+ * default is the IP broadcast address for a single IP network.
+ *
+ *
+ * The following are Defined Constants for the NetBIOS Datagram
+ * Service:
+ *
+ * - DGM_SRVC_UDP_PORT: the globally well-known UDP port allocated
+ * where the NetBIOS Datagram Service receives UDP packets. See
+ * section 6, "Defined Constants", for its value.
+ */
+
+/*
+ *
+ * 6. DEFINED CONSTANTS AND VARIABLES
+ *
+ * GENERAL:
+ *
+ * SCOPE_ID The name of the NetBIOS scope.
+ *
+ * This is expressed as a character
+ * string meeting the requirements of
+ * the domain name system and without
+ * a leading or trailing "dot".
+ *
+ * An implementation may elect to make
+ * this a single global value for the
+ * node or allow it to be specified
+ * with each separate NetBIOS name
+ * (thus permitting cross-scope
+ * references.)
+ *
+ * BROADCAST_ADDRESS An IP address composed of the
+ * node network and subnetwork
+ * numbers with all remaining bits set
+ * to one.
+ *
+ * I.e. "Specific subnet" broadcast
+ * addressing according to section 2.3
+ * of RFC 950.
+ *
+ * BCAST_REQ_RETRY_TIMEOUT 250 milliseconds.
+ * An adaptive timer may be used.
+ *
+ * BCAST_REQ_RETRY_COUNT 3
+ *
+ * UCAST_REQ_RETRY_TIMEOUT 5 seconds
+ * An adaptive timer may be used.
+ *
+ * UCAST_REQ_RETRY_COUNT 3
+ *
+ * MAX_DATAGRAM_LENGTH 576 bytes (default)
+ *
+ * DATAGRAM SERVICE:
+ *
+ * DGM_SRVC_UDP_PORT 138 (decimal)
+ *
+ * FRAGMENT_TO 2 seconds (default)
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <synch.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <smbns_netbios.h>
+
+#include <smbsrv/libsmbns.h>
+
+static int datagram_sock = -1;
+static short datagram_id = 1;
+static struct datagram_queue smb_datagram_queue;
+static mutex_t smb_dgq_mtx;
+
+/*
+ * Function: smb_netbios_datagram_tick(void)
+ *
+ * Description:
+ *
+ * Called once a second to handle time to live timeouts in
+ * datagram assembly queue.
+ *
+ * Inputs:
+ *
+ * Returns:
+ * void -> Nothing at all...
+ */
+
+void
+smb_netbios_datagram_tick(void)
+{
+ struct datagram *entry;
+ struct datagram *next;
+
+ (void) mutex_lock(&smb_dgq_mtx);
+
+ for (entry = smb_datagram_queue.forw;
+ entry != (struct datagram *)((uintptr_t)&smb_datagram_queue);
+ entry = next) {
+ next = entry->forw;
+ if (--entry->discard_timer == 0) {
+ /* Toss it */
+ QUEUE_CLIP(entry);
+ free(entry);
+ }
+ }
+ (void) mutex_unlock(&smb_dgq_mtx);
+}
+
+void
+smb_netbios_datagram_fini()
+{
+ struct datagram *entry;
+
+ (void) mutex_lock(&smb_dgq_mtx);
+ while ((entry = smb_datagram_queue.forw) !=
+ (struct datagram *)((uintptr_t)&smb_datagram_queue)) {
+ QUEUE_CLIP(entry);
+ free(entry);
+ }
+ (void) mutex_unlock(&smb_dgq_mtx);
+}
+
+/*
+ * Function: int smb_netbios_send_Bnode_datagram(unsigned char *data,
+ * struct name_entry *source, struct name_entry *destination,
+ * uint32_t broadcast)
+ *
+ * Description from rfc1002:
+ *
+ * 5.3.1. B NODE TRANSMISSION OF NetBIOS DATAGRAMS
+ *
+ * PROCEDURE send_datagram(data, source, destination, broadcast)
+ *
+ * (*
+ * * user initiated processing on B node
+ * *)
+ *
+ * BEGIN
+ * group = FALSE;
+ *
+ * do name discovery on destination name, returns name type and
+ * IP address;
+ *
+ * IF name type is group name THEN
+ * BEGIN
+ * group = TRUE;
+ * END
+ *
+ * (*
+ * * build datagram service UDP packet;
+ * *)
+ * convert source and destination NetBIOS names into
+ * half-ASCII, biased encoded name;
+ * SOURCE_NAME = cat(source, SCOPE_ID);
+ * SOURCE_IP = this nodes IP address;
+ * SOURCE_PORT = DGM_SRVC_UDP_PORT;
+ *
+ * IF NetBIOS broadcast THEN
+ * BEGIN
+ * DESTINATION_NAME = cat("*", SCOPE_ID)
+ * END
+ * ELSE
+ * BEGIN
+ * DESTINATION_NAME = cat(destination, SCOPE_ID)
+ * END
+ *
+ * MSG_TYPE = select_one_from_set
+ * {BROADCAST, DIRECT_UNIQUE, DIRECT_GROUP}
+ * DGM_ID = next transaction id for Datagrams;
+ * DGM_LENGTH = length of data + length of second level encoded
+ * source and destination names;
+ *
+ * IF (length of the NetBIOS Datagram, including UDP and
+ * IP headers, > MAX_DATAGRAM_LENGTH) THEN
+ * BEGIN
+ * (*
+ * * fragment NetBIOS datagram into 2 UDP packets
+ * *)
+ * Put names into 1st UDP packet and any data that fits
+ * after names;
+ * Set MORE and FIRST bits in 1st UDP packets FLAGS;
+ * OFFSET in 1st UDP = 0;
+ *
+ * Replicate NetBIOS Datagram header from 1st UDP packet
+ * into 2nd UDP packet;
+ * Put rest of data in 2nd UDP packet;
+ * Clear MORE and FIRST bits in 2nd UDP packets FLAGS;
+ * OFFSET in 2nd UDP = DGM_LENGTH - number of name and
+ * data bytes in 1st UDP;
+ * END
+ * BEGIN
+ * (*
+ * * Only need one UDP packet
+ * *)
+ * USER_DATA = data;
+ * Clear MORE bit and set FIRST bit in FLAGS;
+ * OFFSET = 0;
+ * END
+ *
+ * IF (group == TRUE) OR (NetBIOS broadcast) THEN
+ * BEGIN
+ * send UDP packet(s) to BROADCAST_ADDRESS;
+ * END
+ * ELSE
+ * BEGIN
+ * send UDP packet(s) to IP address returned by name
+ * discovery;
+ * END
+ * END (* procedure *)
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MSG_TYPE | FLAGS | DGM_ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_IP |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_PORT | DGM_LENGTH |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | PACKET_OFFSET |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * MSG_TYPE values (in hexidecimal):
+ *
+ * 10 - DIRECT_UNIQUE DATAGRAM
+ * 11 - DIRECT_GROUP DATAGRAM
+ * 12 - BROADCAST DATAGRAM
+ * 13 - DATAGRAM ERROR
+ * 14 - DATAGRAM QUERY REQUEST
+ * 15 - DATAGRAM POSITIVE QUERY RESPONSE
+ * 16 - DATAGRAM NEGATIVE QUERY RESPONSE
+ *
+ * Bit definitions of the FLAGS field:
+ *
+ * 0 1 2 3 4 5 6 7
+ * +---+---+---+---+---+---+---+---+
+ * | 0 | 0 | 0 | 0 | SNT | F | M |
+ * +---+---+---+---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description
+ *
+ * M 7 MORE flag, If set then more NetBIOS datagram
+ * fragments follow.
+ *
+ * F 6 FIRST packet flag, If set then this is first
+ * (and possibly only) fragment of NetBIOS
+ * datagram
+ *
+ * SNT 4,5 Source End-Node type:
+ * 00 = B node
+ * 01 = P node
+ * 10 = M node
+ * 11 = NBDD
+ * RESERVED 0-3 Reserved, must be zero (0)
+ * (But MS sets bit 3 in this field)
+ *
+ */
+
+int
+smb_netbios_datagram_send(struct name_entry *src, struct name_entry *dest,
+ unsigned char *data, int length)
+{
+ uint32_t ipaddr;
+ size_t count, srclen, destlen, sinlen;
+ struct addr_entry *addr;
+ struct sockaddr_in sin;
+ char *buffer;
+ char ha_source[NETBIOS_DOMAIN_NAME_MAX];
+ char ha_dest[NETBIOS_DOMAIN_NAME_MAX];
+ net_cfg_t cfg;
+
+ (void) smb_first_level_name_encode(src, (unsigned char *)ha_source,
+ sizeof (ha_source));
+ srclen = strlen(ha_source) + 1;
+
+ (void) smb_first_level_name_encode(dest, (unsigned char *)ha_dest,
+ sizeof (ha_dest));
+ destlen = strlen(ha_dest) + 1;
+
+ /* give some extra room */
+ buffer = (char *)malloc(MAX_DATAGRAM_LENGTH * 4);
+ if (buffer == 0) {
+ syslog(LOG_ERR, "netbios: datagram send (resource shortage)");
+ return (-1);
+ }
+
+ buffer[0] = DATAGRAM_TYPE_DIRECT_UNIQUE;
+ switch (smb_node_type) {
+ case 'B':
+ buffer[1] = DATAGRAM_FLAGS_B_NODE | DATAGRAM_FLAGS_FIRST;
+ break;
+ case 'P':
+ buffer[1] = DATAGRAM_FLAGS_P_NODE | DATAGRAM_FLAGS_FIRST;
+ break;
+ case 'M':
+ buffer[1] = DATAGRAM_FLAGS_M_NODE | DATAGRAM_FLAGS_FIRST;
+ break;
+ case 'H':
+ default:
+ buffer[1] = DATAGRAM_FLAGS_H_NODE | DATAGRAM_FLAGS_FIRST;
+ break;
+ }
+
+ datagram_id++;
+ BE_OUT16(&buffer[2], datagram_id);
+ (void) memcpy(&buffer[4], &src->addr_list.sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+ (void) memcpy(&buffer[8], &src->addr_list.sin.sin_port,
+ sizeof (uint16_t));
+ BE_OUT16(&buffer[10], length + srclen + destlen);
+ BE_OUT16(&buffer[12], 0);
+
+ bcopy(ha_source, &buffer[14], srclen);
+ bcopy(ha_dest, &buffer[14 + srclen], destlen);
+ bcopy(data, &buffer[14 + srclen + destlen], length);
+ count = &buffer[14 + srclen + destlen + length] - buffer;
+
+ bzero(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sinlen = sizeof (sin);
+ addr = &dest->addr_list;
+ do {
+ ipaddr = addr->sin.sin_addr.s_addr;
+ /* Don't send anything to myself... */
+ if (smb_nic_get_byip(ipaddr, &cfg) != NULL) {
+ goto next;
+ }
+
+ sin.sin_addr.s_addr = ipaddr;
+ sin.sin_port = addr->sin.sin_port;
+ (void) sendto(datagram_sock, buffer, count, 0,
+ (struct sockaddr *)&sin, sinlen);
+
+next: addr = addr->forw;
+ } while (addr != &dest->addr_list);
+ free(buffer);
+ return (0);
+}
+
+
+int
+smb_netbios_datagram_send_to_net(struct name_entry *src,
+ struct name_entry *dest, char *data, int length)
+{
+ uint32_t ipaddr;
+ size_t count, srclen, destlen, sinlen;
+ struct addr_entry *addr;
+ struct sockaddr_in sin;
+ char *buffer;
+ char ha_source[NETBIOS_DOMAIN_NAME_MAX];
+ char ha_dest[NETBIOS_DOMAIN_NAME_MAX];
+ net_cfg_t cfg;
+
+ (void) smb_first_level_name_encode(src, (unsigned char *)ha_source,
+ sizeof (ha_source));
+ srclen = strlen(ha_source) + 1;
+
+ (void) smb_first_level_name_encode(dest, (unsigned char *)ha_dest,
+ sizeof (ha_dest));
+ destlen = strlen(ha_dest) + 1;
+
+ /* give some extra room */
+ buffer = (char *)malloc(MAX_DATAGRAM_LENGTH * 4);
+ if (buffer == 0) {
+ syslog(LOG_ERR, "netbios: datagram send (resource shortage)");
+ return (-1);
+ }
+
+ buffer[0] = DATAGRAM_TYPE_DIRECT_UNIQUE;
+ switch (smb_node_type) {
+ case 'B':
+ buffer[1] = DATAGRAM_FLAGS_B_NODE | DATAGRAM_FLAGS_FIRST;
+ break;
+ case 'P':
+ buffer[1] = DATAGRAM_FLAGS_P_NODE | DATAGRAM_FLAGS_FIRST;
+ break;
+ case 'M':
+ buffer[1] = DATAGRAM_FLAGS_M_NODE | DATAGRAM_FLAGS_FIRST;
+ break;
+ case 'H':
+ default:
+ buffer[1] = DATAGRAM_FLAGS_H_NODE | DATAGRAM_FLAGS_FIRST;
+ break;
+ }
+
+ datagram_id++;
+ BE_OUT16(&buffer[2], datagram_id);
+ (void) memcpy(&buffer[4], &src->addr_list.sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+ (void) memcpy(&buffer[8], &src->addr_list.sin.sin_port,
+ sizeof (uint16_t));
+ BE_OUT16(&buffer[10], length + srclen + destlen);
+ BE_OUT16(&buffer[12], 0);
+
+ bcopy(ha_source, &buffer[14], srclen);
+ bcopy(ha_dest, &buffer[14 + srclen], destlen);
+ bcopy(data, &buffer[14 + srclen + destlen], length);
+ count = &buffer[14 + srclen + destlen + length] - buffer;
+
+ bzero(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sinlen = sizeof (sin);
+ addr = &dest->addr_list;
+ do {
+ ipaddr = addr->sin.sin_addr.s_addr;
+ if (smb_nic_get_byip(ipaddr, &cfg) != NULL) {
+ goto next;
+ }
+ sin.sin_addr.s_addr = ipaddr;
+ sin.sin_port = addr->sin.sin_port;
+ (void) sendto(datagram_sock, buffer, count, 0,
+ (struct sockaddr *)&sin, sinlen);
+
+next: addr = addr->forw;
+ } while (addr != &dest->addr_list);
+ free(buffer);
+ return (0);
+}
+
+
+int
+smb_datagram_decode(struct datagram *datagram, int bytes)
+{
+ unsigned char *ha_src;
+ unsigned char *ha_dest;
+ unsigned char *data;
+
+ if (bytes < DATAGRAM_HEADER_LENGTH) {
+ syslog(LOG_ERR, "NbtDatagramDecode[%d]: too small packet",
+ bytes);
+ return (-1);
+ }
+
+ ha_src = &datagram->rawbuf[DATAGRAM_HEADER_LENGTH];
+ ha_dest = ha_src + strlen((char *)ha_src) + 1;
+ data = ha_dest + strlen((char *)ha_dest) + 1;
+
+ bzero(&datagram->src, sizeof (struct name_entry));
+ bzero(&datagram->dest, sizeof (struct name_entry));
+
+ datagram->rawbytes = bytes;
+ datagram->packet_type = datagram->rawbuf[0];
+ datagram->flags = datagram->rawbuf[1];
+ datagram->datagram_id = BE_IN16(&datagram->rawbuf[2]);
+
+ datagram->src.addr_list.sinlen = sizeof (struct sockaddr_in);
+ (void) memcpy(&datagram->src.addr_list.sin.sin_addr.s_addr,
+ &datagram->rawbuf[4], sizeof (uint32_t));
+ (void) memcpy(&datagram->src.addr_list.sin.sin_port,
+ &datagram->rawbuf[8], sizeof (uint16_t));
+ datagram->src.addr_list.forw = datagram->src.addr_list.back =
+ &datagram->src.addr_list;
+
+ datagram->data = data;
+ datagram->data_length = BE_IN16(&datagram->rawbuf[10]);
+ datagram->offset = BE_IN16(&datagram->rawbuf[12]);
+
+ if (smb_first_level_name_decode(ha_src, &datagram->src) < 0) {
+ syslog(LOG_DEBUG, "NbtDatagram[%s]: invalid calling name",
+ inet_ntoa(datagram->src.addr_list.sin.sin_addr));
+ syslog(LOG_DEBUG, "Calling name: <%02X>%32.32s",
+ ha_src[0], &ha_src[1]);
+ }
+
+ datagram->dest.addr_list.forw = datagram->dest.addr_list.back =
+ &datagram->dest.addr_list;
+
+ if (smb_first_level_name_decode(ha_dest, &datagram->dest) < 0) {
+ syslog(LOG_DEBUG, "NbtDatagram[%s]: invalid called name",
+ inet_ntoa(datagram->src.addr_list.sin.sin_addr));
+ syslog(LOG_DEBUG, "Called name: <%02X>%32.32s", ha_dest[0],
+ &ha_dest[1]);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Function: int smb_netbios_process_BPM_datagram(unsigned char *packet,
+ * struct addr_entry *addr)
+ *
+ * Description from rfc1002:
+ *
+ * 5.3.3. RECEPTION OF NetBIOS DATAGRAMS BY ALL NODES
+ *
+ * The following algorithm discards out of order NetBIOS Datagram
+ * fragments. An implementation which reassembles out of order
+ * NetBIOS Datagram fragments conforms to this specification. The
+ * fragment discard timer is initialized to the value FRAGMENT_TIMEOUT.
+ * This value should be user configurable. The default value is
+ * given in Section 6, "Defined Constants and Variables".
+ *
+ * PROCEDURE datagram_packet(packet)
+ *
+ * (*
+ * * processing initiated by datagram packet reception
+ * * on B, P and M nodes
+ * *)
+ * BEGIN
+ * (*
+ * * if this node is a P node, ignore
+ * * broadcast packets.
+ * *)
+ *
+ * IF this is a P node AND incoming packet is
+ * a broadcast packet THEN
+ * BEGIN
+ * discard packet;
+ * END
+ *
+ * CASE packet type OF
+ *
+ * DATAGRAM SERVICE:
+ * BEGIN
+ * IF FIRST bit in FLAGS is set THEN
+ * BEGIN
+ * IF MORE bit in FLAGS is set THEN
+ * BEGIN
+ * Save 1st UDP packet of the Datagram;
+ * Set this Datagrams fragment discard
+ * timer to FRAGMENT_TIMEOUT;
+ * return;
+ * END
+ * ELSE
+ * Datagram is composed of a single
+ * UDP packet;
+ * END
+ * ELSE
+ * BEGIN
+ * (* Have the second fragment of a Datagram *)
+ *
+ * Search for 1st fragment by source IP address
+ * and DGM_ID;
+ * IF found 1st fragment THEN
+ * Process both UDP packets;
+ * ELSE
+ * BEGIN
+ * discard 2nd fragment UDP packet;
+ * return;
+ * END
+ * END
+ *
+ * IF DESTINATION_NAME is '*' THEN
+ * BEGIN
+ * (* NetBIOS broadcast *)
+ *
+ * deliver USER_DATA from UDP packet(s) to all
+ * outstanding receive broadcast
+ * datagram requests;
+ * return;
+ * END
+ * ELSE
+ * BEGIN (* non-broadcast *)
+ * (* Datagram for Unique or Group Name *)
+ *
+ * IF DESTINATION_NAME is not present in the
+ * local name table THEN
+ * BEGIN
+ * (* destination not present *)
+ * build DATAGRAM ERROR packet, clear
+ * FIRST and MORE bit, put in
+ * this nodes IP and PORT, set
+ * ERROR_CODE;
+ * send DATAGRAM ERROR packet to
+ * source IP address and port
+ * of UDP;
+ * discard UDP packet(s);
+ * return;
+ * END
+ * ELSE
+ * BEGIN (* good *)
+ * (*
+ * * Replicate received NetBIOS datagram for
+ * * each recipient
+ * *)
+ * FOR EACH pending NetBIOS users receive
+ * datagram operation
+ * BEGIN
+ * IF source name of operation
+ * matches destination name
+ * of packet THEN
+ * BEGIN
+ * deliver USER_DATA from UDP
+ * packet(s);
+ * END
+ * END (* for each *)
+ * return;
+ * END (* good *)
+ * END (* non-broadcast *)
+ * END (* datagram service *)
+ *
+ * DATAGRAM ERROR:
+ * BEGIN
+ * (*
+ * * name service returned incorrect information
+ * *)
+ *
+ * inform local name service that incorrect
+ * information was provided;
+ *
+ * IF this is a P or M node THEN
+ * BEGIN
+ * (*
+ * * tell NetBIOS Name Server that it may
+ * * have given incorrect information
+ * *)
+ *
+ * send NAME RELEASE REQUEST with name
+ * and incorrect IP address to NetBIOS
+ * Name Server;
+ * END
+ * END (* datagram error *)
+ *
+ * END (* case *)
+ * END
+ */
+
+static struct datagram *
+smb_netbios_datagram_getq(struct datagram *datagram)
+{
+ struct datagram *prev = 0;
+
+ (void) mutex_lock(&smb_dgq_mtx);
+ for (prev = smb_datagram_queue.forw;
+ prev != (struct datagram *)((uintptr_t)&smb_datagram_queue);
+ prev = prev->forw) {
+ if (prev->src.addr_list.sin.sin_addr.s_addr ==
+ datagram->src.addr_list.sin.sin_addr.s_addr) {
+ /* Something waiting */
+ QUEUE_CLIP(prev);
+ (void) mutex_unlock(&smb_dgq_mtx);
+ bcopy(datagram->data, &prev->data[prev->data_length],
+ datagram->data_length);
+ prev->data_length += datagram->data_length;
+ free(datagram);
+ return (prev);
+ }
+ }
+ (void) mutex_unlock(&smb_dgq_mtx);
+
+ return (0);
+}
+
+static void
+smb_netbios_BPM_datagram(struct datagram *datagram)
+{
+ struct name_entry *entry = 0;
+ struct datagram *qpacket = 0;
+ pthread_t browser_dispatch;
+
+ switch (datagram->packet_type) {
+ case DATAGRAM_TYPE_BROADCAST :
+ if (smb_node_type == 'P') {
+ /*
+ * if this node is a P node, ignore
+ * broadcast packets.
+ */
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case DATAGRAM_TYPE_DIRECT_UNIQUE :
+ case DATAGRAM_TYPE_DIRECT_GROUP :
+ if ((datagram->flags & DATAGRAM_FLAGS_FIRST) != 0) {
+ if (datagram->flags & DATAGRAM_FLAGS_MORE) {
+ /* Save 1st UDP packet of the Datagram */
+ datagram->discard_timer = FRAGMENT_TIMEOUT;
+ (void) mutex_lock(&smb_dgq_mtx);
+ QUEUE_INSERT_TAIL(&smb_datagram_queue, datagram)
+ (void) mutex_unlock(&smb_dgq_mtx);
+ return;
+ }
+ /* process datagram */
+ } else {
+ qpacket = smb_netbios_datagram_getq(datagram);
+ if (qpacket) {
+ datagram = qpacket;
+ goto process_datagram;
+ }
+ break;
+ }
+
+process_datagram:
+ entry = 0;
+ if ((strcmp((char *)datagram->dest.name, "*") == 0) ||
+ ((entry =
+ smb_netbios_cache_lookup(&datagram->dest)) != 0)) {
+ if (entry) {
+ int is_local = IS_LOCAL(entry->attributes);
+ smb_netbios_cache_unlock_entry(entry);
+
+ if (is_local) {
+ (void) pthread_create(&browser_dispatch,
+ 0, smb_browser_dispatch,
+ (void *)datagram);
+ (void) pthread_detach(browser_dispatch);
+ return;
+ }
+ }
+
+ datagram->rawbuf[0] = DATAGRAM_TYPE_ERROR_DATAGRAM;
+ datagram->rawbuf[1] &= DATAGRAM_FLAGS_SRC_TYPE;
+
+ (void) memcpy(&datagram->rawbuf[4],
+ &datagram->src.addr_list.sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+ BE_OUT16(&datagram->rawbuf[8], DGM_SRVC_UDP_PORT);
+
+ (void) sendto(datagram_sock, datagram->rawbuf,
+ datagram->rawbytes, 0,
+ (struct sockaddr *)&datagram->src.addr_list.sin,
+ datagram->src.addr_list.sinlen);
+ }
+ break;
+
+ case DATAGRAM_TYPE_ERROR_DATAGRAM :
+ break;
+ }
+ free(datagram);
+}
+
+
+/*
+ * smb_netbios_process_NBDD_datagram
+ *
+ * Description from rfc1002:
+ *
+ *
+ * 5.3.4. PROTOCOLS FOR THE NBDD
+ *
+ * The key to NetBIOS Datagram forwarding service is the packet
+ * delivered to the destination end node must have the same NetBIOS
+ * header as if the source end node sent the packet directly to the
+ * destination end node. Consequently, the NBDD does not reassemble
+ * NetBIOS Datagrams. It forwards the UDP packet as is.
+ *
+ * PROCEDURE datagram_packet(packet)
+ *
+ * (*
+ * * processing initiated by a incoming datagram service
+ * * packet on a NBDD node.
+ * *)
+ *
+ * BEGIN
+ * CASE packet type OF
+ *
+ * DATAGRAM SERVICE:
+ * BEGIN
+ * IF packet was sent as a directed
+ * NetBIOS datagram THEN
+ * BEGIN
+ * (*
+ * * provide group forwarding service
+ * *
+ * * Forward datagram to each member of the
+ * * group. Can forward via:
+ * * 1) get list of group members and send
+ * * the DATAGRAM SERVICE packet unicast
+ * * to each
+ * * 2) use Group Multicast, if available
+ * * 3) combination of 1) and 2)
+ * *)
+ *
+ * ...
+ *
+ * END
+ *
+ * ELSE
+ * BEGIN
+ * (*
+ * * provide broadcast forwarding service
+ * *
+ * * Forward datagram to every node in the
+ * * NetBIOS scope. Can forward via:
+ * * 1) get list of group members and send
+ * * the DATAGRAM SERVICE packet unicast
+ * * to each
+ * * 2) use Group Multicast, if available
+ * * 3) combination of 1) and 2)
+ * *)
+ *
+ * ...
+ *
+ * END
+ * END (* datagram service *)
+ *
+ * DATAGRAM ERROR:
+ * BEGIN
+ * (*
+ * * Should never receive these because Datagrams
+ * * forwarded have source end node IP address and
+ * * port in NetBIOS header.
+ * *)
+ *
+ * send DELETE NAME REQUEST with incorrect name and
+ * IP address to NetBIOS Name Server;
+ *
+ * END (* datagram error *)
+ *
+ * DATAGRAM QUERY REQUEST:
+ * BEGIN
+ * IF can send packet to DESTINATION_NAME THEN
+ * BEGIN
+ * (*
+ * * NBDD is able to relay Datagrams for
+ * * this name
+ * *)
+ *
+ * send POSITIVE DATAGRAM QUERY RESPONSE to
+ * REQUEST source IP address and UDP port
+ * with requests DGM_ID;
+ * END
+ * ELSE
+ * BEGIN
+ * (*
+ * * NBDD is NOT able to relay Datagrams for
+ * * this name
+ * *)
+ *
+ * send NEGATIVE DATAGRAM QUERY RESPONSE to
+ * REQUEST source IP address and UDP port
+ *
+ * with requests DGM_ID;
+ * END
+ * END (* datagram query request *)
+ *
+ * END (* case *)
+ * END (* procedure *)
+ */
+
+
+/*
+ * Function: int smb_netbios_datagram_service_daemon(void)
+ *
+ * Description:
+ *
+ * 4.4. DATAGRAM SERVICE PACKETS
+ *
+ * 4.4.1. NetBIOS DATAGRAM HEADER
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MSG_TYPE | FLAGS | DGM_ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_IP |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOURCE_PORT | DGM_LENGTH |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | PACKET_OFFSET |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * MSG_TYPE values (in hexidecimal):
+ *
+ * 10 - DIRECT_UNIQUE DATAGRAM
+ * 11 - DIRECT_GROUP DATAGRAM
+ * 12 - BROADCAST DATAGRAM
+ * 13 - DATAGRAM ERROR
+ * 14 - DATAGRAM QUERY REQUEST
+ * 15 - DATAGRAM POSITIVE QUERY RESPONSE
+ * 16 - DATAGRAM NEGATIVE QUERY RESPONSE
+ *
+ * Bit definitions of the FLAGS field:
+ *
+ * 0 1 2 3 4 5 6 7
+ * +---+---+---+---+---+---+---+---+
+ * | 0 | 0 | 0 | 0 | SNT | F | M |
+ * +---+---+---+---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description
+ *
+ * M 7 MORE flag, If set then more NetBIOS datagram
+ * fragments follow.
+ *
+ * F 6 FIRST packet flag, If set then this is first
+ * (and possibly only) fragment of NetBIOS
+ * datagram
+ *
+ * SNT 4,5 Source End-Node type:
+ * 00 = B node
+ * 01 = P node
+ * 10 = M node
+ * 11 = NBDD
+ * RESERVED 0-3 Reserved, must be zero (0)
+ *
+ * Inputs:
+ * Nothing
+ *
+ * Returns:
+ * int -> Description
+ */
+
+/*ARGSUSED*/
+void *
+smb_netbios_datagram_service_daemon(void *arg)
+{
+ struct sockaddr_in sin;
+ struct datagram *datagram;
+ int bytes, flag = 1;
+ net_cfg_t cfg;
+
+ (void) mutex_lock(&smb_dgq_mtx);
+ bzero(&smb_datagram_queue, sizeof (smb_datagram_queue));
+ smb_datagram_queue.forw = smb_datagram_queue.back =
+ (struct datagram *)((uintptr_t)&smb_datagram_queue);
+ (void) mutex_unlock(&smb_dgq_mtx);
+
+ if ((datagram_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR,
+ "smbd: Could not create AF_INET, SOCK_DGRAM, socket");
+ smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_FAILED, 1);
+ return (0);
+ }
+
+ bzero(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(DGM_SRVC_UDP_PORT);
+ if (bind(datagram_sock, (struct sockaddr *)&sin, sizeof (sin)) != 0) {
+ syslog(LOG_ERR, "smbd: Bind to name service port %d failed",
+ DGM_SRVC_UDP_PORT);
+ (void) close(datagram_sock);
+ smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_FAILED, 1);
+ return (0);
+ }
+ (void) setsockopt(datagram_sock, SOL_SOCKET, SO_BROADCAST, &flag,
+ sizeof (flag));
+
+ smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_RUNNING, 1);
+
+ while (((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) ||
+ (nb_status.state & NETBIOS_BROWSER_RUNNING)) {
+ if ((datagram = (struct datagram *)
+ malloc(sizeof (struct datagram))) == 0) {
+ /* Sleep for 10 sec and try again */
+ (void) sleep(10);
+ continue;
+ }
+
+ignore: bzero(&datagram->inaddr, sizeof (struct addr_entry));
+ datagram->inaddr.sinlen = sizeof (datagram->inaddr.sin);
+ datagram->inaddr.forw = datagram->inaddr.back =
+ &datagram->inaddr;
+
+ if ((bytes = recvfrom(datagram_sock, datagram->rawbuf,
+ MAX_DATAGRAM_LENGTH, 0,
+ (struct sockaddr *)&datagram->inaddr.sin,
+ &datagram->inaddr.sinlen)) < 0) {
+ syslog(LOG_ERR,
+ "smbd: NETBIOS datagram - recvfrom failed");
+ smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_FAILED, 1);
+ break;
+ }
+
+ /* Ignore any incoming packets from myself... */
+ if (smb_nic_get_byip(
+ datagram->inaddr.sin.sin_addr.s_addr,
+ &cfg) != NULL) {
+ goto ignore;
+ }
+ if (smb_datagram_decode(datagram, bytes) < 0)
+ goto ignore;
+
+ /*
+ * This code was doing the wrong thing with responses from a
+ * Windows2000 PDC because both DATAGRAM_FLAGS_H_NODE and
+ * DATAGRAM_FLAGS_NBDD are defined to be the same value (see
+ * netbios.h). Since the Windows2000 PDC wants to be an H-Node,
+ * we need to handle all messages via smb_netbios_BPM_datagram.
+ *
+ * if ((datagram->flags & DATAGRAM_FLAGS_SRC_TYPE) ==
+ * DATAGRAM_FLAGS_NBDD)
+ * smb_netbios_NBDD_datagram(datagram);
+ * else
+ * smb_netbios_BPM_datagram(datagram);
+ */
+
+ smb_netbios_BPM_datagram(datagram);
+ }
+
+ smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_RUNNING, 0);
+
+ (void) mutex_lock(&nb_status.mtx);
+ while (nb_status.state & NETBIOS_BROWSER_RUNNING)
+ (void) cond_wait(&nb_status.cv, &nb_status.mtx);
+ (void) mutex_unlock(&nb_status.mtx);
+
+ (void) close(datagram_sock);
+ smb_netbios_datagram_fini();
+ syslog(LOG_DEBUG, "smbd: Netbios Datagram Service is down\n");
+ return (0);
+}
+
+static char
+/* LINTED - E_STATIC_UNUSED */
+nb_fmt_flags(unsigned char flags)
+{
+ switch (flags & DATAGRAM_FLAGS_SRC_TYPE) {
+ case DATAGRAM_FLAGS_B_NODE: return ('B');
+ case DATAGRAM_FLAGS_P_NODE: return ('P');
+ case DATAGRAM_FLAGS_M_NODE: return ('M');
+ case DATAGRAM_FLAGS_H_NODE: return ('H');
+ default: return ('?');
+ }
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_name.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_name.c
new file mode 100644
index 0000000000..2b2d20e3c3
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_name.c
@@ -0,0 +1,4738 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Description:
+ *
+ * Contains base code for netbios name service.
+ *
+ *
+ * 6. DEFINED CONSTANTS AND VARIABLES
+ *
+ * GENERAL:
+ *
+ * SCOPE_ID The name of the NetBIOS scope.
+ *
+ * This is expressed as a character
+ * string meeting the requirements of
+ * the domain name system and without
+ * a leading or trailing "dot".
+ *
+ * An implementation may elect to make
+ * this a single global value for the
+ * node or allow it to be specified
+ * with each separate NetBIOS name
+ * (thus permitting cross-scope
+ * references.)
+ *
+ * BROADCAST_ADDRESS An IP address composed of the
+ * nodes's network and subnetwork
+ * numbers with all remaining bits set
+ * to one.
+ *
+ * I.e. "Specific subnet" broadcast
+ * addressing according to section 2.3
+ * of RFC 950.
+ *
+ * BCAST_REQ_RETRY_TIMEOUT 250 milliseconds.
+ * An adaptive timer may be used.
+ *
+ * BCAST_REQ_RETRY_COUNT 3
+ *
+ * UCAST_REQ_RETRY_TIMEOUT 5 seconds
+ * An adaptive timer may be used.
+ *
+ * UCAST_REQ_RETRY_COUNT 3
+ *
+ * MAX_DATAGRAM_LENGTH 576 bytes (default)
+ *
+ *
+ * NAME SERVICE:
+ *
+ * REFRESH_TIMER Negotiated with NAME for each name.
+ *
+ * CONFLICT_TIMER 1 second
+ * Implementations may chose a longer
+ * value.
+ *
+ *
+ * NAME_SERVICE_TCP_PORT 137 (decimal)
+ *
+ * NAME_SERVICE_UDP_PORT 137 (decimal)
+ *
+ * INFINITE_TTL 0
+ */
+
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <synch.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <arpa/inet.h>
+#include <net/if_arp.h>
+
+#include <smbsrv/libsmbns.h>
+#include <smbns_netbios.h>
+
+#define NAME_HEADER_SIZE 12
+
+typedef struct name_reply {
+ struct name_reply *forw;
+ struct name_reply *back;
+ struct name_packet *packet;
+ struct addr_entry *addr;
+ unsigned short name_trn_id;
+ unsigned short flags;
+} name_reply;
+
+static struct name_reply reply_queue;
+static mutex_t rq_mtx;
+
+static mutex_t reply_mtx;
+static cond_t reply_cv;
+
+static name_queue_t delete_queue;
+static name_queue_t refresh_queue;
+
+/*
+ * Flag to control whether or not NetBIOS name refresh requests
+ * are logged. Set to non-zero to enable logging.
+ */
+
+static unsigned short netbios_name_transcation_id = 1;
+static int name_sock = 0;
+
+static int bcast_num = 0;
+static int nbns_num = 0;
+static struct addr_entry smb_bcast_list[SMB_PI_MAX_NETWORKS];
+static struct addr_entry smb_nbns[SMB_PI_MAX_WINS];
+
+static int smb_netbios_process_response(unsigned short, struct addr_entry *,
+ struct name_packet *, uint32_t);
+
+static int smb_send_name_service_packet(struct addr_entry *addr,
+ struct name_packet *packet);
+
+static int
+smb_end_node_challenge(struct name_reply *reply_info)
+{
+ int rc;
+ uint32_t retry;
+ unsigned short tid;
+ struct resource_record *answer;
+ struct name_question question;
+ struct addr_entry *addr;
+ struct name_entry *destination;
+ struct name_packet packet;
+ struct timespec st;
+
+ /*
+ * The response packet has in it the address of the presumed owner
+ * of the name. Challenge that owner. If owner either does not
+ * respond or indicates that he no longer owns the name, claim the
+ * name. Otherwise, the name cannot be claimed.
+ */
+
+ if ((answer = reply_info->packet->answer) == 0)
+ return (-1);
+
+ destination = answer->name;
+ question.name = answer->name;
+
+ packet.info = NAME_QUERY_REQUEST | NM_FLAGS_UNICAST;
+ packet.qdcount = 1; /* question entries */
+ packet.question = &question;
+ packet.ancount = 0; /* answer recs */
+ packet.answer = NULL;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 0; /* additional recs */
+ packet.additional = NULL;
+
+ addr = &destination->addr_list;
+ for (retry = 0; retry < UCAST_REQ_RETRY_COUNT; retry++) {
+ tid = netbios_name_transcation_id++;
+ packet.name_trn_id = tid;
+ if (smb_send_name_service_packet(addr, &packet) >= 0) {
+ if ((rc = smb_netbios_process_response(tid, addr,
+ &packet, UCAST_REQ_RETRY_TIMEOUT)) != 0)
+ return (rc);
+ }
+ st.tv_sec = 0;
+ st.tv_nsec = (UCAST_REQ_RETRY_TIMEOUT * 1000000);
+ (void) nanosleep(&st, 0);
+ }
+ /* No reply */
+ return (0);
+}
+
+
+static struct name_reply *
+smb_name_get_reply(unsigned short tid, uint32_t timeout)
+{
+ unsigned short info;
+ struct resource_record *answer;
+ struct name_reply *reply;
+ uint32_t wait_time, to_save; /* in millisecond */
+ struct timeval wt;
+ timestruc_t to;
+
+ to_save = timeout;
+ reply = (struct name_reply *)malloc(sizeof (struct name_reply));
+ if (reply != 0) {
+ reply->flags = 0;
+ reply->name_trn_id = tid;
+ (void) mutex_lock(&rq_mtx);
+ QUEUE_INSERT_TAIL(&reply_queue, reply);
+ (void) mutex_unlock(&rq_mtx);
+
+ for (;;) {
+ (void) gettimeofday(&wt, 0);
+ wait_time = wt.tv_usec / 1000;
+
+ (void) mutex_lock(&reply_mtx);
+ to.tv_sec = 0;
+ to.tv_nsec = timeout * 1000000;
+ (void) cond_reltimedwait(&reply_cv, &reply_mtx, &to);
+ (void) mutex_unlock(&reply_mtx);
+
+ if (reply->flags != 0) {
+ info = reply->packet->info;
+ if (PACKET_TYPE(info) == WACK_RESPONSE) {
+ answer = reply->packet->answer;
+ wait_time = (answer) ?
+ TO_MILLISECONDS(answer->ttl) :
+ DEFAULT_TTL;
+ free(reply->addr);
+ free(reply->packet);
+ timeout = to_save + wait_time;
+ reply->flags = 0;
+ reply->name_trn_id = tid;
+ (void) mutex_lock(&rq_mtx);
+ QUEUE_INSERT_TAIL(&reply_queue, reply);
+ (void) mutex_unlock(&rq_mtx);
+ continue;
+ }
+ return (reply);
+ }
+ (void) gettimeofday(&wt, 0);
+ wait_time = (wt.tv_usec / 1000) - wait_time;
+ if (wait_time >= timeout) {
+ (void) mutex_lock(&rq_mtx);
+ QUEUE_CLIP(reply);
+ (void) mutex_unlock(&rq_mtx);
+ free(reply);
+ break;
+ }
+ timeout -= wait_time;
+ }
+ }
+
+ return (0);
+}
+
+static void
+smb_reply_ready(struct name_packet *packet, struct addr_entry *addr)
+{
+ struct name_reply *reply;
+ struct resource_record *answer;
+
+ (void) mutex_lock(&rq_mtx);
+ for (reply = reply_queue.forw; reply != &reply_queue;
+ reply = reply->forw) {
+ if (reply->name_trn_id == packet->name_trn_id) {
+ QUEUE_CLIP(reply);
+ (void) mutex_unlock(&rq_mtx);
+
+ reply->addr = addr;
+ reply->packet = packet;
+
+ (void) mutex_lock(&reply_mtx);
+ reply->flags |= 0x0001; /* reply ready */
+ (void) cond_signal(&reply_cv);
+ (void) mutex_unlock(&reply_mtx);
+
+ return;
+ }
+ }
+ (void) mutex_unlock(&rq_mtx);
+
+ /* Presumably nobody is waiting any more... */
+ free(addr);
+
+ answer = packet->answer;
+ if (answer)
+ smb_netbios_name_freeaddrs(answer->name);
+ free(packet);
+}
+
+static int
+smb_netbios_process_response(unsigned short tid, struct addr_entry *addr,
+ struct name_packet *packet, uint32_t timeout)
+{
+ int rc = 0;
+ unsigned short info;
+ struct name_reply *reply;
+ struct resource_record *answer;
+ struct name_entry *name;
+ struct name_entry *entry;
+ struct name_question *question;
+ uint32_t ttl;
+
+ if ((reply = smb_name_get_reply(tid, timeout)) == 0) {
+ return (0); /* No reply: retry */
+ }
+ info = reply->packet->info;
+ answer = reply->packet->answer;
+
+ /* response */
+ switch (PACKET_TYPE(info)) {
+ case NAME_QUERY_RESPONSE:
+ if (POSITIVE_RESPONSE(info)) {
+ addr = &answer->name->addr_list;
+ do {
+ /*
+ * Make sure that remote name is not
+ * flagged local
+ */
+ addr->attributes &= ~NAME_ATTR_LOCAL;
+
+ addr->refresh_ttl = addr->ttl =
+ (answer && answer->ttl) ?
+ (answer->ttl >> 1) :
+ TO_SECONDS(DEFAULT_TTL);
+ addr = addr->forw;
+ } while (addr != &answer->name->addr_list);
+ smb_netbios_name_dump(answer->name);
+ (void) smb_netbios_cache_insert_list(answer->name);
+ rc = 1;
+ } else {
+ rc = -1;
+ }
+ break;
+
+ case NAME_REGISTRATION_RESPONSE:
+ if (NEGATIVE_RESPONSE(info)) {
+ if (RCODE(info) == RCODE_CFT_ERR) {
+ if (answer == 0) {
+ rc = -RCODE(info);
+ break;
+ }
+
+ name = answer->name;
+ entry = smb_netbios_cache_lookup(name);
+ if (entry) {
+ /*
+ * a name in the state "conflict
+ * detected" does not "logically" exist
+ * on that node. No further session
+ * will be accepted on that name.
+ * No datagrams can be sent against
+ * that name.
+ * Such an entry will not be used for
+ * purposes of processing incoming
+ * request packets.
+ * The only valid user NetBIOS operation
+ * against such a name is DELETE NAME.
+ */
+ entry->attributes |= NAME_ATTR_CONFLICT;
+ syslog(LOG_DEBUG,
+ "NETBIOS Name conflict: %15.15s",
+ entry->name);
+ smb_netbios_cache_unlock_entry(entry);
+ }
+ }
+ rc = -RCODE(info);
+ break;
+ }
+
+ /*
+ * name can be added:
+ * adjust refresh timeout value,
+ * TTL, for this name
+ */
+ question = packet->question;
+ ttl = (answer && answer->ttl) ? answer->ttl >> 1
+ : TO_SECONDS(DEFAULT_TTL);
+ if ((entry = smb_netbios_cache_lookup(question->name)) != 0) {
+ addr = &entry->addr_list;
+ do {
+ if ((addr->refresh_ttl == 0) ||
+ (ttl < addr->refresh_ttl))
+ addr->refresh_ttl = addr->ttl = ttl;
+ addr = addr->forw;
+ } while (addr != &entry->addr_list);
+ smb_netbios_cache_unlock_entry(entry);
+ }
+
+ rc = 1;
+ break;
+
+ case NAME_RELEASE_RESPONSE:
+ rc = 1;
+ break;
+
+ case END_NODE_CHALLENGE_REGISTRATION_REQUEST:
+ /*
+ * The response packet has in it the
+ * address of the presumed owner of the
+ * name. Challenge that owner. If
+ * owner either does not respond or
+ * indicates that he no longer owns the
+ * name, claim the name. Otherwise,
+ * the name cannot be claimed.
+ */
+ rc = smb_end_node_challenge(reply);
+ break;
+
+ default:
+ rc = 0;
+ break;
+ }
+
+ if (answer)
+ smb_netbios_name_freeaddrs(answer->name);
+ free(reply->addr);
+ free(reply->packet);
+ free(reply);
+ return (rc); /* retry */
+}
+
+/*
+ * smb_name_buf_from_packet
+ *
+ * Description:
+ * Convert a NetBIOS Name Server Packet Block (npb)
+ * into the bits and bytes destined for the wire.
+ * The "buf" is used as a heap.
+ *
+ * Inputs:
+ * char * buf -> Buffer, from the wire
+ * unsigned n_buf -> Length of 'buf'
+ * name_packet *npb -> Packet block, decode into
+ * unsigned n_npb -> Max bytes in 'npb'
+ *
+ * Returns:
+ * >0 -> Encode successful, value is length of packet in "buf"
+ * -1 -> Hard error, can not possibly encode
+ * -2 -> Need more memory in buf -- it's too small
+ */
+
+static int
+smb_name_buf_from_packet(unsigned char *buf,
+ int n_buf,
+ struct name_packet *npb)
+{
+ struct addr_entry *raddr;
+ unsigned char *heap = buf;
+ unsigned char *end_heap = heap + n_buf;
+ unsigned char *dnptrs[32];
+ unsigned char comp_name_buf[MAX_NAME_LENGTH];
+ unsigned int tmp;
+ int i, step;
+
+ if (n_buf < NAME_HEADER_SIZE)
+ return (-1); /* no header, impossible */
+
+ dnptrs[0] = heap;
+ dnptrs[1] = 0;
+
+ BE_OUT16(heap, npb->name_trn_id);
+ heap += 2;
+
+ BE_OUT16(heap, npb->info);
+ heap += 2;
+
+ BE_OUT16(heap, npb->qdcount);
+ heap += 2;
+
+ BE_OUT16(heap, npb->ancount);
+ heap += 2;
+
+ BE_OUT16(heap, npb->nscount);
+ heap += 2;
+
+ BE_OUT16(heap, npb->arcount);
+ heap += 2;
+
+ for (i = 0; i < npb->qdcount; i++) {
+ if ((heap + 34 + 4) > end_heap)
+ return (-2);
+
+ (void) smb_first_level_name_encode(npb->question[i].name,
+ comp_name_buf, sizeof (comp_name_buf));
+ (void) strcpy((char *)heap, (char *)comp_name_buf);
+ heap += strlen((char *)comp_name_buf) + 1;
+
+ BE_OUT16(heap, npb->question[i].question_type);
+ heap += 2;
+
+ BE_OUT16(heap, npb->question[i].question_class);
+ heap += 2;
+ }
+
+ for (step = 1; step <= 3; step++) {
+ struct resource_record *nrr;
+ int n;
+
+ /* truly ugly, but saves code copying */
+ if (step == 1) {
+ n = npb->ancount;
+ nrr = npb->answer;
+ } else if (step == 2) {
+ n = npb->nscount;
+ nrr = npb->authority;
+ } else { /* step == 3 */
+ n = npb->arcount;
+ nrr = npb->additional;
+ }
+
+ for (i = 0; i < n; i++) {
+ if ((heap + 34 + 10) > end_heap)
+ return (-2);
+
+ (void) smb_first_level_name_encode(nrr->name,
+ comp_name_buf, sizeof (comp_name_buf));
+ (void) strcpy((char *)heap, (char *)comp_name_buf);
+ heap += strlen((char *)comp_name_buf) + 1;
+
+ BE_OUT16(heap, nrr[i].rr_type);
+ heap += 2;
+
+ BE_OUT16(heap, nrr[i].rr_class);
+ heap += 2;
+
+ BE_OUT32(heap, nrr[i].ttl);
+ heap += 4;
+
+ BE_OUT16(heap, nrr[i].rdlength);
+ heap += 2;
+
+ if ((tmp = nrr[i].rdlength) > 0) {
+ if ((heap + tmp) > end_heap)
+ return (-2);
+
+ if (nrr[i].rr_type == NAME_RR_TYPE_NB &&
+ nrr[i].rr_class == NAME_RR_CLASS_IN &&
+ tmp >= 6 && nrr[i].rdata == 0) {
+ tmp = nrr[i].name->attributes &
+ (NAME_ATTR_GROUP |
+ NAME_ATTR_OWNER_NODE_TYPE);
+ BE_OUT16(heap, tmp);
+ heap += 2;
+
+ raddr = &nrr[i].name->addr_list;
+ (void) memcpy(heap,
+ &raddr->sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+ heap += 4;
+ } else {
+ bcopy(nrr[i].rdata, heap, tmp);
+ heap += tmp;
+ }
+ }
+ }
+ }
+ return (heap - buf);
+}
+
+/*
+ * strnchr
+ *
+ * Lookup for character 'c' in first 'n' chars of string 's'.
+ * Returns pointer to the found char, otherwise returns 0.
+ */
+static char *
+strnchr(const char *s, char c, int n)
+{
+ char *ps = (char *)s;
+ char *es = (char *)s + n;
+
+ while (ps < es && *ps) {
+ if (*ps == c)
+ return (ps);
+
+ ++ps;
+ }
+
+ if (*ps == '\0' && c == '\0')
+ return (ps);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+is_multihome(char *name)
+{
+ return ((smb_nic_get_num() > 1) ? 1 : 0);
+}
+
+/*
+ * smb_netbios_getname
+ *
+ * Get the Netbios name part of the given record.
+ * Does some boundary checks.
+ *
+ * Returns the name length on success, otherwise
+ * returns 0.
+ */
+static int
+smb_netbios_getname(char *name, char *buf, char *buf_end)
+{
+ char *name_end;
+ int name_len;
+
+ if (buf >= buf_end) {
+ /* no room for a NB name */
+ return (0);
+ }
+
+ name_end = strnchr(buf, '\0', buf_end - buf + 1);
+ if (name_end == 0) {
+ /* not a valid NB name */
+ return (0);
+ }
+
+ name_len = name_end - buf + 1;
+
+ (void) strlcpy(name, buf, name_len);
+ return (name_len);
+}
+
+
+/*
+ * smb_name_buf_to_packet
+ *
+ * Description:
+ * Convert the bits and bytes that came from the wire
+ * into a NetBIOS Name Server Packet Block (npb).
+ * The "block" is used as a heap.
+ *
+ * Inputs:
+ * char * buf -> Buffer, from the wire
+ * int n_buf -> Length of 'buf'
+ * name_packet *npb -> Packet block, decode into
+ * int n_npb -> Max bytes in 'npb'
+ *
+ * Returns:
+ * >0 -> Decode (parse) successful, value is byte length of npb
+ * -1 -> Hard error, can not possibly decode
+ * -2 -> Need more memory in npb -- it's too small
+ */
+
+static struct name_packet *
+smb_name_buf_to_packet(char *buf, int n_buf)
+{
+ struct name_packet *npb;
+ unsigned char *heap;
+ unsigned char *scan = (unsigned char *)buf;
+ unsigned char *scan_end = scan + n_buf;
+ char name_buf[MAX_NAME_LENGTH];
+ struct resource_record *nrr = 0;
+ int rc, i, n, nn, ns;
+ unsigned short name_trn_id, info;
+ unsigned short qdcount, ancount, nscount, arcount;
+ struct addr_entry *next;
+ int name_len;
+
+ if (n_buf < NAME_HEADER_SIZE) {
+ /* truncated header */
+ syslog(LOG_DEBUG, "SmbNBNS: packet is too short (%d)",
+ n_buf);
+ return (0);
+ }
+
+ name_trn_id = BE_IN16(scan); scan += 2;
+ info = BE_IN16(scan); scan += 2;
+ qdcount = BE_IN16(scan); scan += 2;
+ ancount = BE_IN16(scan); scan += 2;
+ nscount = BE_IN16(scan); scan += 2;
+ arcount = BE_IN16(scan); scan += 2;
+
+ ns = sizeof (struct name_entry);
+ n = n_buf + sizeof (struct name_packet) +
+ ((unsigned)qdcount * (sizeof (struct name_question) + ns)) +
+ ((unsigned)ancount * (sizeof (struct resource_record) + ns)) +
+ ((unsigned)nscount * (sizeof (struct resource_record) + ns)) +
+ ((unsigned)arcount * (sizeof (struct resource_record) + ns));
+
+ if ((npb = (struct name_packet *)malloc(n)) == 0) {
+ return (0);
+ }
+ bzero(npb, n);
+
+ heap = npb->block_data;
+ npb->name_trn_id = name_trn_id;
+ npb->info = info;
+ npb->qdcount = qdcount;
+ npb->ancount = ancount;
+ npb->nscount = nscount;
+ npb->arcount = arcount;
+
+ /* scan is in position for question entries */
+
+ /*
+ * Measure the space needed for the tables
+ */
+ if (qdcount > 0) {
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ npb->question = (struct name_question *)heap;
+ heap += qdcount * sizeof (struct name_question);
+ for (i = 0; i < qdcount; i++) {
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ npb->question[i].name = (struct name_entry *)heap;
+ heap += sizeof (struct name_entry);
+ }
+ }
+
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ nrr = (struct resource_record *)heap;
+
+ if (ancount > 0) {
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ npb->answer = (struct resource_record *)heap;
+ heap += ancount * sizeof (struct resource_record);
+ }
+
+ if (nscount > 0) {
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ npb->authority = (struct resource_record *)heap;
+ heap += nscount * sizeof (struct resource_record);
+ }
+
+ if (arcount > 0) {
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ npb->additional = (struct resource_record *)heap;
+ heap += arcount * sizeof (struct resource_record);
+ }
+
+ /*
+ * Populate each resource_record's .name field.
+ * Done as a second pass so that all resource records
+ * (answer, authority, additional) are consecutive via nrr[i].
+ */
+ for (i = 0; i < (ancount + nscount + arcount); i++) {
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ nrr[i].name = (struct name_entry *)heap;
+ heap += sizeof (struct name_entry);
+ }
+
+
+ for (i = 0; i < npb->qdcount; i++) {
+ name_len = smb_netbios_getname(name_buf, (char *)scan,
+ (char *)scan_end);
+ if (name_len <= 0) {
+ free(npb);
+ return (0);
+ }
+
+ smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, 0, 0, 0, 0, 0,
+ npb->question[i].name);
+ rc = smb_first_level_name_decode((unsigned char *)name_buf,
+ npb->question[i].name);
+ if (rc < 0) {
+ /* Couldn't decode the question name */
+ free(npb);
+ return (0);
+ }
+
+ scan += name_len;
+ if (scan + 4 > scan_end) {
+ /* no room for Question Type(2) and Class(2) fields */
+ free(npb);
+ return (0);
+ }
+
+ npb->question[i].question_type = BE_IN16(scan); scan += 2;
+ npb->question[i].question_class = BE_IN16(scan); scan += 2;
+ }
+
+ /*
+ * Cheat. Remaining sections are of the same resource_record
+ * format. Table space is consecutive.
+ */
+
+ for (i = 0; i < (ancount + nscount + arcount); i++) {
+ if (scan[0] == 0xc0) {
+ /* Namebuf is reused... */
+ rc = 2;
+ } else {
+ name_len = smb_netbios_getname(name_buf, (char *)scan,
+ (char *)scan_end);
+ if (name_len <= 0) {
+ free(npb);
+ return (0);
+ }
+ rc = name_len;
+ }
+ scan += rc;
+
+ if (scan + 10 > scan_end) {
+ /*
+ * no room for RR_TYPE (2), RR_CLASS (2), TTL (4) and
+ * RDLENGTH (2) fields.
+ */
+ free(npb);
+ return (0);
+ }
+
+ smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, 0, 0, 0, 0, 0,
+ nrr[i].name);
+ if ((rc = smb_first_level_name_decode((unsigned char *)name_buf,
+ nrr[i].name)) < 0) {
+ free(npb);
+ return (0);
+ }
+
+ nrr[i].rr_type = BE_IN16(scan); scan += 2;
+ nrr[i].rr_class = BE_IN16(scan); scan += 2;
+ nrr[i].ttl = BE_IN32(scan); scan += 4;
+ nrr[i].rdlength = BE_IN16(scan); scan += 2;
+
+ if ((n = nrr[i].rdlength) > 0) {
+ if ((scan + n) > scan_end) {
+ /* no room for RDATA */
+ free(npb);
+ return (0);
+ }
+ bcopy(scan, heap, n);
+
+ nn = n;
+ if (nrr[i].rr_type == 0x0020 &&
+ nrr[i].rr_class == 0x01 && n >= 6) {
+ while (nn) {
+ if (nn == 6)
+ next = &nrr[i].name->addr_list;
+ else {
+ next = (struct addr_entry *)
+ malloc(
+ sizeof (struct addr_entry));
+ if (next == 0) {
+ /* not enough memory */
+ free(npb);
+ return (0);
+ }
+ QUEUE_INSERT_TAIL(
+ &nrr[i].name->addr_list,
+ next);
+ }
+ nrr[i].name->attributes =
+ BE_IN16(scan);
+ next->sin.sin_family = AF_INET;
+ next->sinlen = sizeof (next->sin);
+ (void) memcpy(
+ &next->sin.sin_addr.s_addr,
+ scan + 2, sizeof (uint32_t));
+ next->sin.sin_port =
+ htons(DGM_SRVC_UDP_PORT);
+ nn -= 6;
+ scan += 6;
+ }
+ } else {
+ nrr[i].rdata = heap;
+ scan += n;
+ }
+ heap += n;
+ }
+ }
+ return (npb);
+}
+
+
+/*
+ * smb_send_name_service_packet
+ *
+ * Description:
+ *
+ * Send out a name service packet to proper destination.
+ *
+ * Inputs:
+ * struct netbios_name *dest -> NETBIOS name of destination
+ * struct name_packet *packet -> Packet to send
+ *
+ * Returns:
+ * success -> >0
+ * failure -> <=0
+ */
+
+static int
+smb_send_name_service_packet(struct addr_entry *addr,
+ struct name_packet *packet)
+{
+ unsigned char buf[MAX_DATAGRAM_LENGTH];
+ int len;
+
+ if ((len = smb_name_buf_from_packet(buf, sizeof (buf), packet)) < 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (sendto(name_sock, buf, len, MSG_EOR,
+ (struct sockaddr *)&addr->sin, addr->sinlen));
+}
+
+/*
+ * 4.2.1.1. HEADER
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID | OPCODE | NM_FLAGS | RCODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | QDCOUNT | ANCOUNT |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NSCOUNT | ARCOUNT |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Field Description
+ *
+ * NAME_TRN_ID Transaction ID for Name Service Transaction.
+ * Requester places a unique value for each active
+ * transaction. Responder puts NAME_TRN_ID value
+ * from request packet in response packet.
+ *
+ * OPCODE Packet type code, see table below.
+ *
+ * NM_FLAGS Flags for operation, see table below.
+ *
+ * RCODE Result codes of request. Table of RCODE values
+ * for each response packet below.
+ *
+ * QDCOUNT Unsigned 16 bit integer specifying the number of
+ * entries in the question section of a Name
+ *
+ * Service packet. Always zero (0) for responses.
+ * Must be non-zero for all NetBIOS Name requests.
+ *
+ * ANCOUNT Unsigned 16 bit integer specifying the number of
+ * resource records in the answer section of a Name
+ * Service packet.
+ *
+ * NSCOUNT Unsigned 16 bit integer specifying the number of
+ * resource records in the authority section of a
+ * Name Service packet.
+ *
+ * ARCOUNT Unsigned 16 bit integer specifying the number of
+ * resource records in the additional records
+ * section of a Name Service packet.
+ *
+ * The OPCODE field is defined as:
+ *
+ * 0 1 2 3 4
+ * +---+---+---+---+---+
+ * | R | OPCODE |
+ * +---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description
+ *
+ * OPCODE 1-4 Operation specifier:
+ * 0 = query
+ * 5 = registration
+ * 6 = release
+ * 7 = WACK
+ * 8 = refresh
+ *
+ * R 0 RESPONSE flag:
+ * if bit == 0 then request packet
+ * if bit == 1 then response packet.
+ */
+
+
+/*
+ * The NM_FLAGS field is defined as:
+ *
+ *
+ * 0 1 2 3 4 5 6
+ * +---+---+---+---+---+---+---+
+ * |AA |TC |RD |RA | 0 | 0 | B |
+ * +---+---+---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description
+ *
+ * B 6 Broadcast Flag.
+ * = 1: packet was broadcast or multicast
+ * = 0: unicast
+ *
+ * RA 3 Recursion Available Flag.
+ *
+ * Only valid in responses from a NetBIOS Name
+ * Server -- must be zero in all other
+ * responses.
+ *
+ * If one (1) then the NAME supports recursive
+ * query, registration, and release.
+ *
+ * If zero (0) then the end-node must iterate
+ * for query and challenge for registration.
+ *
+ * RD 2 Recursion Desired Flag.
+ *
+ * May only be set on a request to a NetBIOS
+ * Name Server.
+ *
+ * The NAME will copy its state into the
+ * response packet.
+ *
+ * If one (1) the NAME will iterate on the
+ * query, registration, or release.
+ *
+ * TC 1 Truncation Flag.
+ *
+ * Set if this message was truncated because the
+ * datagram carrying it would be greater than
+ * 576 bytes in length. Use TCP to get the
+ * information from the NetBIOS Name Server.
+ *
+ * AA 0 Authoritative Answer flag.
+ *
+ * Must be zero (0) if R flag of OPCODE is zero
+ * (0).
+ *
+ * If R flag is one (1) then if AA is one (1)
+ * then the node responding is an authority for
+ * the domain name.
+ *
+ * End nodes responding to queries always set
+ * this bit in responses.
+ *
+ */
+
+/*
+ * 4.2.1.2. QUESTION SECTION
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | QUESTION_TYPE | QUESTION_CLASS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Field Description
+ *
+ * QUESTION_NAME The compressed name representation of the
+ * NetBIOS name for the request.
+ *
+ * QUESTION_TYPE The type of request. The values for this field
+ * are specified for each request.
+ *
+ * QUESTION_CLASS The class of the request. The values for this
+ * field are specified for each request.
+ *
+ * QUESTION_TYPE is defined as:
+ *
+ * Symbol Value Description:
+ *
+ * NB 0x0020 NetBIOS general Name Service Resource Record
+ * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE
+ * STATUS REQUEST)
+ *
+ * QUESTION_CLASS is defined as:
+ *
+ * Symbol Value Description:
+ *
+ * IN 0x0001 Internet class
+ */
+
+#define QUESTION_TYPE_NETBIOS_GENERAL 0x20
+#define QUESTION_TYPE_NETBIOS_STATUS 0x21
+
+#define QUESTION_CLASS_INTERNET 0x0001
+
+/*
+ *
+ * 4.2.1.3. RESOURCE RECORD
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RR_TYPE | RR_CLASS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RDLENGTH | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * / /
+ * / RDATA /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Field Description
+ *
+ * RR_NAME The compressed name representation of the
+ * NetBIOS name corresponding to this resource
+ * record.
+ *
+ * RR_TYPE Resource record type code
+ *
+ * RR_CLASS Resource record class code
+ *
+ * TTL The Time To Live of a the resource record's
+ * name.
+ *
+ * RDLENGTH Unsigned 16 bit integer that specifies the
+ * number of bytes in the RDATA field.
+ *
+ * RDATA RR_CLASS and RR_TYPE dependent field. Contains
+ * the resource information for the NetBIOS name.
+ *
+ * RESOURCE RECORD RR_TYPE field definitions:
+ *
+ * Symbol Value Description:
+ *
+ * A 0x0001 IP address Resource Record (See REDIRECT NAME
+ * QUERY RESPONSE)
+ * NS 0x0002 Name Server Resource Record (See REDIRECT
+ * NAME QUERY RESPONSE)
+ * NULL 0x000A NULL Resource Record (See WAIT FOR
+ * ACKNOWLEDGEMENT RESPONSE)
+ * NB 0x0020 NetBIOS general Name Service Resource Record
+ * (See NB_FLAGS and NB_ADDRESS, below)
+ * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE
+ * STATUS RESPONSE)
+ */
+
+#define RR_TYPE_IP_ADDRESS_RESOURCE 0x0001
+#define RR_TYPE_NAME_SERVER_RESOURCE 0x0002
+#define RR_TYPE_NULL_RESOURCE 0x000A
+#define RR_TYPE_NETBIOS_RESOURCE 0x0020
+#define RR_TYPE_NETBIOS_STATUS 0x0021
+
+/*
+ *
+ * RESOURCE RECORD RR_CLASS field definitions:
+ *
+ * Symbol Value Description:
+ *
+ * IN 0x0001 Internet class
+ */
+#define RR_CLASS_INTERNET_CLASS 0x0001
+
+/*
+ *
+ * NB_FLAGS field of the RESOURCE RECORD RDATA field for RR_TYPE of
+ * "NB":
+ *
+ * 1 1 1 1 1 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | G | ONT | RESERVED |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * Symbol Bit(s) Description:
+ *
+ * RESERVED 3-15 Reserved for future use. Must be zero (0).
+ * ONT 1,2 Owner Node Type:
+ * 00 = B node
+ * 01 = P node
+ * 10 = M node
+ * 11 = Reserved for future use
+ * For registration requests this is the
+ * claimant's type.
+ * For responses this is the actual owner's
+ * type.
+ *
+ * G 0 Group Name Flag.
+ * If one (1) then the RR_NAME is a GROUP
+ * NetBIOS name.
+ * If zero (0) then the RR_NAME is a UNIQUE
+ * NetBIOS name.
+ *
+ * The NB_ADDRESS field of the RESOURCE RECORD RDATA field for
+ * RR_TYPE of "NB" is the IP address of the name's owner.
+ *
+ */
+#define RR_FLAGS_NB_ONT_MASK 0x6000
+#define RR_FLAGS_NB_ONT_B_NODE 0x0000
+#define RR_FLAGS_NB_ONT_P_NODE 0x2000
+#define RR_FLAGS_NB_ONT_M_NODE 0x4000
+#define RR_FLAGS_NB_ONT_RESERVED 0x6000
+
+#define RR_FLAGS_NB_GROUP_NAME 0x8000
+
+/*
+ * smb_netbios_send_rcv
+ *
+ * This function sends the given NetBIOS packet to the given
+ * address and get back the response. If send operation is not
+ * successful, it's repeated 'retries' times.
+ *
+ * Returns:
+ * 0 Unsuccessful send operation; no reply
+ * 1 Got reply
+ */
+static int
+smb_netbios_send_rcv(int bcast, struct addr_entry *destination,
+ struct name_packet *packet,
+ uint32_t retries, uint32_t timeout)
+{
+ uint32_t retry;
+ unsigned short tid;
+ struct timespec st;
+ int rc;
+
+ for (retry = 0; retry < retries; retry++) {
+ if ((destination->flags & ADDR_FLAG_VALID) == 0)
+ return (0);
+
+ tid = netbios_name_transcation_id++;
+ packet->name_trn_id = tid;
+ if (smb_send_name_service_packet(destination, packet) >= 0) {
+ rc = smb_netbios_process_response(tid, destination,
+ packet, timeout);
+
+ if ((rc > 0) || (bcast == BROADCAST))
+ return (1);
+
+ if (rc != 0)
+ return (0);
+ }
+
+ st.tv_sec = 0;
+ st.tv_nsec = (timeout * 1000000);
+ (void) nanosleep(&st, 0);
+ }
+
+ return (0);
+}
+
+/*
+ * 4.2.2. NAME REGISTRATION REQUEST
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x5 |0|0|1|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Since the RR_NAME is the same name as the QUESTION_NAME, the
+ * RR_NAME representation must use pointers to the QUESTION_NAME
+ * name's labels to guarantee the length of the datagram is less
+ * than the maximum 576 bytes. See section above on name formats
+ * and also page 31 and 32 of RFC 883, Domain Names - Implementation
+ * and Specification, for a complete description of compressed name
+ * label pointers.
+ */
+static int
+smb_send_name_registration_request(int bcast, struct name_question *question,
+ struct resource_record *additional)
+{
+ int gotreply = 0;
+ uint32_t retries;
+ uint32_t timeout;
+ struct addr_entry *destination;
+ struct name_packet packet;
+ unsigned char type;
+ int i, addr_num, rc;
+
+ type = question->name->name[15];
+ if ((type != 0x00) && (type != 0x20)) {
+ syslog(LOG_ERR, "netbios: error trying to register"
+ " non-local name");
+ smb_netbios_name_logf(question->name);
+ question->name->attributes &= ~NAME_ATTR_LOCAL;
+ return (-1);
+ }
+
+ if (bcast == BROADCAST) {
+ if (bcast_num == 0)
+ return (0);
+ destination = smb_bcast_list;
+ addr_num = bcast_num;
+ retries = BCAST_REQ_RETRY_COUNT;
+ timeout = BCAST_REQ_RETRY_TIMEOUT;
+ packet.info = NAME_REGISTRATION_REQUEST | NM_FLAGS_BROADCAST;
+ } else {
+ if (nbns_num == 0)
+ return (0);
+ destination = smb_nbns;
+ addr_num = nbns_num;
+ retries = UCAST_REQ_RETRY_COUNT;
+ timeout = UCAST_REQ_RETRY_TIMEOUT;
+ packet.info = NAME_REGISTRATION_REQUEST | NM_FLAGS_UNICAST;
+ }
+
+ packet.qdcount = 1; /* question entries */
+ packet.question = question;
+ packet.ancount = 0; /* answer recs */
+ packet.answer = NULL;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 1; /* additional recs */
+ packet.additional = additional;
+
+ if (IS_UNIQUE(question->name->attributes) &&
+ (is_multihome((char *)(question->name->name))))
+ packet.info |= NAME_MULTIHOME_REGISTRATION_REQUEST;
+
+ for (i = 0; i < addr_num; i++) {
+ /*
+ * Only register with the Primary WINS server,
+ * unless we got no reply.
+ */
+ if ((bcast == UNICAST) && gotreply)
+ break;
+
+ rc = smb_netbios_send_rcv(bcast, &destination[i], &packet,
+ retries, timeout);
+ if (rc == 1)
+ gotreply = 1;
+ }
+
+ return (gotreply);
+}
+
+/*
+ *
+ * 4.2.4. NAME REFRESH REQUEST
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x8 |0|0|0|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED*/
+static int
+smb_send_name_refresh_request(int bcast, struct name_question *question,
+ struct resource_record *additional, int force)
+{
+ int rc = 0;
+ int gotreply = 0;
+ uint32_t retries;
+ uint32_t timeout;
+ struct addr_entry *addr;
+ struct addr_entry *destination;
+ struct name_packet packet;
+ unsigned char type;
+ int i, addr_num, q_addrs = 0;
+
+ type = question->name->name[15];
+ if ((type != 0x00) && (type != 0x20)) {
+ syslog(LOG_ERR, "attempt to refresh non-local name");
+ smb_netbios_name_logf(question->name);
+ question->name->attributes &= ~NAME_ATTR_LOCAL;
+ return (-1);
+ }
+ switch (bcast) {
+ case BROADCAST :
+ if (bcast_num == 0)
+ return (-1);
+ destination = smb_bcast_list;
+ addr_num = bcast_num;
+ retries = BCAST_REQ_RETRY_COUNT;
+ timeout = BCAST_REQ_RETRY_TIMEOUT;
+ packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_BROADCAST;
+ break;
+
+ case UNICAST :
+ if (nbns_num == 0)
+ return (-1);
+ destination = smb_nbns;
+ addr_num = nbns_num;
+ retries = UCAST_REQ_RETRY_COUNT;
+ timeout = UCAST_REQ_RETRY_TIMEOUT;
+ packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_UNICAST;
+ break;
+
+ default:
+ destination = &question->name->addr_list;
+ /*
+ * the value of addr_num is irrelvant here, because
+ * the code is going to do special_process so it doesn't
+ * need the addr_num. We set a value here just to avoid
+ * compiler warning.
+ */
+ addr_num = 0;
+ retries = UCAST_REQ_RETRY_COUNT;
+ timeout = UCAST_REQ_RETRY_TIMEOUT;
+ packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_UNICAST;
+ q_addrs = 1;
+ break;
+ }
+
+ if (IS_UNIQUE(question->name->attributes) &&
+ (is_multihome((char *)(question->name->name))))
+ packet.info |= NAME_MULTIHOME_REGISTRATION_REQUEST;
+
+ packet.qdcount = 1; /* question entries */
+ packet.question = question;
+ packet.ancount = 0; /* answer recs */
+ packet.answer = NULL;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 1; /* additional recs */
+ packet.additional = additional;
+
+ if (q_addrs)
+ goto special_process;
+
+ for (i = 0; i < addr_num; i++) {
+ rc = smb_netbios_send_rcv(bcast, &destination[i], &packet,
+ retries, timeout);
+ if (rc == 1)
+ gotreply = 1;
+ }
+
+ return (gotreply);
+
+special_process:
+ addr = destination;
+ do {
+ rc = smb_netbios_send_rcv(bcast, addr, &packet,
+ retries, timeout);
+ if (rc == 1)
+ gotreply = 1;
+ addr = addr->forw;
+ } while (addr != destination);
+
+ return (gotreply);
+}
+
+/*
+ * 4.2.5. POSITIVE NAME REGISTRATION RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ *
+ * 4.2.6. NEGATIVE NAME REGISTRATION RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| RCODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * RCODE field values:
+ *
+ * Symbol Value Description:
+ *
+ * FMT_ERR 0x1 Format Error. Request was invalidly
+ * formatted.
+ * SRV_ERR 0x2 Server failure. Problem with NAME, cannot
+ * process name.
+ * IMP_ERR 0x4 Unsupported request error. Allowable only
+ * for challenging NAME when gets an Update type
+ * registration request.
+ * RFS_ERR 0x5 Refused error. For policy reasons server
+ * will not register this name from this host.
+ * ACT_ERR 0x6 Active error. Name is owned by another node.
+ * CFT_ERR 0x7 Name in conflict error. A UNIQUE name is
+ * owned by more than one node.
+ */
+static int
+smb_send_name_registration_response(struct addr_entry *addr,
+ struct name_packet *original_packet, unsigned short rcode)
+{
+ struct name_packet packet;
+ struct resource_record answer;
+
+ bzero(&packet, sizeof (struct name_packet));
+ bzero(&answer, sizeof (struct resource_record));
+
+ packet.name_trn_id = original_packet->name_trn_id;
+ packet.info = NAME_REGISTRATION_RESPONSE | NAME_NM_FLAGS_RA |
+ (rcode & NAME_RCODE_MASK);
+ packet.qdcount = 0; /* question entries */
+ packet.question = NULL;
+ packet.ancount = 1; /* answer recs */
+ packet.answer = &answer;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 0; /* additional recs */
+ packet.additional = NULL;
+
+ answer.name = original_packet->question->name;
+ answer.rr_type = NAME_QUESTION_TYPE_NB;
+ answer.rr_class = NAME_QUESTION_CLASS_IN;
+ answer.ttl = original_packet->additional->ttl;
+ answer.rdlength = original_packet->additional->rdlength;
+ answer.rdata = original_packet->additional->rdata;
+
+ return (smb_send_name_service_packet(addr, &packet));
+}
+
+/*
+ * 4.2.9. NAME RELEASE REQUEST & DEMAND
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x6 |0|0|0|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x00000000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Since the RR_NAME is the same name as the QUESTION_NAME, the
+ * RR_NAME representation must use label string pointers to the
+ * QUESTION_NAME labels to guarantee the length of the datagram is
+ * less than the maximum 576 bytes. This is the same condition as
+ * with the NAME REGISTRATION REQUEST.
+ */
+static int
+smb_send_name_release_request_and_demand(int bcast,
+ struct name_question *question, struct resource_record *additional)
+{
+ int gotreply = 0;
+ int i, rc;
+ int addr_num;
+ uint32_t retries;
+ uint32_t timeout;
+ struct addr_entry *destination;
+ struct name_packet packet;
+
+ if (bcast == BROADCAST) {
+ if (bcast_num == 0)
+ return (-1);
+ destination = smb_bcast_list;
+ addr_num = bcast_num;
+ retries = 1; /* BCAST_REQ_RETRY_COUNT */
+ timeout = 100; /* BCAST_REQ_RETRY_TIMEOUT */
+ packet.info = NAME_RELEASE_REQUEST | NM_FLAGS_BROADCAST;
+ } else {
+ if (nbns_num == 0)
+ return (-1);
+ destination = smb_nbns;
+ addr_num = nbns_num;
+ retries = 1; /* UCAST_REQ_RETRY_COUNT */
+ timeout = 100; /* UCAST_REQ_RETRY_TIMEOUT */
+ packet.info = NAME_RELEASE_REQUEST | NM_FLAGS_UNICAST;
+ }
+
+ packet.qdcount = 1; /* question entries */
+ packet.question = question;
+ packet.ancount = 0; /* answer recs */
+ packet.answer = NULL;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 1; /* additional recs */
+ packet.additional = additional;
+
+ for (i = 0; i < addr_num; i++) {
+ rc = smb_netbios_send_rcv(bcast, &destination[i], &packet,
+ retries, timeout);
+ if (rc == 1)
+ gotreply = 1;
+ }
+
+ return (gotreply);
+}
+
+/*
+ * 4.2.10. POSITIVE NAME RELEASE RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * 4.2.11. NEGATIVE NAME RELEASE RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| RCODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0006 | NB_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * RCODE field values:
+ *
+ * Symbol Value Description:
+ *
+ * FMT_ERR 0x1 Format Error. Request was invalidly
+ * formatted.
+ *
+ * SRV_ERR 0x2 Server failure. Problem with NAME, cannot
+ * process name.
+ *
+ * RFS_ERR 0x5 Refused error. For policy reasons server
+ * will not release this name from this host.
+ *
+ * ACT_ERR 0x6 Active error. Name is owned by another node.
+ * Only that node may release it. A NetBIOS
+ * Name Server can optionally allow a node to
+ * release a name it does not own. This would
+ * facilitate detection of inactive names for
+ * nodes that went down silently.
+ */
+static int
+/* LINTED - E_STATIC_UNUSED */
+smb_send_name_release_response(struct addr_entry *addr,
+ struct name_packet *original_packet, unsigned short rcode)
+{
+ struct name_packet packet;
+ struct resource_record answer;
+
+ bzero(&packet, sizeof (struct name_packet));
+ bzero(&answer, sizeof (struct resource_record));
+
+ packet.name_trn_id = original_packet->name_trn_id;
+ packet.info = NAME_RELEASE_RESPONSE | (rcode & NAME_RCODE_MASK);
+ packet.qdcount = 0; /* question entries */
+ packet.question = NULL;
+ packet.ancount = 1; /* answer recs */
+ packet.answer = &answer;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 0; /* additional recs */
+ packet.additional = NULL;
+
+ answer.name = original_packet->question->name;
+ answer.rr_type = NAME_QUESTION_TYPE_NB;
+ answer.rr_class = NAME_QUESTION_CLASS_IN;
+ answer.ttl = original_packet->additional->ttl;
+ answer.rdlength = original_packet->additional->rdlength;
+ answer.rdata = original_packet->additional->rdata;
+
+ return (smb_send_name_service_packet(addr, &packet));
+}
+
+/*
+ *
+ * 4.2.12. NAME QUERY REQUEST
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |0| 0x0 |0|0|1|0|0 0|B| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0001 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / QUESTION_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static int
+smb_send_name_query_request(int bcast, struct name_question *question)
+{
+ int rc = 0;
+ uint32_t retry, retries;
+ uint32_t timeout;
+ unsigned short tid;
+ struct addr_entry *destination;
+ struct name_packet packet;
+ int i, addr_num;
+ struct timespec st;
+
+ if (bcast == BROADCAST) {
+ if (bcast_num == 0)
+ return (-1);
+ destination = smb_bcast_list;
+ addr_num = bcast_num;
+ retries = BCAST_REQ_RETRY_COUNT;
+ timeout = BCAST_REQ_RETRY_TIMEOUT;
+ packet.info = NAME_QUERY_REQUEST | NM_FLAGS_BROADCAST;
+ } else {
+ if (nbns_num == 0)
+ return (-1);
+ destination = smb_nbns;
+ addr_num = nbns_num;
+ retries = UCAST_REQ_RETRY_COUNT;
+ timeout = UCAST_REQ_RETRY_TIMEOUT;
+ packet.info = NAME_QUERY_REQUEST | NM_FLAGS_UNICAST;
+ }
+ packet.qdcount = 1; /* question entries */
+ packet.question = question;
+ packet.ancount = 0; /* answer recs */
+ packet.answer = NULL;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 0; /* additional recs */
+ packet.additional = NULL;
+
+ for (i = 0; i < addr_num; i++) {
+ for (retry = 0; retry < retries; retry++) {
+ if ((destination->flags & ADDR_FLAG_VALID) == 0)
+ break;
+ tid = netbios_name_transcation_id++;
+ packet.name_trn_id = tid;
+
+ if (smb_send_name_service_packet(&destination[i],
+ &packet) >= 0) {
+ if ((rc = smb_netbios_process_response(tid,
+ &destination[i],
+ &packet, timeout)) != 0)
+ break;
+ }
+ st.tv_sec = 0;
+ st.tv_nsec = (timeout * 1000000);
+ (void) nanosleep(&st, 0);
+ }
+ }
+
+ return (rc);
+}
+
+
+/*
+ *
+ * 4.2.13. POSITIVE NAME QUERY RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x0 |1|T|1|?|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB (0x0020) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RDLENGTH | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ * | |
+ * / ADDR_ENTRY ARRAY /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * The ADDR_ENTRY ARRAY a sequence of zero or more ADDR_ENTRY
+ * records. Each ADDR_ENTRY record represents an owner of a name.
+ * For group names there may be multiple entries. However, the list
+ * may be incomplete due to packet size limitations. Bit 22, "T",
+ * will be set to indicate truncated data.
+ *
+ * Each ADDR_ENTRY has the following format:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_FLAGS | NB_ADDRESS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NB_ADDRESS (continued) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ *
+ * 4.2.14. NEGATIVE NAME QUERY RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x0 |1|0|1|?|0 0|0| RCODE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * / /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NULL (0x000A) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x00000000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * RCODE field values:
+ *
+ * Symbol Value Description
+ *
+ * FMT_ERR 0x1 Format Error. Request was invalidly
+ * formatted.
+ * SRV_ERR 0x2 Server failure. Problem with NAME, cannot
+ * process name.
+ * NAM_ERR 0x3 Name Error. The name requested does not
+ * exist.
+ * IMP_ERR 0x4 Unsupported request error. Allowable only
+ * for challenging NAME when gets an Update type
+ * registration request.
+ * RFS_ERR 0x5 Refused error. For policy reasons server
+ * will not register this name from this host.
+ */
+static int
+smb_send_name_query_response(struct addr_entry *addr,
+ struct name_packet *original_packet, struct name_entry *entry,
+ unsigned short rcode)
+{
+ struct addr_entry *raddr;
+ struct name_packet packet;
+ struct resource_record answer;
+ unsigned short attr;
+ unsigned char data[MAX_DATAGRAM_LENGTH];
+ unsigned char *scan = data;
+
+ packet.name_trn_id = original_packet->name_trn_id;
+ packet.info = NAME_QUERY_RESPONSE | (rcode & NAME_RCODE_MASK);
+ packet.qdcount = 0; /* question entries */
+ packet.question = NULL;
+ packet.ancount = 1; /* answer recs */
+ packet.answer = &answer;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 0; /* additional recs */
+ packet.additional = NULL;
+
+ answer.name = entry;
+ answer.rr_class = NAME_QUESTION_CLASS_IN;
+ answer.ttl = entry->addr_list.ttl;
+ answer.rdata = data;
+ if (rcode) {
+ answer.rr_type = NAME_RR_TYPE_NULL;
+ answer.rdlength = 0;
+ bzero(data, 6);
+ } else {
+ answer.rdlength = 0;
+ answer.rr_type = NAME_QUESTION_TYPE_NB;
+ raddr = &entry->addr_list;
+ scan = data;
+ do {
+ attr = entry->attributes & (NAME_ATTR_GROUP |
+ NAME_ATTR_OWNER_NODE_TYPE);
+
+ BE_OUT16(scan, attr); scan += 2;
+ (void) memcpy(scan, &raddr->sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+ scan += 4;
+
+ answer.rdlength += 6;
+ raddr = raddr->forw;
+ } while (raddr != &entry->addr_list);
+ }
+
+ return (smb_send_name_service_packet(addr, &packet));
+}
+
+/*
+ * 4.2.18. NODE STATUS RESPONSE
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_TRN_ID |1| 0x0 |1|0|0|0|0 0|0| 0x0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0001 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x0000 | 0x0000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / RR_NAME /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NBSTAT (0x0021) | IN (0x0001) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x00000000 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RDLENGTH | NUM_NAMES | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | |
+ * + +
+ * / NODE_NAME ARRAY /
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * / STATISTICS /
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * The NODE_NAME ARRAY is an array of zero or more NUM_NAMES entries
+ * of NODE_NAME records. Each NODE_NAME entry represents an active
+ * name in the same NetBIOS scope as the requesting name in the
+ * local name table of the responder. RR_NAME is the requesting
+ * name.
+ *
+ * NODE_NAME Entry:
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +--- ---+
+ * | |
+ * +--- NETBIOS FORMAT NAME ---+
+ * | |
+ * +--- ---+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NAME_FLAGS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * The NAME_FLAGS field:
+ *
+ * 1 1 1 1 1 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | G | ONT |DRG|CNF|ACT|PRM| RESERVED |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * The NAME_FLAGS field is defined as:
+ *
+ * Symbol Bit(s) Description:
+ *
+ * RESERVED 7-15 Reserved for future use. Must be zero (0).
+ * PRM 6 Permanent Name Flag. If one (1) then entry
+ * is for the permanent node name. Flag is zero
+ * (0) for all other names.
+ * ACT 5 Active Name Flag. All entries have this flag
+ * set to one (1).
+ * CNF 4 Conflict Flag. If one (1) then name on this
+ * node is in conflict.
+ * DRG 3 Deregister Flag. If one (1) then this name
+ * is in the process of being deleted.
+ * ONT 1,2 Owner Node Type:
+ * 00 = B node
+ * 01 = P node
+ * 10 = M node
+ * 11 = Reserved for future use
+ * G 0 Group Name Flag.
+ * If one (1) then the name is a GROUP NetBIOS
+ * name.
+ * If zero (0) then it is a UNIQUE NetBIOS name.
+ */
+#define NAME_FLAGS_PERMANENT_NAME 0x0200
+#define NAME_FLAGS_ACTIVE_NAME 0x0400
+#define NAME_FLAGS_CONFLICT 0x0800
+#define NAME_FLAGS_DEREGISTER 0x1000
+#define NAME_FLAGS_ONT_MASK 0x6000
+#define NAME_FLAGS_ONT_B_NODE 0x0000
+#define NAME_FLAGS_ONT_P_NODE 0x2000
+#define NAME_FLAGS_ONT_M_NODE 0x4000
+#define NAME_FLAGS_ONT_RESERVED 0x6000
+#define NAME_FLAGS_GROUP_NAME 0x8000
+
+
+/*
+ * STATISTICS Field of the NODE STATUS RESPONSE:
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | UNIT_ID (Unique unit ID) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | UNIT_ID,continued | JUMPERS | TEST_RESULT |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VERSION_NUMBER | PERIOD_OF_STATISTICS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_OF_CRCs | NUMBER_ALIGNMENT_ERRORS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_OF_COLLISIONS | NUMBER_SEND_ABORTS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_GOOD_SENDS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_GOOD_RECEIVES |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_RETRANSMITS | NUMBER_NO_RESOURCE_CONDITIONS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NUMBER_FREE_COMMAND_BLOCKS | TOTAL_NUMBER_COMMAND_BLOCKS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |MAX_TOTAL_NUMBER_COMMAND_BLOCKS| NUMBER_PENDING_SESSIONS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MAX_NUMBER_PENDING_SESSIONS | MAX_TOTAL_SESSIONS_POSSIBLE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SESSION_DATA_PACKET_SIZE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+#define MAX_NETBIOS_REPLY_DATA_SIZE 500
+
+static int
+smb_send_node_status_response(struct addr_entry *addr,
+ struct name_packet *original_packet)
+{
+ uint32_t net_ipaddr, max_connections;
+ struct arpreq arpreq;
+ struct name_packet packet;
+ struct resource_record answer;
+ unsigned char *scan;
+ unsigned char *scan_end;
+ unsigned char data[MAX_NETBIOS_REPLY_DATA_SIZE];
+ net_cfg_t cfg;
+
+ bzero(&packet, sizeof (struct name_packet));
+ bzero(&answer, sizeof (struct resource_record));
+
+ packet.name_trn_id = original_packet->name_trn_id;
+ packet.info = NODE_STATUS_RESPONSE;
+ packet.qdcount = 0; /* question entries */
+ packet.question = NULL;
+ packet.ancount = 1; /* answer recs */
+ packet.answer = &answer;
+ packet.nscount = 0; /* authority recs */
+ packet.authority = NULL;
+ packet.arcount = 0; /* additional recs */
+ packet.additional = NULL;
+
+ answer.name = original_packet->question->name;
+ answer.rr_type = NAME_RR_TYPE_NBSTAT;
+ answer.rr_class = NAME_QUESTION_CLASS_IN;
+ answer.ttl = 0;
+ answer.rdata = data;
+
+ scan = smb_netbios_cache_status(data, MAX_NETBIOS_REPLY_DATA_SIZE,
+ original_packet->question->name->scope);
+
+ scan_end = data + MAX_NETBIOS_REPLY_DATA_SIZE;
+
+ if (smb_nic_get_bysubnet(addr->sin.sin_addr.s_addr, &cfg) == NULL)
+ net_ipaddr = 0;
+ else
+ net_ipaddr = cfg.ip;
+
+ smb_config_rdlock();
+ max_connections = smb_config_getnum(SMB_CI_MAX_CONNECTIONS);
+ smb_config_unlock();
+ for (;;) {
+ if ((scan + 6) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+
+ if (net_ipaddr != 0) {
+ struct sockaddr_in *s_in;
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ /* LINTED - E_BAD_PTR_CAST_ALIGN */
+ s_in = (struct sockaddr_in *)&arpreq.arp_pa;
+ s_in->sin_family = AF_INET;
+ s_in->sin_addr.s_addr = net_ipaddr;
+ if (ioctl(s, SIOCGARP, (caddr_t)&arpreq) < 0) {
+ bzero(scan, 6);
+ } else {
+ bcopy(&arpreq.arp_ha.sa_data, scan, 6);
+ }
+ (void) close(s);
+ } else {
+ bzero(scan, 6);
+ }
+ scan += 6;
+
+ if ((scan + 26) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ bzero(scan, 26);
+ scan += 26;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ BE_OUT16(scan, 0); scan += 2;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ BE_OUT16(scan, 0); scan += 2;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ BE_OUT16(scan, 0); scan += 2;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ BE_OUT16(scan, 0); scan += 2;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ BE_OUT16(scan, 0); scan += 2;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ BE_OUT16(scan, 0); scan += 2;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ BE_OUT16(scan, 0); scan += 2;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+ BE_OUT16(scan, max_connections); scan += 2;
+
+ if ((scan + 2) >= scan_end) {
+ packet.info |= NAME_NM_FLAGS_TC;
+ break;
+ }
+
+ BE_OUT16(scan, 0); scan += 2;
+
+ break;
+ /*NOTREACHED*/
+ }
+ answer.rdlength = scan - data;
+ return (smb_send_name_service_packet(addr, &packet));
+}
+
+/*
+ *
+ * 5.1. NAME SERVICE PROTOCOLS
+ *
+ * A REQUEST packet is always sent to the well known UDP port -
+ * NAME_SERVICE_UDP_PORT. The destination address is normally
+ * either the IP broadcast address or the address of the NAME - the
+ * address of the NAME server it set up at initialization time. In
+ * rare cases, a request packet will be sent to an end node, e.g. a
+ * NAME QUERY REQUEST sent to "challenge" a node.
+ *
+ * A RESPONSE packet is always sent to the source UDP port and
+ * source IP address of the request packet.
+ *
+ * A DEMAND packet must always be sent to the well known UDP port -
+ * NAME_SERVICE_UDP_PORT. There is no restriction on the target IP
+ * address.
+ *
+ * Terms used in this section:
+ *
+ * tid - Transaction ID. This is a value composed from
+ * the requestor's IP address and a unique 16 bit
+ * value generated by the originator of the
+ * transaction.
+ */
+
+
+/*
+ *
+ * 5.1.1. B-NODE ACTIVITY
+ *
+ * 5.1.1.1. B-NODE ADD NAME
+ *
+ * PROCEDURE add_name(name)
+ *
+ * (*
+ * * Host initiated processing for a B node
+ * *)
+ * BEGIN
+ *
+ * REPEAT
+ *
+ * (* build name service packet *)
+ *
+ * ONT = B_NODE; (* broadcast node *)
+ * G = UNIQUE; (* unique name *)
+ * TTL = 0;
+ *
+ * broadcast NAME REGISTRATION REQUEST packet;
+ *
+ * (*
+ * * remote node(s) will send response packet
+ * * if applicable
+ * *)
+ * pause(BCAST_REQ_RETRY_TIMEOUT);
+ *
+ * UNTIL response packet is received or
+ * retransmit count has been exceeded
+ *
+ * IF no response packet was received THEN
+ * BEGIN (* no response *)
+ * (*
+ * * Build packet
+ * *)
+ *
+ * ONT = B_NODE; (* broadcast node *)
+ * G = UNIQUE; (* unique name *)
+ * TTL = 0;
+ *
+ * (*
+ * * Let other nodes known you have the name
+ * *)
+ *
+ * broadcast NAME UPDATE REQUEST packet;
+ * (* name can be added to local name table *)
+ * return success;
+ * END (* no response *)
+ * ELSE
+ * BEGIN (* got response *)
+ *
+ * (*
+ * * Match return transaction id
+ * * against tid sent in request
+ * *)
+ *
+ * IF NOT response tid = request tid THEN
+ * BEGIN
+ * ignore response packet;
+ * END
+ * ELSE
+ * CASE packet type OF
+ *
+ * NEGATIVE NAME REGISTRATION RESPONSE:
+ *
+ * return failure; (* name cannot be added *)
+ *
+ * POSITIVE NAME REGISTRATION RESPONSE:
+ * END-NODE CHALLENGE NAME REGISTRATION RESPONSE:
+ *
+ * (*
+ * * B nodes should normally not get this
+ * * response.
+ * *)
+ *
+ * ignore packet;
+ * END (* case *);
+ * END (* got response *)
+ * END (* procedure *)
+ *
+ *
+ *
+ * 5.1.1.2. B-NODE ADD_GROUP NAME
+ *
+ * PROCEDURE add_group_name(name)
+ *
+ * (*
+ * * Host initiated processing for a B node
+ * *)
+ *
+ * BEGIN
+ * (*
+ * * same as for a unique name with the
+ * * exception that the group bit (G) must
+ * * be set in the request packets.
+ * *)
+ *
+ * ...
+ * G = GROUP;
+ * ...
+ * ...
+ *
+ * (*
+ * * broadcast request ...
+ * *)
+ *
+ *
+ * END
+ */
+static int
+smb_name_Bnode_add_name(struct name_entry *name)
+{
+ struct name_question question;
+ struct resource_record additional;
+ unsigned char data[8];
+ unsigned short attr;
+ struct addr_entry *addr;
+ int rc = 0;
+
+ addr = &name->addr_list;
+
+ do {
+ /* build name service packet */
+ question.name = name;
+ /*
+ * question.name->attributes |= NAME_NB_FLAGS_ONT_B;
+ * This is commented because NAME_NB_FLAGS_ONT_B is 0
+ */
+ question.question_type = NAME_QUESTION_TYPE_NB;
+ question.question_class = NAME_QUESTION_CLASS_IN;
+
+ additional.name = name;
+ additional.rr_class = NAME_QUESTION_CLASS_IN;
+ additional.ttl = 0;
+ additional.rdata = data;
+ additional.rdlength = 6;
+ additional.rr_type = NAME_QUESTION_TYPE_NB;
+ attr = name->attributes & (NAME_ATTR_GROUP |
+ NAME_ATTR_OWNER_NODE_TYPE);
+
+ BE_OUT16(&data[0], attr);
+ (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+
+ rc |= smb_send_name_registration_request(BROADCAST, &question,
+ &additional);
+ addr = addr->forw;
+
+ } while (addr != &name->addr_list);
+
+ return (rc);
+}
+
+/*
+ * 5.1.1.3. B-NODE FIND_NAME
+ *
+ * PROCEDURE find_name(name)
+ *
+ * (*
+ * * Host initiated processing for a B node
+ * *)
+ *
+ * BEGIN
+ *
+ * REPEAT
+ * (*
+ * * build packet
+ * *)
+ * ONT = B;
+ * TTL = 0;
+ * G = DONT CARE;
+ * raddr = raddr->forw;
+ *
+ * broadcast NAME QUERY REQUEST packet;
+ * (*
+ * * a node might send response packet
+ * *)
+ *
+ * pause(BCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL response packet received OR
+ * max transmit threshold exceeded
+ *
+ * IF no response packet received THEN
+ * return failure;
+ * ELSE
+ * IF NOT response tid = request tid THEN
+ * ignore packet;
+ * ELSE
+ * CASE packet type OF
+ * POSITIVE NAME QUERY RESPONSE:
+ * (*
+ * * Start a timer to detect conflict.
+ * *
+ * * Be prepared to detect conflict if
+ * * any more response packets are received.
+ * *
+ * *)
+ *
+ * save response as authoritative response;
+ * start_timer(CONFLICT_TIMER);
+ * return success;
+ *
+ * NEGATIVE NAME QUERY RESPONSE:
+ * REDIRECT NAME QUERY RESPONSE:
+ *
+ * (*
+ * * B Node should normally not get either
+ * * response.
+ * *)
+ *
+ * ignore response packet;
+ *
+ * END (* case *)
+ * END (* procedure *)
+ */
+static int
+smb_name_Bnode_find_name(struct name_entry *name)
+{
+ struct name_question question;
+
+ question.name = name;
+ question.question_type = NAME_QUESTION_TYPE_NB;
+ question.question_class = NAME_QUESTION_CLASS_IN;
+
+ return (smb_send_name_query_request(BROADCAST, &question));
+}
+
+/*
+ * 5.1.1.4. B NODE NAME RELEASE
+ *
+ * PROCEDURE delete_name (name)
+ * BEGIN
+ *
+ * REPEAT
+ *
+ * (*
+ * * build packet
+ * *)
+ * ...
+ *
+ * (*
+ * * send request
+ * *)
+ *
+ * broadcast NAME RELEASE REQUEST packet;
+ *
+ * (*
+ * * no response packet expected
+ * *)
+ *
+ * pause(BCAST_REQ_RETRY_TIMEOUT);
+ *
+ * UNTIL retransmit count has been exceeded
+ * END (* procedure *)
+ */
+static int
+smb_name_Bnode_delete_name(struct name_entry *name)
+{
+ struct name_question question;
+ struct resource_record additional;
+ struct addr_entry *raddr;
+ unsigned char data[MAX_DATAGRAM_LENGTH];
+ unsigned char *scan = data;
+ uint32_t attr;
+
+ /* build packet */
+ question.name = name;
+ question.question_type = NAME_QUESTION_TYPE_NB;
+ question.question_class = NAME_QUESTION_CLASS_IN;
+
+ additional.name = name;
+ additional.rr_class = NAME_QUESTION_CLASS_IN;
+ additional.ttl = 0;
+ additional.rdata = data;
+ additional.rdlength = 0;
+ additional.rr_type = NAME_QUESTION_TYPE_NB;
+ raddr = &name->addr_list;
+ scan = data;
+ do {
+ attr = name->attributes & (NAME_ATTR_GROUP |
+ NAME_ATTR_OWNER_NODE_TYPE);
+
+ BE_OUT16(scan, attr); scan += 2;
+ (void) memcpy(scan, &raddr->sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+ scan += 4;
+
+ additional.rdlength += 6;
+ } while (raddr != &name->addr_list);
+
+ return (smb_send_name_release_request_and_demand(BROADCAST,
+ &question, &additional));
+}
+
+/*
+ *
+ * 5.1.2. P-NODE ACTIVITY
+ *
+ * All packets sent or received by P nodes are unicast UDP packets.
+ * A P node sends name service requests to the NAME node that is
+ * specified in the P-node configuration.
+ *
+ * 5.1.2.1. P-NODE ADD_NAME
+ *
+ * PROCEDURE add_name(name)
+ *
+ * (*
+ * * Host initiated processing for a P node
+ * *)
+ *
+ * BEGIN
+ *
+ * REPEAT
+ * (*
+ * * build packet
+ * *)
+ *
+ * ONT = P;
+ * G = UNIQUE;
+ * ...
+ *
+ * (*
+ * * send request
+ * *)
+ *
+ * unicast NAME REGISTRATION REQUEST packet;
+ *
+ * (*
+ * * NAME will send response packet
+ * *)
+ *
+ * IF receive a WACK RESPONSE THEN
+ * pause(time from TTL field of response);
+ * ELSE
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL response packet is received OR
+ * retransmit count has been exceeded
+ *
+ * IF no response packet was received THEN
+ * BEGIN (* no response *)
+ * (*
+ * * NAME is down. Cannot claim name.
+ * *)
+ *
+ * return failure; (* name cannot be claimed *)
+ * END (* no response *)
+ * ELSE
+ * BEGIN (* response *)
+ * IF NOT response tid = request tid THEN
+ * BEGIN
+ * (* Packet may belong to another transaction *)
+ * ignore response packet;
+ * END
+ * ELSE
+ * CASE packet type OF
+ *
+ * POSITIVE NAME REGISTRATION RESPONSE:
+ *
+ * (*
+ * * name can be added
+ * *)
+ *
+ * adjust refresh timeout value, TTL, for this name;
+ * return success; (* name can be added *)
+ *
+ * NEGATIVE NAME REGISTRATION RESPONSE:
+ * return failure; (* name cannot be added *)
+ *
+ * END-NODE CHALLENGE REGISTRATION REQUEST:
+ * BEGIN (* end node challenge *)
+ *
+ * (*
+ * * The response packet has in it the
+ * * address of the presumed owner of the
+ * * name. Challenge that owner.
+ * * If owner either does not
+ * * respond or indicates that he no longer
+ * * owns the name, claim the name.
+ * * Otherwise, the name cannot be claimed.
+ * *
+ * *)
+ *
+ * REPEAT
+ * (*
+ * * build packet
+ * *)
+ * ...
+ *
+ * unicast NAME QUERY REQUEST packet to the
+ * address contained in the END NODE
+ * CHALLENGE RESPONSE packet;
+ *
+ * (*
+ * * remote node may send response packet
+ * *)
+ *
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ *
+ * UNTIL response packet is received or
+ * retransmit count has been exceeded
+ * IF no response packet is received OR
+ * NEGATIVE NAME QUERY RESPONSE packet
+ * received THEN
+ * BEGIN (* update *)
+ *
+ * (*
+ * * name can be claimed
+ * *)
+ *
+ * REPEAT
+ *
+ * (*
+ * * build packet
+ * *)
+ * ...
+ *
+ * unicast NAME UPDATE REQUEST to NAME;
+ *
+ * (*
+ * * NAME node will send response packet
+ * *)
+ *
+ * IF receive a WACK RESPONSE THEN
+ * pause(time from TTL field of response);
+ * ELSE
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL response packet is received or
+ * retransmit count has been exceeded
+ * IF no response packet received THEN
+ * BEGIN (* no response *)
+ *
+ * (*
+ * * name could not be claimed
+ * *)
+ *
+ * return failure;
+ * END (* no response *)
+ * ELSE
+ * CASE packet type OF
+ * POSITIVE NAME REGISTRATION RESPONSE:
+ * (*
+ * * add name
+ * *)
+ * return success;
+ * NEGATIVE NAME REGISTRATION RESPONSE:
+ *
+ * (*
+ * * you lose ...
+ * *)
+ * return failure;
+ * END (* case *)
+ * END (* update *)
+ * ELSE
+ *
+ * (*
+ * * received a positive response to the "challenge"
+ * * Remote node still has name
+ * *)
+ *
+ * return failure;
+ * END (* end node challenge *)
+ * END (* response *)
+ * END (* procedure *)
+ *
+ *
+ * 5.1.2.2. P-NODE ADD GROUP NAME
+ *
+ * PROCEDURE add_group_name(name)
+ *
+ * (*
+ * * Host initiated processing for a P node
+ * *)
+ *
+ * BEGIN
+ * (*
+ * * same as for a unique name, except that the
+ * * request packet must indicate that a
+ * * group name claim is being made.
+ * *)
+ *
+ * ...
+ * G = GROUP;
+ * ...
+ *
+ * (*
+ * * send packet
+ * *)
+ * ...
+ *
+ *
+ * END
+ */
+static int
+smb_name_Pnode_add_name(struct name_entry *name)
+{
+ struct name_question question;
+ struct resource_record additional;
+ unsigned char data[8];
+ unsigned short attr;
+ struct addr_entry *addr;
+ int rc = 0;
+
+ /* build packet */
+ addr = &name->addr_list;
+ do {
+ question.name = name;
+ question.question_type = NAME_QUESTION_TYPE_NB;
+ question.question_class = NAME_QUESTION_CLASS_IN;
+
+ additional.name = name;
+ additional.rr_class = NAME_QUESTION_CLASS_IN;
+ additional.ttl = 0;
+ additional.rdata = data;
+ additional.rdlength = 6;
+ additional.rr_type = NAME_QUESTION_TYPE_NB;
+ attr = name->attributes &
+ (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE);
+
+ BE_OUT16(&data[0], attr);
+ (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+
+ rc |= smb_send_name_registration_request(UNICAST, &question,
+ &additional);
+
+ addr = addr->forw;
+
+ } while (addr != &name->addr_list);
+
+ return (rc);
+}
+
+static int
+smb_name_Pnode_refresh_name(struct name_entry *name)
+{
+ struct name_question question;
+ struct resource_record additional;
+ unsigned char data[8];
+ unsigned short attr;
+ struct addr_entry *addr;
+ int rc = 0;
+
+ /* build packet */
+ addr = &name->addr_list;
+ do {
+ question.name = name;
+ question.question_type = NAME_QUESTION_TYPE_NB;
+ question.question_class = NAME_QUESTION_CLASS_IN;
+
+ additional.name = name;
+ additional.rr_class = NAME_QUESTION_CLASS_IN;
+ additional.ttl = 0;
+ additional.rdata = data;
+ additional.rdlength = 6;
+ additional.rr_type = NAME_QUESTION_TYPE_NB;
+ attr = name->attributes &
+ (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE);
+
+ BE_OUT16(&data[0], attr);
+ (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+
+ rc |= smb_send_name_refresh_request(UNICAST, &question,
+ &additional, 1);
+
+ addr = addr->forw;
+ } while (addr != &name->addr_list);
+
+ return (rc);
+}
+
+/*
+ * 5.1.2.3. P-NODE FIND NAME
+ *
+ * PROCEDURE find_name(name)
+ *
+ * (*
+ * * Host initiated processing for a P node
+ * *)
+ *
+ * BEGIN
+ * REPEAT
+ * (*
+ * * build packet
+ * *)
+ *
+ * ONT = P;
+ * G = DONT CARE;
+ *
+ * unicast NAME QUERY REQUEST packet;
+ *
+ * (*
+ * * a NAME node might send response packet
+ * *)
+ *
+ * IF receive a WACK RESPONSE THEN
+ * pause(time from TTL field of response);
+ * ELSE
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL response packet received OR
+ * max transmit threshold exceeded
+ *
+ * IF no response packet received THEN
+ * return failure;
+ * ELSE
+ * IF NOT response tid = request tid THEN
+ * ignore packet;
+ * ELSE
+ * CASE packet type OF
+ * POSITIVE NAME QUERY RESPONSE:
+ * return success;
+ *
+ * REDIRECT NAME QUERY RESPONSE:
+ *
+ * (*
+ * * NAME node wants this end node
+ * * to use some other NAME node
+ * * to resolve the query.
+ * *)
+ *
+ * repeat query with NAME address
+ * in the response packet;
+ * NEGATIVE NAME QUERY RESPONSE:
+ * return failure;
+ *
+ * END (* case *)
+ * END (* procedure *)
+ */
+static int
+smb_name_Pnode_find_name(struct name_entry *name)
+{
+ struct name_question question;
+
+ /*
+ * Host initiated processing for a P node
+ */
+ question.name = name;
+ question.name->attributes |= NAME_NB_FLAGS_ONT_P;
+ question.question_type = NAME_QUESTION_TYPE_NB;
+ question.question_class = NAME_QUESTION_CLASS_IN;
+
+ return (smb_send_name_query_request(UNICAST, &question));
+}
+
+/*
+ * 5.1.2.4. P-NODE DELETE_NAME
+ *
+ * PROCEDURE delete_name (name)
+ *
+ * (*
+ * * Host initiated processing for a P node
+ * *)
+ *
+ * BEGIN
+ *
+ * REPEAT
+ *
+ * (*
+ * * build packet
+ * *)
+ * ...
+ *
+ * (*
+ * * send request
+ * *)
+ *
+ * unicast NAME RELEASE REQUEST packet;
+ * IF receive a WACK RESPONSE THEN
+ * pause(time from TTL field of response);
+ * ELSE
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL retransmit count has been exceeded
+ * or response been received
+ *
+ * IF response has been received THEN
+ * CASE packet type OF
+ * POSITIVE NAME RELEASE RESPONSE:
+ * return success;
+ * NEGATIVE NAME RELEASE RESPONSE:
+ *
+ * (*
+ * * NAME does want node to delete this
+ * * name !!!
+ * *)
+ *
+ * return failure;
+ * END (* case *)
+ * END (* procedure *)
+ */
+static int
+smb_name_Pnode_delete_name(struct name_entry *name)
+{
+ struct name_question question;
+ struct resource_record additional;
+ struct addr_entry *raddr;
+ unsigned char data[MAX_DATAGRAM_LENGTH];
+ unsigned char *scan = data;
+ uint32_t attr;
+
+ /* build packet */
+ question.name = name;
+ question.name->attributes |= NAME_NB_FLAGS_ONT_P;
+ question.question_type = NAME_QUESTION_TYPE_NB;
+ question.question_class = NAME_QUESTION_CLASS_IN;
+
+ additional.name = name;
+ additional.rr_class = NAME_QUESTION_CLASS_IN;
+ additional.ttl = 0;
+ additional.rdata = data;
+ additional.rdlength = 0;
+ additional.rr_type = NAME_QUESTION_TYPE_NB;
+ raddr = &name->addr_list;
+ do {
+ scan = data;
+ attr = name->attributes & (NAME_ATTR_GROUP |
+ NAME_ATTR_OWNER_NODE_TYPE);
+
+ BE_OUT16(scan, attr); scan += 2;
+ BE_OUT32(scan, raddr->sin.sin_addr.s_addr); scan += 4;
+ (void) memcpy(scan, &raddr->sin.sin_addr.s_addr,
+ sizeof (uint32_t));
+ scan += 4;
+
+ additional.rdlength = 6;
+ raddr = raddr->forw;
+ (void) smb_send_name_release_request_and_demand(UNICAST,
+ &question, &additional);
+ } while (raddr != &name->addr_list);
+
+ return (1);
+}
+
+/*
+ * 5.1.3. M-NODE ACTIVITY
+ *
+ * M nodes behavior is similar to that of P nodes with the addition
+ * of some B node-like broadcast actions. M node name service
+ * proceeds in two steps:
+ *
+ * 1.Use broadcast UDP based name service. Depending on the
+ * operation, goto step 2.
+ *
+ * 2.Use directed UDP name service.
+ *
+ * The following code for M nodes is exactly the same as for a P
+ * node, with the exception that broadcast operations are done
+ * before P type operation is attempted.
+ *
+ * 5.1.3.1. M-NODE ADD NAME
+ *
+ * PROCEDURE add_name(name)
+ *
+ * (*
+ * * Host initiated processing for a M node
+ * *)
+ *
+ * BEGIN
+ *
+ * (*
+ * * check if name exists on the
+ * * broadcast area
+ * *)
+ * REPEAT
+ * (* build packet *)
+ *
+ * ....
+ * broadcast NAME REGISTRATION REQUEST packet;
+ * pause(BCAST_REQ_RETRY_TIMEOUT);
+ *
+ * UNTIL response packet is received or
+ * retransmit count has been exceeded
+ *
+ * IF valid response received THEN
+ * BEGIN
+ * (* cannot claim name *)
+ *
+ * return failure;
+ * END
+ *
+ * (*
+ * * No objections received within the
+ * * broadcast area.
+ * * Send request to name server.
+ * *)
+ *
+ * REPEAT
+ * (*
+ * * build packet
+ * *)
+ *
+ * ONT = M;
+ * ...
+ *
+ * unicast NAME REGISTRATION REQUEST packet;
+ *
+ * (*
+ * * remote NAME will send response packet
+ * *)
+ *
+ * IF receive a WACK RESPONSE THEN
+ * pause(time from TTL field of response);
+ * ELSE
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ *
+ * UNTIL response packet is received or
+ * retransmit count has been exceeded
+ *
+ * IF no response packet was received THEN
+ * BEGIN (* no response *)
+ * (*
+ * * NAME is down. Cannot claim name.
+ * *)
+ * return failure; (* name cannot be claimed *)
+ * END (* no response *)
+ * ELSE
+ * BEGIN (* response *)
+ * IF NOT response tid = request tid THEN
+ * BEGIN
+ * ignore response packet;
+ * END
+ * ELSE
+ * CASE packet type OF
+ * POSITIVE NAME REGISTRATION RESPONSE:
+ *
+ * (*
+ * * name can be added
+ * *)
+ *
+ * adjust refresh timeout value, TTL;
+ * return success; (* name can be added *)
+ *
+ * NEGATIVE NAME REGISTRATION RESPONSE:
+ * return failure; (* name cannot be added *)
+ *
+ * END-NODE CHALLENGE REGISTRATION REQUEST:
+ * BEGIN (* end node challenge *)
+ *
+ * (*
+ * * The response packet has in it the
+ * * address of the presumed owner of the
+ * * name. Challenge that owner.
+ * * If owner either does not
+ * * respond or indicates that he no longer
+ * * owns the name, claim the name.
+ * * Otherwise, the name cannot be claimed.
+ * *
+ * *)
+ *
+ * REPEAT
+ * (*
+ * * build packet
+ * *)
+ * ...
+ *
+ * (*
+ * * send packet to address contained in the
+ * * response packet
+ * *)
+ *
+ * unicast NAME QUERY REQUEST packet;
+ *
+ * (*
+ * * remote node may send response packet
+ * *)
+ *
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ *
+ * UNTIL response packet is received or
+ * retransmit count has been exceeded
+ * IF no response packet is received THEN
+ * BEGIN (* no response *)
+ *
+ * (*
+ * * name can be claimed
+ * *)
+ * REPEAT
+ *
+ * (*
+ * * build packet
+ * *)
+ * ...
+ *
+ * unicast NAME UPDATE REQUEST to NAME;
+ *
+ * (*
+ * * NAME node will send response packet
+ * *)
+ *
+ * IF receive a WACK RESPONSE THEN
+ * pause(time from TTL field of response);
+ * ELSE
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ *
+ * UNTIL response packet is received or
+ * retransmit count has been exceeded
+ * IF no response packet received THEN
+ * BEGIN (* no response *)
+ *
+ * (*
+ * * name could not be claimed
+ * *)
+ *
+ * return failure;
+ * END (* no response *)
+ * ELSE
+ * CASE packet type OF
+ * POSITIVE NAME REGISTRATION RESPONSE:
+ * (*
+ * * add name
+ * *)
+ *
+ * return success;
+ * NEGATIVE NAME REGISTRATION RESPONSE:
+ * (*
+ * * you lose ...
+ * *)
+ *
+ * return failure;
+ * END (* case *)
+ * END (* no response *)
+ * ELSE
+ * IF NOT response tid = request tid THEN
+ * BEGIN
+ * ignore response packet;
+ * END
+ *
+ * (*
+ * * received a response to the "challenge"
+ * * packet
+ * *)
+ *
+ * CASE packet type OF
+ * POSITIVE NAME QUERY:
+ *
+ * (*
+ * * remote node still has name.
+ * *)
+ *
+ * return failure;
+ * NEGATIVE NAME QUERY:
+ *
+ * (*
+ * * remote node no longer has name
+ * *)
+ *
+ * return success;
+ * END (* case *)
+ * END (* end node challenge *)
+ * END (* case *)
+ * END (* response *)
+ * END (* procedure *)
+ *
+ *
+ * 5.1.3.2. M-NODE ADD GROUP NAME
+ *
+ * PROCEDURE add_group_name(name)
+ *
+ * (*
+ * * Host initiated processing for a P node
+ * *)
+ *
+ * BEGIN
+ * (*
+ * * same as for a unique name, except that the
+ * * request packet must indicate that a
+ * * group name claim is being made.
+ * *)
+ *
+ * ...
+ * G = GROUP;
+ * ...
+ *
+ * (*
+ * * send packet
+ * *)
+ * ...
+ *
+ *
+ * END
+ */
+static int
+smb_name_Mnode_add_name(struct name_entry *name)
+{
+ if (smb_name_Bnode_add_name(name) > 0) {
+ if (nbns_num == 0)
+ return (1); /* No name server configured */
+
+ return (smb_name_Pnode_add_name(name));
+ }
+ return (-1);
+}
+
+static int
+smb_name_Hnode_add_name(struct name_entry *name)
+{
+ if (nbns_num > 0) {
+ if (smb_name_Pnode_add_name(name) == 1)
+ return (1);
+ }
+
+ return (smb_name_Bnode_add_name(name));
+}
+
+/*
+ * 5.1.3.3. M-NODE FIND NAME
+ *
+ * PROCEDURE find_name(name)
+ *
+ * (*
+ * * Host initiated processing for a M node
+ * *)
+ *
+ * BEGIN
+ * (*
+ * * check if any node on the broadcast
+ * * area has the name
+ * *)
+ *
+ * REPEAT
+ * (* build packet *)
+ * ...
+ *
+ * broadcast NAME QUERY REQUEST packet;
+ * pause(BCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL response packet received OR
+ * max transmit threshold exceeded
+ *
+ * IF valid response received THEN
+ * BEGIN
+ * save response as authoritative response;
+ * start_timer(CONFLICT_TIMER);
+ * return success;
+ * END
+ *
+ * (*
+ * * no valid response on the b'cast segment.
+ * * Try the name server.
+ * *)
+ *
+ * REPEAT
+ * (*
+ * * build packet
+ * *)
+ *
+ * ONT = M;
+ * G = DONT CARE;
+ *
+ * unicast NAME QUERY REQUEST packet to NAME;
+ *
+ * (*
+ * * a NAME node might send response packet
+ * *)
+ *
+ * IF receive a WACK RESPONSE THEN
+ * pause(time from TTL field of response);
+ * ELSE
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL response packet received OR
+ * max transmit threshold exceeded
+ *
+ * IF no response packet received THEN
+ * return failure;
+ * ELSE
+ * IF NOT response tid = request tid THEN
+ * ignore packet;
+ * ELSE
+ * CASE packet type OF
+ * POSITIVE NAME QUERY RESPONSE:
+ * return success;
+ *
+ * REDIRECT NAME QUERY RESPONSE:
+ *
+ * (*
+ * * NAME node wants this end node
+ * * to use some other NAME node
+ * * to resolve the query.
+ * *)
+ *
+ * repeat query with NAME address
+ * in the response packet;
+ * NEGATIVE NAME QUERY RESPONSE:
+ * return failure;
+ *
+ * END (* case *)
+ * END (* procedure *)
+ */
+static int
+smb_name_Mnode_find_name(struct name_entry *name)
+{
+ if (smb_name_Bnode_find_name(name) == 1)
+ return (1);
+
+ if (nbns_num == 0)
+ return (1); /* No name server configured */
+
+ return (smb_name_Pnode_find_name(name));
+}
+
+static int
+smb_name_Hnode_find_name(struct name_entry *name)
+{
+ if (nbns_num > 0)
+ if (smb_name_Pnode_find_name(name) == 1)
+ return (1);
+
+ return (smb_name_Bnode_find_name(name));
+}
+
+/*
+ * 5.1.3.4. M-NODE DELETE NAME
+ *
+ * PROCEDURE delete_name (name)
+ *
+ * (*
+ * * Host initiated processing for a P node
+ * *)
+ *
+ * BEGIN
+ * (*
+ * * First, delete name on NAME
+ * *)
+ *
+ * REPEAT
+ *
+ * (*
+ * * build packet
+ * struct addr_entry *addr;
+ * *)
+ * ...
+ *
+ * (*
+ * * send request
+ * *)
+ *
+ * unicast NAME RELEASE REQUEST packet to NAME;
+ *
+ * IF receive a WACK RESPONSE THEN
+ * pause(time from TTL field of response);
+ * ELSE
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL retransmit count has been exceeded
+ * or response been received
+ *
+ * IF response has been received THEN
+ * CASE packet type OF
+ * POSITIVE NAME RELEASE RESPONSE:
+ * (*
+ * * Deletion of name on b'cast segment is deferred
+ * * until after NAME has deleted the name
+ * *)
+ *
+ * REPEAT
+ * (* build packet *)
+ *
+ * ...
+ * broadcast NAME RELEASE REQUEST;
+ * pause(BCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL rexmt threshold exceeded
+ *
+ * return success;
+ * NEGATIVE NAME RELEASE RESPONSE:
+ *
+ * (*
+ * * NAME does want node to delete this
+ * * name
+ * *)
+ * return failure;
+ * END (* case *)
+ * END (* procedure *)
+ */
+static int
+smb_name_Mnode_delete_name(struct name_entry *name)
+{
+ (void) smb_name_Bnode_delete_name(name);
+
+ if (nbns_num == 0)
+ return (-1); /* No name server configured */
+
+ if (smb_name_Pnode_delete_name(name) > 0)
+ return (1);
+
+ return (-1);
+}
+
+static int
+smb_name_Hnode_delete_name(struct name_entry *name)
+{
+ if (nbns_num > 0)
+ if (smb_name_Pnode_delete_name(name) > 0)
+ return (1);
+
+ return (smb_name_Bnode_delete_name(name));
+}
+
+/*
+ * 5.1.1.5. B-NODE INCOMING PACKET PROCESSING
+ *
+ * Following processing is done when broadcast or unicast packets
+ * are received at the NAME_SERVICE_UDP_PORT.
+ *
+ * PROCEDURE process_incoming_packet(packet)
+ *
+ * (*
+ * * Processing initiated by incoming packets for a B node
+ * *)
+ *
+ * BEGIN
+ * (*
+ * * Note: response packets are always sent
+ * * to:
+ * * source IP address of request packet
+ * * source UDP port of request packet
+ * *)
+ *
+ * CASE packet type OF
+ *
+ * NAME REGISTRATION REQUEST (UNIQUE):
+ * IF name exists in local name table THEN
+ * send NEGATIVE_NAME_REGISTRATION_RESPONSE ;
+ * NAME REGISTRATION REQUEST (GROUP):
+ * IF name exists in local name table THEN
+ * BEGIN
+ * IF local entry is a unique name THEN
+ * send NEGATIVE_NAME_REGISTRATION_RESPONSE ;
+ * END
+ * NAME QUERY REQUEST:
+ * IF name exists in local name table THEN
+ * BEGIN
+ * build response packet;
+ * send POSITIVE_NAME_QUERY_RESPONSE;
+ * POSITIVE NAME QUERY RESPONSE:
+ * IF name conflict timer is not active THEN
+ * BEGIN
+ * (*
+ * * timer has expired already... ignore this
+ * * packet
+ * *)
+ *
+ * return;
+ * END
+ * ELSE (* timer is active *)
+ * IF a response for this name has previously been
+ * received THEN
+ * BEGIN (* existing entry *)
+ *
+ * (*
+ * * we sent out a request packet, and
+ * * have already received (at least)
+ * * one response
+ * *
+ * * Check if conflict exists.
+ * * If so, send out a conflict packet.
+ * *
+ * * Note: detecting conflict does NOT
+ * * affect any existing sessions.
+ * *
+ * *)
+ *
+ * (*
+ * * Check for name conflict.
+ * * See "Name Conflict" in Concepts and Methods
+ * *)
+ * check saved authoritative response against
+ * information in this response packet;
+ * IF conflict detected THEN
+ * BEGIN
+ * unicast NAME CONFLICT DEMAND packet;
+ * IF entry exists in cache THEN
+ * BEGIN
+ * remove entry from cache;
+ * END
+ * END
+ * END (* existing entry *)
+ * ELSE
+ * BEGIN
+ * (*
+ * * Note: If this was the first response
+ * * to a name query, it would have been
+ * * handled in the
+ * * find_name() procedure.
+ * *)
+ *
+ * ignore packet;
+ * END
+ * NAME CONFLICT DEMAND:
+ * IF name exists in local name table THEN
+ * BEGIN
+ * mark name as conflict detected;
+ *
+ * (*
+ * * a name in the state "conflict detected"
+ * * does not "logically" exist on that node.
+ * * No further session will be accepted on
+ * * that name.
+ * * No datagrams can be sent against that name.
+ * * Such an entry will not be used for
+ * * purposes of processing incoming request
+ * * packets.
+ * * The only valid user NetBIOS operation
+ * * against such a name is DELETE NAME.
+ * *)
+ * END
+ * NAME RELEASE REQUEST:
+ * IF caching is being done THEN
+ * BEGIN
+ * remove entry from cache;
+ * END
+ * NAME UPDATE REQUEST:
+ * IF caching is being done THEN
+ * BEGIN
+ * IF entry exists in cache already,
+ * update cache;
+ * ELSE IF name is "interesting" THEN
+ * BEGIN
+ * add entry to cache;
+ * END
+ * END
+ *
+ * NODE STATUS REQUEST:
+ * IF name exists in local name table THEN
+ * BEGIN
+ * (*
+ * * send only those names that are
+ * * in the same scope as the scope
+ * * field in the request packet
+ * *)
+ *
+ * send NODE STATUS RESPONSE;
+ * END
+ * END
+ */
+static void
+smb_name_process_Bnode_packet(struct name_packet *packet,
+ struct addr_entry *addr)
+{
+ struct name_entry *name;
+ struct name_entry *entry;
+ struct name_question *question;
+ struct resource_record *additional;
+
+ question = packet->question;
+ additional = packet->additional;
+
+ switch (packet->info & NAME_OPCODE_OPCODE_MASK) {
+ case NAME_OPCODE_REFRESH:
+ /* Guard against malformed packets */
+ if ((question == 0) || (additional == 0))
+ break;
+ if (additional->name->addr_list.sin.sin_addr.s_addr == 0)
+ break;
+
+ name = question->name;
+ name->addr_list.ttl = additional->ttl;
+ name->attributes = additional->name->attributes;
+ name->addr_list.sin = additional->name->addr_list.sin;
+ name->addr_list.forw = name->addr_list.back = &name->addr_list;
+
+ if ((entry = smb_netbios_cache_lookup_addr(name)) != 0) {
+ smb_netbios_cache_update_entry(entry, question->name);
+ smb_netbios_cache_unlock_entry(entry);
+ }
+ else
+ (void) smb_netbios_cache_insert(question->name);
+ break;
+
+ case NAME_OPCODE_QUERY:
+ /*
+ * This opcode covers both NAME_QUERY_REQUEST and
+ * NODE_STATUS_REQUEST. They can be distinguished
+ * based on the type of question entry.
+ */
+
+ /* All query requests have to have question entry */
+ if (question == 0)
+ break;
+
+ if (question->question_type == NAME_QUESTION_TYPE_NB) {
+ name = question->name;
+ if ((entry = smb_netbios_cache_lookup(name)) != 0) {
+ (void) smb_send_name_query_response(addr,
+ packet, entry, 0);
+ smb_netbios_cache_unlock_entry(entry);
+ }
+ }
+ else
+ if (question->question_type == NAME_QUESTION_TYPE_NBSTAT) {
+ /*
+ * Name of "*" may be used to force node to
+ * divulge status for administrative purposes
+ */
+ name = question->name;
+ entry = 0;
+ if (NETBIOS_NAME_IS_STAR(name->name) ||
+ ((entry = smb_netbios_cache_lookup(name)) != 0)) {
+ if (entry)
+ smb_netbios_cache_unlock_entry(entry);
+ /*
+ * send only those names that are
+ * in the same scope as the scope
+ * field in the request packet
+ */
+ (void) smb_send_node_status_response(addr,
+ packet);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * 5.1.2.5. P-NODE INCOMING PACKET PROCESSING
+ *
+ * Processing initiated by reception of packets at a P node
+ *
+ * PROCEDURE process_incoming_packet(packet)
+ *
+ * (*
+ * * Processing initiated by incoming packets at a P node
+ * *)
+ *
+ * BEGIN
+ * (*
+ * * always ignore UDP broadcast packets
+ * *)
+ *
+ * IF packet was sent as a broadcast THEN
+ * BEGIN
+ * ignore packet;
+ * return;
+ * END
+ * CASE packet type of
+ *
+ * NAME CONFLICT DEMAND:
+ * IF name exists in local name table THEN
+ * mark name as in conflict;
+ * return;
+ *
+ * NAME QUERY REQUEST:
+ * IF name exists in local name table THEN
+ * BEGIN (* name exists *)
+ *
+ * (*
+ * * build packet
+ * *)
+ * ...
+ *
+ * (*
+ * * send response to the IP address and port
+ * * number from which the request was received.
+ * *)
+ *
+ * send POSITIVE_NAME_QUERY_RESPONSE ;
+ * return;
+ * END (* exists *)
+ * ELSE
+ * BEGIN (* does not exist *)
+ *
+ * (*
+ * * send response to the requestor
+ * *)
+ *
+ * send NEGATIVE_NAME_QUERY_RESPONSE ;
+ * return;
+ * END (* does not exist *)
+ * NODE STATUS REQUEST:
+ * (*
+ * * Name of "*" may be used for force node to
+ * * divulge status for administrative purposes
+ * *)
+ * IF name in local name table OR name = "*" THEN
+ * BEGIN
+ * (*
+ * * Build response packet and
+ * * send to requestor node
+ * * Send only those names that are
+ * * in the same scope as the scope
+ * * in the request packet.
+ * *)
+ *
+ * send NODE_STATUS_RESPONSE;
+ * END
+ *
+ * NAME RELEASE REQUEST:
+ * (*
+ * * This will be received if the NAME wants to flush the
+ * * name from the local name table, or from the local
+ * * cache.
+ * *)
+ *
+ * IF name exists in the local name table THEN
+ * BEGIN
+ * delete name from local name table;
+ * inform user that name has been deleted;
+ * END
+ * END (* case *)
+ * END (* procedure *)
+ *
+ * (*
+ * * Incoming packet processing on a NS node
+ * *)
+ *
+ * BEGIN
+ * IF packet was sent as a broadcast THEN
+ * BEGIN
+ * discard packet;
+ * return;
+ * END
+ * CASE packet type of
+ *
+ * NAME REGISTRATION REQUEST (UNIQUE):
+ * IF unique name exists in data base THEN
+ * BEGIN (* unique name exists *)
+ * (*
+ * * NAME node may be a "passive"
+ * * server in that it expects the
+ * * end node to do the challenge
+ * * server. Such a NAME node is
+ * * called a "non-secure" server.
+ * * A "secure" server will do the
+ * * challenging before it sends
+ * * back a response packet.
+ * *)
+ *
+ * IF non-secure THEN
+ * BEGIN
+ * (*
+ * * build response packet
+ * *)
+ * ...
+ *
+ *
+ * (*
+ * * let end node do the challenge
+ * *)
+ *
+ * send END-NODE CHALLENGE NAME REGISTRATION
+ * RESPONSE;
+ * return;
+ * END
+ * ELSE
+ * (*
+ * * secure server - do the name
+ * * challenge operation
+ * *)
+ *
+ * REPEAT
+ * send NAME QUERY REQUEST;
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL response has been received or
+ * retransmit count has been exceeded
+ * IF no response was received THEN
+ * BEGIN
+ *
+ * (* node down *)
+ *
+ * update data base - remove entry;
+ * update data base - add new entry;
+ * send POSITIVE NAME REGISTRATION RESPONSE;
+ * return;
+ * END
+ * ELSE
+ * BEGIN (* challenged node replied *)
+ * (*
+ * * challenged node replied with
+ * * a response packet
+ * *)
+ *
+ * CASE packet type
+ *
+ * POSITIVE NAME QUERY RESPONSE:
+ *
+ * (*
+ * * name still owned by the
+ * * challenged node
+ * *
+ * * build packet and send response
+ * *)
+ * ...
+ *
+ *
+ * (*
+ * * Note: The NAME will need to
+ * * keep track (based on transaction id) of
+ * * the IP address and port number
+ * * of the original requestor.
+ * *)
+ *
+ * send NEGATIVE NAME REGISTRATION RESPONSE;
+ * return;
+ * NEGATIVE NAME QUERY RESPONSE:
+ *
+ * update data base - remove entry;
+ * update data base - add new entry;
+ *
+ * (*
+ * * build response packet and send
+ * * response
+ * *)
+ * send POSITIVE NAME REGISTRATION RESPONSE;
+ * return;
+ * END (* case *)
+ * END (* challenged node replied *)
+ * END (* unique name exists in data base *)
+ * ELSE
+ * IF group name exists in data base THEN
+ * BEGIN (* group names exists *)
+ *
+ * (*
+ * * Members of a group name are NOT
+ * * challenged.
+ * * Make the assumption that
+ * * at least some of the group members
+ * * are still alive.
+ * * Refresh mechanism will
+ * * allow the NAME to detect when all
+ * * members of a group no longer use that
+ * * name
+ * *)
+ *
+ * send NEGATIVE NAME REGISTRATION RESPONSE;
+ * END (* group name exists *)
+ * ELSE
+ * BEGIN (* name does not exist *)
+ *
+ * (*
+ * * Name does not exist in data base
+ * *
+ * * This code applies to both non-secure
+ * * and secure server.
+ * *)
+ *
+ * update data base - add new entry;
+ * send POSITIVE NAME REGISTRATION RESPONSE;
+ * return;
+ * END
+ *
+ * NAME QUERY REQUEST:
+ * IF name exists in data base THEN
+ * BEGIN
+ * (*
+ * * build response packet and send to
+ * * requestor
+ * *)
+ * ...
+ *
+ * send POSITIVE NAME QUERY RESPONSE;
+ * return;
+ * ELSE
+ * BEGIN
+ * (*
+ * * build response packet and send to
+ * * requestor
+ * *)
+ * ...
+ *
+ * send NEGATIVE NAME QUERY RESPONSE;
+ * return;
+ * END
+ *
+ * NAME REGISTRATION REQUEST (GROUP):
+ * IF name exists in data base THEN
+ * BEGIN
+ * IF local entry is a unique name THEN
+ * BEGIN (* local is unique *)
+ *
+ * IF non-secure THEN
+ * BEGIN
+ * send END-NODE CHALLENGE NAME
+ * REGISTRATION RESPONSE;
+ * return;
+ * END
+ *
+ * REPEAT
+ * send NAME QUERY REQUEST;
+ * pause(UCAST_REQ_RETRY_TIMEOUT);
+ * UNTIL response received or
+ * retransmit count exceeded
+ * IF no response received or
+ * NEGATIVE NAME QUERY RESPONSE
+ * received THEN
+ * BEGIN
+ * update data base - remove entry;
+ * update data base - add new entry;
+ * send POSITIVE NAME REGISTRATION RESPONSE;
+ * return;
+ * END
+ * ELSE
+ * BEGIN
+ * (*
+ * * name still being held
+ * * by challenged node
+ * *)
+ *
+ * send NEGATIVE NAME REGISTRATION RESPONSE;
+ * END
+ * END (* local is unique *)
+ * ELSE
+ * BEGIN (* local is group *)
+ * (*
+ * * existing entry is a group name
+ * *)
+ *
+ * update data base - remove entry;
+ * update data base - add new entry;
+ * send POSITIVE NAME REGISTRATION RESPONSE;
+ * return;
+ * END (* local is group *)
+ * END (* names exists *)
+ * ELSE
+ * BEGIN (* does not exist *)
+ *
+ * (* name does not exist in data base *)
+ *
+ * update data base - add new entry;
+ * send POSITIVE NAME REGISTRATION RESPONSE;
+ * return;
+ * END (* does not exist *)
+ *
+ * NAME RELEASE REQUEST:
+ *
+ * (*
+ * * secure server may choose to disallow
+ * * a node from deleting a name
+ * *)
+ *
+ * update data base - remove entry;
+ * send POSITIVE NAME RELEASE RESPONSE;
+ * return;
+ *
+ * NAME UPDATE REQUEST:
+ *
+ * (*
+ * * End-node completed a successful challenge,
+ * * no update database
+ * *)
+ *
+ * IF secure server THEN
+ * send NEGATIVE NAME REGISTRATION RESPONSE;
+ * ELSE
+ * BEGIN (* new entry *)
+ * IF entry already exists THEN
+ * update data base - remove entry;
+ * update data base - add new entry;
+ * send POSITIVE NAME REGISTRATION RESPONSE;
+ * start_timer(TTL);
+ * END
+ *
+ * NAME REFRESH REQUEST:
+ * check for consistency;
+ *
+ * IF node not allowed to have name THEN
+ * BEGIN
+ *
+ * (*
+ * * tell end node that it can't have name
+ * *)
+ * send NEGATIVE NAME REGISTRATION RESPONSE;
+ * END
+ * ELSE
+ * BEGIN
+ *
+ * (*
+ * * send confirmation response to the
+ * * end node.
+ * *)
+ * send POSITIVE NAME REGISTRATION;
+ * start_timer(TTL);
+ * END
+ * return;
+ * END (* case *)
+ * END (* procedure *)
+ */
+static void
+smb_name_process_Pnode_packet(struct name_packet *packet,
+ struct addr_entry *addr)
+{
+ struct name_entry *name;
+ struct name_entry *entry;
+ struct name_question *question;
+ struct resource_record *additional;
+
+ question = packet->question;
+ additional = packet->additional;
+
+ if (packet->info & NAME_NM_FLAGS_B) {
+ /*
+ * always ignore UDP broadcast packets
+ */
+ return;
+ }
+
+ switch (packet->info & NAME_OPCODE_OPCODE_MASK) {
+ case NAME_OPCODE_REFRESH:
+ /* Guard against malformed packets */
+ if ((question == 0) || (additional == 0))
+ break;
+ if (additional->name->addr_list.sin.sin_addr.s_addr == 0)
+ break;
+
+ name = question->name;
+ name->addr_list.ttl = additional->ttl;
+ name->attributes = additional->name->attributes;
+ name->addr_list.sin = additional->name->addr_list.sin;
+ name->addr_list.forw = name->addr_list.back = &name->addr_list;
+
+ if ((entry = smb_netbios_cache_lookup(name)) != 0) {
+ smb_netbios_cache_update_entry(entry, name);
+ smb_netbios_cache_unlock_entry(entry);
+ }
+ else
+ (void) smb_netbios_cache_insert(name);
+
+ (void) smb_send_name_registration_response(addr, packet, 0);
+ break;
+
+ case NAME_OPCODE_QUERY:
+ /*
+ * This opcode covers both NAME_QUERY_REQUEST and
+ * NODE_STATUS_REQUEST. They can be distinguished
+ * based on the type of question entry.
+ */
+
+ /* All query requests have to have question entry */
+ if (question == 0)
+ break;
+
+ if (question->question_type == NAME_QUESTION_TYPE_NB) {
+ name = question->name;
+ if ((entry = smb_netbios_cache_lookup(name)) != 0) {
+ /*
+ * send response to the IP address and port
+ * number from which the request was received.
+ */
+ (void) smb_send_name_query_response(addr,
+ packet, entry, 0);
+ smb_netbios_cache_unlock_entry(entry);
+ } else {
+ /*
+ * send response to the requestor
+ */
+ (void) smb_send_name_query_response(addr,
+ packet, name, RCODE_NAM_ERR);
+ }
+ }
+ else
+ if (question->question_type == NAME_QUESTION_TYPE_NBSTAT) {
+ /*
+ * Name of "*" may be used to force node to
+ * divulge status for administrative purposes
+ */
+ name = question->name;
+ entry = 0;
+ if (NETBIOS_NAME_IS_STAR(name->name) ||
+ ((entry = smb_netbios_cache_lookup(name)) != 0)) {
+ /*
+ * send only those names that are
+ * in the same scope as the scope
+ * field in the request packet
+ */
+ if (entry)
+ smb_netbios_cache_unlock_entry(entry);
+ (void) smb_send_node_status_response(addr,
+ packet);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * 5.1.3.5. M-NODE INCOMING PACKET PROCESSING
+ *
+ * Processing initiated by reception of packets at a M node
+ *
+ * PROCEDURE process_incoming_packet(packet)
+ *
+ * (*
+ * * Processing initiated by incoming packets at a M node
+ * *)
+ *
+ * BEGIN
+ * CASE packet type of
+ *
+ * NAME CONFLICT DEMAND:
+ * IF name exists in local name table THEN
+ * mark name as in conflict;
+ * return;
+ *
+ * NAME QUERY REQUEST:
+ * IF name exists in local name table THEN
+ * BEGIN (* name exists *)
+ *
+ * (*
+ * * build packet
+ * *)
+ * ...
+ *
+ * (*
+ * * send response to the IP address and port
+ * * number from which the request was received.
+ * *)
+ *
+ * send POSITIVE NAME QUERY RESPONSE ;
+ * return;
+ * END (* exists *)
+ * ELSE
+ * BEGIN (* does not exist *)
+ *
+ * (*
+ * * send response to the requestor
+ * *)
+ *
+ * IF request NOT broadcast THEN
+ * (*
+ * * Don't send negative responses to
+ * * queries sent by B nodes
+ * *)
+ * send NEGATIVE NAME QUERY RESPONSE ;
+ * return;
+ * END (* does not exist *)
+ * NODE STATUS REQUEST:
+ * BEGIN
+ * (*
+ * * Name of "*" may be used to force node to
+ * * divulge status for administrative purposes
+ * *)
+ * IF name in local name table OR name = "*" THEN
+ * (*
+ * * Build response packet and
+ * * send to requestor node
+ * * Send only those names that are
+ * * in the same scope as the scope
+ * * in the request packet.
+ * *)
+ *
+ * send NODE STATUS RESPONSE;
+ * END
+ *
+ * NAME RELEASE REQUEST:
+ * (*
+ * * This will be received if the NAME wants to flush the
+ * * name from the local name table, or from the local
+ * * cache.
+ * *)
+ *
+ * IF name exists in the local name table THEN
+ * BEGIN
+ * delete name from local name table;
+ * inform user that name has been deleted;
+ * END
+ * NAME REGISTRATION REQUEST (UNIQUE):
+ * IF name exists in local name table THEN
+ * send NEGATIVE NAME REGISTRATION RESPONSE ;
+ * NAME REGISTRATION REQUEST (GROUP):
+ * IF name exists in local name table THEN
+ * BEGIN
+ * IF local entry is a unique name THEN
+ * send NEGATIVE NAME REGISTRATION RESPONSE ;
+ * END
+ * END (* case *)
+ * END (* procedure *)
+ */
+static void
+smb_name_process_Mnode_packet(struct name_packet *packet,
+ struct addr_entry *addr)
+{
+ if (packet->info & NAME_NM_FLAGS_B)
+ smb_name_process_Bnode_packet(packet, addr);
+ else
+ smb_name_process_Pnode_packet(packet, addr);
+}
+
+static void
+smb_name_process_Hnode_packet(struct name_packet *packet,
+ struct addr_entry *addr)
+{
+ if (packet->info & NAME_NM_FLAGS_B)
+ smb_name_process_Bnode_packet(packet, addr);
+ else
+ smb_name_process_Pnode_packet(packet, addr);
+}
+
+
+/*
+ * smb_netbios_name_tick
+ *
+ * Called once a second to handle name server timeouts.
+ */
+void
+smb_netbios_name_tick(void)
+{
+ struct name_entry *name;
+ struct name_entry *entry;
+
+ (void) mutex_lock(&refresh_queue.mtx);
+ smb_netbios_cache_refresh(&refresh_queue);
+
+ while ((name = refresh_queue.head.forw) != &refresh_queue.head) {
+ QUEUE_CLIP(name);
+ if (IS_LOCAL(name->attributes)) {
+ if (IS_UNIQUE(name->attributes)) {
+ (void) smb_name_Pnode_refresh_name(name);
+ }
+ } else {
+ entry = smb_name_find_name(name);
+ smb_name_unlock_name(entry);
+ }
+ free(name);
+ }
+ (void) mutex_unlock(&refresh_queue.mtx);
+
+ smb_netbios_cache_reset_ttl();
+}
+
+
+/*
+ * smb_name_find_name
+ *
+ * Lookup name cache for the given name.
+ * If it's not in the cache it'll send a
+ * name query request and then lookup the
+ * cache again. Note that if a name is
+ * returned it's locked and called MUST
+ * unlock it by calling smb_name_unlock_name()
+ */
+struct name_entry *
+smb_name_find_name(struct name_entry *name)
+{
+ struct name_entry *result;
+
+ if ((result = smb_netbios_cache_lookup(name)) == 0) {
+ switch (smb_node_type) {
+ case 'B':
+ (void) smb_name_Bnode_find_name(name);
+ break;
+ case 'P':
+ (void) smb_name_Pnode_find_name(name);
+ break;
+ case 'M':
+ (void) smb_name_Mnode_find_name(name);
+ break;
+ case 'H':
+ default:
+ (void) smb_name_Hnode_find_name(name);
+ break;
+ }
+ return (smb_netbios_cache_lookup(name));
+ }
+
+ return (result);
+}
+
+void
+smb_name_unlock_name(struct name_entry *name)
+{
+ smb_netbios_cache_unlock_entry(name);
+}
+
+int
+smb_name_add_name(struct name_entry *name)
+{
+ int rc = 1;
+
+ smb_netbios_name_dump(name);
+
+ switch (smb_node_type) {
+ case 'B':
+ rc = smb_name_Bnode_add_name(name);
+ break;
+ case 'P':
+ rc = smb_name_Pnode_add_name(name);
+ break;
+ case 'M':
+ rc = smb_name_Mnode_add_name(name);
+ break;
+ case 'H':
+ default:
+ rc = smb_name_Hnode_add_name(name);
+ break;
+ }
+
+ if (rc >= 0)
+ (void) smb_netbios_cache_insert(name);
+
+ return (rc);
+}
+
+int
+smb_name_delete_name(struct name_entry *name)
+{
+ int rc;
+ unsigned char type;
+
+ type = name->name[15];
+ if ((type != 0x00) && (type != 0x20)) {
+ syslog(LOG_ERR,
+ "netbios: error trying to delete non-local name");
+ smb_netbios_name_logf(name);
+ name->attributes &= ~NAME_ATTR_LOCAL;
+ return (-1);
+ }
+
+ smb_netbios_cache_delete(name);
+
+ switch (smb_node_type) {
+ case 'B':
+ rc = smb_name_Bnode_delete_name(name);
+ break;
+ case 'P':
+ rc = smb_name_Pnode_delete_name(name);
+ break;
+ case 'M':
+ rc = smb_name_Mnode_delete_name(name);
+ break;
+ case 'H':
+ default:
+ rc = smb_name_Hnode_delete_name(name);
+ break;
+ }
+
+ if (rc > 0)
+ return (0);
+
+ return (-1);
+}
+
+typedef struct {
+ struct addr_entry *addr;
+ char *buf;
+ int length;
+} worker_param_t;
+
+/*
+ * smb_netbios_worker
+ *
+ * Process incoming request/response packets for Netbios
+ * name service (on port 138).
+ */
+void *
+smb_netbios_worker(void *arg)
+{
+ worker_param_t *p = (worker_param_t *)arg;
+ struct addr_entry *addr = p->addr;
+ struct name_packet *packet;
+
+ if ((packet = smb_name_buf_to_packet(p->buf, p->length)) != 0) {
+ if (packet->info & NAME_OPCODE_R) {
+ /* Reply packet */
+ smb_reply_ready(packet, addr);
+ free(p->buf);
+ free(p);
+ return (0);
+ }
+
+ /* Request packet */
+ switch (smb_node_type) {
+ case 'B':
+ smb_name_process_Bnode_packet(packet, addr);
+ break;
+ case 'P':
+ smb_name_process_Pnode_packet(packet, addr);
+ break;
+ case 'M':
+ smb_name_process_Mnode_packet(packet, addr);
+ break;
+ case 'H':
+ default:
+ smb_name_process_Hnode_packet(packet, addr);
+ break;
+ }
+
+ if (packet->answer)
+ smb_netbios_name_freeaddrs(packet->answer->name);
+ free(packet);
+ }
+ else
+ {
+ syslog(LOG_DEBUG, "SmbNBNS: error decoding received packet");
+ }
+
+ free(addr);
+ free(p->buf);
+ free(p);
+ return (0);
+}
+
+static void
+smb_netbios_wins_config(char *ip)
+{
+ uint32_t ipaddr;
+
+ ipaddr = inet_addr(ip);
+ if (ipaddr != INADDR_NONE) {
+ smb_nbns[nbns_num].flags = ADDR_FLAG_VALID;
+ smb_nbns[nbns_num].sinlen = sizeof (struct sockaddr_in);
+ smb_nbns[nbns_num].sin.sin_family = AF_INET;
+ smb_nbns[nbns_num].sin.sin_addr.s_addr = ipaddr;
+ smb_nbns[nbns_num++].sin.sin_port =
+ htons(NAME_SERVICE_UDP_PORT);
+ smb_node_type = 'H';
+ }
+}
+
+void
+smb_netbios_name_config(void)
+{
+ uint32_t ipaddr;
+ struct name_entry name;
+ char myname[MAXHOSTNAMELEN];
+ int i;
+ int smb_nc_cnt;
+ net_cfg_t cfg;
+
+ if (smb_getnetbiosname(myname, MAXHOSTNAMELEN) != 0)
+ return;
+
+ /* Start with no broadcast addresses */
+ bcast_num = 0;
+ bzero(smb_bcast_list, sizeof (addr_entry_t) * SMB_PI_MAX_NETWORKS);
+
+ smb_nc_cnt = smb_nic_get_num();
+ /* Add all of my broadcast addresses */
+ for (i = 0; i < smb_nc_cnt; i++) {
+ if (smb_nic_get_byind(i, &cfg) == NULL) {
+ break;
+ }
+ smb_bcast_list[bcast_num].flags = ADDR_FLAG_VALID;
+ smb_bcast_list[bcast_num].attributes = NAME_ATTR_LOCAL;
+ smb_bcast_list[bcast_num].sinlen = sizeof (struct sockaddr_in);
+ smb_bcast_list[bcast_num].sin.sin_family = AF_INET;
+ smb_bcast_list[bcast_num].sin.sin_port =
+ htons(NAME_SERVICE_UDP_PORT);
+ smb_bcast_list[bcast_num++].sin.sin_addr.s_addr =
+ cfg.broadcast;
+ }
+
+ /* Start with no WINS */
+ smb_node_type = 'B';
+ nbns_num = 0;
+ bzero(smb_nbns, sizeof (addr_entry_t) * SMB_PI_MAX_WINS);
+
+ /* add any configured WINS */
+ smb_config_rdlock();
+ smb_netbios_wins_config(smb_config_getstr(SMB_CI_WINS_SRV1));
+ smb_netbios_wins_config(smb_config_getstr(SMB_CI_WINS_SRV2));
+ smb_config_unlock();
+
+ for (i = 0; i < smb_nc_cnt; i++) {
+ if (smb_nic_get_byind(i, &cfg) == NULL) {
+ break;
+ }
+ if (cfg.exclude)
+ continue;
+
+ ipaddr = cfg.ip;
+ smb_init_name_struct((unsigned char *)myname, 0x00, 0, ipaddr,
+ htons(DGM_SRVC_UDP_PORT), NAME_ATTR_UNIQUE,
+ NAME_ATTR_LOCAL, &name);
+ (void) smb_name_add_name(&name);
+
+ smb_init_name_struct((unsigned char *)myname, 0x20, 0,
+ ipaddr, htons(DGM_SRVC_UDP_PORT),
+ NAME_ATTR_UNIQUE, NAME_ATTR_LOCAL, &name);
+ (void) smb_name_add_name(&name);
+ }
+}
+
+void
+smb_netbios_name_unconfig(void)
+{
+ struct name_entry *name;
+
+ (void) mutex_lock(&delete_queue.mtx);
+ smb_netbios_cache_delete_locals(&delete_queue);
+
+ while ((name = delete_queue.head.forw) != &delete_queue.head) {
+ QUEUE_CLIP(name);
+ (void) smb_name_delete_name(name);
+ free(name);
+ }
+ (void) mutex_unlock(&delete_queue.mtx);
+}
+
+void
+smb_netbios_name_reconfig(void)
+{
+ smb_netbios_name_unconfig();
+ smb_netbios_name_config();
+}
+
+/*
+ * process_incoming Function: void smb_netbios_name_service_daemon(void)
+ *
+ * Description:
+ *
+ * Put test description here.
+ *
+ * Inputs:
+ * Nothing
+ *
+ * Returns:
+ * int -> Description
+ */
+/*ARGSUSED*/
+void *
+smb_netbios_name_service_daemon(void *arg)
+{
+ struct sockaddr_in sin;
+ struct addr_entry *addr;
+ int len;
+ int flag = 1;
+ char *buf;
+ worker_param_t *worker_param;
+ net_cfg_t cfg;
+
+ /*
+ * Initialize reply_queue
+ */
+ bzero(&reply_queue, sizeof (reply_queue));
+ reply_queue.forw = reply_queue.back = &reply_queue;
+
+ if (!smb_netbios_cache_init())
+ return (0);
+
+ bcast_num = 0;
+ bzero(smb_bcast_list, sizeof (addr_entry_t) * SMB_PI_MAX_NETWORKS);
+
+ if ((name_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR,
+ "smbd: Could not create AF_INET, SOCK_DGRAM, socket");
+ smb_netbios_cache_fini();
+ smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1);
+ return (0);
+ }
+
+ (void) setsockopt(name_sock, SOL_SOCKET, SO_BROADCAST, &flag,
+ sizeof (flag));
+
+ bzero(&sin, sizeof (struct sockaddr_in));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(NAME_SERVICE_UDP_PORT);
+ if (bind(name_sock, (struct sockaddr *)&sin, sizeof (sin)) != 0) {
+ syslog(LOG_ERR,
+ "smbd: Bind to name service port %d failed (%d)",
+ NAME_SERVICE_UDP_PORT, errno);
+ smb_netbios_cache_fini();
+ (void) close(name_sock);
+ smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1);
+ return (0);
+ }
+
+ smb_netbios_chg_status(NETBIOS_NAME_SVC_RUNNING, 1);
+
+ while (((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) ||
+ (nb_status.state & NETBIOS_BROWSER_RUNNING)) {
+ if ((buf = malloc(MAX_DATAGRAM_LENGTH)) == 0) {
+ /* Sleep for 10 sec and try again */
+ (void) sleep(10);
+ continue;
+ }
+ if ((addr = (struct addr_entry *)
+ malloc(sizeof (struct addr_entry))) == 0) {
+ /* Sleep for 10 sec and try again */
+ free(buf);
+ (void) sleep(10);
+ continue;
+ }
+ignore: bzero(addr, sizeof (struct addr_entry));
+ addr->sinlen = sizeof (addr->sin);
+ addr->forw = addr->back = addr;
+
+ if ((len = recvfrom(name_sock, buf, MAX_DATAGRAM_LENGTH,
+ 0, (struct sockaddr *)&addr->sin, &addr->sinlen)) < 0) {
+ if (errno == ENOMEM || errno == ENFILE ||
+ errno == EMFILE) {
+ /* Sleep for 10 sec and try again */
+ free(buf);
+ free(addr);
+ (void) sleep(10);
+ continue;
+ }
+ syslog(LOG_ERR,
+ "smbd: NETBIOS name service - recvfrom failed");
+ free(buf);
+ free(addr);
+ smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1);
+ goto shutdown;
+ }
+
+ /* Ignore any incoming packets from myself... */
+ if (smb_nic_get_byip(addr->sin.sin_addr.s_addr,
+ &cfg) != NULL) {
+ goto ignore;
+ }
+
+ /*
+ * Launch a netbios worker to process the received packet.
+ */
+ worker_param = (worker_param_t *)
+ malloc(sizeof (worker_param_t));
+ if (worker_param) {
+ pthread_t worker;
+ pthread_attr_t tattr;
+
+ worker_param->addr = addr;
+ worker_param->buf = buf;
+ worker_param->length = len;
+
+ (void) pthread_attr_init(&tattr);
+ (void) pthread_attr_setdetachstate(&tattr,
+ PTHREAD_CREATE_DETACHED);
+ (void) pthread_create(&worker, &tattr,
+ smb_netbios_worker, worker_param);
+ (void) pthread_attr_destroy(&tattr);
+ }
+ }
+
+shutdown:
+ smb_netbios_chg_status(NETBIOS_NAME_SVC_RUNNING, 0);
+
+ (void) mutex_lock(&nb_status.mtx);
+ while (nb_status.state & NETBIOS_BROWSER_RUNNING)
+ (void) cond_wait(&nb_status.cv, &nb_status.mtx);
+ (void) mutex_unlock(&nb_status.mtx);
+
+ if ((nb_status.state & NETBIOS_NAME_SVC_FAILED) == 0) {
+ /* this might delay shutdown, do we want to do this? */
+ /*
+ * it'll send name release requests but nobody's waiting
+ * for response and it'll eventually timeout.
+ */
+ smb_netbios_name_unconfig();
+ }
+ (void) close(name_sock);
+ smb_netbios_cache_fini();
+ syslog(LOG_DEBUG, "smbd: Netbios Name Service is down\n");
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netlogon.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netlogon.c
new file mode 100644
index 0000000000..d74bcb168d
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netlogon.c
@@ -0,0 +1,643 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module handles the primary domain controller location protocol.
+ * The document claims to be version 1.15 of the browsing protocol. It also
+ * claims to specify the mailslot protocol.
+ *
+ * The NETLOGON protocol uses \MAILSLOT\NET mailslots. The protocol
+ * specification is incomplete, contains errors and is out-of-date but
+ * it does provide some useful background information. The document
+ * doesn't mention the NETLOGON_SAMLOGON version of the protocol.
+ */
+
+#include <stdlib.h>
+#include <syslog.h>
+#include <alloca.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+
+#include <smbsrv/mailslot.h>
+#include <smbsrv/libsmbns.h>
+#include <smbns_ads.h>
+#include <smbns_browser.h>
+#include <smbns_netbios.h>
+
+static void smb_netlogon_query(struct name_entry *server, char *mailbox,
+ char *domain);
+
+static void smb_netlogon_samlogon(struct name_entry *server, char *mailbox,
+ char *domain);
+
+static void smb_netlogon_send(struct name_entry *name, char *domain,
+ unsigned char *buffer, int count);
+
+static void smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr);
+static int better_dc(uint32_t cur_ip, uint32_t new_ip);
+
+static char resource_domain[SMB_PI_MAX_DOMAIN];
+
+/*
+ * smb_netlogon_request
+ *
+ * This is the entry point locating the resource domain PDC. A netlogon
+ * request is sent using the specified protocol on the specified network.
+ * Note that we need to know the domain SID in order to use the samlogon
+ * format.
+ *
+ * Netlogon responses are received asynchronously and eventually handled
+ * in smb_netlogon_receive.
+ */
+void
+smb_netlogon_request(int net, int protocol, char *domain)
+{
+ struct name_entry *server;
+ nt_domain_t *ntdp;
+
+ server = smb_browser_get_srvname(net);
+ if (server == 0)
+ return;
+
+ (void) strlcpy(resource_domain, domain,
+ sizeof (resource_domain));
+
+ if (strlen(resource_domain) > 0) {
+ ntdp = nt_domain_lookup_name(resource_domain);
+ if (protocol == NETLOGON_PROTO_SAMLOGON && ntdp)
+ smb_netlogon_samlogon(server,
+ MAILSLOT_NETLOGON_SAMLOGON_RDC,
+ resource_domain);
+ else
+ smb_netlogon_query(server,
+ MAILSLOT_NETLOGON_RDC,
+ resource_domain);
+ }
+}
+
+/*
+ * smb_netlogon_receive
+ *
+ * This is where we handle all incoming NetLogon messages. Currently, we
+ * ignore requests from anyone else. We are only interested in responses
+ * to our own requests. The NetLogonResponse provides the name of the PDC.
+ * If we don't already have a controller name, we use the name provided
+ * in the message. Otherwise we use the name already in the environment.
+ */
+void
+smb_netlogon_receive(struct datagram *datagram,
+ char *mailbox,
+ unsigned char *data,
+ int datalen)
+{
+ struct netlogon_opt {
+ char *mailslot;
+ void (*handler)();
+ } netlogon_opt[] = {
+ { MAILSLOT_NETLOGON_RDC, smb_netlogon_rdc_rsp },
+ { MAILSLOT_NETLOGON_SAMLOGON_RDC, smb_netlogon_rdc_rsp },
+ };
+
+ smb_msgbuf_t mb;
+ unsigned short opcode;
+ char src_name[SMB_PI_MAX_HOST];
+ mts_wchar_t unicode_src_name[SMB_PI_MAX_HOST];
+ unsigned int cpid = oem_get_smb_cpid();
+ uint32_t src_ipaddr;
+ char *junk;
+ char *primary;
+ char *domain;
+ int i;
+ char ipstr[16];
+ int rc;
+
+ src_ipaddr = datagram->src.addr_list.sin.sin_addr.s_addr;
+
+ /*
+ * The datagram->src.name is in oem codepage format.
+ * Therefore, we need to convert it to unicode and
+ * store it in multi-bytes format.
+ */
+ (void) oemstounicodes(unicode_src_name, (char *)datagram->src.name,
+ SMB_PI_MAX_HOST, cpid);
+ (void) mts_wcstombs(src_name, unicode_src_name, SMB_PI_MAX_HOST);
+
+ (void) trim_whitespace(src_name);
+
+ (void) inet_ntop(AF_INET, (const void *)(&src_ipaddr), ipstr,
+ sizeof (ipstr));
+ syslog(LOG_DEBUG, "NetLogonReceive: src=%s [%s], mbx=%s",
+ src_name, ipstr, mailbox);
+
+ smb_msgbuf_init(&mb, data, datalen, 0);
+
+ if (smb_msgbuf_decode(&mb, "w", &opcode) < 0) {
+ syslog(LOG_ERR, "NetLogonReceive: decode error");
+ smb_msgbuf_term(&mb);
+ return;
+ }
+
+ switch (opcode) {
+ case LOGON_PRIMARY_RESPONSE:
+ /*
+ * Message contains:
+ * PDC name (MBS), PDC name (Unicode), Domain name (unicode)
+ */
+ rc = smb_msgbuf_decode(&mb, "sUU", &junk, &primary, &domain);
+ if (rc < 0) {
+ syslog(LOG_ERR,
+ "NetLogonResponse: opcode %d decode error",
+ opcode);
+ smb_msgbuf_term(&mb);
+ return;
+ }
+ break;
+
+ case LOGON_SAM_LOGON_RESPONSE:
+ case LOGON_SAM_USER_UNKNOWN:
+ /*
+ * Message contains:
+ * PDC name, User name, Domain name (all unicode)
+ */
+ rc = smb_msgbuf_decode(&mb, "UUU", &primary, &junk, &domain);
+ if (rc < 0) {
+ syslog(LOG_ERR,
+ "NetLogonResponse: opcode %d decode error",
+ opcode);
+ smb_msgbuf_term(&mb);
+ return;
+ }
+
+ /*
+ * skip past the "\\" prefix
+ */
+ primary += strspn(primary, "\\");
+ break;
+
+ default:
+ /*
+ * We don't respond to PDC discovery requests.
+ */
+ syslog(LOG_DEBUG, "NetLogonReceive: opcode 0x%04x", opcode);
+ smb_msgbuf_term(&mb);
+ return;
+ }
+
+ if (domain == 0 || primary == 0) {
+ syslog(LOG_ERR, "NetLogonResponse: malformed packet");
+ smb_msgbuf_term(&mb);
+ return;
+ }
+
+ syslog(LOG_DEBUG, "DC Offer Dom=%s PDC=%s From=%s",
+ domain, primary, src_name);
+
+ if (strcasecmp(domain, resource_domain)) {
+ syslog(LOG_DEBUG, "NetLogonResponse: other domain "
+ "%s, requested %s", domain, resource_domain);
+ smb_msgbuf_term(&mb);
+ return;
+ }
+
+ for (i = 0; i < sizeof (netlogon_opt)/sizeof (netlogon_opt[0]); ++i) {
+ if (strcasecmp(netlogon_opt[i].mailslot, mailbox) == 0) {
+ syslog(LOG_DEBUG, "NetLogonReceive: %s", mailbox);
+ (*netlogon_opt[i].handler)(primary, src_ipaddr);
+ smb_msgbuf_term(&mb);
+ return;
+ }
+ }
+
+ syslog(LOG_DEBUG, "NetLogonReceive[%s]: unknown mailslot", mailbox);
+ smb_msgbuf_term(&mb);
+}
+
+
+
+/*
+ * smb_netlogon_query
+ *
+ * Build and send a LOGON_PRIMARY_QUERY to the MAILSLOT_NETLOGON. At some
+ * point we should receive a LOGON_PRIMARY_RESPONSE in the mailslot we
+ * specify in the request.
+ *
+ * struct NETLOGON_QUERY {
+ * unsigned short Opcode; # LOGON_PRIMARY_QUERY
+ * char ComputerName[]; # ASCII hostname. The response
+ * # is sent to <ComputerName>(00).
+ * char MailslotName[]; # MAILSLOT_NETLOGON
+ * char Pad[]; # Pad to short
+ * wchar_t ComputerName[] # UNICODE hostname
+ * DWORD NT_Version; # 0x00000001
+ * WORD LmNTToken; # 0xffff
+ * WORD Lm20Token; # 0xffff
+ * };
+ */
+static void
+smb_netlogon_query(struct name_entry *server,
+ char *mailbox,
+ char *domain)
+{
+ smb_msgbuf_t mb;
+ int offset, announce_len, data_length, name_lengths;
+ unsigned char buffer[MAX_DATAGRAM_LENGTH];
+ char hostname[MAXHOSTNAMELEN];
+
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0)
+ return;
+
+ name_lengths = strlen(mailbox)+1+strlen(hostname)+1;
+
+ /*
+ * The (name_lengths & 1) part is to word align the name_lengths
+ * before the wc equiv strlen and the "+ 2" is to cover the two
+ * zero bytes that terminate the wchar string.
+ */
+ data_length = sizeof (short) + name_lengths + (name_lengths & 1) +
+ mts_wcequiv_strlen(hostname) + 2 + sizeof (long) + sizeof (short) +
+ sizeof (short);
+
+ offset = smb_browser_load_transact_header(buffer,
+ sizeof (buffer), data_length, ONE_WAY_TRANSACTION,
+ MAILSLOT_NETLOGON);
+
+ if (offset < 0)
+ return;
+
+ smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0);
+
+ announce_len = smb_msgbuf_encode(&mb, "wssUlww",
+ (short)LOGON_PRIMARY_QUERY,
+ hostname,
+ mailbox,
+ hostname,
+ 0x1,
+ 0xffff,
+ 0xffff);
+
+ if (announce_len <= 0) {
+ smb_msgbuf_term(&mb);
+ syslog(LOG_ERR, "NetLogonQuery: encode error");
+ return;
+ }
+
+ smb_netlogon_send(server, domain, buffer, offset + announce_len);
+ smb_msgbuf_term(&mb);
+}
+
+
+/*
+ * smb_netlogon_samlogon
+ *
+ * The SamLogon version of the NetLogon request uses the workstation trust
+ * account and, I think, may be a prerequisite to the challenge/response
+ * netr authentication. The trust account username is the hostname with a
+ * $ appended. The mailslot for this request is MAILSLOT_NTLOGON. At some
+ * we should receive a LOGON_SAM_LOGON_RESPONSE in the mailslot we
+ * specify in the request.
+ *
+ * struct NETLOGON_SAM_LOGON {
+ * unsigned short Opcode; # LOGON_SAM_LOGON_REQUEST
+ * unsigned short RequestCount; # 0
+ * wchar_t UnicodeComputerName; # hostname
+ * wchar_t UnicodeUserName; # hostname$
+ * char *MailslotName; # response mailslot
+ * DWORD AllowableAccountControlBits; # 0x80 = WorkstationTrustAccount
+ * DWORD DomainSidSize; # domain sid length in bytes
+ * BYTE *DomainSid; # domain sid
+ * uint32_t NT_Version; # 0x00000001
+ * unsigned short LmNTToken; # 0xffff
+ * unsigned short Lm20Token; # 0xffff
+ * };
+ */
+static void
+smb_netlogon_samlogon(struct name_entry *server,
+ char *mailbox,
+ char *domain)
+{
+ smb_msgbuf_t mb;
+ nt_domain_t *ntdp;
+ nt_sid_t *domain_sid;
+ unsigned domain_sid_len;
+ char *username;
+ unsigned char buffer[MAX_DATAGRAM_LENGTH];
+ int offset;
+ int announce_len;
+ int data_length;
+ int name_length;
+ char hostname[MAXHOSTNAMELEN];
+
+ syslog(LOG_DEBUG, "NetLogonSamLogonReq: %s", domain);
+
+ if ((ntdp = nt_domain_lookup_name(domain)) == 0) {
+ syslog(LOG_ERR, "NetLogonSamLogonReq[%s]: no sid", domain);
+ return;
+ }
+
+ domain_sid = ntdp->sid;
+ domain_sid_len = nt_sid_length(domain_sid);
+ nt_sid_logf(domain_sid);
+
+ if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0)
+ return;
+
+ /*
+ * The username will be the trust account name on the PDC.
+ */
+ name_length = strlen(hostname) + 2;
+ username = alloca(name_length);
+ (void) snprintf(username, name_length, "%s$", hostname);
+
+ /*
+ * Add 2 to wide-char equivalent strlen to cover the
+ * two zero bytes that terminate the wchar string.
+ */
+ name_length = strlen(mailbox)+1;
+
+ data_length = sizeof (short)
+ + sizeof (short)
+ + mts_wcequiv_strlen(hostname) + 2
+ + mts_wcequiv_strlen(username) + 2
+ + name_length
+ + sizeof (long)
+ + sizeof (long)
+ + domain_sid_len + 3 /* padding */
+ + sizeof (long)
+ + sizeof (short)
+ + sizeof (short);
+
+ offset = smb_browser_load_transact_header(buffer,
+ sizeof (buffer), data_length, ONE_WAY_TRANSACTION,
+ MAILSLOT_NTLOGON);
+
+ if (offset < 0) {
+ syslog(LOG_ERR, "NetLogonSamLogonReq: header error");
+ return;
+ }
+
+ /*
+ * The domain SID is padded with 3 leading zeros.
+ */
+ smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0);
+ announce_len = smb_msgbuf_encode(&mb, "wwUUsll3.#clww",
+ (short)LOGON_SAM_LOGON_REQUEST,
+ 0, /* RequestCount */
+ hostname, /* UnicodeComputerName */
+ username, /* UnicodeUserName */
+ mailbox, /* MailslotName */
+ 0x00000080, /* AllowableAccountControlBits */
+ domain_sid_len, /* DomainSidSize */
+ domain_sid_len, domain_sid, /* DomainSid */
+ 0x00000001, /* NT_Version */
+ 0xffff, /* LmNTToken */
+ 0xffff); /* Lm20Token */
+
+ if (announce_len <= 0) {
+ syslog(LOG_ERR, "NetLogonSamLogonReq: encode error");
+ smb_msgbuf_term(&mb);
+ return;
+ }
+
+ smb_netlogon_send(server, domain, buffer, offset + announce_len);
+ smb_msgbuf_term(&mb);
+}
+
+
+/*
+ * Send a query for each version of the protocol.
+ */
+static void
+smb_netlogon_send(struct name_entry *name,
+ char *domain,
+ unsigned char *buffer,
+ int count)
+{
+ static char suffix[] = { 0x1B, 0x1C };
+ struct name_entry dname;
+ struct name_entry *dest;
+ struct name_entry *dest_dup;
+ int i;
+
+ for (i = 0; i < sizeof (suffix)/sizeof (suffix[0]); i++) {
+ smb_init_name_struct((unsigned char *)domain, suffix[i],
+ 0, 0, 0, 0, 0, &dname);
+
+ syslog(LOG_DEBUG, "smb_netlogon_send");
+ smb_netbios_name_dump(&dname);
+ if ((dest = smb_name_find_name(&dname)) != 0) {
+ dest_dup = smb_netbios_name_dup(dest, 1);
+ smb_name_unlock_name(dest);
+ if (dest_dup) {
+ (void) smb_netbios_datagram_send(name, dest_dup,
+ buffer, count);
+ free(dest_dup);
+ }
+ } else {
+ syslog(LOG_DEBUG, "smbd: NBNS couldn't find %s<0x%X>",
+ domain, suffix[i]);
+ }
+ }
+}
+
+/*
+ * smb_netlogon_rdc_rsp
+ *
+ * This is where we process netlogon responses for the resource domain.
+ * The src_name is the real name of the remote machine.
+ */
+static void
+smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr)
+{
+ static int initialized = 0;
+ smb_ntdomain_t *pi;
+ uint32_t ipaddr;
+ uint32_t prefer_ipaddr = 0;
+ char ipstr[16];
+ char srcip[16];
+ char *p;
+ int rc;
+
+ (void) inet_ntop(AF_INET, (const void *)(&src_ipaddr),
+ srcip, sizeof (srcip));
+
+ smb_config_rdlock();
+ if ((p = smb_config_get(SMB_CI_DOMAIN_SRV)) != 0) {
+ rc = inet_pton(AF_INET, p, &prefer_ipaddr);
+ if (rc == 0)
+ prefer_ipaddr = 0;
+
+ if (!initialized) {
+ (void) inet_ntop(AF_INET,
+ (const void *)(&prefer_ipaddr),
+ ipstr, sizeof (ipstr));
+ syslog(LOG_DEBUG, "SMB DC Preference: %s", ipstr);
+ initialized = 1;
+ }
+ }
+ smb_config_unlock();
+
+ syslog(LOG_DEBUG, "DC Offer [%s]: %s [%s]",
+ resource_domain, src_name, srcip);
+
+ if ((pi = smb_getdomaininfo(0)) != 0) {
+ if (prefer_ipaddr != 0 && prefer_ipaddr == pi->ipaddr) {
+ syslog(LOG_DEBUG, "DC for %s: %s [%s]",
+ resource_domain, src_name, srcip);
+ return;
+ }
+
+ ipaddr = pi->ipaddr;
+ } else
+ ipaddr = 0;
+
+ if (better_dc(ipaddr, src_ipaddr) ||
+ (prefer_ipaddr != 0 && prefer_ipaddr == src_ipaddr)) {
+ smb_setdomaininfo(resource_domain, src_name,
+ src_ipaddr);
+ syslog(LOG_DEBUG, "DC discovered for %s: %s [%s]",
+ resource_domain, src_name, srcip);
+ }
+}
+
+static int
+better_dc(uint32_t cur_ip, uint32_t new_ip)
+{
+ net_cfg_t cfg;
+
+ /*
+ * If we don't have any current DC,
+ * then use the new one of course.
+ */
+ if (cur_ip == 0)
+ return (1);
+
+ if (smb_nic_get_bysubnet(cur_ip, &cfg) != NULL)
+ return (0);
+ if (smb_nic_get_bysubnet(new_ip, &cfg) != NULL)
+ return (1);
+ /*
+ * Otherwise, just keep the old one.
+ */
+ return (0);
+}
+
+/*
+ * msdcs_lookup_ads
+ *
+ * Try to find a domain controller in ADS. Actually we want to query DNS
+ * but we need to find out if ADS is enabled and this is probably the
+ * best way. The IP address isn't set up in the ADS_HANDLE so we need to
+ * make the ads_find_host call. This will only succeed if ADS is enabled.
+ *
+ * Returns 1 if a domain controller was found and its name and IP address
+ * have been updated. Otherwise returns 0.
+ */
+int
+msdcs_lookup_ads(void)
+{
+ ADS_HOST_INFO *hinfo = 0;
+ int ads_port = 0;
+ char ads_domain[MAXHOSTNAMELEN];
+ char site_service[MAXHOSTNAMELEN];
+ char service[MAXHOSTNAMELEN];
+ char *site;
+ char *p;
+ char *ip_addr;
+ struct in_addr ns_list[MAXNS];
+ int i, cnt, go_next;
+
+ if (smb_getdomainname(ads_domain, MAXHOSTNAMELEN) != 0)
+ return (0);
+
+ /*
+ * Initialize the NT domain name.
+ */
+ (void) strlcpy(resource_domain, ads_domain, SMB_PI_MAX_DOMAIN);
+ if ((p = strchr(resource_domain, '.')) != 0)
+ *p = '\0';
+
+ smb_config_rdlock();
+ if (smb_config_getyorn(SMB_CI_MSDCS_DISABLE) != 0) {
+ /*
+ * The system administrator doesn't
+ * want to use ADS to find the PDC.
+ */
+ syslog(LOG_DEBUG, "msdcsLookupADS: disabled");
+ smb_config_unlock();
+ return (0);
+ }
+ site = smb_config_getstr(SMB_CI_ADS_SITE);
+ smb_config_unlock();
+
+ syslog(LOG_DEBUG, "msdcsLookupADS %s, MAXHOSTNAMELEN=%d",
+ ads_domain, MAXHOSTNAMELEN);
+ if (site && *site != 0) {
+ (void) snprintf(site_service, MAXHOSTNAMELEN,
+ "_ldap._tcp.%s._sites.dc._msdcs.%s",
+ site, ads_domain);
+ }
+
+ (void) snprintf(service, MAXHOSTNAMELEN,
+ "_ldap._tcp.dc._msdcs.%s", ads_domain);
+
+ cnt = smb_get_nameservers(ns_list, MAXNS);
+
+ go_next = 0;
+ for (i = 0; i < cnt; i++) {
+ ip_addr = inet_ntoa(ns_list[i]);
+
+ hinfo = ads_find_host(ip_addr, ads_domain, &ads_port,
+ site_service, &go_next);
+
+ if (hinfo == NULL) {
+ hinfo = ads_find_host(ip_addr, ads_domain, &ads_port,
+ service, &go_next);
+ }
+
+ if ((hinfo != NULL) || (go_next == 0))
+ break;
+ }
+
+ if (hinfo == NULL) {
+ syslog(LOG_DEBUG, "msdcsLookupADS: unable to find host");
+ return (0);
+ }
+
+ syslog(LOG_DEBUG, "msdcsLookupADS: %s [%I]", hinfo->name,
+ hinfo->ip_addr);
+
+ /*
+ * Remove the domain extension - the
+ * NetBIOS browser can't handle it.
+ */
+ if ((p = strchr(hinfo->name, '.')) != 0)
+ *p = '\0';
+
+ smb_netlogon_rdc_rsp(hinfo->name, hinfo->ip_addr);
+
+ return (1);
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_nicconfig.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_nicconfig.c
new file mode 100644
index 0000000000..0aaf8338b4
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_nicconfig.c
@@ -0,0 +1,1163 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <libintl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <synch.h>
+#include <stropts.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <inet/ip.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/systeminfo.h>
+
+#include <smbsrv/libsmbns.h>
+
+static int smb_nic_get_list(struct if_list **);
+static void smb_nic_clear_if_list(struct if_list *);
+
+/* This is the list we will monitor */
+static net_cfg_list_t smb_nic_list = { NULL, 0 };
+static pthread_mutex_t smb_nic_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t smb_ns_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Nameserver information */
+static struct __res_state smb_res;
+
+void
+smb_resolver_init(void)
+{
+ int ret;
+ (void) pthread_mutex_lock(&smb_ns_mutex);
+ ret = res_ninit(&smb_res);
+ (void) pthread_mutex_unlock(&smb_ns_mutex);
+ if (ret < 0) {
+ syslog(LOG_ERR, "Failed to initialize resolver lib");
+ }
+}
+
+void
+smb_resolver_close(void)
+{
+ (void) pthread_mutex_lock(&smb_ns_mutex);
+ res_nclose(&smb_res);
+ (void) pthread_mutex_unlock(&smb_ns_mutex);
+}
+
+int
+smb_get_nameservers(struct in_addr *ips, int sz)
+{
+ union res_sockaddr_union set[MAXNS];
+ int i, cnt;
+
+ if (ips == NULL)
+ return (0);
+ (void) pthread_mutex_lock(&smb_ns_mutex);
+ cnt = res_getservers(&smb_res, set, MAXNS);
+ for (i = 0; i < cnt; i++) {
+ if (i >= sz)
+ break;
+ ips[i] = set[i].sin.sin_addr;
+ syslog(LOG_DEBUG, "NS Found %s name server\n",
+ inet_ntoa(ips[i]));
+ }
+ syslog(LOG_DEBUG, "NS Found %d name servers\n", i);
+ (void) pthread_mutex_unlock(&smb_ns_mutex);
+ return (i);
+}
+
+uint16_t
+smb_get_next_resid(void)
+{
+ uint16_t id;
+ (void) pthread_mutex_lock(&smb_ns_mutex);
+ id = ++smb_res.id;
+ (void) pthread_mutex_unlock(&smb_ns_mutex);
+ return (id);
+}
+
+/*
+ * The common NIC library will provide functions to obtain information
+ * on all interfaces. Information will include IP addresses, netmasks
+ * and broadcast address, as well as network statistic details.
+ */
+
+/*
+ * Return IP string address associated with interface argument.
+ * If an error occurs, -1 will be returned.
+ * A return value of 1 indicates an unconfigured IP address
+ */
+static int
+smb_nic_get_ip_addr(char *interface, char *IP, unsigned int IP_length)
+{
+ struct lifreq lifrr;
+ struct sockaddr_in *sa;
+ int sfd;
+
+ sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_IP:socket open failed\n");
+ return (-1);
+ }
+
+ (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
+
+ if (ioctl(sfd, SIOCGLIFADDR, &lifrr) < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_IP: get IP address failed\n");
+ (void) close(sfd);
+ return (-1);
+ }
+ /* Test length of allocated memory to avoid buffer overflow */
+ if (IP_length < SIZE_IP) {
+ syslog(LOG_ERR, "%s", "nic_get_IP: insufficient memory"
+ "allocation\n");
+ (void) close(sfd);
+ return (-1);
+ }
+ sa = (struct sockaddr_in *) &lifrr.lifr_addr;
+ (void) strncpy(IP, inet_ntoa(sa->sin_addr), SIZE_IP);
+ /* Check for unconfigured interface */
+ if (strncmp(IP, "0.0.0.0", sizeof (IP)) == 0) {
+ syslog(LOG_ERR, "%s", "nic_get_IP: unconfigured interface\n");
+ (void) close(sfd);
+ return (1);
+ }
+ (void) close(sfd);
+ return (0);
+}
+
+/*
+ * Return IP address associated with interface argument. If an error occurs,
+ * -1 will be returned. A return value of 1 indicates an unconfigured IP
+ * address
+ */
+int
+smb_nic_get_IP(char *interface, uint32_t *uip)
+{
+ struct lifreq lifrr;
+ struct sockaddr_in *sa;
+ int sfd;
+
+ sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_IP:socket open failed\n");
+ return (-1);
+ }
+
+ (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
+
+ if (ioctl(sfd, SIOCGLIFADDR, &lifrr) < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_IP: get IP address failed\n");
+ (void) close(sfd);
+ return (-1);
+ }
+ sa = (struct sockaddr_in *) &lifrr.lifr_addr;
+ if (uip != NULL)
+ *uip = (uint32_t)sa->sin_addr.s_addr;
+ (void) close(sfd);
+ return (0);
+}
+
+/*
+ * Return broadcast address associated with interface argument.If an error
+ * occurs, -1 will be returned. A return value of 1 indicates an unconfigured
+ * broadcast address
+ */
+int
+smb_nic_get_broadcast(char *interface, uint32_t *uip)
+{
+ struct lifreq lifrr;
+ struct sockaddr_in *sa;
+ int sfd;
+
+ sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_broadcast:"
+ "socket open failed\n");
+ return (-1);
+ }
+
+ (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
+
+ if (ioctl(sfd, SIOCGLIFBRDADDR, &lifrr) < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_broadcast:"
+ "get broadcast address failed\n");
+ (void) close(sfd);
+ return (-1);
+ }
+ sa = (struct sockaddr_in *)&lifrr.lifr_broadaddr;
+ if (uip != NULL)
+ *uip = (uint32_t)sa->sin_addr.s_addr;
+ (void) close(sfd);
+ return (0);
+
+}
+
+/*
+ * Return netmask address associated with interface argument. If error occurs,
+ * -1 will be returned. A return value of 1 indicates an unconfigured netmask
+ * address
+ */
+int
+smb_nic_get_netmask(char *interface, uint32_t *uip)
+{
+ struct lifreq lifrr;
+ struct sockaddr_in *sa;
+ int sfd;
+
+ sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_netmask:"
+ "socket open failed\n");
+ return (-1);
+ }
+
+ (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
+
+ if (ioctl(sfd, SIOCGLIFNETMASK, &lifrr) < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_netmask:"
+ "get netmask address failed\n");
+ (void) close(sfd);
+ return (-1);
+ }
+ sa = (struct sockaddr_in *)&lifrr.lifr_addr;
+ if (uip != NULL)
+ *uip = (uint32_t)sa->sin_addr.s_addr;
+ (void) close(sfd);
+ return (0);
+
+}
+
+/*
+ * Fill ip_alias with IP addresses if any
+ * If it returns 0, there are no associated aliases with the interface.
+ * If it returns -1, there was an error
+ * If it returns 1, there are associated IP aliases with the interface.
+ */
+int
+smb_nic_get_IP_aliases(char *interface, struct ip_alias **list)
+{
+ char ** names = NULL;
+ int result = 0;
+ int numnics, i, ret = 0;
+ char IP[SIZE_IP];
+ struct ip_alias *tmp;
+
+ *list = NULL;
+
+ /* If the interface is a logical interface, return immediately */
+ if (strchr(interface, ':') != NULL) {
+ syslog(LOG_ERR, "%s", "nic_get_IP_aliases:"
+ "invalid physical interface");
+ return (ret);
+ }
+
+ numnics = smb_nic_build_if_name(&names);
+
+ for (i = 0; i < numnics; i++) {
+ /*
+ * Compare passed interface name to all other interface names.
+ * If it matches in the form of :1, it is an associated alias
+ * Example bge1:1's ip address is an ip alias of bge1
+ */
+ if (strncasecmp(interface, names[i], strlen(names[0])) == 0 &&
+ strchr(names[i], ':') != 0) {
+
+ result = smb_nic_get_ip_addr(names[i],
+ IP, sizeof (IP));
+ if (result == -1)
+ return (result);
+
+ tmp = (struct ip_alias *)malloc(
+ sizeof (struct ip_alias));
+ if (tmp == NULL) {
+ syslog(LOG_ERR, "%s", "nic_get"
+ "_IP_aliases: out of memory");
+ (void) smb_nic_clear_name_list(names,
+ numnics);
+ return (-1);
+ }
+
+ (void) strncpy(tmp->name, IP, sizeof (tmp->name));
+ tmp->next = *list;
+ *list = tmp;
+ ret = 1;
+ }
+ }
+ (void) smb_nic_clear_name_list(names, numnics);
+ return (ret);
+}
+
+/*
+ * Return number of plumbed interfaces. Loopback interface is ignored
+ */
+int
+smb_nic_get_number(void)
+{
+ struct lifnum lifn;
+ int numifs = 0, sfd;
+
+ sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_number:"
+ "socket open failed");
+ return (-1);
+ }
+
+ lifn.lifn_family = AF_INET;
+ lifn.lifn_flags = 0;
+
+ if (ioctl(sfd, SIOCGLIFNUM, &lifn) < 0) {
+ syslog(LOG_ERR, "%s", "nic_get_number:"
+ "unable to determine number");
+ (void) close(sfd);
+ return (-1);
+ }
+
+ numifs = lifn.lifn_count - 1; /* loopback */
+ (void) close(sfd);
+ return (numifs);
+}
+
+/*
+ * Given an interface name, return the name of the group it belongs to.
+ */
+int
+smb_nic_get_group(char *lifname, char *grname)
+{
+ struct lifreq lifr;
+ int sfd;
+ int save_errno;
+
+ sfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s", "nic_set_group:socket open failed");
+ return (-1);
+ }
+ if (strchr(lifname, ':') == NULL) {
+ (void) memset(lifr.lifr_groupname, 0,
+ sizeof (lifr.lifr_groupname));
+ (void) strncpy(lifr.lifr_name, lifname,
+ sizeof (lifr.lifr_name));
+ if (ioctl(sfd, SIOCGLIFGROUPNAME, (caddr_t)&lifr) >= 0) {
+ if (strlen(lifr.lifr_groupname) > 0) {
+ (void) strncpy(grname, lifr.lifr_groupname,
+ sizeof (lifr.lifr_groupname));
+ }
+ } else {
+ save_errno = errno;
+ syslog(LOG_ERR, "smb_nic_get_group: ioctl failed");
+ (void) close(sfd);
+ errno = save_errno;
+ return (-1);
+ }
+ }
+ (void) close(sfd);
+ return (0);
+}
+
+/*
+ * Read the /etc/defaultrouter file for the gateway address. If an error occurs,
+ * -1 will be returned.
+ */
+int
+smb_nic_get_default_gateway(char *gw, unsigned int gw_length)
+{
+ FILE *fp;
+
+ fp = fopen(GATEWAY_FILE, "r");
+
+ if (fp == NULL) {
+ (void) fclose(fp);
+ return (-1);
+ } else {
+ /* Test length of allocated memory to avoid buffer overflow */
+ if (gw_length < SIZE_IP) {
+ syslog(LOG_ERR, "%s", "get_default_gateway: "
+ "insufficient memory allocation\n");
+ (void) fclose(fp);
+ return (-1);
+ }
+ (void) fgets(gw, SIZE_IP, fp);
+ (void) fclose(fp);
+ }
+
+ return (0);
+}
+
+/*
+ * Build the list of interface names, both physical and logical.
+ * A pointer to a pointer to a char will be filled with the info
+ */
+int
+smb_nic_build_if_name(char ***if_names)
+{
+ struct if_list *iflist;
+ struct if_list *iflistptr;
+ int num_ifs, i;
+
+ /* Get the interfaces */
+ num_ifs = smb_nic_get_list(&iflist);
+
+ /* Build the list of names */
+ *if_names = (char **)malloc(sizeof (char *) * num_ifs);
+
+ if (if_names == NULL) {
+ syslog(LOG_ERR, "%s", "Unable to build interface names");
+ return (-1);
+ }
+
+ for (i = 0, iflistptr = iflist; i < num_ifs;
+ iflistptr = iflistptr->next, i++) {
+ (*if_names)[i] = (char *)strdup(iflistptr->name);
+ }
+ (void) smb_nic_clear_if_list(iflist);
+ return (num_ifs);
+}
+
+/*
+ * Get number of physical interfaces
+ */
+int
+smb_nic_get_num_physical(void)
+{
+ char **names = NULL;
+ int phys_ifs = 0;
+ int i, result = 0;
+ /* Get list of interface names */
+ result = smb_nic_build_if_name(&names);
+ if (result == -1) {
+ syslog(LOG_ERR, "%s", "Unable to determine num interfaces");
+ return (-1);
+ }
+ for (i = 0; i < result; i++) {
+ if (strchr(names[i], ':') == NULL) {
+ /* It's a physical interface */
+ phys_ifs++;
+ }
+ }
+ (void) smb_nic_clear_name_list(names, result);
+ return (phys_ifs);
+}
+
+/*
+ * Get number of logical interfaces
+ */
+int
+smb_nic_get_num_logical(void)
+{
+ char **names = NULL;
+ int log_ifs = 0;
+ int i, result = 0;
+ /* Get list of interface names */
+ result = smb_nic_build_if_name(&names);
+ if (result == -1) {
+ syslog(LOG_ERR, "%s", "Unable to determine num interfaces");
+ return (-1);
+ }
+ for (i = 0; i < result; i++) {
+ if (strchr(names[i], ':') != NULL) {
+ /* It's a logical interface */
+ log_ifs++;
+ }
+ }
+ (void) smb_nic_clear_name_list(names, result);
+ return (log_ifs);
+}
+
+/*
+ * Get number of aliases associated with an interface
+ */
+int
+smb_nic_get_num_aliases(char *interface)
+{
+ char **names = NULL;
+ int aliases = 0;
+ int i, result = 0;
+
+ if (interface == NULL) {
+ syslog(LOG_ERR, "%s", "Interface name not supplied");
+ return (-1);
+ }
+ /* Get list of interface names */
+ result = smb_nic_build_if_name(&names);
+ if (result == -1) {
+ syslog(LOG_ERR, "%s", "Unable to determine num interfaces");
+ return (-1);
+ }
+ for (i = 0; i < result; i++) {
+ if (strncasecmp(interface, names[i], strlen(names[0])) == 0 &&
+ strchr(names[i], ':') != 0) {
+ /* It's an alias */
+ aliases++;
+ }
+ }
+ (void) smb_nic_clear_name_list(names, result);
+ if (aliases == 0)
+ return (1); /* Minimum of 1 for NULL assignment */
+ else
+ return (aliases);
+}
+
+/*
+ * Get the list of currently plumbed interface names. The loopback(lo0)
+ * port is ignored
+ */
+static int
+smb_nic_get_list(struct if_list **list)
+{
+ int cnt = 0;
+ struct if_list *tmp, *p;
+ int n, s;
+ char *buf;
+ struct ifconf ifc;
+ register struct ifreq *ifrp = NULL;
+ struct ifreq ifr;
+ int numifs = 0;
+ unsigned int bufsize = 0;
+
+ *list = NULL;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ syslog(LOG_ERR, "%s", "get_net_list: socket");
+ return (-1);
+ }
+
+ if (ioctl(s, SIOCGIFNUM, (char *)&numifs) < 0) {
+ syslog(LOG_ERR, "%s", "get number of interfaces");
+ return (-1);
+ }
+
+ bufsize = numifs * sizeof (struct ifreq);
+ buf = (char *)malloc(bufsize);
+ if (buf == NULL) {
+ syslog(LOG_ERR, "%s", "out of memory\n");
+ (void) close(s);
+ return (-1);
+ }
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buf;
+
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ syslog(LOG_ERR, "%s", "Unable to get interface list\n");
+ (void) close(s);
+ (void) free(buf);
+ return (-1);
+ }
+
+ ifrp = ifc.ifc_req;
+ for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifrp++) {
+ /* Get the flags so that we can skip the loopback interface */
+ (void) memset((char *)&ifr, '\0', sizeof (ifr));
+ (void) strncpy(ifr.ifr_name, ifrp->ifr_name,
+ sizeof (ifr.ifr_name));
+
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "%s", "unable to determine flags");
+ (void) close(s);
+ (void) free(buf);
+ return (-1);
+ }
+
+ if (ifr.ifr_flags & IFF_LOOPBACK)
+ continue;
+ if ((ifr.ifr_flags & IFF_UP) == 0)
+ continue;
+ tmp = (struct if_list *)malloc(sizeof (struct if_list));
+ if (tmp == NULL) {
+ syslog(LOG_ERR, "%s", "out of memory\n");
+ (void) close(s);
+ (void) free(buf);
+ return (-1);
+ }
+
+ tmp->next = NULL;
+ (void) strncpy(tmp->name, ifrp->ifr_name, sizeof (tmp->name));
+ if (*list == NULL) {
+ *list = tmp;
+ } else {
+ for (p = *list; p->next; p = p->next)
+ ;
+ p->next = tmp;
+ }
+ cnt++;
+ }
+ (void) close(s);
+ (void) free(buf);
+ return (cnt);
+}
+
+/*
+ * This will mimick the workings of ifconfig -a command. A net_cfg
+ * pointer will be passed, and all information will be assigned
+ * within this function. Memory will be assigned in this function
+ * also so the user doesn't have to worry about it. Freeing memory
+ * will be handled in a different function - smb_nic_clear_memory
+ */
+int
+smb_nic_build_network_structures(net_cfg_t **nc, int *number)
+{
+ char ** names = NULL;
+ int res, numnics = 0;
+ int num_aliases = 0;
+ uint32_t uip;
+ uint64_t flags;
+ int i = 0;
+ int j = 1;
+ int k = 0;
+ struct ip_alias *list = NULL;
+ net_cfg_t *nc_array;
+ char excludestr[MAX_EXCLUDE_LIST_LEN];
+ ipaddr_t exclude[SMB_PI_MAX_NETWORKS];
+ int nexclude;
+ char *winsexclude;
+
+ *number = 0;
+ numnics = smb_nic_build_if_name(&names);
+ nc_array = *nc = malloc(sizeof (net_cfg_t) * numnics);
+ if (nc_array == NULL) {
+ (void) smb_nic_clear_name_list(names, numnics);
+ return (-1);
+ }
+ bzero(nc_array, sizeof (net_cfg_t) * numnics);
+
+ smb_config_rdlock();
+ excludestr[0] = '\0';
+ winsexclude = smb_config_getstr(SMB_CI_WINS_EXCL);
+ if (winsexclude != NULL)
+ (void) strlcpy(excludestr, winsexclude, sizeof (excludestr));
+ smb_config_unlock();
+ nexclude = smb_wins_iplist(excludestr, exclude, SMB_PI_MAX_NETWORKS);
+
+ for (i = 0; i < numnics; i++) {
+
+ if (strchr(names[i], ':') == NULL) {
+ /* Will not provide info on logical interfaces */
+
+ (void) memset ((*nc), 0, sizeof (net_cfg_t));
+ num_aliases = smb_nic_get_num_aliases(names[i]);
+ if (num_aliases == -1) {
+ (void) smb_nic_clear_name_list(names, numnics);
+ free (*nc);
+ return (-1);
+ }
+
+ (*nc)->aliases = (char **)malloc(
+ (sizeof (char) * IP_ABITS) * num_aliases);
+ if ((*nc)->aliases == NULL) {
+ (void) smb_nic_clear_name_list(names, numnics);
+ free (*nc);
+ return (-1);
+ }
+ (void) strncpy((*nc)->ifname, names[i],
+ sizeof ((*nc)->ifname));
+ (*nc)->naliases = num_aliases;
+
+ res = smb_nic_get_IP((*nc)->ifname, &uip);
+ if (res == -1) { /* error retrieving IP address */
+ (void) smb_nic_clear_name_list(names, numnics);
+ free ((*nc)->aliases);
+ free (*nc);
+ return (-1);
+ }
+ (*nc)->ip = uip;
+ if (smb_wins_is_excluded(uip, (ulong_t *)exclude,
+ nexclude))
+ (*nc)->exclude = B_TRUE;
+ res = smb_nic_get_netmask((*nc)->ifname, &uip);
+ if (res == -1) { /* error retrieving netmask address */
+ (void) smb_nic_clear_name_list(names, numnics);
+ free ((*nc)->aliases);
+ free (*nc);
+ return (-1);
+ }
+ (*nc)->mask = uip;
+ res = smb_nic_get_broadcast((*nc)->ifname, &uip);
+ if (res == -1) { /* error retrieving broadcast add */
+ (void) smb_nic_clear_name_list(names, numnics);
+ free ((*nc)->aliases);
+ free (*nc);
+ return (-1);
+ }
+ (*nc)->broadcast = uip;
+ res = smb_nic_get_group((*nc)->ifname,
+ (*nc)->groupname);
+ if (res == -1) { /* error retrieving group name */
+ (void) smb_nic_clear_name_list(names, numnics);
+ free ((*nc)->aliases);
+ free (*nc);
+ return (-1);
+ }
+ res = smb_nic_flags((*nc)->ifname, &flags);
+ if (res == -1) { /* error retrieving flags */
+ (void) smb_nic_clear_name_list(names, numnics);
+ free ((*nc)->aliases);
+ free (*nc);
+ return (-1);
+ }
+ (*nc)->flags = flags;
+ /*
+ * If an interface has no associated alias, the alias
+ * field will be set to NULL
+ */
+ res = smb_nic_get_IP_aliases((*nc)->ifname, &list);
+ if (res == -1) {
+ (*nc)->aliases[k] = NULL;
+ (void) smb_nic_clear_name_list(names, numnics);
+ free ((*nc)->aliases);
+ free (*nc);
+ return (-1);
+ }
+
+ if (res == 0) {
+ (*nc)->aliases[k] = NULL;
+
+ } else { /* There will be aliases */
+
+ (*nc)->aliases[0] = (char *)list->name;
+ while (list->next != NULL) {
+ (*nc)->aliases[j] =
+ (char *)(list->next);
+ j++;
+ list = list->next;
+ }
+ }
+ k++;
+ j = 1;
+ (*nc)++; /* increment pointer */
+ }
+ } /* end for */
+
+ *nc = nc_array;
+ *number = k;
+ (void) smb_nic_clear_name_list(names, numnics);
+ return (0);
+}
+
+/*
+ * Return a space separated list of interface names depending on specified
+ * flags. Either flags argument can be set to 0 if the caller chooses.
+ * Returns NULL if no interfaces match the passed flags
+ * flags_on: flags which must be on in each interface returned
+ * flags_off : flags which must be off in each interface returned
+ */
+char *
+smb_nic_get_ifnames(int flags_on, int flags_off)
+{
+ struct ifconf ifc;
+ int numifs, i, sfd;
+ char *ifnames;
+
+
+ sfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sfd == -1)
+ return (NULL);
+
+ if ((ioctl(sfd, SIOCGIFNUM, &numifs) == -1) || (numifs <= 0)) {
+ (void) close(sfd);
+ return (NULL);
+ }
+
+ ifnames = malloc(numifs * (LIFNAMSIZ + 1));
+ if (ifnames == NULL) {
+ return (NULL);
+ }
+ ifc.ifc_len = (numifs * sizeof (struct ifreq));
+ ifc.ifc_req = malloc(numifs * sizeof (struct ifreq));
+ if (ifc.ifc_req == NULL) {
+ free(ifnames);
+ return (NULL);
+ }
+
+ if (ioctl(sfd, SIOCGIFCONF, &ifc) == -1) {
+ (void) close(sfd);
+ free(ifnames);
+ free(ifc.ifc_req);
+ return (NULL);
+ }
+
+ for (i = 0; i < numifs; i++) {
+ if (ioctl(sfd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0) {
+ if ((ifc.ifc_req[i].ifr_flags &
+ (flags_on | flags_off)) != flags_on) {
+ continue;
+ }
+ }
+
+ (void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
+ (void) strcat(ifnames, " ");
+ }
+
+ if (strlen(ifnames) > 1)
+ ifnames[strlen(ifnames) - 1] = '\0';
+
+ (void) close(sfd);
+ free(ifc.ifc_req);
+
+ return (ifnames);
+}
+
+/*
+ * Function to determine if passed address is of form a.b.c.d.
+ */
+int
+smb_nic_validate_ip_address(char *IP)
+{
+ in_addr_t addr;
+ if ((int)(addr = inet_addr(IP)) == -1) {
+ syslog(LOG_ERR, "%s", "IP-address must be"
+ " of the form a.b.c.d");
+ return (addr);
+ }
+ else
+ return (0);
+
+}
+
+/*
+ * Get flags associated with if
+ * -1 means there was an error retrieving the data
+ * 0 success
+ */
+int
+smb_nic_flags(char *interface, uint64_t *flag)
+{
+ struct lifreq lifrr;
+ int sfd;
+
+ sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s", "smb_get_nic_flags: socket open failed");
+ return (-1);
+ }
+
+ (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
+
+ if (ioctl(sfd, SIOCGLIFFLAGS, &lifrr) < 0) {
+ syslog(LOG_ERR, "%s", "smb_get_nic_flags: get flags failed");
+ (void) close(sfd);
+ return (-1);
+ }
+
+ (void) close(sfd);
+ if (flag != NULL)
+ *flag = lifrr.lifr_flags;
+ return (0);
+}
+
+/*
+ * The following list is taken from if.h. The function takes the
+ * given interface name, and the passed flag(s), and returns true if
+ * the flag is associated with the interface, and false if not.
+ *
+ * IFF_UP interface is up
+ * IFF_BROADCAST broadcast address valid
+ * IFF_LOOPBACK is a loopback net
+ * IFF_POINTOPOINT interface is point-to-point link
+ * IFF_RUNNING resources allocated
+ * IFF_MULTICAST supports multicast
+ * IFF_MULTI_BCAST multicast using broadcast address
+ * IFF_UNNUMBERED non-unique address
+ * IFF_DHCPRUNNING DHCP controls this interface
+ * IFF_PRIVATE do not advertise
+ * IFF_DEPRECATED interface address deprecated
+ * IFF_ANYCAST Anycast address
+ * IFF_IPV4 IPv4 interface
+ * IFF_IPV6 IPv6 interface
+ * IFF_NOFAILOVER Don't failover on NIC failure
+ * IFF_FAILED NIC has failed
+ * IFF_STANDBY Standby NIC to be used on failures
+ * IFF_OFFLINE NIC has been offlined
+ * -1 means there was an error retrieving the data
+ * 0 indicates false - the flag isn't associated
+ * 1 indicates true - the flag is associated
+ */
+int
+smb_nic_status(char *interface, uint64_t flag)
+{
+ struct lifreq lifrr;
+ int sfd;
+
+ sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s", "nic_status: socket open failed");
+ return (-1);
+ }
+
+ (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
+
+ if (ioctl(sfd, SIOCGLIFFLAGS, &lifrr) < 0) {
+ syslog(LOG_ERR, "%s", "nic_status: get flags failed");
+ (void) close(sfd);
+ return (-1);
+ }
+
+ if (lifrr.lifr_flags & flag) {
+ (void) close(sfd);
+ return (1); /* associated */
+ } else {
+ (void) close(sfd);
+ return (0); /* not associated */
+ }
+}
+
+/*
+ * Free allocated memory for net_cfg structures. Takes number of allocated
+ * structures as argument also
+ */
+int
+smb_nic_clear_niclist(net_cfg_t *niclist, int amount)
+{
+ int i, j = 0;
+
+ if (niclist == NULL)
+ return (-1);
+ for (i = 0; i < amount; i++) {
+ while (niclist[i].aliases[j] != NULL) {
+ free(niclist[i].aliases[j]);
+ j++;
+ }
+ free(niclist[i].aliases);
+ j = 0;
+ }
+ free(niclist);
+
+ return (0);
+}
+
+int
+smb_nic_free_niclist(net_cfg_list_t *niclist)
+{
+ return (smb_nic_clear_niclist(niclist->net_cfg_list,
+ niclist->net_cfg_cnt));
+}
+
+/*
+ * Free allocated memory for names lists. Takes number of allocated
+ * pointers as argument also
+ */
+int
+smb_nic_clear_name_list(char **names, int amount)
+{
+ int i;
+
+ for (i = 0; i < amount; i++) {
+ free(names[i]);
+ }
+
+ free(names);
+ return (0);
+}
+
+/* Free allocated memory for names lists. */
+
+static void
+smb_nic_clear_if_list(struct if_list *iflist)
+{
+ struct if_list *tmp;
+
+ if (iflist == NULL)
+ return;
+ for (; iflist != NULL; iflist = tmp) {
+ tmp = iflist->next;
+ free(iflist);
+ }
+}
+
+/* Free allocated memory for alias lists. */
+int
+smb_nic_clear_ip_alias(struct ip_alias *iplist)
+{
+ struct ip_alias *tmp;
+
+ for (; iplist != NULL; iplist = tmp) {
+ tmp = iplist->next;
+ free(iplist);
+ }
+
+ return (0);
+}
+
+/*
+ * smb_nic_lock
+ *
+ * Lock the nic table
+ */
+void
+smb_nic_lock(void)
+{
+ (void) pthread_mutex_lock(&smb_nic_mutex);
+}
+
+/*
+ * smb_nic_unlock
+ *
+ * Unlock the nic table.
+ *
+ * This function MUST be called after lock
+ */
+void
+smb_nic_unlock(void)
+{
+ (void) pthread_mutex_unlock(&smb_nic_mutex);
+}
+
+int
+smb_nic_init()
+{
+ smb_nic_lock();
+ smb_nic_list.net_cfg_cnt = 0;
+ (void) smb_nic_build_network_structures(&smb_nic_list.net_cfg_list,
+ &smb_nic_list.net_cfg_cnt);
+ smb_nic_unlock();
+ return (0);
+}
+
+/*
+ * Initialize interface list.
+ */
+void
+smb_nic_build_info(void)
+{
+ smb_nic_lock();
+ if (smb_nic_list.net_cfg_list) {
+ (void) smb_nic_free_niclist(&smb_nic_list);
+ smb_nic_list.net_cfg_list = NULL;
+ }
+ smb_nic_list.net_cfg_cnt = 0;
+ (void) smb_nic_build_network_structures(&smb_nic_list.net_cfg_list,
+ &smb_nic_list.net_cfg_cnt);
+ if (smb_nic_list.net_cfg_cnt == 0) {
+ syslog(LOG_ERR, "smb: No network interfaces are configured "
+ "smb server may not function properly");
+ }
+ smb_nic_unlock();
+}
+
+/*
+ * Get number of interfaces.
+ */
+int
+smb_nic_get_num(void)
+{
+ int sz;
+ smb_nic_lock();
+ sz = smb_nic_list.net_cfg_cnt;
+ smb_nic_unlock();
+ return (sz);
+}
+
+/*
+ * Get if by index
+ * Returns: NULL if not found.
+ */
+net_cfg_t *
+smb_nic_get_byind(int ind, net_cfg_t *cfg)
+{
+ if (cfg == NULL)
+ return (cfg);
+ smb_nic_lock();
+ if (ind > smb_nic_list.net_cfg_cnt) {
+ smb_nic_unlock();
+ return (NULL);
+ }
+ bcopy(&smb_nic_list.net_cfg_list[ind], cfg, sizeof (net_cfg_t));
+ smb_nic_unlock();
+ return (cfg);
+}
+
+/*
+ * Get if by subnet
+ * Returns: NULL if not found.
+ */
+net_cfg_t *
+smb_nic_get_bysubnet(uint32_t ipaddr, net_cfg_t *cfg)
+{
+ int i;
+ net_cfg_t *tcfg;
+
+ if (cfg == NULL)
+ return (cfg);
+ smb_nic_lock();
+ bzero(cfg, sizeof (net_cfg_t));
+ for (i = 0; i < smb_nic_list.net_cfg_cnt; i++) {
+ tcfg = &smb_nic_list.net_cfg_list[i];
+ if ((ipaddr & tcfg->mask) ==
+ (tcfg->ip & tcfg->mask)) {
+ bcopy(tcfg, cfg, sizeof (net_cfg_t));
+ smb_nic_unlock();
+ return (cfg);
+ }
+ }
+ smb_nic_unlock();
+ return (NULL);
+}
+
+/*
+ * Get if by ip.
+ * Returns: NULL if not found.
+ */
+net_cfg_t *
+smb_nic_get_byip(uint32_t ipaddr, net_cfg_t *cfg)
+{
+ int i;
+ net_cfg_t *tcfg;
+
+ if (cfg == NULL)
+ return (cfg);
+ smb_nic_lock();
+ bzero(cfg, sizeof (net_cfg_t));
+ for (i = 0; i < smb_nic_list.net_cfg_cnt; i++) {
+ tcfg = &smb_nic_list.net_cfg_list[i];
+ if (ipaddr == tcfg->ip) {
+ bcopy(tcfg, cfg, sizeof (net_cfg_t));
+ smb_nic_unlock();
+ return (cfg);
+ }
+ }
+ smb_nic_unlock();
+ return (NULL);
+}
diff --git a/usr/src/lib/smbsrv/libsmbns/i386/Makefile b/usr/src/lib/smbsrv/libsmbns/i386/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libsmbns/sparc/Makefile b/usr/src/lib/smbsrv/libsmbns/sparc/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libsmbns/sparcv9/Makefile b/usr/src/lib/smbsrv/libsmbns/sparcv9/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbns/sparcv9/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libsmbrdr/Makefile b/usr/src/lib/smbsrv/libsmbrdr/Makefile
new file mode 100644
index 0000000000..6b376227e4
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+HDRS= libsmbrdr.h
+
+include ../Makefile.smbsrv
diff --git a/usr/src/lib/smbsrv/libsmbrdr/Makefile.com b/usr/src/lib/smbsrv/libsmbrdr/Makefile.com
new file mode 100644
index 0000000000..822331f26b
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/Makefile.com
@@ -0,0 +1,53 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY = libsmbrdr.a
+VERS = .1
+
+OBJS_COMMON = \
+ smbrdr_ipc_util.o \
+ smbrdr_lib.o \
+ smbrdr_logon.o \
+ smbrdr_netbios.o \
+ smbrdr_netuse.o \
+ smbrdr_read_andx.o \
+ smbrdr_rpcpipe.o \
+ smbrdr_session.o \
+ smbrdr_transact.o
+
+OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED)
+
+include ../../../Makefile.lib
+include ../../Makefile.lib
+
+LDLIBS += -lsmb -lnsl -lsocket -lc
+
+SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
+ $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c)
+
+include ../../Makefile.targ
+include ../../../Makefile.targ
diff --git a/usr/src/lib/smbsrv/libsmbrdr/amd64/Makefile b/usr/src/lib/smbsrv/libsmbrdr/amd64/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/libsmbrdr.h b/usr/src/lib/smbsrv/libsmbrdr/common/libsmbrdr.h
new file mode 100644
index 0000000000..846ac9d306
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/libsmbrdr.h
@@ -0,0 +1,97 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBSMBRDR_H
+#define _LIBSMBRDR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <smbsrv/libsmb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Redirector IPC functions
+ *
+ * The following functions are required by the mlsvc_validate_user to
+ * apply new authentication information for the authenticated IPC, rollback
+ * or commit the changes to the original authentication information.
+ */
+extern void smbrdr_ipc_set(char *, unsigned char *);
+extern void smbrdr_ipc_commit(void);
+extern void smbrdr_ipc_rollback(void);
+extern int smbrdr_ipc_skip_lsa_query(void);
+extern int smbrdr_ipc_get_mode(void);
+extern void smbrdr_ipc_save_mode(char *val);
+extern unsigned smbrdr_ipc_get_flags(void);
+extern void smbrdr_ipc_set_fallback(void);
+extern void smbrdr_ipc_unset_fallback(void);
+extern int smbrdr_ipc_is_fallback(void);
+
+/*
+ * Functions for obtaining the resource domain administrator credentials.
+ */
+extern char *smbrdr_ipc_get_user(void);
+extern char *smbrdr_ipc_get_passwd(void);
+extern int smbrdr_ipc_is_valid(void);
+
+
+/* Redirector LOGON functions */
+extern int mlsvc_anonymous_logon(char *, char *, char **);
+extern int mlsvc_user_logon(char *, char *, char *, char *);
+extern int mlsvc_admin_logon(char *, char *);
+
+extern int smbrdr_rpc_readx(int, char *, int);
+
+
+/* Redirector rpcpipe functions */
+extern int mlsvc_open_pipe(char *, char *, char *, char *);
+extern int mlsvc_close_pipe(int);
+
+
+/* Redirector session functions */
+extern void smbrdr_init(void);
+extern int mlsvc_locate_domain_controller(char *);
+extern int mlsvc_session_native_values(int, int *, int *, int *);
+extern void mlsvc_check_sessions(void);
+extern int mlsvc_echo(char *);
+extern void mlsvc_disconnect(char *);
+
+
+extern int smbrdr_rpc_transact(int, char *, int, char *, int);
+
+
+/* DEBUG functions */
+extern void smbrdr_dump_ofiles(void);
+extern void smbrdr_dump_sessions(void);
+extern void smbrdr_dump_netuse();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSMBRDR_H */
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/llib-lsmbrdr b/usr/src/lib/smbsrv/libsmbrdr/common/llib-lsmbrdr
new file mode 100644
index 0000000000..d238202484
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/llib-lsmbrdr
@@ -0,0 +1,31 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <smbsrv/libsmbrdr.h>
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/mapfile-vers b/usr/src/lib/smbsrv/libsmbrdr/common/mapfile-vers
new file mode 100644
index 0000000000..0ea435ee55
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/mapfile-vers
@@ -0,0 +1,63 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SUNWprivate {
+ global:
+ mlsvc_admin_logon;
+ mlsvc_anonymous_logon;
+ mlsvc_check_sessions;
+ mlsvc_close_pipe;
+ mlsvc_disconnect;
+ mlsvc_echo;
+ mlsvc_install_pdc_cb;
+ mlsvc_locate_domain_controller;
+ mlsvc_open_pipe;
+ mlsvc_session_native_values;
+ mlsvc_user_getauth;
+ mlsvc_user_logon;
+ smbrdr_dump_netuse;
+ smbrdr_dump_ofiles;
+ smbrdr_dump_sessions;
+ smbrdr_init;
+ smbrdr_ipc_get_mode;
+ smbrdr_ipc_commit;
+ smbrdr_ipc_get_flags;
+ smbrdr_ipc_get_user;
+ smbrdr_ipc_rollback;
+ smbrdr_ipc_set;
+ smbrdr_ipc_skip_lsa_query;
+ smbrdr_ipc_is_fallback;
+ smbrdr_ipc_is_valid;
+ smbrdr_ipc_get_passwd;
+ smbrdr_ipc_save_mode;
+ smbrdr_ipc_set_fallback;
+ smbrdr_ipc_unset_fallback;
+ smbrdr_rpc_readx;
+ smbrdr_rpc_transact;
+ local:
+ *;
+};
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr.h b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr.h
new file mode 100644
index 0000000000..35db51adc6
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr.h
@@ -0,0 +1,241 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMBRDR_H_
+#define _SMBRDR_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <synch.h>
+#include <sys/types.h>
+
+#include <smbsrv/libsmbrdr.h>
+
+#include <smbsrv/cifs.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/smb.h>
+#include <smbsrv/wintypes.h>
+
+#define SMBRDR_REQ_BUFSZ 4096
+
+#define MAX_ACCOUNT_NAME 32
+#define MAX_SHARE_NAME 32
+#define MAX_SCOPE_NAME 64
+#define MAX_FILE_PATH 128
+
+/*
+ * The number of shares and pipes is limited to 48 based on the note
+ * below. This really shouldn't cause a problem because we always
+ * our shares and named pipes are always opened and closed round every
+ * RPC transaction. This also tends to limit the number of active
+ * logons because we (currently) need two named pipes per logon.
+ *
+ * Q141709 Limit of 49 named pipe connections from a single workstation.
+ * If a named pipe server creates more than 49 distincly named pipes, a
+ * single client cannot connect more than 49 pipes on the named pipe
+ * server. Chapter 4, p113. Network Programming for Microsoft Windows
+ * Anthony Jones and Jim Ohlund, Microsoft Press, ISBN: 0-7356-0560-2
+ */
+#define N_NETUSE_TABLE 48
+#define N_OFILE_TABLE 48
+
+/*
+ * Logon's states
+ */
+#define SDB_LSTATE_START 0
+#define SDB_LSTATE_INIT 1
+#define SDB_LSTATE_LOGGING_OFF 2
+#define SDB_LSTATE_SETUP 3
+
+#define SDB_LOGON_NONE 0
+#define SDB_LOGON_GUEST 1
+#define SDB_LOGON_ANONYMOUS 2
+#define SDB_LOGON_USER 3
+
+typedef struct sdb_logon {
+ struct sdb_session *session;
+ char username[MAX_ACCOUNT_NAME];
+ unsigned short uid;
+ unsigned int type;
+ unsigned short state;
+ smb_auth_info_t auth;
+} sdb_logon_t;
+
+/*
+ * Session's states
+ *
+ * SDB_SSTATE_START ready to be used
+ * SDB_SSTATE_INIT initialized
+ * SDB_SSTATE_STALE lost transport connection
+ * SDB_SSTATE_DISCONNECTING disconnecting: logoff the user
+ * disconnect trees, close files
+ * SDB_SSTATE_CLEANING was in STALE state now just
+ * cleaning up
+ * SDB_SSTATE_CONNECTED got transport connection
+ * SDB_SSTATE_NEGOTIATED did SMB negotiate
+ */
+#define SDB_SSTATE_START 0
+#define SDB_SSTATE_INIT 1
+#define SDB_SSTATE_STALE 2
+#define SDB_SSTATE_DISCONNECTING 3
+#define SDB_SSTATE_CLEANING 4
+#define SDB_SSTATE_CONNECTED 5
+#define SDB_SSTATE_NEGOTIATED 6
+
+#define SDB_SLCK_READ 1
+#define SDB_SLCK_WRITE 2
+
+struct sdb_session {
+ smb_ntdomain_t di;
+ char scope[SMB_PI_MAX_SCOPE];
+ char native_os[SMB_PI_MAX_NATIVE_OS];
+ char native_lanman[SMB_PI_MAX_LANMAN];
+ int sock;
+ short port;
+ unsigned short secmode;
+ uint32_t sesskey;
+ uint32_t challenge_len;
+ unsigned char challenge_key[32];
+ unsigned char smb_flags;
+ unsigned short smb_flags2;
+ unsigned short vc;
+ uint32_t remote_caps;
+ unsigned short state;
+ unsigned int sid; /* session id */
+ int remote_os;
+ int remote_lm;
+ int pdc_type;
+ smb_sign_ctx_t sign_ctx;
+ sdb_logon_t logon;
+ rwlock_t rwl;
+};
+
+/*
+ * Netuse's states
+ */
+#define SDB_NSTATE_START 0
+#define SDB_NSTATE_INIT 1
+#define SDB_NSTATE_DISCONNECTING 2
+#define SDB_NSTATE_CONNECTED 3
+
+struct sdb_netuse {
+ struct sdb_session *session;
+ unsigned short state;
+ int letter; /* local identity */
+ unsigned int sid;
+ unsigned short uid;
+ unsigned short tid; /* remote identity */
+ char share[MAX_SHARE_NAME];
+ mutex_t mtx;
+};
+
+/*
+ * Ofile's states
+ */
+#define SDB_FSTATE_START 0
+#define SDB_FSTATE_INIT 1
+#define SDB_FSTATE_CLOSING 2
+#define SDB_FSTATE_OPEN 3
+
+struct sdb_ofile {
+ struct sdb_session *session;
+ struct sdb_netuse *netuse;
+ unsigned short state;
+ unsigned int sid;
+ unsigned short uid;
+ unsigned short tid;
+ unsigned short fid; /* remote identity */
+ char path[MAX_FILE_PATH];
+ mutex_t mtx;
+};
+
+typedef struct smbrdr_handle {
+ unsigned char *srh_buf;
+ smb_msgbuf_t srh_mbuf;
+ unsigned int srh_mbflags;
+ unsigned char srh_cmd;
+ struct sdb_session *srh_session;
+ struct sdb_logon *srh_user;
+ struct sdb_netuse *srh_tree;
+} smbrdr_handle_t;
+
+/*
+ * smbrdr_netbios.c
+ */
+void nb_lock(void);
+void nb_unlock(void);
+void nb_close(int);
+int nb_keep_alive(int);
+
+int nb_send(int, unsigned char *, unsigned);
+int nb_rcv(int, unsigned char *, unsigned, long);
+int nb_exchange(int, unsigned char *, unsigned,
+ unsigned char *, unsigned, long);
+int nb_session_request(int, char *, char *, char *, char *);
+
+/*
+ * smbrdr_session.c
+ */
+int smbrdr_negotiate(char *);
+struct sdb_session *smbrdr_session_lock(char *, char *, int);
+void smbrdr_session_unlock(struct sdb_session *);
+
+/*
+ * smbrdr_logon.c
+ */
+int smbrdr_smb_logoff(struct sdb_logon *);
+
+/* smbrdr_netuse.c */
+void smbrdr_netuse_logoff(unsigned short);
+struct sdb_netuse *smbrdr_netuse_get(int);
+unsigned short mlsvc_tree_connect(char *, char *, char *);
+int smbrdr_tree_disconnect(unsigned short);
+void smbrdr_netuse_put(struct sdb_netuse *);
+
+/*
+ * smbrdr_rpcpipe.c
+ */
+void smbrdr_ofile_end_of_share(unsigned short);
+struct sdb_ofile *smbrdr_ofile_get(int);
+void smbrdr_ofile_put(struct sdb_ofile *);
+
+/* smbrdr_lib.c */
+DWORD smbrdr_request_init(smbrdr_handle_t *, unsigned char,
+ struct sdb_session *, struct sdb_logon *, struct sdb_netuse *);
+DWORD smbrdr_send(smbrdr_handle_t *);
+DWORD smbrdr_rcv(smbrdr_handle_t *, int);
+DWORD smbrdr_exchange(smbrdr_handle_t *, smb_hdr_t *, long);
+void smbrdr_handle_free(smbrdr_handle_t *);
+int smbrdr_sign_init(struct sdb_session *, struct sdb_logon *);
+int smbrdr_sign_fini(struct sdb_session *);
+int smbrdr_sign_unset_key(struct sdb_session *);
+
+void smbrdr_lock_transport(void);
+void smbrdr_unlock_transport(void);
+
+#endif /* _SMBRDR_H_ */
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.c
new file mode 100644
index 0000000000..908a439864
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.c
@@ -0,0 +1,382 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The IPC connection information is encapsulated within SMB Redirector.
+ * Utility functions are defined here to allow other modules to get and
+ * set the ipc configuration, as well as, to rollback or commit the
+ * changes to the original authentication information.
+ */
+
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <synch.h>
+
+#include <smbsrv/libsmbrdr.h>
+
+#include <smbsrv/mlsvc.h>
+#include <smbsrv/smbinfo.h>
+#include <smbrdr.h>
+#include <smbrdr_ipc_util.h>
+
+/*
+ * The binary NTLM hash is 16 bytes. When it is converted to hexidecimal,
+ * it will be at most twice as long.
+ */
+#define SMBRDR_IPC_HEX_PASSWD_MAXLEN (SMBAUTH_HASH_SZ * 2) + 1
+#define SMBRDR_IPC_GETDOMAIN_TIMEOUT 10000
+
+static rwlock_t smbrdr_ipc_lock;
+static smbrdr_ipc_t ipc_info;
+static smbrdr_ipc_t orig_ipc_info;
+
+/*
+ * smbrdr_ipc_init
+ *
+ * Get system configuration regarding IPC connection
+ * credentials and initialize related variables.
+ * This function will normally be called at startup
+ * (i.e. at the time smbrdr gets loaded).
+ */
+void
+smbrdr_ipc_init(void)
+{
+ char *p;
+
+ bzero(&ipc_info, sizeof (smbrdr_ipc_t));
+ bzero(&orig_ipc_info, sizeof (smbrdr_ipc_t));
+
+ smb_config_rdlock();
+ p = smb_config_getstr(SMB_CI_RDR_IPCMODE);
+
+ if (!strncasecmp(p, IPC_MODE_AUTH, IPC_MODE_STRLEN)) {
+ ipc_info.mode = MLSVC_IPC_ADMIN;
+
+ p = smb_config_getstr(SMB_CI_RDR_IPCUSER);
+ if (p)
+ (void) strlcpy(ipc_info.user, p,
+ MLSVC_ACCOUNT_NAME_MAX);
+ else
+ syslog(LOG_WARNING, "smbrdr: (ipc) no admin user name");
+
+ p = smb_config_get(SMB_CI_RDR_IPCPWD);
+ if (p) {
+ if (strlen(p) != SMBRDR_IPC_HEX_PASSWD_MAXLEN - 1) {
+ *ipc_info.passwd = 0;
+ syslog(LOG_WARNING,
+ "smbrdr: (ipc) invalid admin password");
+ } else {
+ (void) hextobin(p,
+ SMBRDR_IPC_HEX_PASSWD_MAXLEN - 1,
+ ipc_info.passwd, SMBAUTH_HASH_SZ);
+ }
+ } else {
+ *ipc_info.passwd = 0;
+ syslog(LOG_WARNING, "smbrdr: (ipc) no admin password");
+ }
+
+ } else {
+ if (!strcasecmp(p, IPC_MODE_FALLBACK_ANON))
+ ipc_info.flags |= IPC_FLG_FALLBACK_ANON;
+
+ ipc_info.mode = MLSVC_IPC_ANON;
+ (void) strlcpy(ipc_info.user, MLSVC_ANON_USER,
+ MLSVC_ACCOUNT_NAME_MAX);
+ *ipc_info.passwd = 0;
+ }
+ smb_config_unlock();
+}
+
+/*
+ * smbrdr_ipc_set
+ *
+ * The given username and password hash will be applied to the
+ * ipc_info which will be used by mlsvc_validate_user().
+ *
+ * If mlsvc_validate_user() succeeds, the calling function is responsible
+ * for invoking smbrdr_ipc_commit() for updating the environment
+ * variables. Otherwise, it should invoke smbrdr_ipc_rollback() to restore
+ * the previous credentials.
+ */
+void
+smbrdr_ipc_set(char *plain_user, unsigned char *passwd_hash)
+{
+ (void) rw_wrlock(&smbrdr_ipc_lock);
+ if (ipc_info.flags & IPC_FLG_FALLBACK_ANON)
+ ipc_info.mode = MLSVC_IPC_ADMIN;
+
+ (void) strlcpy(ipc_info.user, plain_user, sizeof (ipc_info.user));
+ (void) memcpy(ipc_info.passwd, passwd_hash, SMBAUTH_HASH_SZ);
+ ipc_info.flags |= IPC_FLG_NEED_VERIFY;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+
+}
+
+/*
+ * smbrdr_ipc_commit
+ *
+ * Save the new admin credentials as environment variables.
+ * The binary NTLM password hash is first converted to a
+ * hex string before storing in the environment variable.
+ *
+ * The credentials also saved to the original IPC info as
+ * rollback data in case the join domain process
+ * fails in the future.
+ */
+void
+smbrdr_ipc_commit()
+{
+ unsigned char hexpass[SMBRDR_IPC_HEX_PASSWD_MAXLEN];
+
+ (void) rw_wrlock(&smbrdr_ipc_lock);
+ smb_config_wrlock();
+ (void) smb_config_set(SMB_CI_RDR_IPCUSER, ipc_info.user);
+ (void) bintohex(ipc_info.passwd, sizeof (ipc_info.passwd),
+ (char *)hexpass, sizeof (hexpass));
+ hexpass[SMBRDR_IPC_HEX_PASSWD_MAXLEN - 1] = 0;
+ (void) smb_config_set(SMB_CI_RDR_IPCPWD, (char *)hexpass);
+
+ ipc_info.flags &= ~IPC_FLG_NEED_VERIFY;
+
+ if (ipc_info.flags & IPC_FLG_FALLBACK_ANON) {
+ ipc_info.flags &= ~IPC_FLG_FALLBACK_ANON;
+ ipc_info.mode = MLSVC_IPC_ADMIN;
+ (void) smb_config_set(SMB_CI_RDR_IPCMODE, IPC_MODE_AUTH);
+ syslog(LOG_DEBUG, "smbrdr: (ipc) Authenticated IPC "
+ "connection has been restored");
+ }
+
+ (void) memcpy(&orig_ipc_info, &ipc_info, sizeof (smbrdr_ipc_t));
+ smb_config_unlock();
+ (void) rw_unlock(&smbrdr_ipc_lock);
+}
+
+/*
+ * smbrdr_ipc_rollback
+ *
+ * Restore the original credentials
+ */
+void
+smbrdr_ipc_rollback()
+{
+ (void) rw_wrlock(&smbrdr_ipc_lock);
+ (void) strlcpy(ipc_info.user, orig_ipc_info.user,
+ sizeof (ipc_info.user));
+ (void) memcpy(ipc_info.passwd, orig_ipc_info.passwd,
+ sizeof (ipc_info.passwd));
+
+ ipc_info.flags &= ~IPC_FLG_NEED_VERIFY;
+
+ if (ipc_info.flags & IPC_FLG_FALLBACK_ANON)
+ ipc_info.mode = MLSVC_IPC_ANON;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+}
+
+/*
+ * Get & Set functions
+ */
+int
+smbrdr_ipc_get_mode()
+{
+ int mode;
+
+ (void) rw_rdlock(&smbrdr_ipc_lock);
+ mode = ipc_info.mode;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+
+ return (mode);
+}
+
+char *
+smbrdr_ipc_get_user()
+{
+ char *user;
+
+ (void) rw_rdlock(&smbrdr_ipc_lock);
+ user = ipc_info.user;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+ return (user);
+}
+
+char *
+smbrdr_ipc_get_passwd()
+{
+ char *passwd;
+
+ (void) rw_rdlock(&smbrdr_ipc_lock);
+ passwd = ipc_info.passwd;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+ return (passwd);
+}
+
+unsigned
+smbrdr_ipc_get_flags()
+{
+ unsigned flags;
+
+ (void) rw_rdlock(&smbrdr_ipc_lock);
+ flags = ipc_info.flags;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+ return (flags);
+}
+
+void
+smbrdr_ipc_set_fallback()
+{
+ (void) rw_wrlock(&smbrdr_ipc_lock);
+ ipc_info.flags |= IPC_FLG_FALLBACK_ANON;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+}
+
+void
+smbrdr_ipc_unset_fallback()
+{
+ (void) rw_wrlock(&smbrdr_ipc_lock);
+ ipc_info.flags &= ~IPC_FLG_FALLBACK_ANON;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+}
+
+/*
+ * Whether the smbrdr.ipc.mode is set to fallback,anon or not
+ */
+int
+smbrdr_ipc_is_fallback()
+{
+ int is_fallback;
+
+ smb_config_rdlock();
+ is_fallback = (!strcasecmp(smb_config_getstr(SMB_CI_RDR_IPCMODE),
+ IPC_MODE_FALLBACK_ANON) ? 1 : 0);
+ smb_config_unlock();
+
+ return (is_fallback);
+}
+
+/*
+ * smbrdr_ipc_save_mode
+ *
+ * Set the SMBRDR_IPC_MODE_ENV variable and update the
+ * IPC mode of the cache.
+ */
+void
+smbrdr_ipc_save_mode(char *val)
+{
+ (void) rw_wrlock(&smbrdr_ipc_lock);
+ smb_config_wrlock();
+ (void) smb_config_set(SMB_CI_RDR_IPCMODE, val);
+ ipc_info.mode = !strncasecmp(val, IPC_MODE_AUTH, IPC_MODE_STRLEN)
+ ? MLSVC_IPC_ADMIN : MLSVC_IPC_ANON;
+ smb_config_unlock();
+ (void) rw_unlock(&smbrdr_ipc_lock);
+}
+
+/*
+ * smbrdr_ipc_skip_lsa_query
+ *
+ * Determine whether LSA monitor should skip the LSA query due to the
+ * incomplete authentication information if IPC is configured to be
+ * authenticated.
+ */
+int
+smbrdr_ipc_skip_lsa_query()
+{
+ char *user, *pwd;
+
+ if (ipc_info.mode != MLSVC_IPC_ADMIN)
+ return (0);
+
+ smb_config_rdlock();
+ user = smb_config_get(SMB_CI_RDR_IPCUSER);
+ pwd = smb_config_get(SMB_CI_RDR_IPCPWD);
+ smb_config_unlock();
+ if ((user == NULL) && pwd)
+ return (1);
+
+ (void) rw_rdlock(&smbrdr_ipc_lock);
+ user = ipc_info.user;
+ pwd = ipc_info.passwd;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+
+ return (!(*user && *pwd));
+}
+
+static char *
+smbrdr_ipc_modestr(int mode)
+{
+ switch (mode) {
+ case MLSVC_IPC_ANON:
+ return ("Anonymous");
+
+ case MLSVC_IPC_ADMIN:
+ return ("Authenticated");
+
+ default:
+ return ("Unknown");
+ }
+}
+
+/*
+ * For debugging purposes only.
+ */
+void
+smbrdr_ipc_loginfo()
+{
+ smbrdr_ipc_t tmp;
+ smbrdr_ipc_t tmporg;
+
+ (void) rw_rdlock(&smbrdr_ipc_lock);
+ (void) memcpy(&tmp, &ipc_info, sizeof (smbrdr_ipc_t));
+ (void) memcpy(&tmporg, &orig_ipc_info, sizeof (smbrdr_ipc_t));
+ (void) rw_unlock(&smbrdr_ipc_lock);
+
+ syslog(LOG_DEBUG, "smbrdr: current IPC info:");
+ syslog(LOG_DEBUG, "\t%s (user=%s, flags:0x%X)",
+ smbrdr_ipc_modestr(tmp.mode), tmp.user, tmp.flags);
+
+ syslog(LOG_DEBUG, "smbrdr: original IPC info:");
+ syslog(LOG_DEBUG, "\t%s (user=%s, flags:0x%X)",
+ smbrdr_ipc_modestr(tmporg.mode), tmporg.user, tmporg.flags);
+}
+
+/*
+ * smbrdr_ipc_is_valid
+ *
+ * Determine whether the ipc_info has been validated or not.
+ *
+ */
+int
+smbrdr_ipc_is_valid()
+{
+ int isvalid;
+
+ (void) rw_rdlock(&smbrdr_ipc_lock);
+ isvalid = (ipc_info.flags & IPC_FLG_NEED_VERIFY) ? 0 : 1;
+ (void) rw_unlock(&smbrdr_ipc_lock);
+
+ return (isvalid);
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.h b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.h
new file mode 100644
index 0000000000..a967e91b86
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.h
@@ -0,0 +1,85 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMBSRV_IPC_UTIL_H
+#define _SMBSRV_IPC_UTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file defines the data structure for the IPC connection and utility
+ * function prototypes.
+ */
+
+#include <smbsrv/mlsvc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SMBRDR_IPC_MODE_ENV "smbrdr.ipc.mode"
+#define SMBRDR_IPC_USER_ENV "smbrdr.ipc.user"
+#define SMBRDR_IPC_PASSWD_ENV "smbrdr.ipc.passwd"
+
+#define IPC_MODE_STRLEN 4
+#define IPC_MODE_ANON "anon"
+#define IPC_MODE_AUTH "auth"
+#define IPC_MODE_FALLBACK_ANON "fallback,anon"
+
+#define IPC_FLG_FALLBACK_ANON 0x00000001
+#define IPC_FLG_NEED_VERIFY 0x00000002
+
+/*
+ * smbrdr_ipc_t
+ *
+ * This structure contains information regarding the IPC configuration,
+ * as well as, the authentication info needed for connecting to the
+ * IPC$ share if the IPC connection is configured to be authenticated.
+ *
+ * IPC connection to the Primary Domain Controller [PDC] can be
+ * configured to be either anonymous or authenticated. Therefore,
+ * the IPC mode will be set to either one of the following values:
+ * MLSVC_IPC_ANON
+ * MLSVC_IPC_ADMIN
+ *
+ * The IPC_FLG_FALLBACK_ANON can be set in flags field to indicate whether
+ * a fallback from authenticated IPC to anonymous IPC has occurred. This
+ * flag will be unset once the join domain operation succeeds.
+ */
+typedef struct {
+ int mode;
+ char user[MLSVC_ACCOUNT_NAME_MAX];
+ char passwd[SMBAUTH_HASH_SZ];
+ unsigned flags;
+} smbrdr_ipc_t;
+
+
+void smbrdr_ipc_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMBSRV_IPC_UTIL_H */
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_lib.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_lib.c
new file mode 100644
index 0000000000..ac4743548c
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_lib.c
@@ -0,0 +1,592 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file provides some common functionality for SMB Redirector
+ * module.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <smbsrv/ntstatus.h>
+#include <smbrdr.h>
+
+static DWORD smbrdr_handle_setup(smbrdr_handle_t *srh, unsigned char cmd,
+ struct sdb_session *session, struct sdb_logon *logon,
+ struct sdb_netuse *netuse);
+
+static int smbrdr_hdr_setup(smbrdr_handle_t *srh);
+
+static DWORD smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr);
+
+static int smbrdr_sign(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb);
+static int smbrdr_sign_chk(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb,
+ unsigned char *signature);
+
+void smbrdr_lock_transport() { nb_lock(); }
+void smbrdr_unlock_transport() { nb_unlock(); }
+
+/*
+ * smbrdr_request_init
+ *
+ * Setup a handle with given information and then
+ * setup a SMB header structure.
+ *
+ * Returns:
+ *
+ * NT_STATUS_NO_MEMORY no memory for creating request
+ * NT_STATUS_INTERNAL_ERROR header encode failed or crypto failed
+ * NT_STATUS_SUCCESS successful
+ */
+DWORD
+smbrdr_request_init(smbrdr_handle_t *srh,
+ unsigned char cmd,
+ struct sdb_session *session,
+ struct sdb_logon *logon,
+ struct sdb_netuse *netuse)
+{
+ DWORD status;
+
+ status = smbrdr_handle_setup(srh, cmd, session, logon, netuse);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_DEBUG, "Smbrdr[%d]: initialization failed", cmd);
+ return (status);
+ }
+
+ if (smbrdr_hdr_setup(srh) < SMB_HEADER_LEN) {
+ syslog(LOG_DEBUG, "Smbrdr[%d]: cannot setup header", cmd);
+ smbrdr_handle_free(srh);
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * smbrdr_send
+ *
+ * Send the SMB packet pointed by the given handle over
+ * network.
+ *
+ * Returns:
+ *
+ * NT_STATUS_INTERNAL_ERROR crypto framework failure
+ * NT_STATUS_UNEXPECTED_NETWORK_ERROR send failed
+ * NT_STATUS_SUCCESS successful
+ */
+DWORD
+smbrdr_send(smbrdr_handle_t *srh)
+{
+ int rc;
+
+ if (smbrdr_sign(&srh->srh_session->sign_ctx, &srh->srh_mbuf) !=
+ SMBAUTH_SUCCESS)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ rc = nb_send(srh->srh_session->sock, srh->srh_buf,
+ smb_msgbuf_used(&srh->srh_mbuf));
+
+ if (rc < 0) {
+ /*
+ * Make the sequence number of the next SMB request even
+ * to avoid DC from failing the next SMB request with
+ * ACCESS_DENIED.
+ */
+ smb_mac_dec_seqnum(&srh->srh_session->sign_ctx);
+ return (NT_STATUS_UNEXPECTED_NETWORK_ERROR);
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * smbrdr_rcv
+ *
+ * Receive a SMB response and decode the packet header.
+ *
+ * "Implementing CIFS" book, SMB requests always have an even sequence
+ * number and replies always have an odd.
+ *
+ * With the original code, if the SMB Redirector skip the counter increment
+ * in the event of any failure during SmbSessionSetupAndX, it causes the
+ * domain controller to fail the next SMB request(odd sequence number)
+ * with ACCESS_DENIED.
+ *
+ * Smbrdr module should use the same sequence number (i.e. ssc_seqnum of the
+ * SMB Sign context) for generating the MAC signature for all incoming
+ * responses per SmbTransact request. Otherwise, the validation will fail.
+ * It is now fixed by decrementing the sequence number prior to validating
+ * the subsequent responses for a single request.
+ *
+ * Returns:
+ *
+ * status code returned by smbrdr_hdr_process()
+ * NT_STATUS_UNEXPECTED_NETWORK_ERROR receive failed
+ * NT_STATUS_SUCCESS successful
+ */
+DWORD
+smbrdr_rcv(smbrdr_handle_t *srh, int is_first_rsp)
+{
+ smb_hdr_t smb_hdr;
+ DWORD status;
+ int rc;
+ smb_sign_ctx_t *sign_ctx = &srh->srh_session->sign_ctx;
+
+ rc = nb_rcv(srh->srh_session->sock, srh->srh_buf, SMBRDR_REQ_BUFSZ, 0);
+ if (rc < 0) {
+ smb_mac_inc_seqnum(sign_ctx);
+ return (NT_STATUS_UNEXPECTED_NETWORK_ERROR);
+ }
+
+ /* initialize for processing response */
+ smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, rc, srh->srh_mbflags);
+
+ status = smbrdr_hdr_process(srh, &smb_hdr);
+ if (status != NT_STATUS_SUCCESS) {
+ smb_mac_inc_seqnum(sign_ctx);
+ return (status);
+ }
+
+ if (!is_first_rsp)
+ smb_mac_dec_seqnum(sign_ctx);
+
+ if (!smbrdr_sign_chk(sign_ctx,
+ &srh->srh_mbuf, smb_hdr.extra.extra.security_sig)) {
+ syslog(LOG_ERR, "SmbrdrExchange[%d]: bad signature",
+ srh->srh_cmd);
+ return (NT_STATUS_INVALID_NETWORK_RESPONSE);
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * smbrdr_exchange
+ *
+ * Send the SMB packet pointed by the given handle over
+ * network. Receive the response and decode the packet header.
+ *
+ * From "Implementing CIFS" book, SMB requests always have an even sequence
+ * number and replies always have an odd.
+ *
+ * With the original code, if the SMB Redirector skips the counter increment
+ * in the event of any failure during SmbSessionSetupAndX, it causes the
+ * domain controller to fail the next SMB request(odd sequence number)
+ * with ACCESS_DENIED.
+ *
+ * Returns:
+ *
+ * status code returned by smbrdr_hdr_process()
+ * NT_STATUS_INTERNAL_ERROR crypto framework failure
+ * NT_STATUS_UNEXPECTED_NETWORK_ERROR send/receive failed
+ * NT_STATUS_SUCCESS successful
+ */
+DWORD
+smbrdr_exchange(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr, long timeout)
+{
+ smb_sign_ctx_t *sign_ctx;
+ smb_msgbuf_t *mb;
+ DWORD status;
+ int rc;
+
+ mb = &srh->srh_mbuf;
+ sign_ctx = &srh->srh_session->sign_ctx;
+
+ if (smbrdr_sign(sign_ctx, mb) != SMBAUTH_SUCCESS)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ rc = nb_exchange(srh->srh_session->sock,
+ srh->srh_buf, smb_msgbuf_used(mb),
+ srh->srh_buf, SMBRDR_REQ_BUFSZ, timeout);
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "SmbrdrExchange[%d]: failed (%d)",
+ srh->srh_cmd, rc);
+
+ if (srh->srh_cmd != SMB_COM_ECHO) {
+ /*
+ * Since SMB echo is used to check the session
+ * status then don't destroy the session if it's
+ * SMB echo.
+ */
+ srh->srh_session->state = SDB_SSTATE_STALE;
+ }
+ smb_mac_inc_seqnum(sign_ctx);
+ return (NT_STATUS_UNEXPECTED_NETWORK_ERROR);
+ }
+
+ /* initialize for processing response */
+ smb_msgbuf_init(mb, srh->srh_buf, rc, srh->srh_mbflags);
+
+ status = smbrdr_hdr_process(srh, smb_hdr);
+ if (status != NT_STATUS_SUCCESS) {
+ smb_mac_inc_seqnum(sign_ctx);
+ return (status);
+ }
+
+ /* Signature validation */
+ if (!smbrdr_sign_chk(sign_ctx, mb, smb_hdr->extra.extra.security_sig)) {
+ syslog(LOG_ERR, "SmbrdrExchange[%d]: bad signature",
+ srh->srh_cmd);
+ return (NT_STATUS_INVALID_NETWORK_RESPONSE);
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * smbrdr_handle_free
+ *
+ * Frees the memories allocated for the given handle.
+ */
+void
+smbrdr_handle_free(smbrdr_handle_t *srh)
+{
+ if (srh) {
+ smb_msgbuf_term(&srh->srh_mbuf);
+ free(srh->srh_buf);
+ }
+}
+
+
+/*
+ * smbrdr_sign_init
+ *
+ * This function is called from SessionSetup and initialize the
+ * signing context for the session if the connected user isn't
+ * anonymous. This has to call before smbrdr_request_init()
+ * because it modifies smb_flags2.
+ *
+ * The following description is taken out from the "Implementing CIFS"
+ * book(pg. 304):
+ *
+ * "Once the MAC signing has been initialized within a session, all
+ * messages are numbered using the same counters and signed using
+ * the same Session Key. This is true even if additional SESSION
+ * SETUP ANDX exchanges occur."
+ *
+ * The original SMB packet signing implementation calculates a MAC
+ * key each time the SMB Redirector sends the SmbSessionSetupAndx
+ * request for any non-anonymous/non-guest user which is not desired
+ * whenever there is a change in the user session key.
+ *
+ * If NTLMv2 authentication is used, the MAC key generated for each
+ * SessionSetup is unique. Since the domain controller expects the
+ * signature of all incoming requests are signed by the same MAC key
+ * (i.e. the one that generated for the first non-anonymous SessionSetup),
+ * access denied is returned for any subsequent SmbSessionSetupAndX
+ * request.
+ */
+int
+smbrdr_sign_init(struct sdb_session *session, struct sdb_logon *logon)
+{
+ smb_sign_ctx_t *sign_ctx;
+ int rc = 0;
+
+ sign_ctx = &session->sign_ctx;
+
+ if ((sign_ctx->ssc_flags & SMB_SCF_REQUIRED) &&
+ !(sign_ctx->ssc_flags & SMB_SCF_STARTED) &&
+ (logon->type != SDB_LOGON_ANONYMOUS)) {
+ if (smb_mac_init(sign_ctx, &logon->auth) != SMBAUTH_SUCCESS) {
+ syslog(LOG_DEBUG, "SmbrdrSignInit: mac_init failed");
+ return (-1);
+ }
+ sign_ctx->ssc_flags |=
+ (SMB_SCF_STARTED | SMB_SCF_KEY_ISSET_THIS_LOGON);
+ session->smb_flags2 |= SMB_FLAGS2_SMB_SECURITY_SIGNATURE;
+ syslog(LOG_DEBUG, "SmbrdrSignInit: mac key is set");
+ rc = 1;
+ }
+
+ return (rc);
+}
+
+/*
+ * smbrdr_sign_fini
+ *
+ * Invalidate the MAC key if the first non-anonymous/non-guest user logon
+ * fail.
+ */
+int
+smbrdr_sign_fini(struct sdb_session *session)
+{
+ smb_sign_ctx_t *sign_ctx;
+ int rc = 0;
+
+ sign_ctx = &session->sign_ctx;
+
+ if (sign_ctx->ssc_flags & SMB_SCF_KEY_ISSET_THIS_LOGON) {
+ sign_ctx->ssc_flags &= ~SMB_SCF_STARTED;
+ sign_ctx->ssc_flags &= ~SMB_SCF_KEY_ISSET_THIS_LOGON;
+ sign_ctx->ssc_seqnum = 0;
+ syslog(LOG_DEBUG, "SmbrdrSignFini: packet signing stopped");
+ rc = 1;
+ }
+
+ return (rc);
+}
+
+/*
+ * smbrdr_sign_unset_key
+ *
+ * The SMB_SCF_KEY_ISSET_THIS_LOGON should be unset upon the successful
+ * SmbSessionSetupAndX request for the first non-anonymous/non-guest
+ * logon.
+ */
+int
+smbrdr_sign_unset_key(struct sdb_session *session)
+{
+ smb_sign_ctx_t *sign_ctx;
+ int rc = 0;
+
+ sign_ctx = &session->sign_ctx;
+
+ if (sign_ctx->ssc_flags & SMB_SCF_KEY_ISSET_THIS_LOGON) {
+ sign_ctx->ssc_flags &= ~SMB_SCF_KEY_ISSET_THIS_LOGON;
+ syslog(LOG_DEBUG, "SmbrdrSignUnsetKey: unset KEY_ISSET flag");
+ rc = 1;
+ }
+
+ return (rc);
+}
+
+/*
+ * smbrdr_handle_setup
+ *
+ * Allocates a buffer for sending/receiving a SMB request.
+ * Initialize a smb_msgbuf structure with the allocated buffer.
+ * Setup given handle (srh) with the specified information.
+ *
+ * Returns:
+ *
+ * NT_STATUS_NO_MEMORY not enough memory
+ * NT_STATUS_SUCCESS successful
+ */
+static DWORD
+smbrdr_handle_setup(smbrdr_handle_t *srh,
+ unsigned char cmd,
+ struct sdb_session *session,
+ struct sdb_logon *logon,
+ struct sdb_netuse *netuse)
+{
+ srh->srh_buf = (unsigned char *)malloc(SMBRDR_REQ_BUFSZ);
+ if (srh->srh_buf == 0) {
+ syslog(LOG_ERR, "Smbrdr[%d]: resource shortage", cmd);
+ return (NT_STATUS_NO_MEMORY);
+ }
+ bzero(srh->srh_buf, SMBRDR_REQ_BUFSZ);
+
+ srh->srh_mbflags = (session->remote_caps & CAP_UNICODE)
+ ? SMB_MSGBUF_UNICODE : 0;
+
+ smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf,
+ SMBRDR_REQ_BUFSZ, srh->srh_mbflags);
+
+ srh->srh_cmd = cmd;
+ srh->srh_session = session;
+ srh->srh_user = logon;
+ srh->srh_tree = netuse;
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * smbrdr_hdr_setup
+ *
+ * Build an SMB header based on the information in the given handle.
+ * The SMB header is described in section 3.2 of the CIFS spec.
+ * As this is a canned function, no error checking is performed here.
+ * The return value from smb_msgbuf_encode is simply returned to the caller.
+ */
+static int
+smbrdr_hdr_setup(smbrdr_handle_t *srh)
+{
+ static unsigned short my_pid = 0;
+
+ if (!my_pid)
+ my_pid = getpid();
+
+ return (smb_msgbuf_encode(&srh->srh_mbuf, "Mb4.bw12.wwww",
+ srh->srh_cmd,
+ srh->srh_session->smb_flags,
+ srh->srh_session->smb_flags2,
+ (srh->srh_tree) ? srh->srh_tree->tid : 0,
+ my_pid,
+ (srh->srh_user) ? srh->srh_user->uid : 0,
+ 0 /* mid */));
+}
+
+/*
+ * Canned SMB header decode.
+ */
+static int
+smb_decode_nt_hdr(smb_msgbuf_t *mb, smb_hdr_t *hdr)
+{
+ return (smb_msgbuf_decode(mb, SMB_HEADER_NT_FMT,
+ &hdr->command,
+ &hdr->status.ntstatus,
+ &hdr->flags,
+ &hdr->flags2,
+ &hdr->pid_high,
+ SMB_SIG_SIZE,
+ &hdr->extra.extra.security_sig,
+ &hdr->tid,
+ &hdr->pid,
+ &hdr->uid,
+ &hdr->mid));
+}
+
+/*
+ * smbrdr_hdr_process
+ *
+ * Assuming 'srh->srh_mbuf' contains a response from a Windows client,
+ * decodes the 32 bytes SMB header.
+ *
+ * Returns:
+ *
+ * NT_STATUS_INVALID_NETWORK_RESPONSE error decoding the header
+ * NT_STATUS_REPLY_MESSAGE_MISMATCH response doesn't match the request
+ * NT_STATUS_SUCCESS successful
+ * smb_hdr->status.ntstatus error returned by server
+ */
+static DWORD
+smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr)
+{
+ int rc;
+
+ /*
+ * Returns the number of decoded bytes on success
+ * or some negative MSGBUF_XXX error code on failure
+ */
+ rc = smb_decode_nt_hdr(&srh->srh_mbuf, smb_hdr);
+
+ if (rc < SMB_HEADER_LEN) {
+ syslog(LOG_ERR, "Smbrdr[%d]: bad SMB header (%d)",
+ srh->srh_cmd, rc);
+ return (NT_STATUS_INVALID_NETWORK_RESPONSE);
+ }
+
+ if (smb_hdr->status.ntstatus != 0) {
+ /*
+ * MSDN article: 193839
+ * "The buffer overflow status is usually returned by a Windows
+ * server to inform the client that there is more data in its
+ * buffer than it could put in the packet.
+ *
+ * The buffer overflow should not be considered as an error.
+ * Subsequent SmbReadX request is required to obtain the
+ * remaining data in the server's buffer.
+ */
+ if (NT_SC_VALUE(smb_hdr->status.ntstatus)
+ == NT_STATUS_BUFFER_OVERFLOW) {
+ syslog(LOG_DEBUG, "Smbrdr[%d]: %s", srh->srh_cmd,
+ xlate_nt_status(smb_hdr->status.ntstatus));
+ } else {
+ syslog(LOG_ERR, "Smbrdr[%d]: request failed (%s)",
+ srh->srh_cmd,
+ xlate_nt_status(smb_hdr->status.ntstatus));
+ return (smb_hdr->status.ntstatus);
+ }
+ }
+
+ if (smb_hdr->command != srh->srh_cmd) {
+ syslog(LOG_ERR, "Smbrdr[%d]: reply mismatch (%d)",
+ srh->srh_cmd, smb_hdr->command);
+ return (NT_STATUS_REPLY_MESSAGE_MISMATCH);
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
+
+/*
+ * smbrdr_sign
+ *
+ * Signs the given outgoing packet according to the
+ * specified signing context.
+ *
+ * The client and server each maintain an integer counter
+ * which they initialize to zero. Both counters are
+ * incremented for every SMB message - that's once for a
+ * request and once for a reply. As a result, requests sent
+ * by SMB Redirector always have an even sequence number
+ * and replies from the Windows server always have an odd
+ * number.
+ *
+ * Based on the observed Windows 2003 behavior, any SMB
+ * request will fail with NT_STATUS_ACCESS_DENIED if its
+ * sequence number is not even.
+ *
+ * The function can fail if there is trouble with the cryptographic
+ * framework and if that happens SMBAUTH_FAILURE is returned. In the
+ * normal case SMBAUTH_SUCCESS is returned.
+ */
+static int
+smbrdr_sign(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb)
+{
+ if (sign_ctx->ssc_flags & SMB_SCF_STARTED) {
+ if (sign_ctx->ssc_seqnum % 2) {
+ syslog(LOG_DEBUG, "SmbrdrSign: even sequence number"
+ " is expected(%d)",
+ sign_ctx->ssc_seqnum);
+ }
+ if (smb_mac_sign(sign_ctx, smb_msgbuf_base(mb),
+ smb_msgbuf_used(mb)) != SMBAUTH_SUCCESS)
+ return (SMBAUTH_FAILURE);
+ sign_ctx->ssc_seqnum++;
+ }
+ return (SMBAUTH_SUCCESS);
+}
+
+
+/*
+ * smbrdr_sign_chk
+ *
+ * Validates SMB MAC signature in the in-coming message.
+ * Return 1 if the signature are match; otherwise, return 0;
+ *
+ * When packet signing is enabled, the sequence number kept in the
+ * sign_ctx structure will be incremented when a SMB request is
+ * sent and upon the receipt of the first SmbTransact response
+ * if SMB fragmentation occurs.
+ */
+static int
+smbrdr_sign_chk(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb,
+ unsigned char *signature)
+{
+ int sign_ok = 1;
+
+ if (sign_ctx->ssc_flags & SMB_SCF_STARTED) {
+ (void) memcpy(sign_ctx->ssc_sign, signature, SMB_SIG_SIZE);
+ sign_ok = smb_mac_chk(sign_ctx, smb_msgbuf_base(mb),
+ smb_msgbuf_size(mb));
+ sign_ctx->ssc_seqnum++;
+ }
+
+ return (sign_ok);
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c
new file mode 100644
index 0000000000..7dee7e41d2
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c
@@ -0,0 +1,710 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SMB session logon and logoff functions. See CIFS section 4.1.
+ */
+
+#include <pthread.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <synch.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <smbsrv/libsmbrdr.h>
+
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/smb.h>
+#include <smbrdr_ipc_util.h>
+#include <smbrdr.h>
+
+#define SMBRDR_PWD_NULL 0
+#define SMBRDR_PWD_USER 1
+#define SMBRDR_PWD_HASH 2
+
+static int smbrdr_smb_session_setupandx(struct sdb_logon *logon);
+static boolean_t smbrdr_logon_validate(char *server, char *username);
+static struct sdb_logon *smbrdr_logon_init(struct sdb_session *session,
+ char *username, char *pwd, int pwd_type);
+static int smbrdr_logon_user(char *server, char *username, char *pwd,
+ int pwd_type);
+static int smbrdr_authenticate(char *primary_domain, char *account_name,
+ char *pwd, int pwd_type);
+
+/*
+ * mlsvc_anonymous_logon
+ *
+ * Set up an anonymous session. If the session to the resource domain
+ * controller appears to be okay we shouldn't need to do anything here.
+ * Otherwise we clean up the stale session and create a new one.
+ */
+int
+mlsvc_anonymous_logon(char *domain_controller, char *domain_name,
+ char **username)
+{
+ int rc = 0;
+
+ if (username == NULL) {
+ syslog(LOG_ERR, "smbrdr: (anon logon) %s",
+ xlate_nt_status(NT_STATUS_INVALID_PARAMETER));
+ return (-1);
+ }
+
+ /*
+ * if the system is configured to establish Authenticated IPC
+ * connection to PDC
+ */
+ if (smbrdr_ipc_get_mode() == MLSVC_IPC_ADMIN) {
+ rc = mlsvc_admin_logon(domain_controller, domain_name);
+ /*
+ * it is possible for the system to fallback to use
+ * anonymous IPC
+ */
+ if (smbrdr_ipc_get_mode() != MLSVC_IPC_ADMIN)
+ *username = MLSVC_ANON_USER;
+ else
+ *username = smbrdr_ipc_get_user();
+
+ syslog(LOG_DEBUG, "smbrdr: (admin logon) %s", *username);
+ return (rc);
+ }
+
+ *username = MLSVC_ANON_USER;
+
+ if (smbrdr_logon_validate(domain_controller, MLSVC_ANON_USER))
+ /* session & user are good use them */
+ return (0);
+
+
+ if (smbrdr_negotiate(domain_name) != 0) {
+ syslog(LOG_ERR, "smbrdr: (anon logon) negotiate <%s> failed",
+ (domain_name ? domain_name : "NoName"));
+ return (-1);
+ }
+
+ if (smbrdr_logon_user(domain_controller, MLSVC_ANON_USER, 0,
+ SMBRDR_PWD_NULL) < 0) {
+ syslog(LOG_ERR, "smbrdr: (anon logon) logon failed");
+ rc = -1;
+ }
+
+ return (rc);
+}
+
+int
+mlsvc_user_getauth(char *domain_controller, char *username,
+ smb_auth_info_t *auth)
+{
+ struct sdb_session *session;
+
+ if (auth) {
+ bzero(auth, sizeof (smb_auth_info_t));
+ session = smbrdr_session_lock(domain_controller, username,
+ SDB_SLCK_READ);
+ if (session) {
+ *auth = session->logon.auth;
+ smbrdr_session_unlock(session);
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+/*
+ * mlsvc_user_logon
+ *
+ * Set up a user session. If the session to the resource domain controller
+ * appears to be okay we shouldn't need to do anything here. Otherwise we
+ * clean up the stale session and create a new one. Once a session is
+ * established, we leave it intact. It should only need to be set up again
+ * due to an inactivity timeout or a domain controller reset.
+ */
+int
+mlsvc_user_logon(char *domain_controller, char *domain_name, char *username,
+ char *password)
+{
+ int erc;
+
+ if (smbrdr_logon_validate(domain_controller, username))
+ return (0);
+
+ if (smbrdr_negotiate(domain_name) != 0) {
+ syslog(LOG_ERR, "smbrdr: (user logon) negotiate failed");
+ return (-1);
+ }
+
+ erc = smbrdr_authenticate(domain_name, username, password,
+ SMBRDR_PWD_USER);
+
+ return ((erc == AUTH_USER_GRANT) ? 0 : -1);
+}
+
+/*
+ * mlsvc_admin_logon
+ *
+ * Unlike mlsvc_user_logon, mlsvc_admin_logon doesn't take
+ * any username or password as function arguments.
+ */
+int
+mlsvc_admin_logon(char *domain_controller, char *domain_name)
+{
+ char password[PASS_LEN + 1];
+ int erc;
+ char *username, *dummy;
+
+ username = smbrdr_ipc_get_user();
+ (void) memcpy(password, smbrdr_ipc_get_passwd(), SMBAUTH_HASH_SZ);
+
+ if (*username == 0) {
+ syslog(LOG_ERR, "smbrdr: admin logon (no admin user)");
+ return (-1);
+ }
+
+ if (smbrdr_logon_validate(domain_controller, username))
+ return (0);
+
+ if (smbrdr_negotiate(domain_name) != 0) {
+ syslog(LOG_ERR, "smbrdr: admin logon (negotiate failed)");
+ return (-1);
+ }
+
+ erc = smbrdr_authenticate(domain_name, username, password,
+ SMBRDR_PWD_HASH);
+
+ /*
+ * Fallback to anonmyous IPC logon if the IPC password hash is no
+ * longer valid. It happens when the administrator password has
+ * been reset from the Domain Controller.
+ */
+ if (erc < 0 && smbrdr_ipc_is_valid()) {
+ if (!smbrdr_ipc_is_fallback())
+ syslog(LOG_DEBUG, "smbrdr: admin logon "
+ "(fallback to anonymous IPC)");
+ smbrdr_ipc_set_fallback();
+ smbrdr_ipc_save_mode(IPC_MODE_FALLBACK_ANON);
+ erc = mlsvc_anonymous_logon(domain_controller, domain_name,
+ &dummy);
+ }
+
+ return ((erc == AUTH_USER_GRANT) ? 0 : -1);
+}
+
+/*
+ * smbrdr_authenticate
+ *
+ * Authenticate primary_domain\account_name.
+ *
+ * Returns:
+ * 0 User access granted
+ * 1 Guest access granted
+ * 2 IPC access granted
+ * (<0) Error
+ */
+static int
+smbrdr_authenticate(char *primary_domain, char *account_name,
+ char *pwd, int pwd_type)
+{
+ smb_ntdomain_t *di;
+
+ if (pwd == NULL)
+ return (AUTH_USER_GRANT | AUTH_IPC_ONLY_GRANT);
+
+
+ if ((di = smb_getdomaininfo(0)) == 0) {
+ syslog(LOG_ERR, "MlsvcAuthenticate[%s]: %s", account_name,
+ xlate_nt_status(NT_STATUS_CANT_ACCESS_DOMAIN_INFO));
+ return (-1);
+ }
+
+ /*
+ * Ensure that the domain name is uppercase.
+ */
+ (void) utf8_strupr(primary_domain);
+
+ /*
+ * We can only authenticate a user via a controller in the user's
+ * primary domain. If the user's domain name doesn't match the
+ * authenticating server's domain, reject the request before we
+ * create a logon entry for the user. Although the logon will be
+ * denied eventually, we don't want a logon structure for a user
+ * in the resource domain that is pointing to a session structure
+ * for the account domain. If this happened to be our resource
+ * domain user, we would not be able to use that account to connect
+ * to the resource domain.
+ */
+ if (strcasecmp(di->domain, primary_domain)) {
+ syslog(LOG_ERR, "MlsvcAuthenticate: %s\\%s: not account domain",
+ primary_domain, account_name);
+ return (-2);
+ }
+
+ return (smbrdr_logon_user(di->server, account_name, pwd, pwd_type));
+}
+
+/*
+ * smbrdr_logon_user
+ *
+ * This is the entry point for logging a user onto the domain. The
+ * session structure should have been obtained via a successful call
+ * to smbrdr_smb_connect. We allocate a logon structure to hold the
+ * user details and attempt to logon using smbrdr_smb_session_setupandx. Note
+ * that we expect the password fields to have been encrypted before
+ * this call.
+ *
+ * On success, the logon structure will be returned. Otherwise a null
+ * pointer will be returned.
+ */
+static int
+smbrdr_logon_user(char *server, char *username, char *pwd, int pwd_type)
+{
+ struct sdb_session *session;
+ struct sdb_logon *logon;
+ struct sdb_logon old_logon;
+
+ if (server == 0 || username == 0 ||
+ ((pwd == 0) && (pwd_type != SMBRDR_PWD_NULL))) {
+ return (-1);
+ }
+
+ session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE);
+ if (session == 0) {
+ syslog(LOG_ERR, "smbrdr: (logon[%s]) no session with %s",
+ username, server);
+ return (-1);
+ }
+
+ bzero(&old_logon, sizeof (struct sdb_logon));
+
+ logon = &session->logon;
+ if (logon->type != SDB_LOGON_NONE) {
+ if (strcasecmp(logon->username, username) == 0) {
+ /* The requested user has already been logged in */
+ smbrdr_session_unlock(session);
+ return ((logon->type == SDB_LOGON_GUEST)
+ ? AUTH_GUEST_GRANT : AUTH_USER_GRANT);
+ }
+
+ old_logon = *logon;
+ }
+
+ logon = smbrdr_logon_init(session, username, pwd, pwd_type);
+
+ if (logon == 0) {
+ syslog(LOG_ERR, "smbrdr: (logon[%s]) resource shortage",
+ username);
+ smbrdr_session_unlock(session);
+ return (-1);
+ }
+
+ if (smbrdr_smb_session_setupandx(logon) < 0) {
+ free(logon);
+ smbrdr_session_unlock(session);
+ return (-1);
+ }
+
+ session->logon = *logon;
+ free(logon);
+
+ if (old_logon.type != SDB_LOGON_NONE) {
+ (void) smbrdr_smb_logoff(&old_logon);
+ }
+
+ smbrdr_session_unlock(session);
+ return ((logon->type == SDB_LOGON_GUEST)
+ ? AUTH_GUEST_GRANT : AUTH_USER_GRANT);
+}
+
+
+/*
+ * smbrdr_smb_session_setupandx
+ *
+ * Build and send an SMB session setup command. This is used to log a
+ * user onto the domain. See CIFS section 4.1.2.
+ *
+ * Returns 0 on success. Otherwise returns a -ve error code.
+ */
+static int
+smbrdr_smb_session_setupandx(struct sdb_logon *logon)
+{
+ struct sdb_session *session;
+ smb_hdr_t smb_hdr;
+ smbrdr_handle_t srh;
+ smb_msgbuf_t *mb;
+ char *native_os;
+ char *native_lanman;
+ unsigned short data_bytes;
+ unsigned short guest;
+ unsigned long capabilities;
+ unsigned short null_size;
+ size_t (*strlen_fn)(const char *s);
+ DWORD status;
+ int rc;
+
+ /*
+ * Paranoia check - we should never get this
+ * far without a valid session structure.
+ */
+ if ((session = logon->session) == 0) {
+ syslog(LOG_ERR, "smbrdr_smb_session_setupandx: no data");
+ return (-1);
+ }
+
+ if (session->remote_caps & CAP_UNICODE) {
+ strlen_fn = mts_wcequiv_strlen;
+ null_size = sizeof (mts_wchar_t);
+ session->smb_flags2 |= SMB_FLAGS2_UNICODE;
+ } else {
+ strlen_fn = strlen;
+ null_size = sizeof (char);
+ }
+
+ if (smbrdr_sign_init(session, logon) < 0)
+ return (-1);
+
+ status = smbrdr_request_init(&srh, SMB_COM_SESSION_SETUP_ANDX,
+ session, 0, 0);
+
+ if (status != NT_STATUS_SUCCESS) {
+ (void) smbrdr_sign_fini(session);
+ syslog(LOG_ERR, "SmbrdrSessionSetup: %s",
+ xlate_nt_status(status));
+ return (-1);
+ }
+ mb = &srh.srh_mbuf;
+
+ /*
+ * Regardless of the server's capabilities or what's
+ * reported in smb_flags2, we should report our full
+ * capabilities.
+ */
+ capabilities = CAP_UNICODE | CAP_NT_SMBS | CAP_STATUS32;
+
+ /*
+ * Compute the BCC for unicode or ASCII strings.
+ */
+ data_bytes = logon->auth.ci_len + logon->auth.cs_len + null_size;
+ data_bytes += strlen_fn(session->native_os) + null_size;
+ data_bytes += strlen_fn(session->native_lanman) + null_size;
+
+ if (logon->type == SDB_LOGON_ANONYMOUS) {
+ /*
+ * Anonymous logon: no username or domain name.
+ * We still need to include two null characters.
+ */
+ data_bytes += (2 * null_size);
+
+ rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllwlu.u.",
+ 13, /* smb_wct */
+ 0xff, /* AndXCommand (none) */
+ 32 + 26 + 3 + data_bytes, /* AndXOffset */
+ SMBRDR_REQ_BUFSZ, /* MaxBufferSize */
+ 1, /* MaxMpxCount */
+ 0, /* VcNumber */
+ 0, /* SessionKey */
+ 1, /* CaseInsensitivePassLength */
+ 0, /* CaseSensitivePassLength */
+ 0, /* Reserved */
+ capabilities, /* Capabilities */
+ data_bytes, /* smb_bcc */
+ 0, /* No user or domain */
+ session->native_os, /* NativeOS */
+ session->native_lanman); /* NativeLanMan */
+ } else {
+ data_bytes += strlen_fn(logon->username) + null_size;
+ data_bytes += strlen_fn(session->di.domain) + null_size;
+
+ rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllw#c#cuuu.u.",
+ 13, /* smb_wct */
+ 0xff, /* AndXCommand (none) */
+ 32 + 26 + 3 + data_bytes, /* AndXOffset */
+ SMBRDR_REQ_BUFSZ, /* MaxBufferSize */
+ 1, /* MaxMpxCount */
+ session->vc, /* VcNumber */
+ session->sesskey, /* SessionKey */
+ logon->auth.ci_len, /* CaseInsensitivePassLength */
+ logon->auth.cs_len, /* CaseSensitivePassLength */
+ 0, /* Reserved */
+ capabilities, /* Capabilities */
+ data_bytes, /* smb_bcc */
+ logon->auth.ci_len, /* ci length spec */
+ logon->auth.ci, /* CaseInsensitivePassword */
+ logon->auth.cs_len, /* cs length spec */
+ logon->auth.cs, /* CaseSensitivePassword */
+ logon->username, /* AccountName */
+ session->di.domain, /* PrimaryDomain */
+ session->native_os, /* NativeOS */
+ session->native_lanman); /* NativeLanMan */
+ }
+
+ if (rc <= 0) {
+ syslog(LOG_ERR, "smbrdr_smb_session_setupandx: encode failed");
+ smbrdr_handle_free(&srh);
+ (void) smbrdr_sign_fini(session);
+ return (-1);
+ }
+
+ status = smbrdr_exchange(&srh, &smb_hdr, 0);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrSessionSetup: %s",
+ xlate_nt_status(status));
+ smbrdr_handle_free(&srh);
+ (void) smbrdr_sign_fini(session);
+ return (-1);
+ }
+
+ rc = smb_msgbuf_decode(mb, "5.w2.u", &guest, &native_os);
+
+ /*
+ * There was a problem in decoding response from
+ * a Samba 2.x PDC. This server sends strings in ASCII
+ * format and there is one byte with value 0 between
+ * native_os and native_lm:
+ *
+ * FF 53 4D 42 73 00 .SMBs.
+ * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................
+ * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 01 ......d.........
+ * 00 1C 00 55 6E 69 78 00 53 61 6D 62 61 20 32 2E ...Unix.Samba.2.
+ * 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 2.8a.SAMBA_DOM.
+ *
+ * The byte doesn't seem to be padding because when change in
+ * native OS from Unix to Unix1 the 0 byte is still there:
+ *
+ * FF 53 4D 42 73 00 .SMBs.
+ * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................
+ * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 00 ......d.........
+ * 00 1D 00 55 6E 69 78 31 00 53 61 6D 62 61 20 32 ...Unix1.Samba.2
+ * 2E 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 .2.8a.SAMBA_DOM.
+ */
+ if (rc > 0) {
+ if (session->remote_caps & CAP_UNICODE)
+ rc = smb_msgbuf_decode(mb, "u", &native_lanman);
+ else
+ rc = smb_msgbuf_decode(mb, ".u", &native_lanman);
+ }
+
+ if (rc <= 0) {
+ syslog(LOG_ERR, "RdrSessionSetup: decode failed");
+ smbrdr_handle_free(&srh);
+ (void) smbrdr_sign_fini(session);
+ return (-1);
+ }
+
+ session->remote_os = smbnative_os_value(native_os);
+ session->remote_lm = smbnative_lm_value(native_lanman);
+ session->pdc_type = smbnative_pdc_value(native_lanman);
+
+ logon->uid = smb_hdr.uid;
+ if (guest)
+ logon->type = SDB_LOGON_GUEST;
+
+ smbrdr_handle_free(&srh);
+ (void) smbrdr_sign_unset_key(session);
+
+ logon->state = SDB_LSTATE_SETUP;
+
+ return (0);
+}
+
+/*
+ * smbrdr_smb_logoff
+ *
+ * Build and send an SMB session logoff (SMB_COM_LOGOFF_ANDX) command.
+ * This is the inverse of an SMB_COM_SESSION_SETUP_ANDX. See CIFS
+ * section 4.1.3. The logon structure should have been obtained from a
+ * successful call to smbrdr_logon_user.
+ *
+ * Returns 0 on success. Otherwise returns a -ve error code.
+ */
+int
+smbrdr_smb_logoff(struct sdb_logon *logon)
+{
+ struct sdb_session *session;
+ smbrdr_handle_t srh;
+ smb_hdr_t smb_hdr;
+ DWORD status;
+ int rc;
+
+ if (logon->state != SDB_LSTATE_SETUP) {
+ /* No user to logoff */
+ bzero(logon, sizeof (struct sdb_logon));
+ return (0);
+ }
+
+ if ((session = logon->session) == 0) {
+ bzero(logon, sizeof (struct sdb_logon));
+ return (0);
+ }
+
+ logon->state = SDB_LSTATE_LOGGING_OFF;
+ smbrdr_netuse_logoff(logon->uid);
+
+ if ((session->state != SDB_SSTATE_NEGOTIATED) &&
+ (session->state != SDB_SSTATE_DISCONNECTING)) {
+ bzero(logon, sizeof (struct sdb_logon));
+ return (0);
+ }
+
+ status = smbrdr_request_init(&srh, SMB_COM_LOGOFF_ANDX,
+ session, logon, 0);
+
+ if (status != NT_STATUS_SUCCESS) {
+ logon->state = SDB_LSTATE_SETUP;
+ syslog(LOG_ERR, "smbrdr: logoff %s (%s)", logon->username,
+ xlate_nt_status(status));
+ return (-1);
+ }
+
+ rc = smb_msgbuf_encode(&srh.srh_mbuf, "bbbww", 2, 0xff, 0, 0, 0);
+ if (rc < 0) {
+ logon->state = SDB_LSTATE_SETUP;
+ smbrdr_handle_free(&srh);
+ syslog(LOG_ERR, "smbrdr: logoff %s (encode failed)",
+ logon->username);
+ return (rc);
+ }
+
+ status = smbrdr_exchange(&srh, &smb_hdr, 0);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "smbrdr: logoff %s (%s)", logon->username,
+ xlate_nt_status(status));
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+ bzero(logon, sizeof (struct sdb_logon));
+ smbrdr_handle_free(&srh);
+ return (rc);
+}
+
+
+/*
+ * smbrdr_logon_init
+ *
+ * Find a slot for account logon information. The account information
+ * is associated with a session so we need a valid session slot before
+ * calling this function. If we already have a record of the specified
+ * account, a pointer to that record is returned. Otherwise we attempt
+ * to allocate a new one.
+ */
+static struct sdb_logon *
+smbrdr_logon_init(struct sdb_session *session, char *username,
+ char *pwd, int pwd_type)
+{
+ struct sdb_logon *logon;
+ int smbrdr_lmcomplvl;
+ int rc;
+
+ logon = (struct sdb_logon *)malloc(sizeof (sdb_logon_t));
+ if (logon == 0)
+ return (0);
+
+ bzero(logon, sizeof (struct sdb_logon));
+ logon->session = session;
+
+ if (strcmp(username, "IPC$") == 0)
+ logon->type = SDB_LOGON_ANONYMOUS;
+ else
+ logon->type = SDB_LOGON_USER;
+
+ (void) strlcpy(logon->username, username, MAX_ACCOUNT_NAME);
+
+ smb_config_rdlock();
+ smbrdr_lmcomplvl = smb_config_getnum(SMB_CI_LM_LEVEL);
+ smb_config_unlock();
+
+ switch (pwd_type) {
+ case SMBRDR_PWD_USER:
+ rc = smb_auth_set_info(username, pwd, 0, session->di.domain,
+ session->challenge_key, session->challenge_len,
+ smbrdr_lmcomplvl, &logon->auth);
+
+ if (rc != 0) {
+ free(logon);
+ return (0);
+ }
+ break;
+
+ case SMBRDR_PWD_HASH:
+ rc = smb_auth_set_info(username, 0, (unsigned char *)pwd,
+ session->di.domain, session->challenge_key,
+ session->challenge_len, smbrdr_lmcomplvl, &logon->auth);
+
+ if (rc != 0) {
+ free(logon);
+ return (0);
+ }
+ break;
+
+ case SMBRDR_PWD_NULL:
+ logon->auth.ci_len = 1;
+ *(logon->auth.ci) = 0;
+ logon->auth.cs_len = 0;
+ break;
+
+ default:
+ /* Unknown password type */
+ free(logon);
+ return (0);
+ }
+
+ logon->state = SDB_LSTATE_INIT;
+ return (logon);
+}
+
+/*
+ * smbrdr_logon_validate
+ *
+ * if session is there and it's alive and also the required
+ * user is already logged in don't need to do anything
+ * otherwise clear the session structure.
+ */
+static boolean_t
+smbrdr_logon_validate(char *server, char *username)
+{
+ struct sdb_session *session;
+ boolean_t valid = B_FALSE;
+
+ session = smbrdr_session_lock(server, username, SDB_SLCK_WRITE);
+ if (session) {
+ if (nb_keep_alive(session->sock) == 0) {
+ valid = B_TRUE;
+ } else {
+ session->state = SDB_SSTATE_STALE;
+ syslog(LOG_DEBUG, "smbrdr: (logon) stale session");
+ }
+
+ smbrdr_session_unlock(session);
+ }
+
+ return (valid);
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netbios.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netbios.c
new file mode 100644
index 0000000000..2066dab051
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netbios.c
@@ -0,0 +1,457 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * NetBIOS support functions. NetBIOS is documented in the following
+ * RFC documents:
+ *
+ * RFC 1001: Protocol Standard for a NetBIOS Service on a TCP/UDP
+ * Transport: Concepts and Methods
+ *
+ * RFC 1002: Protocol Standard for a NetBIOS Service on a TCP/UDP
+ * Transport: Detailed Specifications
+ *
+ */
+
+#define BSD_BYTE_STRING_PROTOTYPES
+
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <synch.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <pthread.h>
+
+#define MAX_NETBIOS_NAME_SIZE 16
+
+#define SESSION_MESSAGE 0x00
+#define SESSION_REQUEST 0x81
+#define POSITIVE_SESSION_RESPONSE 0x82
+#define NEGATIVE_SESSION_RESPONSE 0x83
+#define RETARGET_SESSION_RESPONSE 0x84
+#define SESSION_KEEP_ALIVE 0x85
+
+#define NB_READ_MSG_ERR_EOF 0
+#define NB_READ_MSG_ERR -1
+#define NB_READ_MSG_ERR_OVERFLOW -2
+#define NB_READ_MSG_ERR_UNDERFLOW -3
+#define NB_RCV_MSG_ERR_INVTYPE -4
+
+/*
+ * Semaphore object used to serialize access through NetBIOS exchange.
+ */
+static mutex_t nb_mutex;
+
+static int nb_write_msg(int fd, unsigned char *buf, unsigned count, int type);
+static int nb_read_msg(int fd, unsigned char *buf, unsigned max_buf,
+ int *type, long timeout);
+static int nb_read_itter(int fd, unsigned char *buf, unsigned cnt);
+static int nb_first_level_name_encode(char *name, char *scope,
+ unsigned char *out, int max_out);
+
+
+/*
+ * nb_lock
+ *
+ * Acquire semaphore for doing netbios operations
+ */
+void
+nb_lock()
+{
+ (void) mutex_lock(&nb_mutex);
+}
+
+/*
+ * nb_lock
+ *
+ * Release netbios semaphore.
+ */
+void
+nb_unlock()
+{
+ (void) mutex_unlock(&nb_mutex);
+}
+
+void
+nb_close(int fd)
+{
+ (void) mutex_lock(&nb_mutex);
+ if (fd > 0) {
+ (void) close(fd);
+ (void) printf("[%d] socket (%d) closed\n", pthread_self(), fd);
+ }
+ (void) mutex_unlock(&nb_mutex);
+}
+
+/*
+ * nb_keep_alive
+ *
+ * Send the NetBIOS keep alive message. No response is expected but we
+ * do need to ignore keep-alive messages in nb_exchange. The semaphore
+ * ensures compatibility/serialization with nb_exchange to allow us to
+ * call this function from a separate thread.
+ */
+int
+nb_keep_alive(int fd)
+{
+ int nothing;
+ int rc;
+
+ (void) mutex_lock(&nb_mutex);
+
+ rc = nb_write_msg(fd, (unsigned char *)&nothing, 0, SESSION_KEEP_ALIVE);
+ if (rc < 0)
+ syslog(LOG_ERR, "nb_keep_alive: write failed");
+
+ (void) mutex_unlock(&nb_mutex);
+ return (rc);
+}
+
+/*
+ * nb_send
+ *
+ * This is just a wrapper round the nb_write_msg.
+ */
+int
+nb_send(int fd, unsigned char *send_buf, unsigned send_cnt)
+{
+ int rc;
+
+ if ((rc = nb_write_msg(fd, send_buf, send_cnt, SESSION_MESSAGE)) < 0)
+ syslog(LOG_ERR, "nb_send: write failed: rc=%d", rc);
+
+ return (rc);
+}
+
+/*
+ * nb_rcv
+ *
+ * This is a wrapper round the nb_read_msg() so that if a
+ * keep-alive message is received, just discard it and go
+ * back to look for the real response.
+ */
+int
+nb_rcv(int fd, unsigned char *recv_buf, unsigned recv_max, long timeout)
+{
+ int rc;
+ int type;
+
+ do {
+ rc = nb_read_msg(fd, recv_buf, recv_max, &type, timeout);
+ if (rc < 0) {
+ syslog(LOG_ERR, "nb_rcv: read failed: rc=%d", rc);
+ return (rc);
+ }
+ } while (type == SESSION_KEEP_ALIVE);
+
+ if (type != SESSION_MESSAGE) {
+ syslog(LOG_ERR, "nb_rcv: invalid type: %d", type);
+ return (NB_RCV_MSG_ERR_INVTYPE);
+ }
+
+ return (rc);
+}
+
+/*
+ * nb_exchange
+ *
+ * This is the NetBIOS workhorse function where we do the send/receive
+ * message exchange. A semaphore is used to serialize access because
+ * we may get swapped out between the send and receive operations and
+ * another thread could enter here and collect our response. If a
+ * keep-alive message is received, just discard it and go back to look
+ * for the real response.
+ *
+ * Note: With the addition of support for SMB over TCP, this function
+ * may be exchanging NetBIOS-less SMB data.
+ */
+int
+nb_exchange(int fd, unsigned char *send_buf, unsigned send_cnt,
+ unsigned char *recv_buf, unsigned recv_max, long timeout)
+{
+ int rc;
+
+ (void) mutex_lock(&nb_mutex);
+
+ rc = nb_send(fd, send_buf, send_cnt);
+ if (rc == send_cnt)
+ rc = nb_rcv(fd, recv_buf, recv_max, timeout);
+
+ (void) mutex_unlock(&nb_mutex);
+ return (rc);
+}
+
+/*
+ * nb_session_request
+ *
+ * We should never see descriptor 0 (stdin) or -1.
+ */
+int
+nb_session_request(int fd, char *called_name, char *called_scope,
+ char *calling_name, char *calling_scope)
+{
+ unsigned char sr_buf[200];
+ int len;
+ int rc;
+ int type;
+
+ if (fd == 0 || fd == -1)
+ return (-1);
+
+ rc = nb_first_level_name_encode(called_name, called_scope, sr_buf, 100);
+ len = rc;
+ rc = nb_first_level_name_encode(calling_name, calling_scope,
+ sr_buf+len, 100);
+ len += rc;
+
+ (void) mutex_lock(&nb_mutex);
+
+ rc = nb_write_msg(fd, (unsigned char *)sr_buf, len, SESSION_REQUEST);
+ if (rc < 0) {
+ syslog(LOG_ERR, "nb_session_request: write failed:"
+ " rc=%d", rc);
+ (void) mutex_unlock(&nb_mutex);
+ return (rc);
+ }
+
+ for (;;) {
+ rc = nb_read_msg(fd, (unsigned char *)sr_buf,
+ sizeof (sr_buf), &type, 0);
+ if (rc < 0) {
+ (void) mutex_unlock(&nb_mutex);
+ return (rc);
+ }
+
+ if ((rc == 0) && (type == -1)) {
+ (void) mutex_unlock(&nb_mutex);
+ return (-1); /* EOF */
+ }
+
+ if (type == POSITIVE_SESSION_RESPONSE) {
+ (void) mutex_unlock(&nb_mutex);
+ return (0);
+ }
+
+ if (type == NEGATIVE_SESSION_RESPONSE) {
+ (void) mutex_unlock(&nb_mutex);
+ return (-1);
+ }
+ }
+
+ /* NOTREACHED */
+ (void) mutex_unlock(&nb_mutex);
+ return (-1);
+}
+
+
+
+/*
+ * nb_write_msg
+ */
+static int
+nb_write_msg(int fd, unsigned char *buf, unsigned count, int type)
+{
+ struct iovec iov[2];
+ unsigned char header[4];
+ int rc;
+
+ if (fd == 0 || fd == -1) {
+ /*
+ * We should never see descriptor 0 (stdin).
+ */
+ syslog(LOG_ERR, "nb_write_msg: invalid descriptor (%d)", fd);
+ return (-1);
+ }
+
+ /*
+ * The NetBIOS message length is limited to 17 bits but
+ * we use this layer for SMB over both NetBIOS and TCP
+ * (NetBIOS-less SMB). When using SMB over TCP the length
+ * is 24 bits but we are ignoring that for now because we
+ * don't expect any messages larger than 64KB.
+ */
+ header[0] = type;
+ header[1] = (count >> 16) & 1;
+ header[2] = count >> 8;
+ header[3] = count;
+
+ iov[0].iov_base = (caddr_t)header;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = (caddr_t)buf;
+ iov[1].iov_len = count;
+
+ rc = writev(fd, iov, 2);
+ if (rc != 4 + count) {
+ syslog(LOG_ERR, "nb_write_msg: writev rc=%d", rc);
+ return (-3); /* error */
+ }
+
+ return (count);
+}
+
+
+/*
+ * nb_read_msg
+ *
+ * Added select to ensure that we don't block forever waiting for a
+ * message.
+ */
+static int
+nb_read_msg(int fd, unsigned char *buf, unsigned max_buf,
+ int *type, long timeout)
+{
+ unsigned char header[4];
+ int length;
+ int rc;
+ fd_set readfds;
+ struct timeval tval;
+
+ *type = -1;
+
+ if (fd == 0 || fd == -1) {
+ /*
+ * We should never see descriptor 0 (stdin).
+ */
+ syslog(LOG_ERR, "nb_write_msg: invalid descriptor (%d)", fd);
+ return (NB_READ_MSG_ERR);
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ tval.tv_sec = (timeout == 0) ? 45 : timeout;
+ tval.tv_usec = 0;
+
+ if ((rc = select(fd + 1, &readfds, 0, 0, &tval)) <= 0) {
+ syslog(LOG_ERR, "nb_read_msg: select: %d", rc);
+ return (NB_READ_MSG_ERR);
+ }
+
+ if ((rc = nb_read_itter(fd, header, 4)) < 0)
+ return (rc); /* error */
+
+ if (rc != 4)
+ return (NB_READ_MSG_ERR_EOF); /* EOF */
+
+ /*
+ * The NetBIOS message length is limited to 17 bits but
+ * we use this layer for SMB over both NetBIOS and TCP
+ * (NetBIOS-less SMB). When using SMB over TCP the length
+ * is 24 bits but we are ignoring that for now because we
+ * don't expect any messages larger than 64KB.
+ */
+ *type = header[0];
+ length = ((header[1]&1) << 16) + (header[2]<<8) + header[3];
+
+ if (length > max_buf)
+ return (NB_READ_MSG_ERR_OVERFLOW); /* error overflow */
+
+ if ((rc = nb_read_itter(fd, buf, length)) != length)
+ return (NB_READ_MSG_ERR_UNDERFLOW); /* error underflow */
+
+ return (rc);
+}
+
+
+/*
+ * nb_read_itter
+ *
+ * We should never see descriptor 0 (stdin) or -1.
+ */
+static int
+nb_read_itter(int fd, unsigned char *buf, unsigned cnt)
+{
+ int ix;
+ int rc;
+
+ for (ix = 0; ix < cnt; ix += rc) {
+ if (fd == 0 || fd == -1)
+ return (-1);
+
+ if ((rc = read(fd, buf+ix, cnt-ix)) < 0)
+ return (rc);
+
+ if (rc == 0)
+ break;
+ }
+
+ return (ix);
+}
+
+
+/*
+ * nb_first_level_name_encode
+ */
+static int
+nb_first_level_name_encode(char *name, char *scope,
+ unsigned char *out, int max_out)
+{
+ unsigned char ch, len;
+ unsigned char *in;
+ unsigned char *lp;
+ unsigned char *op = out;
+ unsigned char *op_end = op + max_out;
+
+ in = (unsigned char *)name;
+ *op++ = 0x20;
+ for (len = 0; ((ch = *in) != 0) && len < MAX_NETBIOS_NAME_SIZE;
+ len++, in++) {
+ *op++ = 'A' + ((ch >> 4) & 0xF);
+ *op++ = 'A' + ((ch) & 0xF);
+ }
+
+ for (; len < MAX_NETBIOS_NAME_SIZE; len++) {
+ ch = ' ';
+ *op++ = 'A' + ((ch >> 4) & 0xF);
+ *op++ = 'A' + ((ch) & 0xF);
+ }
+
+ in = (unsigned char *)scope;
+ len = 0;
+ lp = op++;
+ for (; op < op_end; in++) {
+ ch = *in;
+ if (ch == 0) {
+ if ((*lp = len) != 0)
+ *op++ = 0;
+ break;
+ }
+ if (ch == '.') {
+ *lp = (op - lp) - 1;
+ lp = op++;
+ len = 0;
+ } else {
+ *op++ = ch;
+ len++;
+ }
+ }
+
+ return ((int)(op - out));
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netuse.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netuse.c
new file mode 100644
index 0000000000..508b258cd6
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netuse.c
@@ -0,0 +1,458 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Tree connect and disconnect functions to support SMB shares.
+ * These functions are described in the CIFS draft 1.0 Protocol
+ * Specification (December 19, 1997).
+ */
+
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <synch.h>
+#include <pthread.h>
+
+#include <smbsrv/libsmbrdr.h>
+
+#include <smbrdr.h>
+#include <smbsrv/ntstatus.h>
+
+
+/*
+ * The table of shares set up with the domain controller.
+ */
+static struct sdb_netuse netuse_table[N_NETUSE_TABLE];
+
+static int smbrdr_smb_tcon(struct sdb_session *session,
+ struct sdb_netuse *netuse, char *path, int path_len);
+
+static struct sdb_netuse *smbrdr_netuse_alloc(struct sdb_session *session,
+ char *sharename);
+static int smbrdr_smb_tdcon(struct sdb_netuse *netuse);
+
+static void
+smbrdr_netuse_clear(struct sdb_netuse *netuse)
+{
+ bzero(netuse, sizeof (struct sdb_netuse) - sizeof (mutex_t));
+}
+
+static void
+smbrdr_netuse_free(struct sdb_netuse *netuse)
+{
+ smbrdr_netuse_clear(netuse);
+ (void) mutex_unlock(&netuse->mtx);
+}
+
+/*
+ * mlsvc_tree_connect
+ *
+ * Establish a share (tree connect). We need to retrieve the session
+ * for the specified host and allocate a netuse structure. We set up
+ * the path here (UNC encoded) to make handling the malloc/free easier
+ * and pass everything on to smbrdr_smb_tcon where, if everything goes well,
+ * a valid tid will be stored in the netuse structure.
+ *
+ * On success, a pointer to the netuse is returned. Otherwise the
+ * netuse is cleared and a null pointer is returned.
+ */
+unsigned short
+mlsvc_tree_connect(char *hostname, char *username, char *sharename)
+{
+ struct sdb_session *session;
+ struct sdb_netuse *netuse;
+ char *path;
+ int path_len;
+
+ /*
+ * Make sure there is a session & logon for given info
+ */
+ session = smbrdr_session_lock(hostname, username, SDB_SLCK_READ);
+ if (session == 0) {
+ syslog(LOG_ERR, "smbrdr: (tcon) no session for %s@%s",
+ username, hostname);
+ return (0);
+ }
+
+
+ if ((netuse = smbrdr_netuse_alloc(session, sharename)) == 0) {
+ syslog(LOG_ERR, "smbrdr: (tcon) init failed");
+ smbrdr_session_unlock(session);
+ return (0);
+ }
+
+ /*
+ * Add some padding for the back-slash separators
+ * and the null-terminator.
+ */
+ path_len = SMB_PI_MAX_HOST + MAX_SHARE_NAME + 5;
+
+ if ((path = (char *)malloc(path_len)) == 0) {
+ smbrdr_netuse_free(netuse);
+ smbrdr_session_unlock(session);
+ syslog(LOG_ERR, "smbrdr: (tcon) resource shortage");
+ return (0);
+ }
+
+ bzero(path, path_len);
+ (void) snprintf(path, path_len, "\\\\%s\\%s", hostname, sharename);
+ if (session->remote_caps & CAP_UNICODE)
+ path_len = mts_wcequiv_strlen(path);
+ else
+ path_len = strlen(path);
+
+ if (smbrdr_smb_tcon(session, netuse, path, path_len) < 0) {
+ smbrdr_netuse_free(netuse);
+ smbrdr_session_unlock(session);
+ free(path);
+ syslog(LOG_ERR, "smbrdr: (tcon) failed connecting to %s", path);
+ return (0);
+ }
+
+ free(path);
+ (void) mutex_unlock(&netuse->mtx);
+ smbrdr_session_unlock(session);
+ return (netuse->tid);
+}
+
+
+/*
+ * smbrdr_smb_tcon
+ *
+ * This message requests a share (tree connect) request to the server
+ * associated with the session. The password is not relevant here if
+ * the session was establishment using setup_andx. The outgoing tid
+ * will be ignored - a valid one will be returned by the server.
+ *
+ * Returns 0 on success. Otherwise returns a -ve error code.
+ */
+static int
+smbrdr_smb_tcon(struct sdb_session *session, struct sdb_netuse *netuse,
+ char *path, int path_len)
+{
+ smb_hdr_t smb_hdr;
+ smbrdr_handle_t srh;
+ smb_msgbuf_t *mb;
+ unsigned short flags;
+ char *password;
+ unsigned short password_len;
+ char *service;
+ unsigned service_len;
+ unsigned short data_bytes;
+ DWORD status;
+ int rc;
+
+ status = smbrdr_request_init(&srh, SMB_COM_TREE_CONNECT_ANDX,
+ session, &session->logon, 0);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrTcon: %s", xlate_nt_status(status));
+ return (-1);
+ }
+
+ mb = &srh.srh_mbuf;
+
+ flags = 0; /* no flags */
+ password = "";
+ password_len = 1; /* including nul */
+ service = "?????"; /* does this work? */
+ service_len = strlen(service);
+
+ /*
+ * Calculate the BCC. The path is in UNICODE
+ * but the service is in ASCII.
+ */
+ data_bytes = password_len;
+ data_bytes += path_len + 1;
+ data_bytes += service_len + 1;
+
+ rc = smb_msgbuf_encode(mb, "bb1.wwww#cus",
+ 4, /* smb_wct */
+ 0xff, /* AndXCommand (none) */
+ 0xffff, /* AndXOffset */
+ flags, /* Flags */
+ password_len, /* PasswordLength */
+ data_bytes+1, /* smb_bcc */
+ password_len, password, /* Password */
+ path, /* Path */
+ service); /* Service */
+
+ if (rc <= 0) {
+ syslog(LOG_ERR, "smbrdr_smb_tcon: encode failed");
+ smbrdr_handle_free(&srh);
+ return (-1);
+ }
+
+ status = smbrdr_exchange(&srh, &smb_hdr, 0);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrTcon: %s", xlate_nt_status(status));
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+ netuse->tid = smb_hdr.tid;
+ netuse->state = SDB_NSTATE_CONNECTED;
+ smbrdr_handle_free(&srh);
+ return (rc);
+}
+
+
+/*
+ * smbrdr_netuse_logoff
+ *
+ * This function can be used when closing a session to ensure that all
+ * shares associated with the specified session are disconnected and
+ * the resources released. We also notify the pipe interface to ensure
+ * that any pipes associated with this share are also closed. This
+ * function silently ignores errors because we have no idea what state
+ * the session is in. We are more interested in releasing resources.
+ */
+void
+smbrdr_netuse_logoff(unsigned short uid)
+{
+ struct sdb_netuse *netuse;
+ int i;
+
+ for (i = 0; i < N_NETUSE_TABLE; ++i) {
+ netuse = &netuse_table[i];
+ (void) mutex_lock(&netuse->mtx);
+ if (netuse->uid == uid)
+ (void) smbrdr_smb_tdcon(netuse);
+ (void) mutex_unlock(&netuse->mtx);
+ }
+}
+
+int
+smbrdr_tree_disconnect(unsigned short tid)
+{
+ struct sdb_netuse *netuse;
+ int rc = -1;
+
+ netuse = smbrdr_netuse_get(tid);
+ if (netuse) {
+ (void) smbrdr_smb_tdcon(netuse);
+ smbrdr_netuse_put(netuse);
+ rc = 0;
+ }
+
+ return (rc);
+}
+
+/*
+ * smbrdr_smb_tdcon
+ *
+ * Disconnect a share. This message informs the server that we no longer
+ * wish to access the resource specified by tid, obtained via a prior
+ * mlsvc_tree_connect. The tid is passed in the SMB header so the setup
+ * for this call is very straightforward.
+ *
+ * Returns 0 on success. Otherwise returns a -ve error code.
+ */
+static int
+smbrdr_smb_tdcon(struct sdb_netuse *netuse)
+{
+ struct sdb_session *session;
+ smbrdr_handle_t srh;
+ smb_hdr_t smb_hdr;
+ DWORD status;
+ int rc;
+
+ netuse->state = SDB_NSTATE_DISCONNECTING;
+ smbrdr_ofile_end_of_share(netuse->tid);
+
+ if ((session = netuse->session) == 0) {
+ smbrdr_netuse_clear(netuse);
+ return (0);
+ }
+
+ if ((session->state != SDB_SSTATE_NEGOTIATED) &&
+ (session->state != SDB_SSTATE_DISCONNECTING)) {
+ smbrdr_netuse_clear(netuse);
+ return (0);
+ }
+
+ status = smbrdr_request_init(&srh, SMB_COM_TREE_DISCONNECT,
+ session, &session->logon, netuse);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "smbrdr: (tdcon) %s", xlate_nt_status(status));
+ /* should we clear here? */
+ smbrdr_netuse_clear(netuse);
+ return (-1);
+ }
+
+ rc = smb_msgbuf_encode(&srh.srh_mbuf, "bw.", 0, 0);
+ if (rc < 0) {
+ syslog(LOG_ERR, "smbrdr: (tdcon) encode failed");
+ smbrdr_handle_free(&srh);
+ /* should we clear here? */
+ smbrdr_netuse_clear(netuse);
+ return (rc);
+ }
+
+ status = smbrdr_exchange(&srh, &smb_hdr, 0);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "smbrdr: (tdcon) %s", xlate_nt_status(status));
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+ smbrdr_handle_free(&srh);
+ smbrdr_netuse_clear(netuse);
+ return (rc);
+}
+
+
+/*
+ * smbrdr_netuse_alloc
+ *
+ * Find a slot in the table for a share. Each share is associated with
+ * a session and assigned a local drive letter name and a sharename.
+ * If a slot is already allocated to the specified share, a pointer to
+ * it is returned. Otherwise we allocate and initialize a new slot in
+ * the table. If the table is full, a null pointer will be returned.
+ *
+ * IMPORTANT! the returned netuse will be locked caller has to unlock
+ * it after it's done with the pointer.
+ */
+static struct sdb_netuse *
+smbrdr_netuse_alloc(struct sdb_session *session, char *sharename)
+{
+ struct sdb_netuse *netuse;
+ int i;
+
+ if (session == 0 || sharename == 0) {
+ syslog(LOG_ERR, "smbrdr: (tcon) invalid arg");
+ return (0);
+ }
+
+ for (i = 0; i < N_NETUSE_TABLE; ++i) {
+ netuse = &netuse_table[i];
+
+ (void) mutex_lock(&netuse->mtx);
+ if (netuse->state == SDB_NSTATE_START) {
+ netuse->session = session;
+ netuse->letter = i + '0';
+ netuse->sid = session->sid;
+ netuse->uid = session->logon.uid;
+ netuse->tid = 0;
+ (void) strcpy(netuse->share, sharename);
+ netuse->state = SDB_NSTATE_INIT;
+ return (netuse);
+ }
+ (void) mutex_unlock(&netuse->mtx);
+ }
+
+ syslog(LOG_WARNING, "smbrdr: (tcon) table full");
+ return (0);
+}
+
+/*
+ * smbrdr_netuse_put
+ *
+ * Unlock given netuse structure.
+ */
+void
+smbrdr_netuse_put(struct sdb_netuse *netuse)
+{
+ (void) mutex_unlock(&netuse->mtx);
+}
+
+/*
+ * smbrdr_netuse_get
+ *
+ * Find the netuse structure associated with the specified tid and
+ * return a pointer to it. A null pointer is returned if no match
+ * can be found.
+ *
+ * IMPORTANT! the returned netuse will be locked caller has to unlock
+ * it after it's done with the pointer.
+ */
+struct sdb_netuse *
+smbrdr_netuse_get(int tid)
+{
+ struct sdb_session *session;
+ struct sdb_netuse *netuse;
+ int i;
+
+ for (i = 0; i < N_NETUSE_TABLE; ++i) {
+ netuse = &netuse_table[i];
+
+ (void) mutex_lock(&netuse->mtx);
+
+ if (netuse->tid == tid) {
+ session = netuse->session;
+
+ /*
+ * status check:
+ * make sure all the structures are in the right state
+ */
+ if (session &&
+ (netuse->state == SDB_NSTATE_CONNECTED) &&
+ (session->logon.state == SDB_LSTATE_SETUP) &&
+ (session->state == SDB_SSTATE_NEGOTIATED)) {
+ /* sanity check */
+ if ((netuse->sid == session->sid) &&
+ (netuse->uid == session->logon.uid))
+ return (netuse);
+ else
+ /* invalid structure */
+ smbrdr_netuse_clear(netuse);
+ }
+
+ }
+
+ (void) mutex_unlock(&netuse->mtx);
+ }
+
+ syslog(LOG_WARNING, "smbrdr: (lookup) no such TID %d", tid);
+ return (0);
+}
+
+/*
+ * smbrdr_dump_netuse
+ */
+void
+smbrdr_dump_netuse()
+{
+ struct sdb_netuse *netuse;
+ int i;
+
+ for (i = 0; i < N_NETUSE_TABLE; ++i) {
+ netuse = &netuse_table[i];
+ (void) mutex_lock(&netuse->mtx);
+ if (netuse->session) {
+ syslog(LOG_DEBUG, "tree[%d]: %s (tid=%d)", i,
+ netuse->share, netuse->tid);
+ syslog(LOG_DEBUG, "tree[%d]: session(%d), user(%d)",
+ i, netuse->session->sock, netuse->uid);
+ }
+ (void) mutex_unlock(&netuse->mtx);
+ }
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_read_andx.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_read_andx.c
new file mode 100644
index 0000000000..ef90fa79ac
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_read_andx.c
@@ -0,0 +1,210 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SMB ReadX functions to support MLRPC.
+ */
+
+#include <syslog.h>
+#include <strings.h>
+
+#include <smbsrv/libsmbrdr.h>
+
+#include <smbsrv/netbios.h>
+#include <smbsrv/ntstatus.h>
+#include <smbrdr.h>
+
+#define SMBRDR_READX_RSP_OVERHEAD \
+ (NETBIOS_HDR_SZ + SMB_HEADER_LEN + sizeof (smb_read_andx_rsp_t))
+#define SMBRDR_READX_RSP_DATA_MAXLEN \
+ (SMBRDR_REQ_BUFSZ - SMBRDR_READX_RSP_OVERHEAD)
+
+static int smbrdr_decode_readx_rsp(smb_msgbuf_t *, char *, unsigned,
+ smb_read_andx_rsp_t *);
+
+static void smbrdr_dump_readx_rsp(smb_read_andx_rsp_t *);
+
+/*
+ * smbrdr_rpc_readx
+ *
+ * Send SMB_COM_READ_ANDX request.
+ */
+int
+smbrdr_rpc_readx(int fid, char *in_buf, int in_len)
+{
+ struct sdb_netuse *netuse;
+ struct sdb_ofile *ofile;
+ smb_read_andx_rsp_t rsp;
+ smbrdr_handle_t srh;
+ smb_msgbuf_t *mb;
+ DWORD status;
+ int rc, max_return;
+
+ if ((ofile = smbrdr_ofile_get(fid)) == 0)
+ return (-1);
+
+ netuse = ofile->netuse;
+
+ status = smbrdr_request_init(&srh, SMB_COM_READ_ANDX,
+ netuse->session, &netuse->session->logon, netuse);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrReadAndx: %s", xlate_nt_status(status));
+ smbrdr_ofile_put(ofile);
+ return (-1);
+ }
+
+ mb = &(srh.srh_mbuf);
+
+ max_return = (in_len > SMBRDR_READX_RSP_DATA_MAXLEN) ?
+ SMBRDR_READX_RSP_DATA_MAXLEN : in_len;
+
+ rc = smb_msgbuf_encode(mb, "bbbwwlwwlwlw",
+ 12, /* Count of parameter words */
+ 0xFF, /* Secondary (X) command; 0xFF = none */
+ 0, /* Reserved (must be 0) */
+ 0, /* Offset to next command WordCount */
+ ofile->fid, /* File handle */
+ 0, /* Offset in file to begin read */
+ max_return, /* Max number of bytes to return */
+ /* Reserved for obsolescent requests [0 = non-blocking read] */
+ max_return,
+ /*
+ * High 16 bits of MaxCount if CAP_LARGE_READX;
+ * else MUST BE ZERO
+ */
+ 0,
+ max_return, /* Reserved for obsolescent requests */
+ /* Upper 32 bits of offset (only if WordCount is 12) */
+ 0,
+ 0); /* Count of data bytes = 0 */
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "SmbrdrReadAndx: smbrdr_prep_readx_req failed");
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_put(ofile);
+ return (rc);
+ }
+
+ smbrdr_lock_transport();
+
+ status = smbrdr_send(&srh);
+ if (status != NT_STATUS_SUCCESS) {
+ smbrdr_unlock_transport();
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_put(ofile);
+ syslog(LOG_ERR, "SmbrdrReadAndx: send failed");
+ return (-1);
+ }
+
+ status = smbrdr_rcv(&srh, 1);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrReadAndx: nb_rcv failed");
+ smbrdr_unlock_transport();
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_put(ofile);
+ return (-1);
+ }
+
+ rc = smbrdr_decode_readx_rsp(mb, in_buf, in_len, &rsp);
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "SmbrdrReadAndx: read decode failure!");
+ smbrdr_unlock_transport();
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_put(ofile);
+ return (-1);
+ }
+
+ smbrdr_unlock_transport();
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_put(ofile);
+
+ return ((rc < 0) ? rc : rsp.DataLength);
+}
+
+static void
+smbrdr_dump_readx_rsp(smb_read_andx_rsp_t *rsp)
+{
+
+ syslog(LOG_DEBUG, "[SmbReadX Rsp] WordCount:%x,AndXCmd:%x,"
+ " AndXReserved:%x, AndXOffset:%d",
+ rsp->WordCount, rsp->AndXCmd, rsp->AndXReserved, rsp->AndXOffset);
+
+ syslog(LOG_DEBUG, "[SmbReadX Rsp] Remaining:%d, Mode:%d, Reserved:%x, "
+ "DataLen:%d, DataOffset:%d, ByteCount: %d",
+ rsp->Remaining, rsp->DataCompactionMode, rsp->Reserved,
+ rsp->DataLength, rsp->DataOffset, rsp->ByteCount);
+}
+
+/*
+ * smbrdr_decode_readx_rsp
+ *
+ * Decode the response from the SMB_COM_READ_ANDX request. The payload
+ * of the response is appended to the end of SmbTransact response data
+ * in the MLRPC receive buffer.
+ *
+ * Return -1 on error, 0 upon success.
+ */
+static int
+smbrdr_decode_readx_rsp(smb_msgbuf_t *mb,
+ char *in,
+ unsigned in_len,
+ smb_read_andx_rsp_t *rsp)
+{
+ int rc;
+
+ rc = smb_msgbuf_decode(mb, "bbbwwwwwwlwwww",
+ &rsp->WordCount,
+ &rsp->AndXCmd,
+ &rsp->AndXReserved,
+ &rsp->AndXOffset,
+ &rsp->Remaining,
+ &rsp->DataCompactionMode,
+ &rsp->Reserved,
+ &rsp->DataLength,
+ &rsp->DataOffset,
+ &rsp->DataLengthHigh,
+ &rsp->Reserved2[0],
+ &rsp->Reserved2[1],
+ &rsp->Reserved2[2],
+ &rsp->ByteCount);
+
+ if (rc <= 0)
+ return (-1);
+
+ smbrdr_dump_readx_rsp(rsp);
+
+ /* it should never happen, but check anyway */
+ if (rsp->DataLength > in_len)
+ return (-1);
+
+ bcopy(mb->base + rsp->DataOffset, in, rsp->DataLength);
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_rpcpipe.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_rpcpipe.c
new file mode 100644
index 0000000000..b89eddf275
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_rpcpipe.c
@@ -0,0 +1,518 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Functions to open and close named pipes. These functions are
+ * described in the CIFS 1.0 Protocol Specification (December 19, 1997).
+ */
+
+#include <alloca.h>
+#include <pthread.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <synch.h>
+
+#include <smbsrv/libsmbrdr.h>
+
+#include <smbsrv/ntstatus.h>
+#include <smbrdr.h>
+
+static int smbrdr_smb_close(struct sdb_ofile *ofile);
+static DWORD smbrdr_smb_ntcreate(struct sdb_ofile *ofile);
+static struct sdb_ofile *smbrdr_ofile_alloc(struct sdb_netuse *netuse,
+ char *name);
+
+static void
+smbrdr_ofile_clear(struct sdb_ofile *ofile)
+{
+ bzero(ofile, sizeof (struct sdb_ofile) - sizeof (mutex_t));
+}
+
+static void
+smbrdr_ofile_free(struct sdb_ofile *ofile)
+{
+ smbrdr_ofile_clear(ofile);
+ (void) mutex_unlock(&ofile->mtx);
+}
+
+
+/*
+ * The ofile table.
+ */
+static struct sdb_ofile ofile_table[N_OFILE_TABLE];
+
+static int mlsvc_pipe_recon_wait = 50;
+static int mlsvc_pipe_recon_tries = 3;
+
+
+/*
+ * mlsvc_open_pipe
+ *
+ * Open an RPC pipe on hostname. On success, return the fid. Otherwise
+ * returns a -ve error code.
+ */
+int
+mlsvc_open_pipe(char *hostname, char *domain, char *username, char *pipename)
+{
+ struct sdb_netuse *netuse;
+ struct sdb_ofile *ofile;
+ unsigned short tid;
+ DWORD status;
+ int retry;
+ struct timespec st;
+
+ tid = mlsvc_tree_connect(hostname, username, "IPC$");
+ if (tid == 0) {
+ syslog(LOG_ERR, "smbrdr: (open) %s %s %s %s %s",
+ hostname, domain, username, pipename,
+ xlate_nt_status(NT_STATUS_UNEXPECTED_NETWORK_ERROR));
+ return (-1);
+ }
+
+ netuse = smbrdr_netuse_get(tid);
+ if (netuse == 0) {
+ syslog(LOG_ERR, "smbrdr: (open) %s %s %s %s %s",
+ hostname, domain, username, pipename,
+ xlate_nt_status(NT_STATUS_CONNECTION_INVALID));
+ return (-1);
+ }
+
+ if ((ofile = smbrdr_ofile_alloc(netuse, pipename)) == 0) {
+ syslog(LOG_ERR, "smbrdr: (open) %s %s %s %s %s",
+ hostname, domain, username, pipename,
+ xlate_nt_status(NT_STATUS_INSUFFICIENT_RESOURCES));
+ smbrdr_netuse_put(netuse);
+ return (-1);
+ }
+
+ status = NT_STATUS_OPEN_FAILED;
+
+ for (retry = 0; retry < mlsvc_pipe_recon_tries; retry++) {
+ status = smbrdr_smb_ntcreate(ofile);
+
+ switch (status) {
+ case NT_STATUS_SUCCESS:
+ (void) mutex_unlock(&ofile->mtx);
+ smbrdr_netuse_put(netuse);
+ return (ofile->fid);
+
+ case NT_STATUS_PIPE_NOT_AVAILABLE:
+ case NT_STATUS_PIPE_BUSY:
+ /*
+ * The server might return this error if it is
+ * temporarily busy or unable to create a pipe.
+ * We wait here before trying again to see if
+ * the pipe becomes available.
+ */
+ st.tv_sec = 0;
+ st.tv_nsec = mlsvc_pipe_recon_wait * 1000000;
+ (void) nanosleep(&st, 0);
+ break;
+
+ default:
+ /*
+ * Something else went wrong: no more retries.
+ */
+ retry = mlsvc_pipe_recon_tries;
+ break;
+ }
+ }
+
+ syslog(LOG_DEBUG, "smbrdr: (open) %s %s %s %s %s",
+ hostname, domain, username, pipename,
+ xlate_nt_status(status));
+ smbrdr_ofile_free(ofile);
+ smbrdr_netuse_put(netuse);
+ return (-1);
+}
+
+/*
+ * mlsvc_close_pipe
+ *
+ * Close the named pipe represented by fid.
+ */
+int
+mlsvc_close_pipe(int fid)
+{
+ struct sdb_ofile *ofile;
+ unsigned short tid;
+ int rc;
+
+ if ((ofile = smbrdr_ofile_get(fid)) == 0) {
+ syslog(LOG_ERR, "mlsvc_close_pipe: unknown file (%d)", fid);
+ return (-1);
+ }
+
+ tid = ofile->tid;
+ rc = smbrdr_smb_close(ofile);
+ smbrdr_ofile_put(ofile);
+
+ if (rc == 0)
+ (void) smbrdr_tree_disconnect(tid);
+
+ return (rc);
+}
+
+/*
+ * smbrdr_ofile_put
+ *
+ * Unlock given ofile structure.
+ */
+void
+smbrdr_ofile_put(struct sdb_ofile *ofile)
+{
+ if (ofile)
+ (void) mutex_unlock(&ofile->mtx);
+}
+
+/*
+ * smbrdr_ofile_get
+ *
+ * Locate the ofile for the specified fid. Just to be safe, ensure that
+ * the netuse pointer is valid. Return a pointer to the ofile structure.
+ * Return a null pointer if a valid ofile cannot be found.
+ */
+struct sdb_ofile *
+smbrdr_ofile_get(int fid)
+{
+ struct sdb_session *session;
+ struct sdb_netuse *netuse;
+ struct sdb_ofile *ofile;
+ int i;
+
+ for (i = 0; i < N_OFILE_TABLE; ++i) {
+ ofile = &ofile_table[i];
+
+ (void) mutex_lock(&ofile->mtx);
+
+ if (ofile->fid == fid) {
+ session = ofile->session;
+ netuse = ofile->netuse;
+
+ /*
+ * status check:
+ * make sure all the structures are in the right state
+ */
+ if (session && netuse &&
+ (ofile->state == SDB_FSTATE_OPEN) &&
+ (netuse->state == SDB_NSTATE_CONNECTED) &&
+ (session->logon.state == SDB_LSTATE_SETUP) &&
+ (session->state == SDB_SSTATE_NEGOTIATED)) {
+ /* sanity check */
+ if ((ofile->sid == session->sid) &&
+ (ofile->uid == session->logon.uid) &&
+ (ofile->tid == netuse->tid)) {
+ return (ofile);
+ } else {
+ /* invalid structure */
+ smbrdr_ofile_clear(ofile);
+ }
+ }
+ }
+
+ (void) mutex_unlock(&ofile->mtx);
+ }
+
+ syslog(LOG_WARNING, "smbrdr: (lookup) no such FID %d", fid);
+ return (0);
+}
+
+/*
+ * smbrdr_ofile_end_of_share
+ *
+ * This function can be used when closing a share to ensure that all
+ * ofiles resources are released. Don't call mlsvc_close_pipe because
+ * that will call mlsvc_smb_tdcon and we don't know what state
+ * the share is in. The server will probably close all files anyway.
+ * We are more interested in releasing the ofile resources.
+ */
+void
+smbrdr_ofile_end_of_share(unsigned short tid)
+{
+ struct sdb_ofile *ofile;
+ int i;
+
+ for (i = 0; i < N_OFILE_TABLE; ++i) {
+ ofile = &ofile_table[i];
+ (void) mutex_lock(&ofile->mtx);
+ if (ofile->tid == tid)
+ (void) smbrdr_smb_close(ofile);
+ (void) mutex_unlock(&ofile->mtx);
+ }
+}
+
+/*
+ * smbrdr_dump_ofiles
+ *
+ * Dump the open files table.
+ */
+void
+smbrdr_dump_ofiles()
+{
+ struct sdb_ofile *ofile;
+ struct sdb_netuse *netuse;
+ int i;
+
+ for (i = 0; i < N_OFILE_TABLE; ++i) {
+ ofile = &ofile_table[i];
+ (void) mutex_lock(&ofile->mtx);
+ netuse = ofile->netuse;
+
+ if (netuse) {
+ syslog(LOG_DEBUG, "file[%d]: %s (fid=%d)", i,
+ ofile->path, ofile->fid);
+ syslog(LOG_DEBUG,
+ "file[%d]: session(%d), user(%d), tree(%d)",
+ i, netuse->session->sock, netuse->uid,
+ netuse->tid);
+ }
+ (void) mutex_unlock(&ofile->mtx);
+ }
+}
+
+/*
+ * Private Functions
+ */
+
+/*
+ * smbrdr_smb_close
+ *
+ * Send SMBClose request for the given open file.
+ */
+static int
+smbrdr_smb_close(struct sdb_ofile *ofile)
+{
+ struct sdb_session *session;
+ struct sdb_netuse *netuse;
+ struct sdb_logon *logon;
+ smbrdr_handle_t srh;
+ smb_hdr_t smb_hdr;
+ DWORD status;
+ int fid;
+ int rc;
+
+ if (ofile == 0)
+ return (0);
+
+ ofile->state = SDB_FSTATE_CLOSING;
+
+ if ((session = ofile->session) == 0) {
+ smbrdr_ofile_clear(ofile);
+ return (0);
+ }
+
+ if ((session->state != SDB_SSTATE_NEGOTIATED) &&
+ (session->state != SDB_SSTATE_DISCONNECTING)) {
+ smbrdr_ofile_clear(ofile);
+ return (0);
+ }
+
+ fid = ofile->fid;
+
+ netuse = ofile->netuse;
+ logon = &session->logon;
+
+ status = smbrdr_request_init(&srh, SMB_COM_CLOSE,
+ session, logon, netuse);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrClose: %s", xlate_nt_status(status));
+ smbrdr_ofile_clear(ofile);
+ return (-1);
+ }
+
+ rc = smb_msgbuf_encode(&srh.srh_mbuf,
+ "(wct)b (fid)w (lwrtm)l (bcc)w (pad).",
+ 3, /* WordCount */
+ fid, /* Fid */
+ 0x00000000ul, /* LastWriteTime */
+ 0); /* ByteCount */
+
+ if (rc <= 0) {
+ syslog(LOG_ERR, "SmbrdrClose: encode failed");
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_clear(ofile);
+ return (-1);
+ }
+
+ status = smbrdr_exchange(&srh, &smb_hdr, 0);
+ if (status != NT_STATUS_SUCCESS)
+ syslog(LOG_ERR, "SmbrdrClose: %s", xlate_nt_status(status));
+
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_clear(ofile);
+ return (0);
+}
+
+/*
+ * smbrdr_ofile_alloc
+ *
+ * Allocate an ofile for the specified name. File info is associated
+ * with a share so we need a valid share before calling this function.
+ * If a slot is already allocated to the specified file, a pointer to
+ * that slot is returned. Otherwise we allocate and initialize a new
+ * slot in the table. If the table is full, a null pointer will be
+ * returned.
+ */
+static struct sdb_ofile *
+smbrdr_ofile_alloc(struct sdb_netuse *netuse, char *name)
+{
+ struct sdb_ofile *ofile;
+ int i;
+
+ for (i = 0; i < N_OFILE_TABLE; ++i) {
+ ofile = &ofile_table[i];
+
+ (void) mutex_lock(&ofile->mtx);
+ if (ofile->netuse == 0) {
+
+ ofile->session = netuse->session;
+ ofile->netuse = netuse;
+ ofile->sid = netuse->session->sid;
+ ofile->uid = netuse->session->logon.uid;
+ ofile->tid = netuse->tid;
+ ofile->fid = 0;
+ (void) strcpy(ofile->path, name);
+ ofile->state = SDB_FSTATE_INIT;
+ return (ofile);
+ }
+
+ (void) mutex_unlock(&ofile->mtx);
+ }
+
+ syslog(LOG_WARNING, "smbrdr: (open) table full");
+ return (0);
+}
+
+/*
+ * smbrdr_smb_ntcreate
+ *
+ * This will do an SMB_COM_NT_CREATE_ANDX with lots of default values.
+ * All of the underlying session and share data should already be set
+ * up before we get here. If everything works we'll get a valid fid.
+ */
+static DWORD
+smbrdr_smb_ntcreate(struct sdb_ofile *ofile)
+{
+ struct sdb_logon *logon;
+ struct sdb_netuse *netuse;
+ struct sdb_session *sess;
+ smbrdr_handle_t srh;
+ smb_hdr_t smb_hdr;
+ smb_msgbuf_t *mb;
+ char *path;
+ unsigned path_len;
+ int data_bytes;
+ int rc;
+ unsigned short fid;
+ int null_size;
+ DWORD status;
+
+ netuse = ofile->netuse;
+ sess = netuse->session;
+ logon = &sess->logon;
+
+ /*
+ * If this was a general purpose interface, we should support
+ * full UNC semantics but we only use this for RPC over named
+ * pipes with well-known endpoints.
+ */
+ path_len = strlen(ofile->path) + 2;
+ path = alloca(path_len);
+
+ if (ofile->path[0] != '\\')
+ (void) snprintf(path, path_len, "\\%s", ofile->path);
+ else
+ (void) strcpy(path, ofile->path);
+
+ if (sess->remote_caps & CAP_UNICODE) {
+ path_len = mts_wcequiv_strlen(path);
+ null_size = sizeof (mts_wchar_t);
+ } else {
+ path_len = strlen(path);
+ null_size = sizeof (char);
+ }
+
+ syslog(LOG_DEBUG, "SmbRdrNtCreate: %d %s", path_len, path);
+
+ status = smbrdr_request_init(&srh, SMB_COM_NT_CREATE_ANDX,
+ sess, logon, netuse);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrNtCreate: %s", xlate_nt_status(status));
+ return (NT_STATUS_INVALID_PARAMETER_1);
+ }
+
+ mb = &srh.srh_mbuf;
+
+ data_bytes = path_len + null_size;
+
+ rc = smb_msgbuf_encode(mb,
+ "(wct)b (andx)b1.w (resv). (nlen)w (flg)l"
+ "(rdf)l (dacc)l (allo)q (efa)l (shr)l (cdisp)l (copt)l (impl)l"
+ "(secf)b (bcc)w (name)u",
+ 24, /* smb_wct */
+ 0xff, /* AndXCommand (none) */
+ 0x0000, /* AndXOffset */
+ path_len, /* Unicode NameLength */
+ 0x00000006ul, /* Flags (oplocks) */
+ 0, /* RootDirectoryFid */
+ 0x0002019Ful, /* DesiredAccess */
+ 0x0ull, /* AllocationSize */
+ 0x00000000ul, /* ExtFileAttributes */
+ 0x00000003ul, /* ShareAccess (RW) */
+ 0x00000001ul, /* CreateDisposition (OpenExisting) */
+ 0x00000000ul, /* CreateOptions */
+ 0x00000002ul, /* ImpersonationLevel */
+ 0x01u, /* SecurityFlags */
+ data_bytes, /* smb_bcc */
+ path); /* Name */
+
+ if (rc <= 0) {
+ smbrdr_handle_free(&srh);
+ return (NT_STATUS_INVALID_PARAMETER_1);
+ }
+
+ status = smbrdr_exchange(&srh, &smb_hdr, 0);
+ if (status != NT_STATUS_SUCCESS) {
+ smbrdr_handle_free(&srh);
+ return (NT_SC_VALUE(status));
+ }
+
+ rc = smb_msgbuf_decode(mb, "(wct). (andx)4. (opl)1. (fid)w", &fid);
+ if (rc <= 0) {
+ smbrdr_handle_free(&srh);
+ return (NT_STATUS_INVALID_PARAMETER_2);
+ }
+
+ ofile->fid = fid;
+ ofile->state = SDB_FSTATE_OPEN;
+ syslog(LOG_DEBUG, "SmbRdrNtCreate: fid=%d", ofile->fid);
+ smbrdr_handle_free(&srh);
+ return (NT_STATUS_SUCCESS);
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_session.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_session.c
new file mode 100644
index 0000000000..535ad537d9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_session.c
@@ -0,0 +1,889 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module provides the netbios and SMB negotiation, connect and
+ * disconnect interface.
+ */
+
+#include <unistd.h>
+#include <syslog.h>
+#include <synch.h>
+#include <string.h>
+#include <strings.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <netdb.h>
+
+#include <smbsrv/libsmbrdr.h>
+#include <smbsrv/netbios.h>
+#include <smbsrv/cifs.h>
+
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/mlsvc.h>
+#include <smbrdr.h>
+#include <smbrdr_ipc_util.h>
+
+
+static uint16_t smbrdr_ports[] = {
+ SMB_SRVC_TCP_PORT,
+ SSN_SRVC_TCP_PORT
+};
+
+static int smbrdr_nports = sizeof (smbrdr_ports) / sizeof (smbrdr_ports[0]);
+
+/*
+ * Pointer to the PDC location interface.
+ * To be set up by SMB when it loads.
+ */
+static mlsvc_locate_pdc_t mlsvc_locate_pdc;
+
+/*
+ * This is a temporary hack to stop the DC from closing a session
+ * due to inactivity.
+ */
+#define MLSVC_SESSION_FORCE_KEEPALIVE 10
+
+/*
+ * This is the session data table.
+ *
+ * The rwlock synchronizes access to the session table
+ *
+ * The mutex is to make session lookup and create atomic
+ * so we don't end up with two sessions with the same
+ * system.
+ */
+static struct sdb_session session_table[MLSVC_DOMAIN_MAX];
+static mutex_t smbrdr_screate_mtx;
+static unsigned int session_id = 0;
+
+static struct sdb_session *smbrdr_session_init(smb_ntdomain_t *di);
+static int smbrdr_trnsprt_connect(struct sdb_session *, uint16_t);
+static int smbrdr_session_connect(smb_ntdomain_t *di);
+static int smbrdr_smb_negotiate(struct sdb_session *session);
+static int smbrdr_smb_echo(struct sdb_session *session);
+static void smbrdr_session_disconnect(struct sdb_session *session, int cleanup);
+static int smbrdr_locate_dc(char *domain);
+
+static void
+smbrdr_session_clear(struct sdb_session *session)
+{
+ bzero(session, sizeof (struct sdb_session) - sizeof (rwlock_t));
+}
+
+/*
+ * mlsvc_install_pdc_cb
+ *
+ * Function to be called by SMB initialization code to set up a
+ * callback to the PDC location interface.
+ */
+void
+mlsvc_install_pdc_cb(mlsvc_locate_pdc_t locate_pdc_cb)
+{
+ mlsvc_locate_pdc = locate_pdc_cb;
+}
+
+/*
+ * mlsvc_locate_domain_controller
+ *
+ * Locate a domain controller. Note that this may close an existing
+ * connection to the current domain controller.
+ */
+int
+mlsvc_locate_domain_controller(char *domain)
+{
+ if (mlsvc_locate_pdc)
+ return (mlsvc_locate_pdc(domain));
+
+ return (0);
+}
+
+/*
+ * Entry pointy for smbrdr initialization.
+ */
+void
+smbrdr_init(void)
+{
+ smbrdr_ipc_init();
+}
+
+/*
+ * mlsvc_disconnect
+ *
+ * Disconnects the session with given server.
+ */
+void
+mlsvc_disconnect(char *server)
+{
+ struct sdb_session *session;
+
+ session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE);
+ if (session) {
+ smbrdr_session_disconnect(session, 0);
+ smbrdr_session_unlock(session);
+ }
+}
+
+/*
+ * smbrdr_negotiate
+ *
+ * Negotiate a session with a domain controller in the specified domain.
+ * The domain must be one of values from the smbinfo that indicates the
+ * resource domain or the account domain.
+ *
+ * If a session already exists, we can use that one. Otherwise we create
+ * a new one. This sets up the session key and session security info that
+ * we'll need later to authenticate the user. The session security info
+ * is returned to support the SMB client pass-through authentication
+ * interface.
+ *
+ * Returns 0 on success, otherwise -1.
+ */
+int
+smbrdr_negotiate(char *domain_name)
+{
+ struct sdb_session *session = 0;
+ smb_ntdomain_t *di;
+ int retry = 1;
+ int res = 0;
+
+ if ((di = smb_getdomaininfo(0)) == 0) {
+ /*
+ * Attempting to locate a domain controller
+ * will shutdown an existing PDC connection.
+ */
+ (void) smbrdr_locate_dc(domain_name);
+ di = smb_getdomaininfo(0);
+ }
+
+ if (di == 0) {
+ syslog(LOG_ERR, "smbrdr: negotiate (cannot access domain)");
+ return (-1);
+ }
+
+ /*
+ * The mutex is to make session lookup and create atomic
+ * so we don't end up with two sessions with the same
+ * server.
+ */
+ (void) mutex_lock(&smbrdr_screate_mtx);
+ while (retry > 0) {
+ session = smbrdr_session_lock(di->server, 0, SDB_SLCK_WRITE);
+ if (session != 0) {
+ if (nb_keep_alive(session->sock) == 0) {
+ /* session is good, use it */
+ smbrdr_session_unlock(session);
+ break;
+ } else {
+ /* stale session */
+ session->state = SDB_SSTATE_STALE;
+ smbrdr_session_unlock(session);
+ }
+ }
+
+ if (smbrdr_session_connect(di) != 0) {
+ if (retry > 0) {
+ /* Do we really need to do this here? */
+ (void) smbrdr_locate_dc(domain_name);
+ di = smb_getdomaininfo(0);
+ if (di == 0) {
+ syslog(LOG_ERR, "smbrdr: negotiate"
+ " (cannot access domain)");
+ res = -1;
+ break;
+ }
+ retry--;
+ }
+ } else {
+ /* session is created */
+ retry = 0;
+ }
+ }
+ (void) mutex_unlock(&smbrdr_screate_mtx);
+
+ return (res);
+}
+
+/*
+ * smbrdr_session_connect
+ *
+ * This is the entry point for establishing an SMB connection to a
+ * domain controller. A session structure is allocated, a netbios
+ * session is set up and the SMB protocol is negotiated. If this is
+ * successful, the returned session structure can be used to logon
+ * to the the domain. A null pointer is returned if the connect fails.
+ */
+static int
+smbrdr_session_connect(smb_ntdomain_t *di)
+{
+ struct sdb_session *session;
+ uint16_t port;
+ int rc = 0;
+
+ /*
+ * smbrdr_session_init() will lock the session so that it wouldn't
+ * be accessible until it's established otherwise another thread
+ * might get access to a session which is not fully established.
+ */
+ if ((session = smbrdr_session_init(di)) == 0) {
+ syslog(LOG_ERR, "smbrdr: session init failed");
+ return (-1);
+ }
+
+ for (port = 0; port < smbrdr_nports; ++port) {
+ syslog(LOG_DEBUG, "smbrdr: trying port %d",
+ smbrdr_ports[port]);
+
+ rc = smbrdr_trnsprt_connect(session, smbrdr_ports[port]);
+
+ if (rc == 0) {
+ syslog(LOG_DEBUG, "smbrdr: connected port %d",
+ smbrdr_ports[port]);
+ break;
+ }
+ }
+
+ if (rc < 0) {
+ smbrdr_session_clear(session);
+ smbrdr_session_unlock(session);
+ syslog(LOG_ERR, "smbrdr: NBT/TCP connect failed");
+ return (-1);
+ }
+
+ if (smbrdr_smb_negotiate(session) < 0) {
+ (void) close(session->sock);
+ smbrdr_session_clear(session);
+ smbrdr_session_unlock(session);
+ syslog(LOG_ERR, "smbrdr: SMB negotiate failed");
+ return (-1);
+ }
+
+ smbrdr_session_unlock(session);
+ return (0);
+}
+
+
+/*
+ * smbrdr_trnsprt_connect
+ *
+ * Set up the TCP/IP and NETBIOS protocols for a session. This is just
+ * standard socket sutff. The paranoia check for socket descriptor 0
+ * is because we had a problem with this value and the console telnet
+ * interface will lock up if we use and/or close stdin (0).
+ *
+ * Return 0 on success. Otherwise return (-1) to indicate a problem.
+ */
+static int
+smbrdr_trnsprt_connect(struct sdb_session *sess, uint16_t port)
+{
+ char hostname[MAXHOSTNAMELEN];
+ struct sockaddr_in sin;
+ int sock, rc;
+ mts_wchar_t unicode_server_name[SMB_PI_MAX_DOMAIN];
+ char server_name[SMB_PI_MAX_DOMAIN];
+ unsigned int cpid = oem_get_smb_cpid();
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) <= 0) {
+ /*
+ * We should never see descriptor 0 (stdin).
+ */
+ syslog(LOG_ERR, "smbrdr: socket(%d) failed (%s)", sock,
+ strerror(errno));
+ return (-1);
+ }
+
+ bzero(&sin, sizeof (struct sockaddr_in));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = sess->di.ipaddr;
+ sin.sin_port = htons(port);
+
+ if ((rc = connect(sock, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
+ syslog(LOG_ERR, "smbrdr: connect failed (%s)", strerror(errno));
+ if (sock != 0)
+ (void) close(sock);
+ return (-1);
+ }
+
+ (void) mts_mbstowcs(unicode_server_name, sess->di.server,
+ SMB_PI_MAX_DOMAIN);
+ rc = unicodestooems(server_name, unicode_server_name,
+ SMB_PI_MAX_DOMAIN, cpid);
+ if (rc == 0) {
+ syslog(LOG_ERR, "smbrdr: unicode conversion failed");
+ if (sock != 0)
+ (void) close(sock);
+ return (-1);
+ }
+
+ /*
+ * If we are using NetBIOS, we need to set up a NETBIOS session.
+ * This typically implies that we will be using port 139.
+ * Otherwise, we're doing NetBIOS-less SMB, i.e. SMB over TCP,
+ * which is typically on port 445.
+ */
+ if (port == SSN_SRVC_TCP_PORT) {
+ if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) != 0) {
+ syslog(LOG_ERR, "smbrdr: no hostname");
+ if (sock != 0)
+ (void) close(sock);
+ return (-1);
+ }
+
+ rc = nb_session_request(sock,
+ server_name, sess->scope, hostname, sess->scope);
+
+ if (rc != 0) {
+ syslog(LOG_ERR,
+ "smbrdr: NBT session request to %s failed %d",
+ server_name, rc);
+ if (sock != 0)
+ (void) close(sock);
+ return (-1);
+ }
+ }
+
+ sess->sock = sock;
+ sess->port = port;
+ syslog(LOG_DEBUG, "smbrdr: connected on port %d", port);
+ sess->state = SDB_SSTATE_CONNECTED;
+ return (0);
+}
+
+/*
+ * smbrdr_smb_negotiate
+ *
+ * Negotiate the protocol we are going to use as described in CIFS
+ * section 4.1.1. The only protocol we support is NT LM 0.12, so we
+ * really expect to see dialect 0 in the response. The only other
+ * data gathered is the session key.
+ *
+ * Negotiate using ASCII strings.
+ *
+ * Return 0 on success. Otherwise return a -ve error code.
+ */
+static int
+smbrdr_smb_negotiate(struct sdb_session *sess)
+{
+ unsigned short dialect;
+ smbrdr_handle_t srh;
+ smb_hdr_t smb_hdr;
+ smb_msgbuf_t *mb;
+ DWORD status;
+ int rc;
+ uint8_t tmp_secmode;
+ uint8_t tmp_clen;
+
+ status = smbrdr_request_init(&srh, SMB_COM_NEGOTIATE, sess, 0, 0);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "smbrdr: negotiate (%s)",
+ xlate_nt_status(status));
+ return (-1);
+ }
+
+ mb = &srh.srh_mbuf;
+ rc = smb_msgbuf_encode(mb, "(wct)b (bcc)w (dialect)bs",
+ 0, /* smb_wct */
+ 12, /* smb_bcc */
+ 0x02, /* dialect marker */
+ "NT LM 0.12"); /* only dialect we care about */
+
+ if (rc <= 0) {
+ syslog(LOG_ERR, "smbrdr: negotiate (encode failed)");
+ smbrdr_handle_free(&srh);
+ return (-1);
+ }
+
+ status = smbrdr_exchange(&srh, &smb_hdr, 0);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "smbrdr: negotiate (%s)",
+ xlate_nt_status(status));
+ smbrdr_handle_free(&srh);
+ return (-1);
+ }
+
+ sess->secmode = 0;
+ sess->sesskey = 0;
+ sess->challenge_len = 0;
+
+ rc = smb_msgbuf_decode(mb,
+ "(wordcnt)1.(dialect)w(secm)b12.(skey)l(cap)l10.(klen)b2.",
+ &dialect, &tmp_secmode, &sess->sesskey, &sess->remote_caps,
+ &tmp_clen);
+
+ if (rc <= 0 || dialect != 0) {
+ syslog(LOG_ERR, "smbrdr: negotiate (response error)");
+ smbrdr_handle_free(&srh);
+ return (-1);
+ }
+ sess->secmode = tmp_secmode;
+ sess->challenge_len = tmp_clen;
+
+ rc = smb_msgbuf_decode(mb, "#c",
+ sess->challenge_len, sess->challenge_key);
+ if (rc <= 0) {
+ syslog(LOG_ERR, "smbrdr: negotiate (decode error)");
+ smbrdr_handle_free(&srh);
+ return (-1);
+ }
+
+ smbrdr_handle_free(&srh);
+
+ if ((sess->secmode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) &&
+ (sess->secmode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) {
+ sess->sign_ctx.ssc_flags |= SMB_SCF_REQUIRED;
+ syslog(LOG_DEBUG, "smbrdr: %s requires signing",
+ sess->di.server);
+ }
+
+ sess->state = SDB_SSTATE_NEGOTIATED;
+ return (0);
+}
+
+/*
+ * smbrdr_session_init
+ *
+ * Allocate an available slot in session table for the specified domain
+ * information.
+ *
+ * IMPORTANT! the returned session will be locked caller has to unlock
+ * it by calling smbrdr_session_unlock() after it's done with
+ * the pointer.
+ */
+static struct sdb_session *
+smbrdr_session_init(smb_ntdomain_t *di)
+{
+ struct sdb_session *session = 0;
+ int i;
+ char *p;
+
+ if (di == 0)
+ return (0);
+
+ for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
+ session = &session_table[i];
+
+ (void) rw_wrlock(&session->rwl);
+ if (session->state == SDB_SSTATE_START) {
+ smbrdr_session_clear(session);
+ bcopy(di, &session->di, sizeof (smb_ntdomain_t));
+ (void) utf8_strupr(session->di.domain);
+ (void) utf8_strupr(session->di.server);
+
+ smb_config_rdlock();
+ p = smb_config_getstr(SMB_CI_NBSCOPE);
+ (void) strlcpy(session->scope, p, SMB_PI_MAX_SCOPE);
+ smb_config_unlock();
+
+ (void) strlcpy(session->native_os,
+ "Solaris", SMB_PI_MAX_NATIVE_OS);
+ (void) strlcpy(session->native_lanman,
+ "Windows NT 4.0", SMB_PI_MAX_LANMAN);
+ session->sock = -1;
+ session->port = smbrdr_ports[0];
+ session->smb_flags = SMB_FLAGS_CANONICALIZED_PATHS
+ | SMB_FLAGS_CASE_INSENSITIVE;
+
+ session->smb_flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES
+ | SMB_FLAGS2_KNOWS_EAS;
+
+ /*
+ * Note that by sending vc=0 server will shutdown all
+ * the other connections with NAS if there is any.
+ */
+ session->vc = 0;
+ session->sid = ++session_id;
+ if (session->sid == 0)
+ session->sid = 1;
+ session->state = SDB_SSTATE_INIT;
+ return (session);
+ }
+ (void) rw_unlock(&session->rwl);
+ }
+
+ syslog(LOG_WARNING, "smbrdr: no session available");
+ return (0);
+}
+
+/*
+ * smbrdr_session_disconnect
+ *
+ * This is the entry point for disconnecting an SMB connection. Ensure
+ * that all logons and shares associated with this session are
+ * terminated and then free the session.
+ *
+ * if 'cleanup' is 1 it means that only sessions that are not active
+ * should be cleaned up. if 'cleanup' is 0 disconnect the session in any
+ * states.
+ */
+static void
+smbrdr_session_disconnect(struct sdb_session *session, int cleanup)
+{
+ int state;
+
+ if (session == 0) {
+ syslog(LOG_ERR, "smbrdr: (disconnect) null session");
+ return;
+ }
+
+ state = session->state;
+ if ((state != SDB_SSTATE_DISCONNECTING) &&
+ (state != SDB_SSTATE_CLEANING) &&
+ (state != SDB_SSTATE_START)) {
+ if ((cleanup == 0) || (state == SDB_SSTATE_STALE)) {
+ /*
+ * if session is in stale state it means the connection
+ * is lost so no logoff, tdcon, or close can actually
+ * be sent, thus only cleanup our side.
+ */
+ session->state = (state == SDB_SSTATE_STALE)
+ ? SDB_SSTATE_CLEANING : SDB_SSTATE_DISCONNECTING;
+ (void) smbrdr_smb_logoff(&session->logon);
+ nb_close(session->sock);
+ smbrdr_session_clear(session);
+ }
+ }
+}
+
+/*
+ * smbrdr_session_unlock
+ *
+ * Unlock given session structure.
+ */
+void
+smbrdr_session_unlock(struct sdb_session *session)
+{
+ if (session)
+ (void) rw_unlock(&session->rwl);
+}
+
+/*
+ * smbrdr_session_lock
+ *
+ * Lookup the session associated with the specified domain controller.
+ * If a match is found, we return a pointer to the session, Otherwise
+ * we return null. Only sessions in "negotiated" state are checked.
+ * This mechanism is very simple and implies that we
+ * should only ever have one session open to any domain controller.
+ *
+ * IMPORTANT! the returned session will be locked caller has to unlock
+ * it by calling smbrdr_session_unlock() after it's done with
+ * the pointer.
+ */
+struct sdb_session *
+smbrdr_session_lock(char *server, char *username, int lmode)
+{
+ struct sdb_session *session;
+ int i;
+
+ if (server == 0) {
+ syslog(LOG_ERR, "smbrdr: (lookup) no server specified");
+ return (0);
+ }
+
+ for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
+ session = &session_table[i];
+
+ (lmode == SDB_SLCK_READ) ? (void) rw_rdlock(&session->rwl) :
+ (void) rw_wrlock(&session->rwl);
+
+ if ((session->state == SDB_SSTATE_NEGOTIATED) &&
+ (strcasecmp(session->di.server, server) == 0)) {
+ if (username) {
+ if (strcasecmp(username,
+ session->logon.username) == 0)
+ return (session);
+
+ (void) rw_unlock(&session->rwl);
+ return (0);
+ }
+ return (session);
+ }
+
+ (void) rw_unlock(&session->rwl);
+ }
+
+ return (0);
+}
+
+/*
+ * mlsvc_session_native_values
+ *
+ * Given a file id (i.e. a named pipe fid), return the remote native
+ * OS and LM values for the associated session.
+ */
+int
+mlsvc_session_native_values(int fid, int *remote_os,
+ int *remote_lm, int *pdc_type)
+{
+ struct sdb_session *session;
+ struct sdb_netuse *netuse;
+ struct sdb_ofile *ofile;
+
+ if (remote_os == 0 || remote_lm == 0) {
+ syslog(LOG_ERR, "mlsvc_session_native_values: null");
+ return (-1);
+ }
+
+ if ((ofile = smbrdr_ofile_get(fid)) == 0) {
+ syslog(LOG_ERR,
+ "mlsvc_session_native_values: unknown file (%d)", fid);
+ return (-1);
+ }
+
+ netuse = ofile->netuse;
+ session = netuse->session;
+
+ *remote_os = session->remote_os;
+ *remote_lm = session->remote_lm;
+ if (pdc_type)
+ *pdc_type = session->pdc_type;
+ smbrdr_ofile_put(ofile);
+ return (0);
+}
+
+/*
+ * smbrdr_disconnect_sessions
+ *
+ * Disconnects/cleanups all the sessions
+ */
+static void
+smbrdr_disconnect_sessions(int cleanup)
+{
+ struct sdb_session *session;
+ int i;
+
+ for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
+ session = &session_table[i];
+ (void) rw_wrlock(&session->rwl);
+ smbrdr_session_disconnect(&session_table[i], cleanup);
+ (void) rw_unlock(&session->rwl);
+ }
+}
+
+
+/*
+ * mlsvc_check_sessions
+ *
+ * This function should be run in an independent thread. At the time of
+ * writing it is called periodically from an infinite loop in the start
+ * up thread once initialization is complete. It sends a NetBIOS keep-
+ * alive message on each active session and handles cleanup if a session
+ * is closed from the remote end. Testing demonstrated that the domain
+ * controller will close a session after 15 minutes of inactivity. Note
+ * that neither NetBIOS keep-alive nor SMB echo is deemed as activity
+ * in this case, however, RPC requests appear to reset the timeout and
+ * keep the session open. Note that the NetBIOS request does stop the
+ * remote NetBIOS layer from timing out the connection.
+ */
+void
+mlsvc_check_sessions(void)
+{
+ static int session_keep_alive;
+ struct sdb_session *session;
+ smb_ntdomain_t di;
+ int i;
+
+ ++session_keep_alive;
+
+ for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
+ session = &session_table[i];
+
+ (void) rw_wrlock(&session->rwl);
+
+ if (session->state < SDB_SSTATE_CONNECTED) {
+ (void) rw_unlock(&session->rwl);
+ continue;
+ }
+
+ /*
+ * NetBIOS is only used on with port 139. The keep alive
+ * is not relevant over NetBIOS-less SMB over port 445.
+ * This is just to see if the socket is still alive.
+ */
+ if (session->port == SSN_SRVC_TCP_PORT) {
+ if (nb_keep_alive(session->sock) != 0) {
+ session->state = SDB_SSTATE_STALE;
+ (void) rw_unlock(&session->rwl);
+ continue;
+ }
+ }
+
+ if (session_keep_alive >= MLSVC_SESSION_FORCE_KEEPALIVE) {
+ if (smbrdr_smb_echo(session) != 0) {
+ syslog(LOG_WARNING,
+ "smbrdr: monitor[%s] cannot contact %s",
+ session->di.domain, session->di.server);
+ (void) memcpy(&di, &session->di,
+ sizeof (smb_ntdomain_t));
+ session->state = SDB_SSTATE_STALE;
+ (void) rw_unlock(&session->rwl);
+ if (smb_getdomaininfo(0) == 0)
+ (void) smbrdr_locate_dc(di.domain);
+ }
+ } else
+ (void) rw_unlock(&session->rwl);
+ }
+
+ if (session_keep_alive >= MLSVC_SESSION_FORCE_KEEPALIVE) {
+ session_keep_alive = 0;
+ /* cleanup */
+ smbrdr_disconnect_sessions(1);
+ }
+}
+
+/*
+ * smbrdr_dump_sessions
+ *
+ * Debug function to dump the session table.
+ */
+void
+smbrdr_dump_sessions(void)
+{
+ struct sdb_session *session;
+ struct sdb_logon *logon;
+ char ipstr[16];
+ int i;
+
+ for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) {
+ session = &session_table[i];
+
+ (void) rw_rdlock(&session->rwl);
+ if (session->state != SDB_SSTATE_START) {
+ (void) inet_ntop(AF_INET,
+ (const void *)(&session->di.ipaddr),
+ ipstr, sizeof (ipstr));
+
+ syslog(LOG_DEBUG, "session[%d]: state=%d",
+ i, session->state);
+ syslog(LOG_DEBUG, "session[%d]: %s %s (%s)", i,
+ session->di.domain, session->di.server, ipstr);
+ syslog(LOG_DEBUG, "session[%d]: %s %s (sock=%d)", i,
+ session->native_os, session->native_lanman,
+ session->sock);
+
+ logon = &session->logon;
+ if (logon->type != SDB_LOGON_NONE)
+ syslog(LOG_DEBUG, "logon[%d]: %s (uid=%d)",
+ i, logon->username, logon->uid);
+ }
+ (void) rw_unlock(&session->rwl);
+ }
+}
+
+/*
+ * mlsvc_echo
+ */
+int
+mlsvc_echo(char *server)
+{
+ struct sdb_session *session;
+ int res = 0;
+
+ if ((session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE)) == 0)
+ return (1);
+
+ if (smbrdr_smb_echo(session) != 0) {
+ session->state = SDB_SSTATE_STALE;
+ res = -1;
+ }
+
+ smbrdr_session_unlock(session);
+ return (res);
+}
+
+/*
+ * smbrdr_smb_echo
+ *
+ * This request can be used to test the connection to the server. The
+ * server should echo the data sent. The server should ignore the tid
+ * in the header, so this request when there are no tree connections.
+ * See CIFS/1.0 section 4.1.7.
+ *
+ * Return 0 on success. Otherwise return a -ve error code.
+ */
+static int
+smbrdr_smb_echo(struct sdb_session *session)
+{
+ static char *echo_str = "smbrdr";
+ smbrdr_handle_t srh;
+ smb_hdr_t smb_hdr;
+ DWORD status;
+ int rc;
+
+ if ((session->state == SDB_SSTATE_DISCONNECTING) ||
+ (session->state == SDB_SSTATE_CLEANING) ||
+ (session->state == SDB_SSTATE_STALE)) {
+ return (-1);
+ }
+
+ status = smbrdr_request_init(&srh, SMB_COM_ECHO, session, 0, 0);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrEcho: %s", xlate_nt_status(status));
+ return (-1);
+ }
+
+ rc = smb_msgbuf_encode(&srh.srh_mbuf, "bwws", 1, 1,
+ strlen(echo_str), echo_str);
+ if (rc <= 0) {
+ syslog(LOG_ERR, "SmbrdrEcho: encode failed");
+ smbrdr_handle_free(&srh);
+ return (-1);
+ }
+
+ status = smbrdr_exchange(&srh, &smb_hdr, 10);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrEcho: %s", xlate_nt_status(status));
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+ smbrdr_handle_free(&srh);
+ return (rc);
+}
+
+/*
+ * smbrdr_locate_dc
+ *
+ * Locate a domain controller. Note that this may close an existing
+ * connection to the current domain controller.
+ */
+static int
+smbrdr_locate_dc(char *domain)
+{
+ if (mlsvc_locate_pdc)
+ return (mlsvc_locate_pdc(domain));
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_transact.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_transact.c
new file mode 100644
index 0000000000..24bf3a5cea
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_transact.c
@@ -0,0 +1,254 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SMB transaction functions to support MLRPC.
+ */
+
+#include <syslog.h>
+#include <strings.h>
+
+#include <smbsrv/libsmbrdr.h>
+
+#include <smbsrv/ntstatus.h>
+#include <smbsrv/smb.h>
+#include <smbrdr.h>
+
+/*
+ * The pipe filename, length (including the null terminator)
+ * and the buffer size for the transaction. Moving to unicode
+ * revealed that the length should not include the null.
+ */
+#define TX_FILENAME "\\PIPE\\"
+#define TX_FILENAME_ASCII_LEN 6
+#define TX_FILENAME_WCHAR_LEN 14
+
+
+static int prep_smb_transact(smb_msgbuf_t *, unsigned short, char *,
+ unsigned short, unsigned short, unsigned);
+static int decode_smb_transact(smb_msgbuf_t *, char *, unsigned,
+ smb_transact_rsp_t *);
+
+/*
+ * smbrdr_rpc_transact
+ *
+ * Send a SMB_COM_TRANSACTION request.
+ */
+int
+smbrdr_rpc_transact(int fid, char *out_buf, int out_len,
+ char *in_buf, int in_len)
+{
+ struct sdb_session *session;
+ struct sdb_netuse *netuse;
+ struct sdb_ofile *ofile;
+ struct sdb_logon *logon;
+ smb_transact_rsp_t rsp;
+ smbrdr_handle_t srh;
+ smb_msgbuf_t *mb;
+ DWORD status;
+ int rc;
+ unsigned short rcv_dcnt;
+ int cur_inlen;
+ int first_rsp;
+
+ if ((ofile = smbrdr_ofile_get(fid)) == 0)
+ return (-1);
+
+ netuse = ofile->netuse;
+ session = netuse->session;
+ logon = &session->logon;
+
+ status = smbrdr_request_init(&srh, SMB_COM_TRANSACTION,
+ session, logon, netuse);
+
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "SmbrdrTransact: %s", xlate_nt_status(status));
+ smbrdr_ofile_put(ofile);
+ return (-1);
+ }
+
+ mb = &srh.srh_mbuf;
+
+ rc = prep_smb_transact(mb, ofile->fid, out_buf, out_len, in_len,
+ session->remote_caps & CAP_UNICODE);
+ if (rc < 0) {
+ syslog(LOG_ERR,
+ "smbrdr_rpc_transact: prep_smb_transact failed");
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_put(ofile);
+ return (rc);
+ }
+
+ smbrdr_lock_transport();
+
+ status = smbrdr_send(&srh);
+ if (status != NT_STATUS_SUCCESS) {
+ smbrdr_unlock_transport();
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_put(ofile);
+ syslog(LOG_ERR, "smbrdr_rpc_transact: send failed");
+ return (-1);
+ }
+
+ rcv_dcnt = 0;
+ cur_inlen = in_len;
+ first_rsp = 1;
+
+ do {
+ if (smbrdr_rcv(&srh, first_rsp) != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR, "smbrdr_rpc_transact: nb_rcv failed");
+ rc = -1;
+ break;
+ }
+
+ rc = decode_smb_transact(mb, in_buf, cur_inlen, &rsp);
+ if (rc < 0 || rsp.TotalDataCount > in_len) {
+ syslog(LOG_ERR,
+ "SmbTransact: transact decode failure!");
+ rc = -1;
+ break;
+ }
+
+ rcv_dcnt += rsp.DataCount;
+ cur_inlen -= rsp.DataCount;
+ first_rsp = 0;
+
+ } while (rcv_dcnt < rsp.TotalDataCount);
+
+ smbrdr_unlock_transport();
+ smbrdr_handle_free(&srh);
+ smbrdr_ofile_put(ofile);
+
+ return ((rc < 0) ? rc : rcv_dcnt);
+}
+
+
+/*
+ * prep_smb_transact
+ *
+ * Prepare the SMB_COM_TRANSACTION request.
+ */
+static int
+prep_smb_transact(smb_msgbuf_t *mb, unsigned short fid, char *out,
+ unsigned short out_len, unsigned short in_max, unsigned unicode)
+{
+ int data_off;
+ int rc;
+ unsigned short bcc;
+
+ /*
+ * The byte count seems to include the pad
+ * byte to word align the filename and two
+ * spurious pad bytes between the filename
+ * and the transaction data.
+ */
+ bcc = out_len + 3;
+ bcc += (unicode) ? TX_FILENAME_WCHAR_LEN : TX_FILENAME_ASCII_LEN;
+
+ data_off = 32; /* sizeof SMB header up to smb_wct */
+ data_off += 1; /* sizeof smb_wct */
+ data_off += 16*2; /* sizeof word parameters */
+ data_off += 2; /* sizeof smb_bcc */
+ data_off += (unicode) ? TX_FILENAME_WCHAR_LEN : TX_FILENAME_ASCII_LEN;
+ data_off += 3;
+ /* this is where data starts */
+
+ rc = smb_msgbuf_encode(mb,
+ "(wct)b"
+ "(tpscnt)w (tdscnt)w (mprcnt)w (mdrcnt)w (msrcnt)b"
+ "(rsvd). (flags)w (timeo)l (rsvd1)2."
+ "(pscnt)w (psoff)w (dscnt)w (dsoff)w (suwcnt)b"
+ "(rsvd2). (pipop)w (fid)w (bcc)w (fname)u",
+ 16, /* smb_wct */
+ 0, /* total parm bytes */
+ out_len, /* total data bytes */
+ 0, /* max parm bytes to ret */
+ in_max, /* max data bytes to ret */
+ 0, /* max setup words to ret */
+ 0, /* transact flags */
+ 0, /* transact timeout */
+ 0, /* parameter bytes */
+ data_off, /* parameter offset */
+ out_len, /* data bytes */
+ data_off, /* data offset */
+ 2, /* total setup words */
+ 0x0026, /* OP=TransactNmPipe */
+ fid, /* FID */
+ bcc, /* byte count */
+ TX_FILENAME); /* file name */
+
+ /*
+ * Transaction data - padded.
+ */
+ rc = smb_msgbuf_encode(mb, "..#c", out_len, out);
+ return (rc);
+}
+
+
+/*
+ * decode_smb_transact
+ *
+ * Decode the response from the SMB_COM_TRANSACTION request.
+ */
+static int
+decode_smb_transact(smb_msgbuf_t *mb, char *in, unsigned in_len,
+ smb_transact_rsp_t *rsp)
+{
+ int rc;
+
+ rc = smb_msgbuf_decode(mb, "b", &rsp->WordCount);
+ if (rc <= 0 || rsp->WordCount < 10) {
+ syslog(LOG_ERR, "SmbTransact: invalid word count");
+ return (-1);
+ }
+
+ rc = smb_msgbuf_decode(mb,
+ "(tpscnt)w (tdscnt)w (rsvd)2."
+ "(pscnt)w (psoff)w (psdisp)w (dscnt)w (dsoff)w"
+ "(dsdisp)w (suwcnt)b (rsvd). (bcc)w",
+ &rsp->TotalParamCount, /* Total parm bytes */
+ &rsp->TotalDataCount, /* Total data bytes */
+ &rsp->ParamCount, /* Parm bytes this buffer */
+ &rsp->ParamOffset, /* Parm offset from hdr */
+ &rsp->ParamDisplacement, /* Parm displacement */
+ &rsp->DataCount, /* Data bytes this buffer */
+ &rsp->DataOffset, /* Data offset from hdr */
+ &rsp->DataDisplacement, /* Data displacement */
+ &rsp->SetupCount, /* Setup word count */
+ &rsp->BCC); /* smb_bcc */
+
+ if (rc <= 0)
+ return (-1);
+
+ if (rsp->DataCount > in_len)
+ return (-1);
+
+ bcopy(mb->base + rsp->DataOffset,
+ in + rsp->DataDisplacement, rsp->DataCount);
+
+ return (0);
+}
diff --git a/usr/src/lib/smbsrv/libsmbrdr/i386/Makefile b/usr/src/lib/smbsrv/libsmbrdr/i386/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libsmbrdr/sparc/Makefile b/usr/src/lib/smbsrv/libsmbrdr/sparc/Makefile
new file mode 100644
index 0000000000..f91f0270e9
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/smbsrv/libsmbrdr/sparcv9/Makefile b/usr/src/lib/smbsrv/libsmbrdr/sparcv9/Makefile
new file mode 100644
index 0000000000..a2f97019c8
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmbrdr/sparcv9/Makefile
@@ -0,0 +1,31 @@
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)