summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/Makefile8
-rw-r--r--usr/src/lib/common/inc/c_synonyms.h1
-rw-r--r--usr/src/lib/libbc/inc/include/tzfile.h139
-rw-r--r--usr/src/lib/libbsm/audit_event.txt2
-rw-r--r--usr/src/lib/libbsm/common/adt.xml55
-rw-r--r--usr/src/lib/libc/Makefile.targ4
-rw-r--r--usr/src/lib/libc/amd64/Makefile5
-rw-r--r--usr/src/lib/libc/i386/Makefile.com6
-rw-r--r--usr/src/lib/libc/port/gen/attrat.c273
-rw-r--r--usr/src/lib/libc/port/gen/privlib.c2
-rw-r--r--usr/src/lib/libc/port/llib-lc1
-rw-r--r--usr/src/lib/libc/port/mapfile-vers12
-rw-r--r--usr/src/lib/libc/port/sys/fsmisc.c13
-rw-r--r--usr/src/lib/libc/sparc/Makefile6
-rw-r--r--usr/src/lib/libc/sparcv9/Makefile6
-rw-r--r--usr/src/lib/libcmdutils/Makefile.com8
-rw-r--r--usr/src/lib/libcmdutils/common/mapfile-vers8
-rw-r--r--usr/src/lib/libcmdutils/common/process_xattrs.c334
-rw-r--r--usr/src/lib/libcmdutils/common/sysattrs.c128
-rw-r--r--usr/src/lib/libcmdutils/common/writefile.c230
-rw-r--r--usr/src/lib/libcmdutils/libcmdutils.h57
-rw-r--r--usr/src/lib/libsec/common/acl.y4
-rw-r--r--usr/src/lib/libsec/common/acl_lex.l73
-rw-r--r--usr/src/lib/libsec/common/acltext.c55
-rw-r--r--usr/src/lib/libsec/common/aclutils.c1388
-rw-r--r--usr/src/lib/libsec/common/aclutils.h24
-rw-r--r--usr/src/lib/libsecdb/auth_attr.txt3
-rwxr-xr-xusr/src/lib/libsecdb/help/auths/AuthReadSMB.html38
-rw-r--r--usr/src/lib/libsecdb/help/auths/Makefile3
-rw-r--r--usr/src/lib/libsecdb/help/auths/SmfSMBStates.html40
-rw-r--r--usr/src/lib/libsecdb/help/auths/SmfValueSMB.html40
-rw-r--r--usr/src/lib/libsecdb/help/profiles/Makefile1
-rw-r--r--usr/src/lib/libsecdb/help/profiles/RtSMBMngmnt.html40
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt3
-rw-r--r--usr/src/lib/libshare/Makefile2
-rw-r--r--usr/src/lib/libshare/common/libshare.c1194
-rw-r--r--usr/src/lib/libshare/common/libshare.h48
-rw-r--r--usr/src/lib/libshare/common/libshare_impl.h13
-rw-r--r--usr/src/lib/libshare/common/libshare_zfs.c388
-rw-r--r--usr/src/lib/libshare/common/libsharecore.c236
-rw-r--r--usr/src/lib/libshare/common/mapfile-vers19
-rw-r--r--usr/src/lib/libshare/common/plugin.c175
-rw-r--r--usr/src/lib/libshare/common/scfutil.c418
-rw-r--r--usr/src/lib/libshare/nfs/libshare_nfs.c47
-rw-r--r--usr/src/lib/libshare/smb/Makefile51
-rw-r--r--usr/src/lib/libshare/smb/Makefile.com88
-rw-r--r--usr/src/lib/libshare/smb/amd64/Makefile31
-rw-r--r--usr/src/lib/libshare/smb/i386/Makefile30
-rw-r--r--usr/src/lib/libshare/smb/libshare_smb.c1641
-rw-r--r--usr/src/lib/libshare/smb/libshare_smb.h75
-rw-r--r--usr/src/lib/libshare/smb/mapfile-vers34
-rw-r--r--usr/src/lib/libshare/smb/smb_share_doorclnt.c951
-rw-r--r--usr/src/lib/libshare/smb/sparc/Makefile30
-rw-r--r--usr/src/lib/libshare/smb/sparcv9/Makefile31
-rw-r--r--usr/src/lib/libsldap/common/ns_writes.c2
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h12
-rw-r--r--usr/src/lib/libzfs/common/libzfs_changelist.c62
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c80
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h23
-rw-r--r--usr/src/lib/libzfs/common/libzfs_mount.c313
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c4
-rw-r--r--usr/src/lib/libzfs/common/mapfile-vers7
-rw-r--r--usr/src/lib/libzpool/common/kernel.c15
-rw-r--r--usr/src/lib/libzpool/common/sys/zfs_context.h115
-rw-r--r--usr/src/lib/pam_modules/Makefile3
-rw-r--r--usr/src/lib/pam_modules/smb/Makefile56
-rw-r--r--usr/src/lib/pam_modules/smb/Makefile.com42
-rw-r--r--usr/src/lib/pam_modules/smb/amd64/Makefile35
-rw-r--r--usr/src/lib/pam_modules/smb/i386/Makefile34
-rw-r--r--usr/src/lib/pam_modules/smb/mapfile-vers33
-rw-r--r--usr/src/lib/pam_modules/smb/smb_passwd.c202
-rw-r--r--usr/src/lib/pam_modules/smb/sparc/Makefile34
-rw-r--r--usr/src/lib/pam_modules/smb/sparcv9/Makefile35
-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
207 files changed, 63402 insertions, 2069 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 68613f9412..ab96c6624e 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -234,6 +234,7 @@ SUBDIRS += \
libidmap \
libipmi \
libexacct/demo \
+ smbsrv \
$($(MACH)_SUBDIRS)
sparc_SUBDIRS= .WAIT \
@@ -421,6 +422,7 @@ HDRSUBDIRS= \
libkrb5 \
libshare \
libidmap \
+ smbsrv \
$($(MACH)_HDRSUBDIRS)
$(CLOSED_BUILD)HDRSUBDIRS += \
@@ -506,7 +508,7 @@ libinetcfg: libnsl libsocket libdevinfo
libkmf: libcryptoutil pkcs11 openssl
libnsl: libmd5 libscf
libmapid: libresolv
-libuuid: libdlpi libdladm
+libuuid: libdlpi libdladm
libinetutil: libsocket
libsecdb: libnsl
libsasl: libgss libsocket pkcs11 libmd
@@ -528,7 +530,7 @@ libwrap: libnsl libsocket
libwanboot: libnvpair libresolv libnsl libsocket libdevinfo libinetutil \
libdhcputil openssl
libwanbootutil: libnsl
-pam_modules: libproject passwdutil $(SMARTCARD)
+pam_modules: libproject passwdutil $(SMARTCARD) smbsrv
libscf: libuutil libmd libgen
libinetsvc: libscf
librestart: libuutil libscf
@@ -545,6 +547,8 @@ brand: libc libsocket
libshare: libscf libzfs libuuid libfsmgt libsecdb
libexacct/demo: libexacct libproject libsocket libnsl
libtsalarm: libpcp
+smbsrv: libsocket libnsl libmd libxnet libpthread librt \
+ libshare libidmap pkcs11
#
# The reason this rule checks for the existence of the
diff --git a/usr/src/lib/common/inc/c_synonyms.h b/usr/src/lib/common/inc/c_synonyms.h
index 50bb091a58..8b8a610531 100644
--- a/usr/src/lib/common/inc/c_synonyms.h
+++ b/usr/src/lib/common/inc/c_synonyms.h
@@ -744,6 +744,7 @@ extern "C" {
#define readv _readv
#define realpath _realpath
#define remque _remque
+#define renameat _renameat
#define resolvepath _resolvepath
#define rmdir _rmdir
#define rwlock_destroy _rwlock_destroy
diff --git a/usr/src/lib/libbc/inc/include/tzfile.h b/usr/src/lib/libbc/inc/include/tzfile.h
index d6e43f03c2..b094236256 100644
--- a/usr/src/lib/libbc/inc/include/tzfile.h
+++ b/usr/src/lib/libbc/inc/include/tzfile.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,139 +19,17 @@
* CDDL HEADER END
*/
/*
- * Copyright 1989 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* from Arthur Olson's 6.1 */
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#ifndef TZFILE_H
-
-#define TZFILE_H
-
-/*
-** Information about time zone files.
-*/
-
-#define TZDIR "/usr/share/lib/zoneinfo" /* Time zone object file directory */
-
-#define TZDEFAULT (getenv("TZ"))
-
-#define TZDEFRULES "posixrules"
-
-/*
-** Each file begins with. . .
-*/
-
-struct tzhead {
- char tzh_reserved[24]; /* reserved for future use */
- char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
- char tzh_leapcnt[4]; /* coded number of leap seconds */
- char tzh_timecnt[4]; /* coded number of transition times */
- char tzh_typecnt[4]; /* coded number of local time types */
- char tzh_charcnt[4]; /* coded number of abbr. chars */
-};
-
-/*
-** . . .followed by. . .
-**
-** tzh_timecnt (char [4])s coded transition times a la time(2)
-** tzh_timecnt (unsigned char)s types of local time starting at above
-** tzh_typecnt repetitions of
-** one (char [4]) coded GMT offset in seconds
-** one (unsigned char) used to set tm_isdst
-** one (unsigned char) that's an abbreviation list index
-** tzh_charcnt (char)s '\0'-terminated zone abbreviations
-** tzh_leapcnt repetitions of
-** one (char [4]) coded leap second transition times
-** one (char [4]) total correction after above
-** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
-** time is standard time, if FALSE,
-** transition time is wall clock time
-** if absent, transition times are
-** assumed to be wall clock time
-*/
-
-/*
-** In the current implementation, "tzset()" refuses to deal with files that
-** exceed any of the limits below.
-*/
+#ifndef _TZFILE_H
+#define _TZFILE_H
-/*
-** The TZ_MAX_TIMES value below is enough to handle a bit more than a
-** year's worth of solar time (corrected daily to the nearest second) or
-** 138 years of Pacific Presidential Election time
-** (where there are three time zone transitions every fourth year).
-*/
-#define TZ_MAX_TIMES 370
-
-#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
-
-#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
-
-#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
-
-#define SECSPERMIN 60
-#define MINSPERHOUR 60
-#define HOURSPERDAY 24
-#define DAYSPERWEEK 7
-#define DAYSPERNYEAR 365
-#define DAYSPERLYEAR 366
-#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
-#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
-#define MONSPERYEAR 12
-
-#define TM_SUNDAY 0
-#define TM_MONDAY 1
-#define TM_TUESDAY 2
-#define TM_WEDNESDAY 3
-#define TM_THURSDAY 4
-#define TM_FRIDAY 5
-#define TM_SATURDAY 6
-
-#define TM_JANUARY 0
-#define TM_FEBRUARY 1
-#define TM_MARCH 2
-#define TM_APRIL 3
-#define TM_MAY 4
-#define TM_JUNE 5
-#define TM_JULY 6
-#define TM_AUGUST 7
-#define TM_SEPTEMBER 8
-#define TM_OCTOBER 9
-#define TM_NOVEMBER 10
-#define TM_DECEMBER 11
-
-#define TM_YEAR_BASE 1900
-
-#define EPOCH_YEAR 1970
-#define EPOCH_WDAY TM_THURSDAY
-
-/*
-** Accurate only for the past couple of centuries;
-** that will probably do.
-*/
-
-#define isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0)
-
-/*
-** Use of the underscored variants may cause problems if you move your code to
-** certain System-V-based systems; for maximum portability, use the
-** underscore-free variants. The underscored variants are provided for
-** backward compatibility only; they may disappear from future versions of
-** this file.
-*/
+#pragma ident "%Z%%M% %I% %E% SMI"
-#define SECS_PER_MIN SECSPERMIN
-#define MINS_PER_HOUR MINSPERHOUR
-#define HOURS_PER_DAY HOURSPERDAY
-#define DAYS_PER_WEEK DAYSPERWEEK
-#define DAYS_PER_NYEAR DAYSPERNYEAR
-#define DAYS_PER_LYEAR DAYSPERLYEAR
-#define SECS_PER_HOUR SECSPERHOUR
-#define SECS_PER_DAY SECSPERDAY
-#define MONS_PER_YEAR MONSPERYEAR
+#include <sys/tzfile.h>
-#endif /* !defined TZFILE_H */
+#endif /* _TZFILE_H */
diff --git a/usr/src/lib/libbsm/audit_event.txt b/usr/src/lib/libbsm/audit_event.txt
index 8192080da3..125ef50174 100644
--- a/usr/src/lib/libbsm/audit_event.txt
+++ b/usr/src/lib/libbsm/audit_event.txt
@@ -444,6 +444,8 @@
6241:AUE_uadmin_remount:uadmin(1m) - remount:ss
6242:AUE_uadmin_ftrace:uadmin(1m) - ftrace:ss
6243:AUE_uadmin_swapctl:uadmin(1m) - swapctl:ss
+6237:AUE_smbd_session:smbd(1m) session setup:lo
+6238:AUE_smbd_logoff:smbd(1m) session logoff:lo
#
# Trusted Extensions events:
#
diff --git a/usr/src/lib/libbsm/common/adt.xml b/usr/src/lib/libbsm/common/adt.xml
index 3ae2957188..a91361e402 100644
--- a/usr/src/lib/libbsm/common/adt.xml
+++ b/usr/src/lib/libbsm/common/adt.xml
@@ -1231,8 +1231,61 @@ Use is subject to license terms.
<see>uadmin(1M)</see>
</event>
+<!-- smbd service event; smbd session setup -->
+ <event id="AUE_smbd_session" header="0" idNo="58" omit="JNI">
+ <title>smbd</title>
+ <program>/usr/lib/smbsrv/smbd</program>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="domain">
+ <internal token="text"/>
+ <external opt="required" type="char*"/>
+ <comment>domain</comment>
+ </entry>
+ <entry id="username">
+ <internal token="text"/>
+ <external opt="required" type="char*"/>
+ <comment>username</comment>
+ </entry>
+ <entry id="sid">
+ <internal token="text"/>
+ <external opt="optional" type="char*"/>
+ <comment>sid</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+<!-- smbd service event; smbd session logoff -->
+ <event id="AUE_smbd_logoff" header="0" idNo="59" omit="JNI">
+ <title>smbd</title>
+ <program>/usr/lib/smbsrv/smbd</program>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="domain">
+ <internal token="text"/>
+ <external opt="required" type="char*"/>
+ <comment>domain</comment>
+ </entry>
+ <entry id="username">
+ <internal token="text"/>
+ <external opt="required" type="char*"/>
+ <comment>username</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
<!-- add new events here with the next higher idNo -->
-<!-- Highest idNo is 57, so next is 58, then fix this comment -->
+<!-- Highest idNo is 59, so next is 60, then fix this comment -->
<!-- end of C Only events -->
diff --git a/usr/src/lib/libc/Makefile.targ b/usr/src/lib/libc/Makefile.targ
index b8237246a4..95a0a3b0e3 100644
--- a/usr/src/lib/libc/Makefile.targ
+++ b/usr/src/lib/libc/Makefile.targ
@@ -272,6 +272,10 @@ $(COMOBJS:%=pics/%): $(SRC)/common/util/$$(@F:.o=.c)
$(COMPILE.c) -o $@ $(SRC)/common/util/$(@F:.o=.c)
$(POST_PROCESS_O)
+$(XATTROBJS:%=pics/%): $(SRC)/common/xattr/$$(@F:.o=.c)
+ $(COMPILE.c) -o $@ $(SRC)/common/xattr/$(@F:.o=.c)
+ $(POST_PROCESS_O)
+
$(DTRACEOBJS:%=pics/%): $(SRC)/common/dtrace/$$(@F:.o=.c)
$(COMPILE.c) -o $@ $(SRC)/common/dtrace/$(@F:.o=.c)
$(POST_PROCESS_O)
diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile
index 0e2d924805..0539645b34 100644
--- a/usr/src/lib/libc/amd64/Makefile
+++ b/usr/src/lib/libc/amd64/Makefile
@@ -85,6 +85,8 @@ FPASMOBJS= \
ATOMICOBJS= \
atomic.o
+XATTROBJS= \
+ xattr_common.o
COMOBJS= \
bcmp.o \
bcopy.o \
@@ -99,6 +101,7 @@ GENOBJS= \
_getsp.o \
abs.o \
alloca.o \
+ attrat.o \
byteorder.o \
cache.o \
cuexit.o \
@@ -849,6 +852,7 @@ MOSTOBJS= \
$(I386FPOBJS) \
$(FPASMOBJS) \
$(ATOMICOBJS) \
+ $(XATTROBJS) \
$(COMOBJS) \
$(GENOBJS) \
$(PORTFP) \
@@ -966,6 +970,7 @@ CLOBBERFILES += $(LIB_PIC)
# list of C source for lint
SRCS= \
$(ATOMICOBJS:%.o=$(SRC)/common/atomic/%.c) \
+ $(XATTROBJS:%.o=$(SRC)/common/xattr/%.c) \
$(COMOBJS:%.o=$(SRC)/common/util/%.c) \
$(PORTFP:%.o=../port/fp/%.c) \
$(PORTGEN:%.o=../port/gen/%.c) \
diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com
index 6fe1a1c5cc..5fb4ffea84 100644
--- a/usr/src/lib/libc/i386/Makefile.com
+++ b/usr/src/lib/libc/i386/Makefile.com
@@ -83,6 +83,9 @@ FPASMOBJS= \
ATOMICOBJS= \
atomic.o
+XATTROBJS= \
+ xattr_common.o
+
COMOBJS= \
bcmp.o \
bcopy.o \
@@ -389,6 +392,7 @@ PORTGEN= \
atoi.o \
atol.o \
atoll.o \
+ attrat.o \
attropen.o \
atexit.o \
atfork.o \
@@ -884,6 +888,7 @@ MOSTOBJS= \
$(FPOBJS) \
$(FPASMOBJS) \
$(ATOMICOBJS) \
+ $(XATTROBJS) \
$(COMOBJS) \
$(DTRACEOBJS) \
$(GENOBJS) \
@@ -1026,6 +1031,7 @@ CLOBBERFILES += $(LIB_PIC)
# list of C source for lint
SRCS= \
$(ATOMICOBJS:%.o=$(SRC)/common/atomic/%.c) \
+ $(XATTROBJS:%.o=$(SRC)/common/xattr/%.c) \
$(COMOBJS:%.o=$(SRC)/common/util/%.c) \
$(DTRACEOBJS:%.o=$(SRC)/common/dtrace/%.c) \
$(PORTFP:%.o=../port/fp/%.c) \
diff --git a/usr/src/lib/libc/port/gen/attrat.c b/usr/src/lib/libc/port/gen/attrat.c
new file mode 100644
index 0000000000..f2a26d17cd
--- /dev/null
+++ b/usr/src/lib/libc/port/gen/attrat.c
@@ -0,0 +1,273 @@
+/*
+ * 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 "synonyms.h"
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <mtlib.h>
+#include <attr.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/filio.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <stdio.h>
+
+static int (*nvpacker)(nvlist_t *, char **, size_t *, int, int);
+static int (*nvsize)(nvlist_t *, size_t *, int);
+static int (*nvunpacker)(char *, size_t, nvlist_t **);
+static mutex_t attrlock = DEFAULTMUTEX;
+static int initialized;
+extern int __openattrdirat(int basefd, const char *name);
+
+static char *xattr_view_name[XATTR_VIEW_LAST] = {
+ VIEW_READONLY,
+ VIEW_READWRITE
+};
+
+static int
+attrat_init()
+{
+ if (initialized == 0) {
+ lmutex_lock(&attrlock);
+ if (initialized == 1) {
+ lmutex_unlock(&attrlock);
+ return (0);
+ }
+
+ void *libnvhandle = dlopen("libnvpair.so.1", RTLD_LAZY);
+ if (libnvhandle == NULL || (nvpacker = (int (*)(nvlist_t *,
+ char **, size_t *, int, int)) dlsym(libnvhandle,
+ "nvlist_pack")) == NULL ||
+ (nvsize = (int (*)(nvlist_t *,
+ size_t *, int)) dlsym(libnvhandle,
+ "nvlist_size")) == NULL ||
+ (nvunpacker = (int (*)(char *, size_t,
+ nvlist_t **)) dlsym(libnvhandle,
+ "nvlist_unpack")) == NULL) {
+ if (libnvhandle)
+ dlclose(libnvhandle);
+ lmutex_unlock(&attrlock);
+ return (EINVAL);
+ }
+
+ initialized = 1;
+ lmutex_unlock(&attrlock);
+ }
+ return (0);
+}
+
+static int
+attr_nv_pack(nvlist_t *request, void **nv_request, size_t *nv_requestlen)
+{
+ size_t bufsize;
+ char *packbuf = NULL;
+
+ if (nvsize(request, &bufsize, NV_ENCODE_XDR) != 0) {
+ return (EINVAL);
+ }
+
+ packbuf = malloc(bufsize);
+ if (packbuf == NULL)
+ return (EINVAL);
+ if (nvpacker(request, &packbuf, &bufsize, NV_ENCODE_XDR, 0) != 0) {
+ free(packbuf);
+ } else {
+ *nv_request = (void *)packbuf;
+ *nv_requestlen = bufsize;
+ }
+ return (0);
+}
+
+static const char *
+view_to_name(xattr_view_t view)
+{
+ if (view >= XATTR_VIEW_LAST || view < 0)
+ return (NULL);
+ return (xattr_view_name[view]);
+}
+
+static int
+xattr_openat(int basefd, xattr_view_t view, int mode)
+{
+ const char *xattrname;
+ int xattrfd;
+ int oflag;
+
+ switch (view) {
+ case XATTR_VIEW_READONLY:
+ oflag = O_RDONLY;
+ break;
+ case XATTR_VIEW_READWRITE:
+ oflag = mode & O_RDWR;
+ break;
+ default:
+ (void) __set_errno(EINVAL);
+ return (-1);
+ }
+ if (mode & O_XATTR)
+ oflag |= O_XATTR;
+
+ xattrname = view_to_name(view);
+ xattrfd = openat(basefd, xattrname, oflag);
+ if (xattrfd < 0)
+ return (xattrfd);
+ /* Don't cache sysattr info (advisory) */
+ (void) directio(xattrfd, DIRECTIO_ON);
+ return (xattrfd);
+}
+
+static int
+cgetattr(int fd, nvlist_t **response)
+{
+ int error;
+ int bytesread;
+ void *nv_response;
+ size_t nv_responselen;
+ struct stat buf;
+
+ if (error = attrat_init())
+ return (__set_errno(error));
+ if ((error = fstat(fd, &buf)) != 0)
+ return (__set_errno(error));
+ nv_responselen = buf.st_size;
+
+ if ((nv_response = malloc(nv_responselen)) == NULL)
+ return (__set_errno(ENOMEM));
+ bytesread = read(fd, nv_response, nv_responselen);
+ if (bytesread != nv_responselen)
+ return (__set_errno(EFAULT));
+
+ error = nvunpacker(nv_response, nv_responselen, response);
+ free(nv_response);
+ return (error);
+}
+
+static int
+csetattr(int fd, nvlist_t *request)
+{
+ int error, saveerrno;
+ int byteswritten;
+ void *nv_request;
+ size_t nv_requestlen;
+
+ if (error = attrat_init())
+ return (__set_errno(error));
+
+ if ((error = attr_nv_pack(request, &nv_request, &nv_requestlen)) != 0)
+ return (__set_errno(error));
+
+ (void) __set_errno(0);
+ byteswritten = write(fd, nv_request, nv_requestlen);
+ if (byteswritten != nv_requestlen) {
+ saveerrno = errno;
+ free(nv_request);
+ errno = saveerrno;
+ return (__set_errno(errno));
+ }
+
+ free(nv_request);
+ return (0);
+}
+
+int
+fgetattr(int basefd, xattr_view_t view, nvlist_t **response)
+{
+ int error, saveerrno, xattrfd;
+
+ if ((xattrfd = xattr_openat(basefd, view, O_XATTR)) < 0)
+ return (xattrfd);
+
+ error = cgetattr(xattrfd, response);
+ saveerrno = errno;
+ (void) close(xattrfd);
+ errno = saveerrno;
+ return (error);
+}
+
+int
+fsetattr(int basefd, xattr_view_t view, nvlist_t *request)
+{
+ int error, saveerrno, xattrfd;
+
+ if ((xattrfd = xattr_openat(basefd, view, O_RDWR | O_XATTR)) < 0)
+ return (xattrfd);
+ error = csetattr(xattrfd, request);
+ saveerrno = errno;
+ (void) close(xattrfd);
+ errno = saveerrno;
+ return (error);
+}
+
+int
+getattrat(int basefd, xattr_view_t view, const char *name, nvlist_t **response)
+{
+ int error, saveerrno, namefd, xattrfd;
+
+ if ((namefd = __openattrdirat(basefd, name)) < 0)
+ return (namefd);
+
+ if ((xattrfd = xattr_openat(namefd, view, 0)) < 0) {
+ saveerrno = errno;
+ (void) close(namefd);
+ errno = saveerrno;
+ return (xattrfd);
+ }
+
+ error = cgetattr(xattrfd, response);
+ saveerrno = errno;
+ (void) close(namefd);
+ (void) close(xattrfd);
+ errno = saveerrno;
+ return (error);
+}
+
+int
+setattrat(int basefd, xattr_view_t view, const char *name, nvlist_t *request)
+{
+ int error, saveerrno, namefd, xattrfd;
+
+ if ((namefd = __openattrdirat(basefd, name)) < 0)
+ return (namefd);
+
+ if ((xattrfd = xattr_openat(namefd, view, O_RDWR)) < 0) {
+ saveerrno = errno;
+ (void) close(namefd);
+ errno = saveerrno;
+ return (xattrfd);
+ }
+
+ error = csetattr(xattrfd, request);
+ saveerrno = errno;
+ (void) close(namefd);
+ (void) close(xattrfd);
+ errno = saveerrno;
+ return (error);
+}
diff --git a/usr/src/lib/libc/port/gen/privlib.c b/usr/src/lib/libc/port/gen/privlib.c
index 1968f7eaa4..57218876bc 100644
--- a/usr/src/lib/libc/port/gen/privlib.c
+++ b/usr/src/lib/libc/port/gen/privlib.c
@@ -442,7 +442,7 @@ priv_set(priv_op_t op, priv_ptype_t setname, ...)
/*
* priv_ineffect(privilege).
- * tests the existance of a privilege against the effective set.
+ * tests the existence of a privilege against the effective set.
*/
boolean_t
priv_ineffect(const char *priv)
diff --git a/usr/src/lib/libc/port/llib-lc b/usr/src/lib/libc/port/llib-lc
index 3e3bba59ca..e74313a424 100644
--- a/usr/src/lib/libc/port/llib-lc
+++ b/usr/src/lib/libc/port/llib-lc
@@ -31,6 +31,7 @@
#include <aio.h>
#include <alloca.h>
+#include <attr.h>
#include <atomic.h>
#include <ctype.h>
#include <deflt.h>
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index 3e658f68e6..717c4d9186 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -64,8 +64,11 @@ SUNW_1.23 { # SunOS 5.11 (Solaris 11)
err;
errx;
fdatasync;
+ fgetattr;
forkallx;
forkx;
+ fsetattr;
+ getattrat;
htonl;
htons;
lio_listio;
@@ -111,6 +114,7 @@ SUNW_1.23 { # SunOS 5.11 (Solaris 11)
sem_trywait;
sem_unlink;
sem_wait;
+ setattrat;
_sharefs;
shm_open;
shm_unlink;
@@ -1408,6 +1412,11 @@ SUNWprivate_1.1 {
_atomic_swap_uint = NODYNSORT;
_atomic_swap_ulong = NODYNSORT;
_atomic_swap_ushort = NODYNSORT;
+ attr_count;
+ attr_to_data_type;
+ attr_to_name;
+ attr_to_option;
+ attr_to_xattr_view;
_autofssys;
_brk;
__btowc_dense;
@@ -1762,6 +1771,7 @@ SUNWprivate_1.1 {
__mutex_trylock;
_mutex_unlock = NODYNSORT;
__mutex_unlock;
+ name_to_attr;
_nanosleep;
__nan_read;
__nan_written;
@@ -1807,6 +1817,8 @@ SUNWprivate_1.1 {
__nsw_getconfig_v1;
__nthreads;
__numeric_init;
+ __openattrdirat;
+ option_to_attr;
_openlog;
_plock;
_port_alert;
diff --git a/usr/src/lib/libc/port/sys/fsmisc.c b/usr/src/lib/libc/port/sys/fsmisc.c
index 88415c5363..491cc73732 100644
--- a/usr/src/lib/libc/port/sys/fsmisc.c
+++ b/usr/src/lib/libc/port/sys/fsmisc.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -60,3 +59,9 @@ renameat(int fromfd, const char *fromname, int tofd, const char *toname)
{
return (syscall(SYS_fsat, 7, fromfd, fromname, tofd, toname));
}
+
+int
+__openattrdirat(int fd, const char *name)
+{
+ return (syscall(SYS_fsat, 9, fd, name));
+}
diff --git a/usr/src/lib/libc/sparc/Makefile b/usr/src/lib/libc/sparc/Makefile
index c67463652b..37e0818dc6 100644
--- a/usr/src/lib/libc/sparc/Makefile
+++ b/usr/src/lib/libc/sparc/Makefile
@@ -102,6 +102,9 @@ FPASMOBJS= \
ATOMICOBJS= \
atomic.o
+XATTROBJS= \
+ xattr_common.o
+
COMOBJS= \
bcmp.o \
bcopy.o \
@@ -413,6 +416,7 @@ PORTGEN= \
atoi.o \
atol.o \
atoll.o \
+ attrat.o \
attropen.o \
atexit.o \
atfork.o \
@@ -912,6 +916,7 @@ MOSTOBJS= \
$(FPOBJS) \
$(FPASMOBJS) \
$(ATOMICOBJS) \
+ $(XATTROBJS) \
$(COMOBJS) \
$(DTRACEOBJS) \
$(GENOBJS) \
@@ -1044,6 +1049,7 @@ CLOBBERFILES += $(LIB_PIC)
# list of C source for lint
SRCS= \
$(ATOMICOBJS:%.o=$(SRC)/common/atomic/%.c) \
+ $(XATTROBJS:%.o=$(SRC)/common/xattr/%.c) \
$(COMOBJS:%.o=$(SRC)/common/util/%.c) \
$(DTRACEOBJS:%.o=$(SRC)/common/dtrace/%.c) \
$(PORTFP:%.o=../port/fp/%.c) \
diff --git a/usr/src/lib/libc/sparcv9/Makefile b/usr/src/lib/libc/sparcv9/Makefile
index c114126bfe..c837e39361 100644
--- a/usr/src/lib/libc/sparcv9/Makefile
+++ b/usr/src/lib/libc/sparcv9/Makefile
@@ -107,6 +107,9 @@ $(__GNUC)FPASMOBJS += \
ATOMICOBJS= \
atomic.o
+XATTROBJS= \
+ xattr_common.o
+
COMOBJS= \
bcmp.o \
bcopy.o \
@@ -371,6 +374,7 @@ PORTGEN= \
abort.o \
addsev.o \
assert.o \
+ attrat.o \
atof.o \
atoi.o \
atol.o \
@@ -858,6 +862,7 @@ MOSTOBJS= \
$(FPOBJS64) \
$(FPASMOBJS) \
$(ATOMICOBJS) \
+ $(XATTROBJS) \
$(COMOBJS) \
$(GENOBJS) \
$(PORTFP) \
@@ -978,6 +983,7 @@ CLOBBERFILES += $(LIB_PIC)
# list of C source for lint
SRCS= \
$(ATOMICOBJS:%.o=$(SRC)/common/atomic/%.c) \
+ $(XATTROBJS:%.o=$(SRC)/common/xattr/%.c) \
$(COMOBJS:%.o=$(SRC)/common/util/%.c) \
$(PORTFP:%.o=../port/fp/%.c) \
$(PORTGEN:%.o=../port/gen/%.c) \
diff --git a/usr/src/lib/libcmdutils/Makefile.com b/usr/src/lib/libcmdutils/Makefile.com
index 50ca57cedf..e12e5b07c5 100644
--- a/usr/src/lib/libcmdutils/Makefile.com
+++ b/usr/src/lib/libcmdutils/Makefile.com
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -27,14 +27,14 @@
LIBRARY= libcmdutils.a
VERS= .1
-OBJECTS= avltree.o
+OBJECTS= avltree.o sysattrs.o writefile.o process_xattrs.o
include ../../Makefile.lib
include ../../Makefile.rootfs
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS += -lc -lavl
+LDLIBS += -lc -lavl -lnvpair
SRCDIR = ../common
$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
@@ -43,7 +43,7 @@ CFLAGS += $(CCVERBOSE)
# All commands using the common avltree interfaces must
# be largefile aware.
-CPPFLAGS += -I.. -D_REENTRANT -D_FILE_OFFSET_BITS=64
+CPPFLAGS += -I.. -I../../common/inc -D_REENTRANT -D_FILE_OFFSET_BITS=64
.KEEP_STATE:
diff --git a/usr/src/lib/libcmdutils/common/mapfile-vers b/usr/src/lib/libcmdutils/common/mapfile-vers
index 48d26ca9af..295493845b 100644
--- a/usr/src/lib/libcmdutils/common/mapfile-vers
+++ b/usr/src/lib/libcmdutils/common/mapfile-vers
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -30,6 +30,12 @@ SUNWprivate_1.1 {
add_tnode;
destroy_tree;
tnode_compare;
+ sysattr_type;
+ sysattr_support;
+ writefile;
+ get_attrdirs;
+ mv_xattrs;
+ sysattr_list;
local:
*;
};
diff --git a/usr/src/lib/libcmdutils/common/process_xattrs.c b/usr/src/lib/libcmdutils/common/process_xattrs.c
new file mode 100644
index 0000000000..5c8df946a1
--- /dev/null
+++ b/usr/src/lib/libcmdutils/common/process_xattrs.c
@@ -0,0 +1,334 @@
+/*
+ * 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 <c_synonyms.h>
+#include "libcmdutils.h"
+
+
+/*
+ * Gets file descriptors of attribute directories for source and target
+ * attribute files
+ */
+int
+get_attrdirs(int indfd, int outdfd, char *attrfile, int *sfd, int *tfd)
+{
+ int pwdfd;
+ int fd1;
+ int fd2;
+
+ pwdfd = open(".", O_RDONLY);
+ if ((pwdfd != -1) && (fchdir(indfd) == 0)) {
+ if ((fd1 = attropen(attrfile, ".", O_RDONLY)) == -1) {
+ (void) fchdir(pwdfd);
+ (void) close(pwdfd);
+ return (1);
+ }
+ *sfd = fd1;
+ } else {
+ (void) fchdir(pwdfd);
+ (void) close(pwdfd);
+ return (1);
+ }
+ if (fchdir(outdfd) == 0) {
+ if ((fd2 = attropen(attrfile, ".", O_RDONLY)) == -1) {
+ (void) fchdir(pwdfd);
+ (void) close(pwdfd);
+ return (1);
+ }
+ *tfd = fd2;
+ } else {
+ (void) fchdir(pwdfd);
+ (void) close(pwdfd);
+ return (1);
+ }
+ (void) fchdir(pwdfd);
+ return (0);
+}
+
+/*
+ * mv_xattrs - Copies the content of the extended attribute files. Then
+ * moves the extended system attributes from the input attribute files
+ * to the target attribute files. Moves the extended system attributes
+ * from source to the target file. This function returns 0 on success
+ * and nonzero on error.
+ */
+int
+mv_xattrs(char *cmd, char *infile, char *outfile, int sattr, int silent)
+{
+ int srcfd = -1;
+ int indfd = -1;
+ int outdfd = -1;
+ int tmpfd = -1;
+ int sattrfd = -1;
+ int tattrfd = -1;
+ int asfd = -1;
+ int atfd = -1;
+ DIR *dirp = NULL;
+ struct dirent *dp = NULL;
+ char *etext = NULL;
+ struct stat st1;
+ struct stat st2;
+ nvlist_t *response = NULL;
+ nvlist_t *res = NULL;
+
+ if ((srcfd = open(infile, O_RDONLY)) == -1) {
+ etext = dgettext(TEXT_DOMAIN, "cannot open source");
+ goto error;
+ }
+ if (sattr)
+ response = sysattr_list(cmd, srcfd, infile);
+
+ if ((indfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
+ etext = dgettext(TEXT_DOMAIN, "cannot openat source");
+ goto error;
+ }
+ if ((outdfd = attropen(outfile, ".", O_RDONLY)) == -1) {
+ etext = dgettext(TEXT_DOMAIN, "cannot attropen target");
+ goto error;
+ }
+ if ((tmpfd = dup(indfd)) == -1) {
+ etext = dgettext(TEXT_DOMAIN, "cannot dup descriptor");
+ goto error;
+
+ }
+ if ((dirp = fdopendir(tmpfd)) == NULL) {
+ etext = dgettext(TEXT_DOMAIN, "cannot access source");
+ goto error;
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+ if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
+ (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
+ dp->d_name[2] == '\0') ||
+ (sysattr_type(dp->d_name) == _RO_SATTR) ||
+ (sysattr_type(dp->d_name) == _RW_SATTR))
+ continue;
+
+ if ((sattrfd = openat(indfd, dp->d_name,
+ O_RDONLY)) == -1) {
+ etext = dgettext(TEXT_DOMAIN,
+ "cannot open src attribute file");
+ goto error;
+ }
+ if (fstat(sattrfd, &st1) < 0) {
+ etext = dgettext(TEXT_DOMAIN,
+ "could not stat attribute file");
+ goto error;
+ }
+ if ((tattrfd = openat(outdfd, dp->d_name,
+ O_RDWR|O_CREAT|O_TRUNC, st1.st_mode)) == -1) {
+ etext = dgettext(TEXT_DOMAIN,
+ "cannot open target attribute file");
+ goto error;
+ }
+ if (fstat(tattrfd, &st2) < 0) {
+ etext = dgettext(TEXT_DOMAIN,
+ "could not stat attribute file");
+ goto error;
+ }
+ if (writefile(sattrfd, tattrfd, infile, outfile, dp->d_name,
+ dp->d_name, &st1, &st2) != 0) {
+ etext = dgettext(TEXT_DOMAIN,
+ "failed to copy extended attribute "
+ "from source to target");
+ goto error;
+ }
+
+ errno = 0;
+ if (sattr) {
+ /*
+ * Gets non default extended system attributes from
+ * source to copy to target.
+ */
+ if (dp->d_name != NULL)
+ res = sysattr_list(cmd, sattrfd, dp->d_name);
+
+ if (res != NULL &&
+ get_attrdirs(indfd, outdfd, dp->d_name, &asfd,
+ &atfd) != 0) {
+ etext = dgettext(TEXT_DOMAIN,
+ "Failed to open attribute files");
+ goto error;
+ }
+ /*
+ * Copy extended system attribute from source
+ * attribute file to target attribute file
+ */
+ if (res != NULL &&
+ (renameat(asfd, VIEW_READWRITE, atfd,
+ VIEW_READWRITE) != 0)) {
+ if (errno == EPERM)
+ etext = dgettext(TEXT_DOMAIN,
+ "Permission denied -"
+ "failed to move system attribute");
+ else
+ etext = dgettext(TEXT_DOMAIN,
+ "failed to move extended "
+ "system attribute");
+ goto error;
+ }
+ }
+ if (sattrfd != -1)
+ (void) close(sattrfd);
+ if (tattrfd != -1)
+ (void) close(tattrfd);
+ if (asfd != -1)
+ (void) close(asfd);
+ if (atfd != -1)
+ (void) close(atfd);
+ if (res != NULL) {
+ nvlist_free(res);
+ res = NULL;
+ }
+ }
+ errno = 0;
+ /* Copy extended system attribute from source to target */
+
+ if (response != NULL) {
+ if (renameat(indfd, VIEW_READWRITE, outdfd,
+ VIEW_READWRITE) == 0)
+ goto done;
+
+ if (errno == EPERM)
+ etext = dgettext(TEXT_DOMAIN, "Permission denied");
+ else
+ etext = dgettext(TEXT_DOMAIN,
+ "failed to move system attribute");
+ }
+error:
+ if (res != NULL)
+ nvlist_free(res);
+ if (silent == 0 && etext != NULL) {
+ if (!sattr)
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "%s: %s: cannot move extended attributes, "),
+ cmd, infile);
+ else
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "%s: %s: cannot move extended system "
+ "attributes, "), cmd, infile);
+ perror(etext);
+ }
+done:
+ if (dirp)
+ (void) closedir(dirp);
+ if (sattrfd != -1)
+ (void) close(sattrfd);
+ if (tattrfd != -1)
+ (void) close(tattrfd);
+ if (asfd != -1)
+ (void) close(asfd);
+ if (atfd != -1)
+ (void) close(atfd);
+ if (indfd != -1)
+ (void) close(indfd);
+ if (outdfd != -1)
+ (void) close(outdfd);
+ if (response != NULL)
+ nvlist_free(response);
+ if (etext != NULL)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * The function returns non default extended system attribute list
+ * associated with 'fname' and returns NULL when an error has occured
+ * or when only extended system attributes other than archive,
+ * av_modified or crtime are set.
+ *
+ * The function returns system attribute list for the following cases:
+ *
+ * - any extended system attribute other than the default attributes
+ * ('archive', 'av_modified' and 'crtime') is set
+ * - nvlist has NULL name string
+ * - nvpair has data type of 'nvlist'
+ * - default data type.
+ */
+
+nvlist_t *
+sysattr_list(char *cmd, int fd, char *fname)
+{
+ boolean_t value;
+ data_type_t type;
+ nvlist_t *response;
+ nvpair_t *pair;
+ f_attr_t fattr;
+ char *name;
+
+ if (nvlist_alloc(&response, NV_UNIQUE_NAME, 0) != 0) {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "%s: %s: nvlist_alloc failed\n"),
+ cmd, fname);
+ return (NULL);
+ }
+ if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "%s: %s: fgetattr failed\n"),
+ cmd, fname);
+ nvlist_free(response);
+ return (NULL);
+ }
+ pair = NULL;
+ while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
+
+ name = nvpair_name(pair);
+
+ if (name != NULL)
+ fattr = name_to_attr(name);
+ else
+ return (response);
+
+ type = nvpair_type(pair);
+ switch (type) {
+ case DATA_TYPE_BOOLEAN_VALUE:
+ if (nvpair_value_boolean_value(pair,
+ &value) != 0) {
+ (void) fprintf(stderr,
+ dgettext(TEXT_DOMAIN, "%s "
+ "nvpair_value_boolean_value "
+ "failed\n"), cmd);
+ continue;
+ }
+ if (value && fattr != F_ARCHIVE &&
+ fattr != F_AV_MODIFIED)
+ return (response);
+ break;
+ case DATA_TYPE_UINT64_ARRAY:
+ if (fattr != F_CRTIME)
+ return (response);
+ break;
+ case DATA_TYPE_NVLIST:
+ default:
+ return (response);
+ break;
+ }
+ }
+ if (response != NULL)
+ nvlist_free(response);
+ return (NULL);
+}
diff --git a/usr/src/lib/libcmdutils/common/sysattrs.c b/usr/src/lib/libcmdutils/common/sysattrs.c
new file mode 100644
index 0000000000..a22e8e5e21
--- /dev/null
+++ b/usr/src/lib/libcmdutils/common/sysattrs.c
@@ -0,0 +1,128 @@
+/*
+ * 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 <c_synonyms.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <attr.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "libcmdutils.h"
+
+/*
+ * Returns the status of attempting to obtain the extended system
+ * attributes in the specified view.
+ *
+ * Note: If obtaining status for an extended attribute file, the caller must
+ * chdir into the hidden directory prior to calling sysattr_status().
+ *
+ * Returns 1 if the extended system attributes were obtained, otherwise
+ * returns 0.
+ */
+int
+sysattr_status(char *file, xattr_view_t view)
+{
+ nvlist_t *response;
+ int saveerrno;
+ int status;
+
+ if (nvlist_alloc(&response, NV_UNIQUE_NAME, 0) != 0) {
+ return (0);
+ }
+
+ status = getattrat(AT_FDCWD, view, file, &response);
+
+ saveerrno = errno;
+ (void) nvlist_free(response);
+ errno = saveerrno;
+
+ return (status == 0);
+}
+
+/*
+ * Returns the type of the specified in file. If the file name matches
+ * the name of either a read-only or read-write extended system attribute
+ * file then sysattr_type() returns the type of file:
+ * return value file type
+ * ------------ ---------
+ * _RO_SATTR read-only extended system attribute file
+ * _RW_SATTR read-write extended system attribute file
+ * _NOT_SATTR neither a read-only or read-write extended system
+ * attribute file.
+ */
+int
+sysattr_type(char *file)
+{
+ if (file == NULL) {
+ errno = ENOENT;
+ return (_NOT_SATTR);
+ }
+
+ if (strcmp(basename(file), file) != 0) {
+ errno = EINVAL;
+ return (_NOT_SATTR);
+ }
+
+ errno = 0;
+ if (strcmp(file, VIEW_READONLY) == 0) {
+ return (_RO_SATTR);
+ } else if (strcmp(file, VIEW_READWRITE) == 0) {
+ return (_RW_SATTR);
+ } else {
+ return (_NOT_SATTR);
+ }
+}
+
+/*
+ * Call sysattr_support() instead of pathconf(file, _PC_SATTR_ENABLED) or
+ * pathconf(file, _PC_SATTR_EXISTS) so that if pathconf() fails over NFS, we
+ * can still try to figure out if extended system attributes are supported by
+ * testing for a valid extended system attribute file.
+ *
+ * 'name' can have the values _PC_SATTR_ENABLED or _PC_SATTR_EXISTS.
+ *
+ * Returns 1 if the underlying file system supports extended system attributes,
+ * otherwise, returns -1.
+ */
+int
+sysattr_support(char *file, int name)
+{
+ int rc;
+
+ errno = 0;
+ if ((name != _PC_SATTR_ENABLED) &&
+ (name != _PC_SATTR_EXISTS)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if (((rc = pathconf(file, name)) == 1) || (errno != EINVAL)) {
+ return (rc);
+ }
+ return (sysattr_status(file, XATTR_VIEW_READONLY));
+}
diff --git a/usr/src/lib/libcmdutils/common/writefile.c b/usr/src/lib/libcmdutils/common/writefile.c
new file mode 100644
index 0000000000..3e36f90c61
--- /dev/null
+++ b/usr/src/lib/libcmdutils/common/writefile.c
@@ -0,0 +1,230 @@
+/*
+ * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <c_synonyms.h>
+#include "libcmdutils.h"
+
+
+int
+writefile(int fi, int fo, char *infile, char *outfile, char *asfile,
+ char *atfile, struct stat *s1p, struct stat *s2p)
+{
+ int mapsize, munmapsize;
+ caddr_t cp;
+ off_t filesize = s1p->st_size;
+ off_t offset;
+ int nbytes;
+ int remains;
+ int n;
+ size_t src_size;
+ size_t targ_size;
+ char *srcbuf;
+ char *targbuf;
+
+ if (asfile != NULL) {
+ src_size = strlen(infile) + strlen(asfile) +
+ strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1;
+ } else {
+ src_size = strlen(infile) + 1;
+ }
+ srcbuf = malloc(src_size);
+ if (srcbuf == NULL) {
+ (void) fprintf(stderr,
+ dgettext(TEXT_DOMAIN, "could not allocate memory"
+ " for path buffer: "));
+ return (1);
+ }
+ if (asfile != NULL) {
+ (void) snprintf(srcbuf, src_size, "%s%s%s",
+ infile, dgettext(TEXT_DOMAIN, " attribute "), asfile);
+ } else {
+ (void) snprintf(srcbuf, src_size, "%s", infile);
+ }
+
+ if (atfile != NULL) {
+ targ_size = strlen(outfile) + strlen(atfile) +
+ strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1;
+ } else {
+ targ_size = strlen(outfile) + 1;
+ }
+ targbuf = malloc(targ_size);
+ if (targbuf == NULL) {
+ (void) fprintf(stderr,
+ dgettext(TEXT_DOMAIN, "could not allocate memory"
+ " for path buffer: "));
+ return (1);
+ }
+ if (atfile != NULL) {
+ (void) snprintf(targbuf, targ_size, "%s%s%s",
+ outfile, dgettext(TEXT_DOMAIN, " attribute "), atfile);
+ } else {
+ (void) snprintf(targbuf, targ_size, "%s", outfile);
+ }
+
+ if (ISREG(*s1p) && s1p->st_size > SMALLFILESIZE) {
+ /*
+ * Determine size of initial mapping. This will determine the
+ * size of the address space chunk we work with. This initial
+ * mapping size will be used to perform munmap() in the future.
+ */
+ mapsize = MAXMAPSIZE;
+ if (s1p->st_size < mapsize) mapsize = s1p->st_size;
+ munmapsize = mapsize;
+
+ /*
+ * Mmap time!
+ */
+ if ((cp = mmap((caddr_t)NULL, mapsize, PROT_READ,
+ MAP_SHARED, fi, (off_t)0)) == MAP_FAILED)
+ mapsize = 0; /* can't mmap today */
+ } else
+ mapsize = 0;
+
+ if (mapsize != 0) {
+ offset = 0;
+
+ for (;;) {
+ nbytes = write(fo, cp, mapsize);
+ /*
+ * if we write less than the mmaped size it's due to a
+ * media error on the input file or out of space on
+ * the output file. So, try again, and look for errno.
+ */
+ if ((nbytes >= 0) && (nbytes != (int)mapsize)) {
+ remains = mapsize - nbytes;
+ while (remains > 0) {
+ nbytes = write(fo,
+ cp + mapsize - remains, remains);
+ if (nbytes < 0) {
+ if (errno == ENOSPC)
+ perror(targbuf);
+ else
+ perror(srcbuf);
+ (void) close(fi);
+ (void) close(fo);
+ (void) munmap(cp, munmapsize);
+ if (ISREG(*s2p))
+ (void) unlink(targbuf);
+ return (1);
+ }
+ remains -= nbytes;
+ if (remains == 0)
+ nbytes = mapsize;
+ }
+ }
+ /*
+ * although the write manual page doesn't specify this
+ * as a possible errno, it is set when the nfs read
+ * via the mmap'ed file is accessed, so report the
+ * problem as a source access problem, not a target file
+ * problem
+ */
+ if (nbytes < 0) {
+ if (errno == EACCES)
+ perror(srcbuf);
+ else
+ perror(targbuf);
+ (void) close(fi);
+ (void) close(fo);
+ (void) munmap(cp, munmapsize);
+ if (ISREG(*s2p))
+ (void) unlink(targbuf);
+ if (srcbuf != NULL)
+ free(srcbuf);
+ if (targbuf != NULL)
+ free(targbuf);
+ return (1);
+ }
+ filesize -= nbytes;
+ if (filesize == 0)
+ break;
+ offset += nbytes;
+ if (filesize < mapsize)
+ mapsize = filesize;
+ if (mmap(cp, mapsize, PROT_READ, MAP_SHARED |
+ MAP_FIXED, fi, offset) == MAP_FAILED) {
+ perror(srcbuf);
+ (void) close(fi);
+ (void) close(fo);
+ (void) munmap(cp, munmapsize);
+ if (ISREG(*s2p))
+ (void) unlink(targbuf);
+ if (srcbuf != NULL)
+ free(srcbuf);
+ if (targbuf != NULL)
+ free(targbuf);
+ return (1);
+ }
+ }
+ (void) munmap(cp, munmapsize);
+ } else {
+ char buf[SMALLFILESIZE];
+ for (;;) {
+ n = read(fi, buf, sizeof (buf));
+ if (n == 0) {
+ return (0);
+ } else if (n < 0) {
+ (void) close(fi);
+ (void) close(fo);
+ if (ISREG(*s2p))
+ (void) unlink(targbuf);
+ if (srcbuf != NULL)
+ free(srcbuf);
+ if (targbuf != NULL)
+ free(targbuf);
+ return (1);
+ } else if (write(fo, buf, n) != n) {
+ (void) close(fi);
+ (void) close(fo);
+ if (ISREG(*s2p))
+ (void) unlink(targbuf);
+ if (srcbuf != NULL)
+ free(srcbuf);
+ if (targbuf != NULL)
+ free(targbuf);
+ return (1);
+ }
+ }
+ }
+ if (srcbuf != NULL)
+ free(srcbuf);
+ if (targbuf != NULL)
+ free(targbuf);
+ return (0);
+}
diff --git a/usr/src/lib/libcmdutils/libcmdutils.h b/usr/src/lib/libcmdutils/libcmdutils.h
index 28a9e996d4..d1c3a0b193 100644
--- a/usr/src/lib/libcmdutils/libcmdutils.h
+++ b/usr/src/lib/libcmdutils/libcmdutils.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,14 +37,35 @@
* this file.
*/
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <libintl.h>
+#include <string.h>
+#include <dirent.h>
+#include <attr.h>
#include <sys/avl.h>
#include <sys/types.h>
-#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <libnvpair.h>
#ifdef __cplusplus
extern "C" {
#endif
+/* extended system attribute support */
+#define _NOT_SATTR 0
+#define _RO_SATTR 1
+#define _RW_SATTR 2
+
+#define MAXMAPSIZE (1024*1024*8) /* map at most 8MB */
+#define SMALLFILESIZE (32*1024) /* don't use mmap on little file */
+#define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG)
+
/* avltree */
#define OFFSETOF(s, m) ((size_t)(&(((s *)0)->m)))
@@ -56,11 +76,38 @@ typedef struct tree_node {
avl_node_t avl_link;
} tree_node_t;
+
+ /* extended system attribute support */
+
+/* Determine if a file is the name of an extended system attribute file */
+extern int sysattr_type(char *);
+
+/* Determine if the underlying file system supports system attributes */
+extern int sysattr_support(char *, int);
+
+/* Copies the content of the source file to the target file */
+extern int writefile(int, int, char *, char *, char *, char *,
+struct stat *, struct stat *);
+
+/* Gets file descriptors of the source and target attribute files */
+extern int get_attrdirs(int, int, char *, int *, int *);
+
+/* Move extended attribute and extended system attribute */
+extern int mv_xattrs(char *, char *, char *, int, int);
+
+/* Returns non default extended system attribute list */
+extern nvlist_t *sysattr_list(char *, int, char *);
+
+
+
+ /* avltree */
+
/*
* Used to compare two nodes. We are attempting to match the 1st
* argument (node) against the 2nd argument (a node which
* is already in the search tree).
*/
+
extern int tnode_compare(const void *, const void *);
/*
diff --git a/usr/src/lib/libsec/common/acl.y b/usr/src/lib/libsec/common/acl.y
index 684ab0ba3d..8b5d29509e 100644
--- a/usr/src/lib/libsec/common/acl.y
+++ b/usr/src/lib/libsec/common/acl.y
@@ -19,13 +19,13 @@
*
* CDDL HEADER END
*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
-#include <sys/acl.h>
+#include <acl_common.h>
#include <aclutils.h>
extern int yyinteractive;
diff --git a/usr/src/lib/libsec/common/acl_lex.l b/usr/src/lib/libsec/common/acl_lex.l
index ce08a1da28..890c063cc7 100644
--- a/usr/src/lib/libsec/common/acl_lex.l
+++ b/usr/src/lib/libsec/common/acl_lex.l
@@ -59,6 +59,7 @@ int yybufpos;
%}
+%e 1500
%s TS NS PS AIS AS US ES
/*
* TS = type state
@@ -73,7 +74,7 @@ int yybufpos;
ID [0-9]+
LOGNAME [^:]+:
PERM_STR [rRwWxpdDaAcCos-]+
-INHERIT_STR [fdinFS-]+
+INHERIT_STR [fdinFSI-]+
%%
@@ -398,6 +399,32 @@ INHERIT_STR [fdinFS-]+
yylval.val = ACE_ACCESS_DENIED_ACE_TYPE;
return (ACCESS_TYPE);
}
+<AS>audit/[:,\n] {
+ int c;
+
+ c = input();
+ unput(c);
+ if (c == ',' || c == '\n')
+ BEGIN ES;
+ else
+ BEGIN US;
+
+ yylval.val = ACE_SYSTEM_AUDIT_ACE_TYPE;
+ return (ACCESS_TYPE);
+ }
+<AS>alarm/[:,\n] {
+ int c;
+
+ c = input();
+ unput(c);
+ if (c == ',' || c == '\n')
+ BEGIN ES;
+ else
+ BEGIN US;
+
+ yylval.val = ACE_SYSTEM_ALARM_ACE_TYPE;
+ return (ACCESS_TYPE);
+ }
<AS>: {
acl_error(dgettext(TEXT_DOMAIN,
@@ -466,6 +493,33 @@ INHERIT_STR [fdinFS-]+
yylval.val = ACE_ACCESS_DENIED_ACE_TYPE;
return (ACCESS_TYPE);
}
+<AIS>audit/[:,\n] {
+ int c;
+
+ c = input();
+ unput(c);
+ if (c == ',' || c == '\n')
+ BEGIN ES;
+ else
+ BEGIN US;
+
+ yylval.val = ACE_SYSTEM_AUDIT_ACE_TYPE;
+ return (ACCESS_TYPE);
+ }
+<AIS>alarm/[:,\n] {
+
+ int c;
+
+ c = input();
+ unput(c);
+ if (c == ',' || c == '\n')
+ BEGIN ES;
+ else
+ BEGIN US;
+
+ yylval.val = ACE_SYSTEM_ALARM_ACE_TYPE;
+ return (ACCESS_TYPE);
+ }
<AIS>file_inherit/[:/,] {
yylval.val = ACE_FILE_INHERIT_ACE;
return (ACE_INHERIT);
@@ -482,6 +536,19 @@ INHERIT_STR [fdinFS-]+
yylval.val = ACE_INHERIT_ONLY_ACE;
return (ACE_INHERIT);
}
+
+<AIS>successful_access/[/:,] {
+ yylval.val = ACE_SUCCESSFUL_ACCESS_ACE_FLAG;
+ return (ACE_INHERIT);
+ }
+<AIS>failed_access/[/:,] {
+ yylval.val = ACE_FAILED_ACCESS_ACE_FLAG;
+ return (ACE_INHERIT);
+ }
+<AIS>inherited/[/:,] {
+ yylval.val = ACE_INHERITED_ACE;
+ return (ACE_INHERIT);
+ }
<AIS>{INHERIT_STR}/[:] {
yylval.str = strdup(yytext);
if (yylval.str == NULL) {
@@ -625,7 +692,7 @@ INHERIT_STR [fdinFS-]+
* used for retrieving illegal data in ACL specification.
*
* The first set of characters is retrieved from yytext.
- * subseequent characters are pulled from the input stream,
+ * subsequent characters are pulled from the input stream,
* until either EOF or one of the requested terminators is scene.
* Result is returned in yylval.str which is malloced.
*/
@@ -783,7 +850,7 @@ acl_str_to_id(char *str, int *id)
uid_t value;
errno = 0;
- value = strtol(str, &end, 10);
+ value = strtoul(str, &end, 10);
if (errno != 0 || *end != '\0')
return (EACL_INVALID_USER_GROUP);
diff --git a/usr/src/lib/libsec/common/acltext.c b/usr/src/lib/libsec/common/acltext.c
index cdfd171c82..c0e1bb1e58 100644
--- a/usr/src/lib/libsec/common/acltext.c
+++ b/usr/src/lib/libsec/common/acltext.c
@@ -540,8 +540,12 @@ ace_inherit_txt(char *buf, char **endp, uint32_t iflags, int flags)
buf[5] = 'F';
else
buf[5] = '-';
- buf[6] = '\0';
- *endp = buf + 6;
+ if (iflags & ACE_INHERITED_ACE)
+ buf[6] = 'I';
+ else
+ buf[6] = '-';
+ buf[7] = '\0';
+ *endp = buf + 7;
} else {
if (iflags & ACE_FILE_INHERIT_ACE) {
strcpy(lend, "file_inherit/");
@@ -559,6 +563,18 @@ ace_inherit_txt(char *buf, char **endp, uint32_t iflags, int flags)
strcpy(lend, "inherit_only/");
lend += sizeof ("inherit_only/") - 1;
}
+ if (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG) {
+ strcpy(lend, "successful_access/");
+ lend += sizeof ("successful_access/") - 1;
+ }
+ if (iflags & ACE_FAILED_ACCESS_ACE_FLAG) {
+ strcpy(lend, "failed_access/");
+ lend += sizeof ("failed_access/") - 1;
+ }
+ if (iflags & ACE_INHERITED_ACE) {
+ strcpy(lend, "inherited/");
+ lend += sizeof ("inherited/") - 1;
+ }
if (*(lend - 1) == '/')
*--lend = '\0';
@@ -829,16 +845,19 @@ increase_length(struct dynaclstr *dacl, size_t increase)
* The length of a perms entry is 144 i.e read_data/write_data...
* to each acl entry.
*
- * iflags: file_inherit/dir_inherit/inherit_only/no_propagate
+ * iflags: file_inherit/dir_inherit/inherit_only/no_propagate/successful_access
+ * /failed_access
*
*/
#define ACE_ENTRYTYPLEN 6
-#define IFLAGS_SIZE 51
+#define IFLAGS_STR "file_inherit/dir_inherit/inherit_only/no_propagate/" \
+ "successful_access/failed_access/inherited"
+#define IFLAGS_SIZE (sizeof (IFLAGS_STR) - 1)
#define ACCESS_TYPE_SIZE 7 /* if unknown */
#define COLON_CNT 3
#define PERMS_LEN 216
-#define ACE_ENTRY_SIZE (ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN +\
+#define ACE_ENTRY_SIZE (ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN + \
ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT + APPENDED_ID_MAX)
static char *
@@ -871,7 +890,9 @@ ace_acltotext(acl_t *aceaclp, int flags)
(void) ace_inherit_txt(endp, &endp, aclp->a_flags, flags);
if (flags & ACL_COMPACT_FMT || aclp->a_flags &
(ACE_FILE_INHERIT_ACE | ACE_DIRECTORY_INHERIT_ACE |
- (ACE_INHERIT_ONLY_ACE | ACE_NO_PROPAGATE_INHERIT_ACE))) {
+ (ACE_INHERIT_ONLY_ACE | ACE_NO_PROPAGATE_INHERIT_ACE |
+ ACE_INHERITED_ACE | ACE_SUCCESSFUL_ACCESS_ACE_FLAG |
+ ACE_FAILED_ACCESS_ACE_FLAG))) {
*endp++ = ':';
*endp = '\0';
}
@@ -972,7 +993,7 @@ ace_compact_printacl(acl_t *aclp)
aclp->acl_flags & ACL_IS_DIR, ACL_COMPACT_FMT));
(void) printf("%s:",
ace_inherit_txt(endp, &endp, acep->a_flags,
- ACL_COMPACT_FMT));
+ ACL_COMPACT_FMT));
(void) printf("%s\n", ace_access_txt(endp, &endp,
acep->a_type));
}
@@ -1038,16 +1059,14 @@ typedef struct value_table {
uint32_t p_value; /* value for perm when pletter found */
} value_table_t;
-#define ACE_PERM_COUNT 14
-
/*
- * The permission tables are layed out in positional order
+ * The permission tables are laid out in positional order
* a '-' character will indicate a permission at a given
* position is not specified. The '-' is not part of the
* table, but will be checked for in the permission computation
* routine.
*/
-value_table_t ace_perm_table[ACE_PERM_COUNT] = {
+value_table_t ace_perm_table[] = {
{ 'r', ACE_READ_DATA},
{ 'w', ACE_WRITE_DATA},
{ 'x', ACE_EXECUTE},
@@ -1064,24 +1083,28 @@ value_table_t ace_perm_table[ACE_PERM_COUNT] = {
{ 's', ACE_SYNCHRONIZE}
};
-#define ACLENT_PERM_COUNT 3
+#define ACE_PERM_COUNT (sizeof (ace_perm_table) / sizeof (value_table_t))
-value_table_t aclent_perm_table[ACLENT_PERM_COUNT] = {
+value_table_t aclent_perm_table[] = {
{ 'r', S_IROTH},
{ 'w', S_IWOTH},
{ 'x', S_IXOTH}
};
-#define IFLAG_COUNT 6
-value_table_t inherit_table[IFLAG_COUNT] = {
+#define ACLENT_PERM_COUNT (sizeof (aclent_perm_table) / sizeof (value_table_t))
+
+value_table_t inherit_table[] = {
{'f', ACE_FILE_INHERIT_ACE},
{'d', ACE_DIRECTORY_INHERIT_ACE},
{'i', ACE_INHERIT_ONLY_ACE},
{'n', ACE_NO_PROPAGATE_INHERIT_ACE},
{'S', ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
- {'F', ACE_FAILED_ACCESS_ACE_FLAG}
+ {'F', ACE_FAILED_ACCESS_ACE_FLAG},
+ {'I', ACE_INHERITED_ACE}
};
+#define IFLAG_COUNT (sizeof (inherit_table) / sizeof (value_table_t))
+
/*
* compute value from a permission table or inheritance table
* based on string passed in. If positional is set then
diff --git a/usr/src/lib/libsec/common/aclutils.c b/usr/src/lib/libsec/common/aclutils.c
index d90ad4b171..0200db6c66 100644
--- a/usr/src/lib/libsec/common/aclutils.c
+++ b/usr/src/lib/libsec/common/aclutils.c
@@ -33,171 +33,23 @@
#include <pwd.h>
#include <strings.h>
#include <sys/types.h>
-#include <sys/acl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/varargs.h>
#include <locale.h>
#include <aclutils.h>
-#include <acl_common.h>
#include <sys/avl.h>
-
-#define offsetof(s, m) ((size_t)(&(((s *)0)->m)))
+#include <acl_common.h>
#define ACL_PATH 0
#define ACL_FD 1
-#define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
- ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
- ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
-
-
-#define ACL_SYNCHRONIZE_SET_DENY 0x0000001
-#define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002
-#define ACL_SYNCHRONIZE_ERR_DENY 0x0000004
-#define ACL_SYNCHRONIZE_ERR_ALLOW 0x0000008
-
-#define ACL_WRITE_OWNER_SET_DENY 0x0000010
-#define ACL_WRITE_OWNER_SET_ALLOW 0x0000020
-#define ACL_WRITE_OWNER_ERR_DENY 0x0000040
-#define ACL_WRITE_OWNER_ERR_ALLOW 0x0000080
-
-#define ACL_DELETE_SET_DENY 0x0000100
-#define ACL_DELETE_SET_ALLOW 0x0000200
-#define ACL_DELETE_ERR_DENY 0x0000400
-#define ACL_DELETE_ERR_ALLOW 0x0000800
-
-#define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000
-#define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000
-#define ACL_WRITE_ATTRS_OWNER_ERR_DENY 0x0004000
-#define ACL_WRITE_ATTRS_OWNER_ERR_ALLOW 0x0008000
-
-#define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000
-#define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000
-#define ACL_WRITE_ATTRS_WRITER_ERR_DENY 0x0040000
-#define ACL_WRITE_ATTRS_WRITER_ERR_ALLOW 0x0080000
-
-#define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000
-#define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000
-#define ACL_WRITE_NAMED_WRITER_ERR_DENY 0x0400000
-#define ACL_WRITE_NAMED_WRITER_ERR_ALLOW 0x0800000
-
-#define ACL_READ_NAMED_READER_SET_DENY 0x1000000
-#define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000
-#define ACL_READ_NAMED_READER_ERR_DENY 0x4000000
-#define ACL_READ_NAMED_READER_ERR_ALLOW 0x8000000
-
-
-#define ACE_VALID_MASK_BITS (\
- ACE_READ_DATA | \
- ACE_LIST_DIRECTORY | \
- ACE_WRITE_DATA | \
- ACE_ADD_FILE | \
- ACE_APPEND_DATA | \
- ACE_ADD_SUBDIRECTORY | \
- ACE_READ_NAMED_ATTRS | \
- ACE_WRITE_NAMED_ATTRS | \
- ACE_EXECUTE | \
- ACE_DELETE_CHILD | \
- ACE_READ_ATTRIBUTES | \
- ACE_WRITE_ATTRIBUTES | \
- ACE_DELETE | \
- ACE_READ_ACL | \
- ACE_WRITE_ACL | \
- ACE_WRITE_OWNER | \
- ACE_SYNCHRONIZE)
-
-#define ACE_MASK_UNDEFINED 0x80000000
-
-#define ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \
- ACE_DIRECTORY_INHERIT_ACE | \
- ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \
- ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \
- ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
-
-/*
- * ACL conversion helpers
- */
-
-typedef enum {
- ace_unused,
- ace_user_obj,
- ace_user,
- ace_group, /* includes GROUP and GROUP_OBJ */
- ace_other_obj
-} ace_to_aent_state_t;
-
-typedef struct acevals {
- uid_t key;
- avl_node_t avl;
- uint32_t mask;
- uint32_t allowed;
- uint32_t denied;
- int aent_type;
-} acevals_t;
-
-typedef struct ace_list {
- acevals_t user_obj;
- avl_tree_t user;
- int numusers;
- acevals_t group_obj;
- avl_tree_t group;
- int numgroups;
- acevals_t other_obj;
- uint32_t acl_mask;
- int hasmask;
- int dfacl_flag;
- ace_to_aent_state_t state;
- int seen; /* bitmask of all aclent_t a_type values seen */
-} ace_list_t;
typedef union {
const char *file;
int fd;
} acl_inp;
-acl_t *
-acl_alloc(enum acl_type type)
-{
- acl_t *aclp;
-
- aclp = malloc(sizeof (acl_t));
-
- if (aclp == NULL)
- return (NULL);
-
- aclp->acl_aclp = NULL;
- aclp->acl_cnt = 0;
-
- switch (type) {
- case ACE_T:
- aclp->acl_type = ACE_T;
- aclp->acl_entry_size = sizeof (ace_t);
- break;
- case ACLENT_T:
- aclp->acl_type = ACLENT_T;
- aclp->acl_entry_size = sizeof (aclent_t);
- break;
- default:
- acl_free(aclp);
- aclp = NULL;
- }
- return (aclp);
-}
-
-/*
- * Free acl_t structure
- */
-void
-acl_free(acl_t *aclp)
-{
- if (aclp == NULL)
- return;
-
- if (aclp->acl_aclp)
- free(aclp->acl_aclp);
- free(aclp);
-}
/*
* Determine whether a file has a trivial ACL
@@ -242,1194 +94,6 @@ acl_trivial(const char *filename)
return (val);
}
-static uint32_t
-access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
-{
- uint32_t access_mask = 0;
- int acl_produce;
- int synchronize_set = 0, write_owner_set = 0;
- int delete_set = 0, write_attrs_set = 0;
- int read_named_set = 0, write_named_set = 0;
-
- acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
- ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
- ACL_WRITE_ATTRS_WRITER_SET_DENY);
-
- if (isallow) {
- synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
- write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
- delete_set = ACL_DELETE_SET_ALLOW;
- if (hasreadperm)
- read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
- if (haswriteperm)
- write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
- if (isowner)
- write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
- else if (haswriteperm)
- write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
- } else {
-
- synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
- write_owner_set = ACL_WRITE_OWNER_SET_DENY;
- delete_set = ACL_DELETE_SET_DENY;
- if (hasreadperm)
- read_named_set = ACL_READ_NAMED_READER_SET_DENY;
- if (haswriteperm)
- write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
- if (isowner)
- write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
- else if (haswriteperm)
- write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
- else
- /*
- * If the entity is not the owner and does not
- * have write permissions ACE_WRITE_ATTRIBUTES will
- * always go in the DENY ACE.
- */
- access_mask |= ACE_WRITE_ATTRIBUTES;
- }
-
- if (acl_produce & synchronize_set)
- access_mask |= ACE_SYNCHRONIZE;
- if (acl_produce & write_owner_set)
- access_mask |= ACE_WRITE_OWNER;
- if (acl_produce & delete_set)
- access_mask |= ACE_DELETE;
- if (acl_produce & write_attrs_set)
- access_mask |= ACE_WRITE_ATTRIBUTES;
- if (acl_produce & read_named_set)
- access_mask |= ACE_READ_NAMED_ATTRS;
- if (acl_produce & write_named_set)
- access_mask |= ACE_WRITE_NAMED_ATTRS;
-
- return (access_mask);
-}
-
-/*
- * Given an mode_t, convert it into an access_mask as used
- * by nfsace, assuming aclent_t -> nfsace semantics.
- */
-static uint32_t
-mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
-{
- uint32_t access = 0;
- int haswriteperm = 0;
- int hasreadperm = 0;
-
- if (isallow) {
- haswriteperm = (mode & 02);
- hasreadperm = (mode & 04);
- } else {
- haswriteperm = !(mode & 02);
- hasreadperm = !(mode & 04);
- }
-
- /*
- * The following call takes care of correctly setting the following
- * mask bits in the access_mask:
- * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
- * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
- */
- access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
-
- if (isallow) {
- access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
- if (isowner)
- access |= ACE_WRITE_ACL;
- } else {
- if (! isowner)
- access |= ACE_WRITE_ACL;
- }
-
- /* read */
- if (mode & 04) {
- access |= ACE_READ_DATA;
- }
- /* write */
- if (mode & 02) {
- access |= ACE_WRITE_DATA |
- ACE_APPEND_DATA;
- if (isdir)
- access |= ACE_DELETE_CHILD;
- }
- /* exec */
- if (mode & 01) {
- access |= ACE_EXECUTE;
- }
-
- return (access);
-}
-
-/*
- * Given an nfsace (presumably an ALLOW entry), make a
- * corresponding DENY entry at the address given.
- */
-static void
-ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
-{
- (void) memcpy(deny, allow, sizeof (ace_t));
-
- deny->a_who = allow->a_who;
-
- deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
- deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
- if (isdir)
- deny->a_access_mask ^= ACE_DELETE_CHILD;
-
- deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
- ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
- ACE_WRITE_NAMED_ATTRS);
- deny->a_access_mask |= access_mask_set((allow->a_access_mask &
- ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
- B_FALSE);
-}
-/*
- * Make an initial pass over an array of aclent_t's. Gather
- * information such as an ACL_MASK (if any), number of users,
- * number of groups, and whether the array needs to be sorted.
- */
-static int
-ln_aent_preprocess(aclent_t *aclent, int n,
- int *hasmask, mode_t *mask,
- int *numuser, int *numgroup, int *needsort)
-{
- int error = 0;
- int i;
- int curtype = 0;
-
- *hasmask = 0;
- *mask = 07;
- *needsort = 0;
- *numuser = 0;
- *numgroup = 0;
-
- for (i = 0; i < n; i++) {
- if (aclent[i].a_type < curtype)
- *needsort = 1;
- else if (aclent[i].a_type > curtype)
- curtype = aclent[i].a_type;
- if (aclent[i].a_type & USER)
- (*numuser)++;
- if (aclent[i].a_type & (GROUP | GROUP_OBJ))
- (*numgroup)++;
- if (aclent[i].a_type & CLASS_OBJ) {
- if (*hasmask) {
- error = EINVAL;
- goto out;
- } else {
- *hasmask = 1;
- *mask = aclent[i].a_perm;
- }
- }
- }
-
- if ((! *hasmask) && (*numuser + *numgroup > 1)) {
- error = EINVAL;
- goto out;
- }
-
-out:
- return (error);
-}
-
-/*
- * Convert an array of aclent_t into an array of nfsace entries,
- * following POSIX draft -> nfsv4 conversion semantics as outlined in
- * the IETF draft.
- */
-static int
-ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
-{
- int error = 0;
- mode_t mask;
- int numuser, numgroup, needsort;
- int resultsize = 0;
- int i, groupi = 0, skip;
- ace_t *acep, *result = NULL;
- int hasmask;
-
- error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
- &numuser, &numgroup, &needsort);
- if (error != 0)
- goto out;
-
- /* allow + deny for each aclent */
- resultsize = n * 2;
- if (hasmask) {
- /*
- * stick extra deny on the group_obj and on each
- * user|group for the mask (the group_obj was added
- * into the count for numgroup)
- */
- resultsize += numuser + numgroup;
- /* ... and don't count the mask itself */
- resultsize -= 2;
- }
-
- /* sort the source if necessary */
- if (needsort)
- ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
-
- result = acep = calloc(1, resultsize * sizeof (ace_t));
- if (result == NULL)
- goto out;
-
- for (i = 0; i < n; i++) {
- /*
- * don't process CLASS_OBJ (mask); mask was grabbed in
- * ln_aent_preprocess()
- */
- if (aclent[i].a_type & CLASS_OBJ)
- continue;
-
- /* If we need an ACL_MASK emulator, prepend it now */
- if ((hasmask) &&
- (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
- acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
- acep->a_flags = 0;
- if (aclent[i].a_type & GROUP_OBJ) {
- acep->a_who = (uid_t)-1;
- acep->a_flags |=
- (ACE_IDENTIFIER_GROUP|ACE_GROUP);
- } else if (aclent[i].a_type & USER) {
- acep->a_who = aclent[i].a_id;
- } else {
- acep->a_who = aclent[i].a_id;
- acep->a_flags |= ACE_IDENTIFIER_GROUP;
- }
- if (aclent[i].a_type & ACL_DEFAULT) {
- acep->a_flags |= ACE_INHERIT_ONLY_ACE |
- ACE_FILE_INHERIT_ACE |
- ACE_DIRECTORY_INHERIT_ACE;
- }
- /*
- * Set the access mask for the prepended deny
- * ace. To do this, we invert the mask (found
- * in ln_aent_preprocess()) then convert it to an
- * DENY ace access_mask.
- */
- acep->a_access_mask = mode_to_ace_access((mask ^ 07),
- isdir, 0, 0);
- acep += 1;
- }
-
- /* handle a_perm -> access_mask */
- acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
- isdir, aclent[i].a_type & USER_OBJ, 1);
-
- /* emulate a default aclent */
- if (aclent[i].a_type & ACL_DEFAULT) {
- acep->a_flags |= ACE_INHERIT_ONLY_ACE |
- ACE_FILE_INHERIT_ACE |
- ACE_DIRECTORY_INHERIT_ACE;
- }
-
- /*
- * handle a_perm and a_id
- *
- * this must be done last, since it involves the
- * corresponding deny aces, which are handled
- * differently for each different a_type.
- */
- if (aclent[i].a_type & USER_OBJ) {
- acep->a_who = (uid_t)-1;
- acep->a_flags |= ACE_OWNER;
- ace_make_deny(acep, acep + 1, isdir, B_TRUE);
- acep += 2;
- } else if (aclent[i].a_type & USER) {
- acep->a_who = aclent[i].a_id;
- ace_make_deny(acep, acep + 1, isdir, B_FALSE);
- acep += 2;
- } else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
- if (aclent[i].a_type & GROUP_OBJ) {
- acep->a_who = (uid_t)-1;
- acep->a_flags |= ACE_GROUP;
- } else {
- acep->a_who = aclent[i].a_id;
- }
- acep->a_flags |= ACE_IDENTIFIER_GROUP;
- /*
- * Set the corresponding deny for the group ace.
- *
- * The deny aces go after all of the groups, unlike
- * everything else, where they immediately follow
- * the allow ace.
- *
- * We calculate "skip", the number of slots to
- * skip ahead for the deny ace, here.
- *
- * The pattern is:
- * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
- * thus, skip is
- * (2 * numgroup) - 1 - groupi
- * (2 * numgroup) to account for MD + A
- * - 1 to account for the fact that we're on the
- * access (A), not the mask (MD)
- * - groupi to account for the fact that we have
- * passed up groupi number of MD's.
- */
- skip = (2 * numgroup) - 1 - groupi;
- ace_make_deny(acep, acep + skip, isdir, B_FALSE);
- /*
- * If we just did the last group, skip acep past
- * all of the denies; else, just move ahead one.
- */
- if (++groupi >= numgroup)
- acep += numgroup + 1;
- else
- acep += 1;
- } else if (aclent[i].a_type & OTHER_OBJ) {
- acep->a_who = (uid_t)-1;
- acep->a_flags |= ACE_EVERYONE;
- ace_make_deny(acep, acep + 1, isdir, B_FALSE);
- acep += 2;
- } else {
- error = EINVAL;
- goto out;
- }
- }
-
- *acepp = result;
- *rescount = resultsize;
-
-out:
- if (error != 0) {
- if ((result != NULL) && (resultsize > 0)) {
- free(result);
- }
- }
-
- return (error);
-}
-
-static int
-convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
- ace_t **retacep, int *retacecnt)
-{
- ace_t *acep;
- ace_t *dfacep;
- int acecnt = 0;
- int dfacecnt = 0;
- int dfaclstart = 0;
- int dfaclcnt = 0;
- aclent_t *aclp;
- int i;
- int error;
-
- ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
-
- for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
- if (aclp->a_type & ACL_DEFAULT)
- break;
- }
-
- if (i < aclcnt) {
- dfaclstart = i;
- dfaclcnt = aclcnt - i;
- }
-
- if (dfaclcnt && isdir == 0) {
- return (-1);
- }
-
- error = ln_aent_to_ace(aclentp, i, &acep, &acecnt, isdir);
- if (error)
- return (-1);
-
- if (dfaclcnt) {
- error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
- &dfacep, &dfacecnt, isdir);
- if (error) {
- if (acep) {
- free(acep);
- }
- return (-1);
- }
- }
-
- if (dfacecnt != 0) {
- acep = realloc(acep, sizeof (ace_t) * (acecnt + dfacecnt));
- if (acep == NULL)
- return (-1);
- if (dfaclcnt) {
- (void) memcpy(acep + acecnt, dfacep,
- sizeof (ace_t) * dfacecnt);
- }
- }
- if (dfaclcnt)
- free(dfacep);
-
- *retacecnt = acecnt + dfacecnt;
- *retacep = acep;
- return (0);
-}
-
-static void
-acevals_init(acevals_t *vals, uid_t key)
-{
- bzero(vals, sizeof (*vals));
- vals->allowed = ACE_MASK_UNDEFINED;
- vals->denied = ACE_MASK_UNDEFINED;
- vals->mask = ACE_MASK_UNDEFINED;
- vals->key = key;
-}
-
-static void
-ace_list_init(ace_list_t *al, int dfacl_flag)
-{
- acevals_init(&al->user_obj, NULL);
- acevals_init(&al->group_obj, NULL);
- acevals_init(&al->other_obj, NULL);
- al->numusers = 0;
- al->numgroups = 0;
- al->acl_mask = 0;
- al->hasmask = 0;
- al->state = ace_unused;
- al->seen = 0;
- al->dfacl_flag = dfacl_flag;
-}
-
-/*
- * Find or create an acevals holder for a given id and avl tree.
- *
- * Note that only one thread will ever touch these avl trees, so
- * there is no need for locking.
- */
-static acevals_t *
-acevals_find(ace_t *ace, avl_tree_t *avl, int *num)
-{
- acevals_t key, *rc;
- avl_index_t where;
-
- key.key = ace->a_who;
- rc = avl_find(avl, &key, &where);
- if (rc != NULL)
- return (rc);
-
- /* this memory is freed by ln_ace_to_aent()->ace_list_free() */
- rc = calloc(1, sizeof (acevals_t));
- if (rc == NULL)
- return (rc);
- acevals_init(rc, ace->a_who);
- avl_insert(avl, rc, where);
- (*num)++;
-
- return (rc);
-}
-
-static int
-access_mask_check(ace_t *acep, int mask_bit, int isowner)
-{
- int set_deny, err_deny;
- int set_allow, err_allow;
- int acl_consume;
- int haswriteperm, hasreadperm;
-
- if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
- haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1;
- hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1;
- } else {
- haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0;
- hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0;
- }
-
- acl_consume = (ACL_SYNCHRONIZE_ERR_DENY |
- ACL_DELETE_ERR_DENY |
- ACL_WRITE_OWNER_ERR_DENY |
- ACL_WRITE_OWNER_ERR_ALLOW |
- ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
- ACL_WRITE_ATTRS_OWNER_ERR_DENY |
- ACL_WRITE_ATTRS_WRITER_SET_DENY |
- ACL_WRITE_ATTRS_WRITER_ERR_ALLOW |
- ACL_WRITE_NAMED_WRITER_ERR_DENY |
- ACL_READ_NAMED_READER_ERR_DENY);
-
- if (mask_bit == ACE_SYNCHRONIZE) {
- set_deny = ACL_SYNCHRONIZE_SET_DENY;
- err_deny = ACL_SYNCHRONIZE_ERR_DENY;
- set_allow = ACL_SYNCHRONIZE_SET_ALLOW;
- err_allow = ACL_SYNCHRONIZE_ERR_ALLOW;
- } else if (mask_bit == ACE_WRITE_OWNER) {
- set_deny = ACL_WRITE_OWNER_SET_DENY;
- err_deny = ACL_WRITE_OWNER_ERR_DENY;
- set_allow = ACL_WRITE_OWNER_SET_ALLOW;
- err_allow = ACL_WRITE_OWNER_ERR_ALLOW;
- } else if (mask_bit == ACE_DELETE) {
- set_deny = ACL_DELETE_SET_DENY;
- err_deny = ACL_DELETE_ERR_DENY;
- set_allow = ACL_DELETE_SET_ALLOW;
- err_allow = ACL_DELETE_ERR_ALLOW;
- } else if (mask_bit == ACE_WRITE_ATTRIBUTES) {
- if (isowner) {
- set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY;
- err_deny = ACL_WRITE_ATTRS_OWNER_ERR_DENY;
- set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
- err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW;
- } else if (haswriteperm) {
- set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY;
- err_deny = ACL_WRITE_ATTRS_WRITER_ERR_DENY;
- set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
- err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW;
- } else {
- if ((acep->a_access_mask & mask_bit) &&
- (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) {
- return (ENOTSUP);
- }
- return (0);
- }
- } else if (mask_bit == ACE_READ_NAMED_ATTRS) {
- if (!hasreadperm)
- return (0);
-
- set_deny = ACL_READ_NAMED_READER_SET_DENY;
- err_deny = ACL_READ_NAMED_READER_ERR_DENY;
- set_allow = ACL_READ_NAMED_READER_SET_ALLOW;
- err_allow = ACL_READ_NAMED_READER_ERR_ALLOW;
- } else if (mask_bit == ACE_WRITE_NAMED_ATTRS) {
- if (!haswriteperm)
- return (0);
-
- set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY;
- err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY;
- set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
- err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW;
- } else {
- return (EINVAL);
- }
-
- if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
- if (acl_consume & set_deny) {
- if (!(acep->a_access_mask & mask_bit)) {
- return (ENOTSUP);
- }
- } else if (acl_consume & err_deny) {
- if (acep->a_access_mask & mask_bit) {
- return (ENOTSUP);
- }
- }
- } else {
- /* ACE_ACCESS_ALLOWED_ACE_TYPE */
- if (acl_consume & set_allow) {
- if (!(acep->a_access_mask & mask_bit)) {
- return (ENOTSUP);
- }
- } else if (acl_consume & err_allow) {
- if (acep->a_access_mask & mask_bit) {
- return (ENOTSUP);
- }
- }
- }
- return (0);
-}
-
-static int
-ace_to_aent_legal(ace_t *acep)
-{
- int error = 0;
- int isowner;
-
- /* only ALLOW or DENY */
- if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) &&
- (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) {
- error = ENOTSUP;
- goto out;
- }
-
- /* check for invalid flags */
- if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) {
- error = EINVAL;
- goto out;
- }
-
- /* some flags are illegal */
- if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG |
- ACE_FAILED_ACCESS_ACE_FLAG |
- ACE_NO_PROPAGATE_INHERIT_ACE)) {
- error = ENOTSUP;
- goto out;
- }
-
- /* check for invalid masks */
- if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) {
- error = EINVAL;
- goto out;
- }
-
- if ((acep->a_flags & ACE_OWNER)) {
- isowner = 1;
- } else {
- isowner = 0;
- }
-
- error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner);
- if (error)
- goto out;
-
- error = access_mask_check(acep, ACE_WRITE_OWNER, isowner);
- if (error)
- goto out;
-
- error = access_mask_check(acep, ACE_DELETE, isowner);
- if (error)
- goto out;
-
- error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner);
- if (error)
- goto out;
-
- error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner);
- if (error)
- goto out;
-
- error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner);
- if (error)
- goto out;
-
- /* more detailed checking of masks */
- if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
- if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) {
- error = ENOTSUP;
- goto out;
- }
- if ((acep->a_access_mask & ACE_WRITE_DATA) &&
- (! (acep->a_access_mask & ACE_APPEND_DATA))) {
- error = ENOTSUP;
- goto out;
- }
- if ((! (acep->a_access_mask & ACE_WRITE_DATA)) &&
- (acep->a_access_mask & ACE_APPEND_DATA)) {
- error = ENOTSUP;
- goto out;
- }
- }
-
- /* ACL enforcement */
- if ((acep->a_access_mask & ACE_READ_ACL) &&
- (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) {
- error = ENOTSUP;
- goto out;
- }
- if (acep->a_access_mask & ACE_WRITE_ACL) {
- if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) &&
- (isowner)) {
- error = ENOTSUP;
- goto out;
- }
- if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) &&
- (! isowner)) {
- error = ENOTSUP;
- goto out;
- }
- }
-
-out:
- return (error);
-}
-
-static int
-ace_mask_to_mode(uint32_t mask, o_mode_t *modep, int isdir)
-{
- int error = 0;
- o_mode_t mode = 0;
- uint32_t bits, wantbits;
-
- /* read */
- if (mask & ACE_READ_DATA)
- mode |= 04;
-
- /* write */
- wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA);
- if (isdir)
- wantbits |= ACE_DELETE_CHILD;
- bits = mask & wantbits;
- if (bits != 0) {
- if (bits != wantbits) {
- error = ENOTSUP;
- goto out;
- }
- mode |= 02;
- }
-
- /* exec */
- if (mask & ACE_EXECUTE) {
- mode |= 01;
- }
-
- *modep = mode;
-
-out:
- return (error);
-}
-
-static int
-ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir)
-{
- /* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */
- if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) !=
- (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) {
- return (ENOTSUP);
- }
-
- return (ace_mask_to_mode(mask, modep, isdir));
-}
-
-static int
-acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list,
- uid_t owner, gid_t group, int isdir)
-{
- int error;
- uint32_t flips = ACE_POSIX_SUPPORTED_BITS;
-
- if (isdir)
- flips |= ACE_DELETE_CHILD;
- if (vals->allowed != (vals->denied ^ flips)) {
- error = ENOTSUP;
- goto out;
- }
- if ((list->hasmask) && (list->acl_mask != vals->mask) &&
- (vals->aent_type & (USER | GROUP | GROUP_OBJ))) {
- error = ENOTSUP;
- goto out;
- }
- error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir);
- if (error != 0)
- goto out;
- dest->a_type = vals->aent_type;
- if (dest->a_type & (USER | GROUP)) {
- dest->a_id = vals->key;
- } else if (dest->a_type & USER_OBJ) {
- dest->a_id = owner;
- } else if (dest->a_type & GROUP_OBJ) {
- dest->a_id = group;
- } else if (dest->a_type & OTHER_OBJ) {
- dest->a_id = 0;
- } else {
- error = EINVAL;
- goto out;
- }
-
-out:
- return (error);
-}
-
-static int
-ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt,
- uid_t owner, gid_t group, int isdir)
-{
- int error = 0;
- aclent_t *aent, *result = NULL;
- acevals_t *vals;
- int resultcount;
-
- if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) !=
- (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) {
- error = ENOTSUP;
- goto out;
- }
- if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) {
- error = ENOTSUP;
- goto out;
- }
-
- resultcount = 3 + list->numusers + list->numgroups;
- /*
- * This must be the same condition as below, when we add the CLASS_OBJ
- * (aka ACL mask)
- */
- if ((list->hasmask) || (! list->dfacl_flag))
- resultcount += 1;
-
- result = aent = calloc(1, resultcount * sizeof (aclent_t));
-
- if (result == NULL) {
- error = ENOMEM;
- goto out;
- }
-
- /* USER_OBJ */
- if (!(list->user_obj.aent_type & USER_OBJ)) {
- error = EINVAL;
- goto out;
- }
-
- error = acevals_to_aent(&list->user_obj, aent, list, owner, group,
- isdir);
-
- if (error != 0)
- goto out;
- ++aent;
- /* USER */
- vals = NULL;
- for (vals = avl_first(&list->user); vals != NULL;
- vals = AVL_NEXT(&list->user, vals)) {
- if (!(vals->aent_type & USER)) {
- error = EINVAL;
- goto out;
- }
- error = acevals_to_aent(vals, aent, list, owner, group,
- isdir);
- if (error != 0)
- goto out;
- ++aent;
- }
- /* GROUP_OBJ */
- if (!(list->group_obj.aent_type & GROUP_OBJ)) {
- error = EINVAL;
- goto out;
- }
- error = acevals_to_aent(&list->group_obj, aent, list, owner, group,
- isdir);
- if (error != 0)
- goto out;
- ++aent;
- /* GROUP */
- vals = NULL;
- for (vals = avl_first(&list->group); vals != NULL;
- vals = AVL_NEXT(&list->group, vals)) {
- if (!(vals->aent_type & GROUP)) {
- error = EINVAL;
- goto out;
- }
- error = acevals_to_aent(vals, aent, list, owner, group,
- isdir);
- if (error != 0)
- goto out;
- ++aent;
- }
- /*
- * CLASS_OBJ (aka ACL_MASK)
- *
- * An ACL_MASK is not fabricated if the ACL is a default ACL.
- * This is to follow UFS's behavior.
- */
- if ((list->hasmask) || (! list->dfacl_flag)) {
- if (list->hasmask) {
- uint32_t flips = ACE_POSIX_SUPPORTED_BITS;
- if (isdir)
- flips |= ACE_DELETE_CHILD;
- error = ace_mask_to_mode(list->acl_mask ^ flips,
- &aent->a_perm, isdir);
- if (error != 0)
- goto out;
- } else {
- /* fabricate the ACL_MASK from the group permissions */
- error = ace_mask_to_mode(list->group_obj.allowed,
- &aent->a_perm, isdir);
- if (error != 0)
- goto out;
- }
- aent->a_id = 0;
- aent->a_type = CLASS_OBJ | list->dfacl_flag;
- ++aent;
- }
- /* OTHER_OBJ */
- if (!(list->other_obj.aent_type & OTHER_OBJ)) {
- error = EINVAL;
- goto out;
- }
- error = acevals_to_aent(&list->other_obj, aent, list, owner, group,
- isdir);
- if (error != 0)
- goto out;
- ++aent;
-
- *aclentp = result;
- *aclcnt = resultcount;
-
-out:
- if (error != 0) {
- if (result != NULL)
- free(result);
- }
-
- return (error);
-}
-
-/*
- * free all data associated with an ace_list
- */
-static void
-ace_list_free(ace_list_t *al)
-{
- acevals_t *node;
- void *cookie;
-
- if (al == NULL)
- return;
-
- cookie = NULL;
- while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL)
- free(node);
- cookie = NULL;
- while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL)
- free(node);
-
- avl_destroy(&al->user);
- avl_destroy(&al->group);
-
- /* free the container itself */
- free(al);
-}
-
-static int
-acevals_compare(const void *va, const void *vb)
-{
- const acevals_t *a = va, *b = vb;
-
- if (a->key == b->key)
- return (0);
-
- if (a->key > b->key)
- return (1);
-
- else
- return (-1);
-}
-
-/*
- * Convert a list of ace_t entries to equivalent regular and default
- * aclent_t lists. Return error (ENOTSUP) when conversion is not possible.
- */
-static int
-ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group,
- aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt,
- int isdir)
-{
- int error = 0;
- ace_t *acep;
- uint32_t bits;
- int i;
- ace_list_t *normacl = NULL, *dfacl = NULL, *acl;
- acevals_t *vals;
-
- *aclentp = NULL;
- *aclcnt = 0;
- *dfaclentp = NULL;
- *dfaclcnt = 0;
-
- /* we need at least user_obj, group_obj, and other_obj */
- if (n < 6) {
- error = ENOTSUP;
- goto out;
- }
- if (ace == NULL) {
- error = EINVAL;
- goto out;
- }
-
- normacl = calloc(1, sizeof (ace_list_t));
-
- if (normacl == NULL) {
- error = errno;
- goto out;
- }
-
- avl_create(&normacl->user, acevals_compare, sizeof (acevals_t),
- offsetof(acevals_t, avl));
- avl_create(&normacl->group, acevals_compare, sizeof (acevals_t),
- offsetof(acevals_t, avl));
-
- ace_list_init(normacl, 0);
-
- dfacl = calloc(1, sizeof (ace_list_t));
- if (dfacl == NULL) {
- error = errno;
- goto out;
- }
- avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t),
- offsetof(acevals_t, avl));
- avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t),
- offsetof(acevals_t, avl));
- ace_list_init(dfacl, ACL_DEFAULT);
-
- /* process every ace_t... */
- for (i = 0; i < n; i++) {
- acep = &ace[i];
-
- /* rule out certain cases quickly */
- error = ace_to_aent_legal(acep);
- if (error != 0)
- goto out;
-
- /*
- * Turn off these bits in order to not have to worry about
- * them when doing the checks for compliments.
- */
- acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE |
- ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES |
- ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS);
-
- /* see if this should be a regular or default acl */
- bits = acep->a_flags &
- (ACE_INHERIT_ONLY_ACE |
- ACE_FILE_INHERIT_ACE |
- ACE_DIRECTORY_INHERIT_ACE);
- if (bits != 0) {
- /* all or nothing on these inherit bits */
- if (bits != (ACE_INHERIT_ONLY_ACE |
- ACE_FILE_INHERIT_ACE |
- ACE_DIRECTORY_INHERIT_ACE)) {
- error = ENOTSUP;
- goto out;
- }
- acl = dfacl;
- } else {
- acl = normacl;
- }
-
- if ((acep->a_flags & ACE_OWNER)) {
- if (acl->state > ace_user_obj) {
- error = ENOTSUP;
- goto out;
- }
- acl->state = ace_user_obj;
- acl->seen |= USER_OBJ;
- vals = &acl->user_obj;
- vals->aent_type = USER_OBJ | acl->dfacl_flag;
- } else if ((acep->a_flags & ACE_EVERYONE)) {
- acl->state = ace_other_obj;
- acl->seen |= OTHER_OBJ;
- vals = &acl->other_obj;
- vals->aent_type = OTHER_OBJ | acl->dfacl_flag;
- } else if (acep->a_flags & ACE_IDENTIFIER_GROUP) {
- if (acl->state > ace_group) {
- error = ENOTSUP;
- goto out;
- }
- if ((acep->a_flags & ACE_GROUP)) {
- acl->seen |= GROUP_OBJ;
- vals = &acl->group_obj;
- vals->aent_type = GROUP_OBJ | acl->dfacl_flag;
- } else {
- acl->seen |= GROUP;
- vals = acevals_find(acep, &acl->group,
- &acl->numgroups);
- if (vals == NULL) {
- error = ENOMEM;
- goto out;
- }
- vals->aent_type = GROUP | acl->dfacl_flag;
- }
- acl->state = ace_group;
- } else {
- if (acl->state > ace_user) {
- error = ENOTSUP;
- goto out;
- }
- acl->state = ace_user;
- acl->seen |= USER;
- vals = acevals_find(acep, &acl->user,
- &acl->numusers);
- if (vals == NULL) {
- error = ENOMEM;
- goto out;
- }
- vals->aent_type = USER | acl->dfacl_flag;
- }
-
- if (!(acl->state > ace_unused)) {
- error = EINVAL;
- goto out;
- }
-
- if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
- /* no more than one allowed per aclent_t */
- if (vals->allowed != ACE_MASK_UNDEFINED) {
- error = ENOTSUP;
- goto out;
- }
- vals->allowed = acep->a_access_mask;
- } else {
- /*
- * it's a DENY; if there was a previous DENY, it
- * must have been an ACL_MASK.
- */
- if (vals->denied != ACE_MASK_UNDEFINED) {
- /* ACL_MASK is for USER and GROUP only */
- if ((acl->state != ace_user) &&
- (acl->state != ace_group)) {
- error = ENOTSUP;
- goto out;
- }
-
- if (! acl->hasmask) {
- acl->hasmask = 1;
- acl->acl_mask = vals->denied;
- /* check for mismatched ACL_MASK emulations */
- } else if (acl->acl_mask != vals->denied) {
- error = ENOTSUP;
- goto out;
- }
- vals->mask = vals->denied;
- }
- vals->denied = acep->a_access_mask;
- }
- }
-
- /* done collating; produce the aclent_t lists */
- if (normacl->state != ace_unused) {
- error = ace_list_to_aent(normacl, aclentp, aclcnt,
- owner, group, isdir);
- if (error != 0) {
- goto out;
- }
- }
- if (dfacl->state != ace_unused) {
- error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt,
- owner, group, isdir);
- if (error != 0) {
- goto out;
- }
- }
-
-out:
- if (normacl != NULL)
- ace_list_free(normacl);
- if (dfacl != NULL)
- ace_list_free(dfacl);
-
- return (error);
-}
-
-static int
-convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir,
- uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt)
-{
- int error;
- aclent_t *aclentp, *dfaclentp;
- int aclcnt, dfaclcnt;
-
- error = ln_ace_to_aent(acebufp, acecnt, owner, group,
- &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir);
-
- if (error)
- return (error);
-
-
- if (dfaclcnt != 0) {
- /*
- * Slap aclentp and dfaclentp into a single array.
- */
- aclentp = realloc(aclentp, (sizeof (aclent_t) * aclcnt) +
- (sizeof (aclent_t) * dfaclcnt));
- if (aclentp != NULL) {
- (void) memcpy(aclentp + aclcnt,
- dfaclentp, sizeof (aclent_t) * dfaclcnt);
- } else {
- error = -1;
- }
- }
-
- if (aclentp) {
- *retaclentp = aclentp;
- *retaclcnt = aclcnt + dfaclcnt;
- }
-
- if (dfaclentp)
- free(dfaclentp);
-
- return (error);
-}
static int
cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
@@ -1576,56 +240,6 @@ facl_get(int fd, int get_flag, acl_t **aclp)
return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
}
-static int
-acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner,
- gid_t group)
-{
- int aclcnt;
- void *acldata;
- int error;
-
- /*
- * See if we need to translate
- */
- if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) ||
- (target_flavor == _ACL_ACLENT_ENABLED &&
- aclp->acl_type == ACLENT_T))
- return (0);
-
- if (target_flavor == -1)
- return (-1);
-
- if (target_flavor == _ACL_ACE_ENABLED &&
- aclp->acl_type == ACLENT_T) {
- error = convert_aent_to_ace(aclp->acl_aclp,
- aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt);
- if (error) {
- errno = error;
- return (-1);
- }
- } else if (target_flavor == _ACL_ACLENT_ENABLED &&
- aclp->acl_type == ACE_T) {
- error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt,
- isdir, owner, group, (aclent_t **)&acldata, &aclcnt);
- if (error) {
- errno = error;
- return (-1);
- }
- } else {
- errno = ENOTSUP;
- return (-1);
- }
-
- /*
- * replace old acl with newly translated acl
- */
- free(aclp->acl_aclp);
- aclp->acl_aclp = acldata;
- aclp->acl_cnt = aclcnt;
- aclp->acl_type = (target_flavor == _ACL_ACE_ENABLED) ? ACE_T : ACLENT_T;
- return (0);
-}
-
/*
* Set an ACL, translates acl to ace_t when appropriate.
*/
diff --git a/usr/src/lib/libsec/common/aclutils.h b/usr/src/lib/libsec/common/aclutils.h
index e5c34347bf..1db0aa4752 100644
--- a/usr/src/lib/libsec/common/aclutils.h
+++ b/usr/src/lib/libsec/common/aclutils.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -29,6 +29,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
+#include <sys/acl.h>
#include <strings.h>
#include <locale.h>
#include <ctype.h>
@@ -50,26 +51,6 @@ extern "C" {
* append_data/add_subdirectory
* when object of ACL is known.
*/
-#define ACL_IS_DIR 0x2
-
-typedef enum acl_type {
- ACLENT_T = 0,
- ACE_T = 1
-} acl_type_t;
-
-/*
- * acl flags
- */
-#define ACL_IS_TRIVIAL 0x1
-
-struct acl_info {
- acl_type_t acl_type; /* style of acl */
- int acl_cnt; /* number of acl entries */
- int acl_entry_size; /* sizeof acl entry */
- int acl_flags; /* special flags about acl */
- void *acl_aclp; /* the acl */
-};
-
#define PERM_TYPE_ACE 0x1 /* permissions are of ACE type */
#define PERM_TYPE_UNKNOWN 0x2 /* permission type not yet known */
@@ -105,7 +86,6 @@ extern void acl_error(const char *, ...);
extern int acl_parse(const char *, acl_t **);
extern int yyparse(void);
extern void yyreset(void);
-extern acl_t *acl_alloc(enum acl_type);
extern acl_t *acl_to_aclp(enum acl_type, void *, int);
#ifdef __cplusplus
diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt
index afb8349e40..0b81cd20f0 100644
--- a/usr/src/lib/libsecdb/auth_attr.txt
+++ b/usr/src/lib/libsecdb/auth_attr.txt
@@ -121,6 +121,7 @@ solaris.smf.manage.mdns:::Manage Multicast DNS Service States::help=SmfMDNSState
solaris.smf.manage.name-service-cache:::Manage Name Service Cache Daemon Service States::help=SmfNscdStates.html
solaris.smf.manage.nwam:::Manage Network Auto-Magic Service States::help=SmfNWAMStates.html
solaris.smf.manage.power:::Manage Power Management Service States::help=SmfPowerStates.html
+solaris.smf.manage.smb:::Manage SMB Service States::help=SmfSMBStates.html
solaris.smf.manage.rmvolmgr:::Manage Rmvolmgr Service States::help=SmfRmvolmgrStates.html
solaris.smf.manage.routing:::Manage Routing Service States::help=SmfRoutingStates.html
solaris.smf.manage.rpc.bind:::Manage RPC Program number mapper::help=SmfRPCBind.html
@@ -137,6 +138,8 @@ solaris.smf.value.inetd:::Change values of SMF Inetd configuration paramaters::h
solaris.smf.value.ipsec:::Change Values of SMF IPsec Properties::help=SmfValueIPsec.html
solaris.smf.value.mdns:::Change Values of MDNS Service Properties::help=SmfValueMDNS.html
solaris.smf.value.nwam:::Change Values of SMF Network Auto-Magic Properties::help=SmfValueNWAM.html
+solaris.smf.value.smb:::Change Values of SMB Service Properties::help=SmfValueSMB.html
+solaris.smf.read.smb:::Read permission for protected SMF SMB Service Properties::help=AuthReadSMB.html
solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html
solaris.smf.value.tnd:::Change Trusted Network Daemon Service Property Values::help=ValueTND.html
#
diff --git a/usr/src/lib/libsecdb/help/auths/AuthReadSMB.html b/usr/src/lib/libsecdb/help/auths/AuthReadSMB.html
new file mode 100755
index 0000000000..aec2ed97e8
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/AuthReadSMB.html
@@ -0,0 +1,38 @@
+<HTML>
+<!--
+ 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.
+-->
+<HEAD>
+ <TITLE> </TITLE>
+
+
+</HEAD>
+<BODY>
+<!-- ident "%Z%%M% %I% %E% SMI" -->
+
+When View Rights is in the Authorizations Included column, it grants the authorization to list and read rights, in the Users tools of the Solaris Management Console.
+<p>
+If View Rights is grayed, then you are not entitled to Add or Remove this authorization.
+<p>
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile
index 1bdd12e0ce..d28be1d4c5 100644
--- a/usr/src/lib/libsecdb/help/auths/Makefile
+++ b/usr/src/lib/libsecdb/help/auths/Makefile
@@ -93,6 +93,9 @@ HTMLENTS = \
SmfValueNADD.html \
SmfValueNWAM.html \
SmfValueRouting.html \
+ SmfValueSMB.html \
+ AuthReadSMB.html \
+ SmfSMBStates.html \
SmfWpaStates.html \
NetworkHeader.html \
WifiConfig.html \
diff --git a/usr/src/lib/libsecdb/help/auths/SmfSMBStates.html b/usr/src/lib/libsecdb/help/auths/SmfSMBStates.html
new file mode 100644
index 0000000000..baa85f002d
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SmfSMBStates.html
@@ -0,0 +1,40 @@
+<HTML>
+<!--
+ 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.
+-->
+<!-- SCCS keyword
+#pragma ident "%Z%%M% %I% %E% SMI"
+-->
+<!--
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+-->
+<BODY>
+When Manage SMB Service States is in the Authorizations Include
+column, it grants the authorization to enable, disable, or restart
+the SMB service.
+<p>
+If Manage SMB Service States is grayed, then you are not entitled
+to Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/auths/SmfValueSMB.html b/usr/src/lib/libsecdb/help/auths/SmfValueSMB.html
new file mode 100644
index 0000000000..0e5c3bc277
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SmfValueSMB.html
@@ -0,0 +1,40 @@
+<HTML>
+<!--
+ 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.
+-->
+<!-- SCCS keyword
+#pragma ident "%Z%%M% %I% %E% SMI"
+-->
+<!--
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+-->
+<BODY>
+When Value SMB Properties is in the Authorizations Include
+column, it grants the authorization to change SMB service
+property values.
+<P>
+If Value SMB Properties is grayed, then you are not entitled to
+Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/profiles/Makefile b/usr/src/lib/libsecdb/help/profiles/Makefile
index 9ad6ede803..e0300d339b 100644
--- a/usr/src/lib/libsecdb/help/profiles/Makefile
+++ b/usr/src/lib/libsecdb/help/profiles/Makefile
@@ -62,6 +62,7 @@ HTMLENTS = \
RtPrntAdmin.html \
RtProcManagement.html \
RtRightsDelegate.html \
+ RtSMBMngmnt.html \
RtSoftwareInstall.html \
RtSysEvMngmnt.html \
RtUserMngmnt.html \
diff --git a/usr/src/lib/libsecdb/help/profiles/RtSMBMngmnt.html b/usr/src/lib/libsecdb/help/profiles/RtSMBMngmnt.html
new file mode 100644
index 0000000000..bbfef648ab
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/profiles/RtSMBMngmnt.html
@@ -0,0 +1,40 @@
+<HTML>
+<!--
+ 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.
+-->
+<HEAD>
+ <TITLE> </TITLE>
+
+
+</HEAD>
+<BODY>
+<!-- ident "%Z%%M% %I% %E% SMI" -->
+
+When SMB Service Management is in the Rights Included column, it grants
+the right to administer the SMB service.
+<p>
+If SMB Service Management is grayed, then you are not entitled to Add or
+Remove this right.
+<p>
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index 96107e01f4..1c171d427b 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -42,7 +42,7 @@ Log Management:::Manage log files:help=RtLogMngmnt.html
Basic Solaris User:::Automatically assigned rights:auths=solaris.profmgr.read,solaris.jobs.user,solaris.mail.mailq,solaris.device.mount.removable;profiles=All;help=RtDefault.html
Device Security:::Manage devices and Volume Manager:auths=solaris.device.*;help=RtDeviceSecurity.html
DHCP Management:::Manage the DHCP service:auths=solaris.dhcpmgr.*;help=RtDHCPMngmnt.html
-File System Management:::Manage, mount, share file systems:auths=solaris.smf.manage.autofs,solaris.smf.manage.shares.*,solaris.smf.value.shares.*;help=RtFileSysMngmnt.html
+File System Management:::Manage, mount, share file systems:profiles=SMB Management;auths=solaris.smf.manage.autofs,solaris.smf.manage.shares.*,solaris.smf.value.shares.*;help=RtFileSysMngmnt.html
File System Security:::Manage file system security attributes:help=RtFileSysSecurity.html
HAL Management:::Manage HAL SMF service:auths=solaris.smf.manage.hal;help=RtHALMngmnt.html
Idmap Name Mapping Management:::Manage Name-based Mapping Rules of Identity Mapping Service:auths=solaris.admin.idmap.rules;help=RtIdmapNameRulesMngmnt.html
@@ -75,6 +75,7 @@ Crypto Management:::Cryptographic Framework Administration:help=RtCryptoMngmnt.h
Kerberos Client Management:::Maintain and Administer Kerberos excluding the servers:help=RtKerberosClntMngmnt.html
Kerberos Server Management:::Maintain and Administer Kerberos Servers:profiles=Kerberos Client Management;help=RtKerberosSrvrMngmnt.html
DAT Administration:::Manage the DAT configuration:help=RtDatAdmin.html
+SMB Management:::Manage the SMB service:auths=solaris.smf.manage.smb,solaris.smf.value.smb,solaris.smf.read.smb;help=RtSMBMngmnt.html
ZFS File System Management:::Create and Manage ZFS File Systems:help=RtZFSFileSysMngmnt.html
ZFS Storage Management:::Create and Manage ZFS Storage Pools:help=RtZFSStorageMngmnt.html
Zone Management:::Zones Virtual Application Environment Administration:help=RtZoneMngmnt.html
diff --git a/usr/src/lib/libshare/Makefile b/usr/src/lib/libshare/Makefile
index fb19160065..0c333e4459 100644
--- a/usr/src/lib/libshare/Makefile
+++ b/usr/src/lib/libshare/Makefile
@@ -35,7 +35,7 @@ $(BUILD64)MACHS += $(MACH64)
# Add plugin module directories here. They need to build after the libshare
# objects are built.
-PLUGINS = nfs
+PLUGINS = smb nfs
$(PLUGINS): $(MACHS)
SUBDIRS = $(MACHS) $(PLUGINS)
diff --git a/usr/src/lib/libshare/common/libshare.c b/usr/src/lib/libshare/common/libshare.c
index 25213e97a4..49104b2abe 100644
--- a/usr/src/lib/libshare/common/libshare.c
+++ b/usr/src/lib/libshare/common/libshare.c
@@ -58,6 +58,16 @@
#define SA_STRSIZE 256 /* max string size for names */
/*
+ * internal object type values returned by sa_get_object_type()
+ */
+#define SA_TYPE_UNKNOWN 0
+#define SA_TYPE_GROUP 1
+#define SA_TYPE_SHARE 2
+#define SA_TYPE_RESOURCE 3
+#define SA_TYPE_OPTIONSET 4
+#define SA_TYPE_ALTSPACE 5
+
+/*
* internal data structures
*/
@@ -69,16 +79,20 @@ extern int gettransients(sa_handle_impl_t, xmlNodePtr *);
extern int sa_valid_property(void *, char *, sa_property_t);
extern char *sa_fstype(char *);
extern int sa_is_share(void *);
+extern int sa_is_resource(void *);
extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
extern int sa_group_is_zfs(sa_group_t);
extern int sa_path_is_zfs(char *);
extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
+extern int sa_zfs_set_sharesmb(sa_group_t, char *, int);
extern void update_legacy_config(sa_handle_t);
extern int issubdir(char *, char *);
extern int sa_zfs_init(sa_handle_impl_t);
extern void sa_zfs_fini(sa_handle_impl_t);
extern void sablocksigs(sigset_t *);
extern void saunblocksigs(sigset_t *);
+static sa_group_t sa_get_optionset_parent(sa_optionset_t);
+static char *get_node_attr(void *, char *);
/*
* Data structures for finding/managing the document root to access
@@ -188,6 +202,21 @@ sa_errorstr(int err)
case SA_NOT_SHARED:
ret = dgettext(TEXT_DOMAIN, "not shared");
break;
+ case SA_NO_SUCH_RESOURCE:
+ ret = dgettext(TEXT_DOMAIN, "no such resource");
+ break;
+ case SA_RESOURCE_REQUIRED:
+ ret = dgettext(TEXT_DOMAIN, "resource name required");
+ break;
+ case SA_MULTIPLE_ERROR:
+ ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols");
+ break;
+ case SA_PATH_IS_SUBDIR:
+ ret = dgettext(TEXT_DOMAIN, "path is a subpath of share");
+ break;
+ case SA_PATH_IS_PARENTDIR:
+ ret = dgettext(TEXT_DOMAIN, "path is parent of a share");
+ break;
default:
(void) snprintf(errstr, sizeof (errstr),
dgettext(TEXT_DOMAIN, "unknown %d"), err);
@@ -376,6 +405,36 @@ is_shared(sa_share_t share)
}
/*
+ * excluded_protocol(share, proto)
+ *
+ * Returns B_TRUE if the specified protocol appears in the "exclude"
+ * property. This is used to prevent sharing special case shares
+ * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is
+ * returned if the protocol isn't in the list.
+ */
+static boolean_t
+excluded_protocol(sa_share_t share, char *proto)
+{
+ char *protolist;
+ char *str;
+ char *token;
+
+ protolist = sa_get_share_attr(share, "exclude");
+ if (protolist != NULL) {
+ str = protolist;
+ while ((token = strtok(str, ",")) != NULL) {
+ if (strcmp(token, proto) == 0) {
+ sa_free_attr_string(protolist);
+ return (B_TRUE);
+ }
+ str = NULL;
+ }
+ sa_free_attr_string(protolist);
+ }
+ return (B_FALSE);
+}
+
+/*
* checksubdirgroup(group, newpath, strictness)
*
* check all the specified newpath against all the paths in the
@@ -392,6 +451,11 @@ checksubdirgroup(sa_group_t group, char *newpath, int strictness)
sa_share_t share;
char *path;
int issub = SA_OK;
+ int subdir;
+ int parent;
+
+ if (newpath == NULL)
+ return (SA_INVALID_PATH);
for (share = sa_get_share(group, NULL); share != NULL;
share = sa_get_next_share(share)) {
@@ -417,13 +481,18 @@ checksubdirgroup(sa_group_t group, char *newpath, int strictness)
*/
if (path == NULL)
continue;
- if (newpath != NULL &&
- (strcmp(path, newpath) == 0 || issubdir(newpath, path) ||
- issubdir(path, newpath))) {
- sa_free_attr_string(path);
- path = NULL;
+
+ if (strcmp(path, newpath) == 0) {
issub = SA_INVALID_PATH;
- break;
+ } else {
+ subdir = issubdir(newpath, path);
+ parent = issubdir(path, newpath);
+ if (subdir || parent) {
+ sa_free_attr_string(path);
+ path = NULL;
+ return (subdir ?
+ SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR);
+ }
}
sa_free_attr_string(path);
path = NULL;
@@ -446,15 +515,16 @@ static int
checksubdir(sa_handle_t handle, char *newpath, int strictness)
{
sa_group_t group;
- int issub;
+ int issub = SA_OK;
char *path = NULL;
- for (issub = 0, group = sa_get_group(handle, NULL);
- group != NULL && !issub; group = sa_get_next_group(group)) {
+ for (group = sa_get_group(handle, NULL);
+ group != NULL && issub == SA_OK;
+ group = sa_get_next_group(group)) {
if (sa_group_is_zfs(group)) {
sa_group_t subgroup;
for (subgroup = sa_get_sub_group(group);
- subgroup != NULL && !issub;
+ subgroup != NULL && issub == SA_OK;
subgroup = sa_get_next_group(subgroup))
issub = checksubdirgroup(subgroup, newpath,
strictness);
@@ -517,14 +587,17 @@ validpath(sa_handle_t handle, char *path, int strictness)
/*
* check to see if group/share is persistent.
+ *
+ * "group" can be either an sa_group_t or an sa_share_t. (void *)
+ * works since both thse types are also void *.
*/
-static int
-is_persistent(sa_group_t group)
+int
+sa_is_persistent(void *group)
{
char *type;
int persist = 1;
- type = sa_get_group_attr(group, "type");
+ type = sa_get_group_attr((sa_group_t)group, "type");
if (type != NULL && strcmp(type, "transient") == 0)
persist = 0;
if (type != NULL)
@@ -590,6 +663,35 @@ is_zfs_group(sa_group_t group)
}
/*
+ * sa_get_object_type(object)
+ *
+ * This function returns a numeric value representing the object
+ * type. This allows using simpler checks when doing type specific
+ * operations.
+ */
+
+static int
+sa_get_object_type(void *object)
+{
+ xmlNodePtr node = (xmlNodePtr)object;
+ int type;
+
+ if (xmlStrcmp(node->name, (xmlChar *)"group") == 0)
+ type = SA_TYPE_GROUP;
+ else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0)
+ type = SA_TYPE_SHARE;
+ else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
+ type = SA_TYPE_RESOURCE;
+ else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0)
+ type = SA_TYPE_OPTIONSET;
+ else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0)
+ type = SA_TYPE_ALTSPACE;
+ else
+ assert(0);
+ return (type);
+}
+
+/*
* sa_optionset_name(optionset, oname, len, id)
* return the SMF name for the optionset. If id is not NULL, it
* will have the GUID value for a share and should be used
@@ -605,15 +707,34 @@ static int
sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
{
char *proto;
+ void *parent;
+ int ptype;
if (id == NULL)
id = "optionset";
- proto = sa_get_optionset_attr(optionset, "type");
- len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default");
+ parent = sa_get_optionset_parent(optionset);
+ if (parent != NULL) {
+ ptype = sa_get_object_type(parent);
+ proto = sa_get_optionset_attr(optionset, "type");
+ if (ptype != SA_TYPE_RESOURCE) {
+ len = snprintf(oname, len, "%s_%s", id,
+ proto ? proto : "default");
+ } else {
+ char *index;
+ index = get_node_attr((void *)parent, "id");
+ if (index != NULL)
+ len = snprintf(oname, len, "%s_%s_%s", id,
+ proto ? proto : "default", index);
+ else
+ len = 0;
+ }
- if (proto != NULL)
- sa_free_attr_string(proto);
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ } else {
+ len = 0;
+ }
return (len);
}
@@ -660,6 +781,7 @@ verifydefgroupopts(sa_handle_t handle)
{
sa_group_t defgrp;
sa_optionset_t opt;
+
defgrp = sa_get_group(handle, "default");
if (defgrp != NULL) {
opt = sa_get_optionset(defgrp, NULL);
@@ -1187,7 +1309,7 @@ sa_find_share(sa_handle_t handle, char *sharepath)
/*
* sa_check_path(group, path, strictness)
*
- * check that path is a valid path relative to the group. Currently,
+ * Check that path is a valid path relative to the group. Currently,
* we are ignoring the group and checking only the NFS rules. Later,
* we may want to use the group to then check against the protocols
* enabled on the group. The strictness values mean:
@@ -1206,16 +1328,84 @@ sa_check_path(sa_group_t group, char *path, int strictness)
}
/*
- * _sa_add_share(group, sharepath, persist, *error)
+ * mark_excluded_protos(group, share, flags)
+ *
+ * Walk through all the protocols enabled for the group and check to
+ * see if the share has any of them should be in the exclude list
+ * based on the featureset of the protocol. If there are any, add the
+ * "exclude" property to the share.
+ */
+static void
+mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags)
+{
+ sa_optionset_t optionset;
+ char exclude_list[SA_STRSIZE];
+ char *sep = "";
+
+ exclude_list[0] = '\0';
+ for (optionset = sa_get_optionset(group, NULL);
+ optionset != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+ char *value;
+ uint64_t features;
+ value = sa_get_optionset_attr(optionset, "type");
+ if (value == NULL)
+ continue;
+ features = sa_proto_get_featureset(value);
+ sa_free_attr_string(value);
+ if (!(features & flags)) {
+ (void) strlcat(exclude_list, sep,
+ sizeof (exclude_list));
+ (void) strlcat(exclude_list, value,
+ sizeof (exclude_list));
+ sep = ",";
+ }
+ }
+ if (exclude_list[0] != '\0')
+ xmlSetProp(share, (xmlChar *)"exclude",
+ (xmlChar *)exclude_list);
+}
+
+/*
+ * get_all_features(group)
*
- * common code for all types of add_share. sa_add_share() is the
+ * Walk through all the protocols on the group and collect all
+ * possible enabled features. This is the OR of all the featuresets.
+ */
+static uint64_t
+get_all_features(sa_group_t group)
+{
+ sa_optionset_t optionset;
+ uint64_t features = 0;
+
+ for (optionset = sa_get_optionset(group, NULL);
+ optionset != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+ char *value;
+ value = sa_get_optionset_attr(optionset, "type");
+ if (value == NULL)
+ continue;
+ features |= sa_proto_get_featureset(value);
+ sa_free_attr_string(value);
+ }
+ return (features);
+}
+
+
+/*
+ * _sa_add_share(group, sharepath, persist, *error, flags)
+ *
+ * Common code for all types of add_share. sa_add_share() is the
* public API, we also need to be able to do this when parsing legacy
* files and construction of the internal configuration while
- * extracting config info from SMF.
+ * extracting config info from SMF. "flags" indicates if some
+ * protocols need relaxed rules while other don't. These values are
+ * the featureset values defined in libshare.h.
*/
sa_share_t
-_sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
+_sa_add_share(sa_group_t group, char *sharepath, int persist, int *error,
+ uint64_t flags)
{
xmlNodePtr node = NULL;
int err;
@@ -1223,52 +1413,60 @@ _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
err = SA_OK; /* assume success */
node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
- if (node != NULL) {
- xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
- xmlSetProp(node, (xmlChar *)"type",
- persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
- if (persist != SA_SHARE_TRANSIENT) {
- /*
- * persistent shares come in two flavors: SMF and
- * ZFS. Sort this one out based on target group and
- * path type. Currently, only NFS is supported in the
- * ZFS group and it is always on.
- */
- if (sa_group_is_zfs(group) &&
- sa_path_is_zfs(sharepath)) {
+ if (node == NULL) {
+ if (error != NULL)
+ *error = SA_NO_MEMORY;
+ return (node);
+ }
+
+ xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
+ xmlSetProp(node, (xmlChar *)"type",
+ persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
+ if (flags != 0)
+ mark_excluded_protos(group, node, flags);
+ if (persist != SA_SHARE_TRANSIENT) {
+ /*
+ * persistent shares come in two flavors: SMF and
+ * ZFS. Sort this one out based on target group and
+ * path type. Both NFS and SMB are supported. First,
+ * check to see if the protocol is enabled on the
+ * subgroup and then setup the share appropriately.
+ */
+ if (sa_group_is_zfs(group) &&
+ sa_path_is_zfs(sharepath)) {
+ if (sa_get_optionset(group, "nfs") != NULL)
err = sa_zfs_set_sharenfs(group, sharepath, 1);
+ else if (sa_get_optionset(group, "smb") != NULL)
+ err = sa_zfs_set_sharesmb(group, sharepath, 1);
+ } else {
+ sa_handle_impl_t impl_handle;
+ impl_handle =
+ (sa_handle_impl_t)sa_find_group_handle(group);
+ if (impl_handle != NULL) {
+ err = sa_commit_share(impl_handle->scfhandle,
+ group, (sa_share_t)node);
} else {
- sa_handle_impl_t impl_handle;
- impl_handle =
- (sa_handle_impl_t)sa_find_group_handle(
- group);
- if (impl_handle != NULL) {
- err = sa_commit_share(
- impl_handle->scfhandle, group,
- (sa_share_t)node);
- } else {
- err = SA_SYSTEM_ERR;
- }
+ err = SA_SYSTEM_ERR;
}
}
- if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) {
- /* called by the dfstab parser so could be a show */
- err = SA_OK;
- }
- if (err != SA_OK) {
- /*
- * we couldn't commit to the repository so undo
- * our internal state to reflect reality.
- */
- xmlUnlinkNode(node);
- xmlFreeNode(node);
- node = NULL;
- }
- } else {
- err = SA_NO_MEMORY;
}
+ if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER)
+ /* called by the dfstab parser so could be a show */
+ err = SA_OK;
+
+ if (err != SA_OK) {
+ /*
+ * we couldn't commit to the repository so undo
+ * our internal state to reflect reality.
+ */
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ node = NULL;
+ }
+
if (error != NULL)
*error = err;
+
return (node);
}
@@ -1285,9 +1483,10 @@ sa_share_t
sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
{
xmlNodePtr node = NULL;
- sa_share_t dup;
int strictness = SA_CHECK_NORMAL;
sa_handle_t handle;
+ uint64_t special = 0;
+ uint64_t features;
/*
* If the share is to be permanent, use strict checking so a
@@ -1304,12 +1503,33 @@ sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
handle = sa_find_group_handle(group);
- if ((dup = sa_find_share(handle, sharepath)) == NULL &&
- (*error = sa_check_path(group, sharepath, strictness)) == SA_OK) {
- node = _sa_add_share(group, sharepath, persist, error);
- }
- if (dup != NULL)
+ /*
+ * need to determine if the share is valid. The rules are:
+ * - The path must not already exist
+ * - The path must not be a subdir or parent dir of an
+ * existing path unless at least one protocol allows it.
+ * The sub/parent check is done in sa_check_path().
+ */
+
+ if (sa_find_share(handle, sharepath) == NULL) {
+ *error = sa_check_path(group, sharepath, strictness);
+ features = get_all_features(group);
+ switch (*error) {
+ case SA_PATH_IS_SUBDIR:
+ if (features & SA_FEATURE_ALLOWSUBDIRS)
+ special |= SA_FEATURE_ALLOWSUBDIRS;
+ break;
+ case SA_PATH_IS_PARENTDIR:
+ if (features & SA_FEATURE_ALLOWPARDIRS)
+ special |= SA_FEATURE_ALLOWPARDIRS;
+ break;
+ }
+ if (*error == SA_OK || special != SA_FEATURE_NONE)
+ node = _sa_add_share(group, sharepath, persist,
+ error, special);
+ } else {
*error = SA_DUPLICATE_NAME;
+ }
return ((sa_share_t)node);
}
@@ -1324,28 +1544,52 @@ sa_enable_share(sa_share_t share, char *protocol)
{
char *sharepath;
struct stat st;
- int err = 0;
+ int err = SA_OK;
+ int ret;
sharepath = sa_get_share_attr(share, "path");
+ if (sharepath == NULL)
+ return (SA_NO_MEMORY);
if (stat(sharepath, &st) < 0) {
err = SA_NO_SUCH_PATH;
} else {
/* tell the server about the share */
if (protocol != NULL) {
+ if (excluded_protocol(share, protocol))
+ goto done;
+
/* lookup protocol specific handler */
err = sa_proto_share(protocol, share);
if (err == SA_OK)
- (void) sa_set_share_attr(share, "shared",
- "true");
+ (void) sa_set_share_attr(share,
+ "shared", "true");
} else {
- /*
- * Tell all protocols. Only NFS for now but
- * SMB is coming.
- */
- err = sa_proto_share("nfs", share);
+ /* Tell all protocols about the share */
+ sa_group_t group;
+ sa_optionset_t optionset;
+
+ group = sa_get_parent_group(share);
+
+ for (optionset = sa_get_optionset(group, NULL);
+ optionset != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+ char *proto;
+ proto = sa_get_optionset_attr(optionset,
+ "type");
+ if (proto != NULL) {
+ if (!excluded_protocol(share, proto)) {
+ ret = sa_proto_share(proto,
+ share);
+ if (ret != SA_OK)
+ err = ret;
+ }
+ sa_free_attr_string(proto);
+ }
+ }
(void) sa_set_share_attr(share, "shared", "true");
}
}
+done:
if (sharepath != NULL)
sa_free_attr_string(sharepath);
return (err);
@@ -1353,31 +1597,47 @@ sa_enable_share(sa_share_t share, char *protocol)
/*
* sa_disable_share(share, protocol)
- * Disable the specified share to the specified protocol.
- * If protocol is NULL, then all protocols.
+ * Disable the specified share to the specified protocol. If
+ * protocol is NULL, then all protocols that are enabled for the
+ * share should be disabled.
*/
int
sa_disable_share(sa_share_t share, char *protocol)
{
char *path;
- char *shared;
+ int err = SA_OK;
int ret = SA_OK;
path = sa_get_share_attr(share, "path");
- shared = sa_get_share_attr(share, "shared");
if (protocol != NULL) {
ret = sa_proto_unshare(share, protocol, path);
} else {
/* need to do all protocols */
- ret = sa_proto_unshare(share, "nfs", path);
+ sa_group_t group;
+ sa_optionset_t optionset;
+
+ group = sa_get_parent_group(share);
+
+ /* Tell all protocols about the share */
+ for (optionset = sa_get_optionset(group, NULL);
+ optionset != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+ char *proto;
+
+ proto = sa_get_optionset_attr(optionset, "type");
+ if (proto != NULL) {
+ err = sa_proto_unshare(share, proto, path);
+ if (err != SA_OK)
+ ret = err;
+ sa_free_attr_string(proto);
+ }
+ }
}
if (ret == SA_OK)
(void) sa_set_share_attr(share, "shared", NULL);
if (path != NULL)
sa_free_attr_string(path);
- if (shared != NULL)
- sa_free_attr_string(shared);
return (ret);
}
@@ -1415,7 +1675,7 @@ sa_remove_share(sa_share_t share)
/* only do SMF action if permanent */
if (!transient || zfs != NULL) {
/* remove from legacy dfstab as well as possible SMF */
- ret = sa_delete_legacy(share);
+ ret = sa_delete_legacy(share, NULL);
if (ret == SA_OK) {
if (!sa_group_is_zfs(group)) {
sa_handle_impl_t impl_handle;
@@ -1497,7 +1757,7 @@ sa_move_share(sa_group_t group, sa_share_t share)
/*
* sa_get_parent_group(share)
*
- * Return the containg group for the share. If a group was actually
+ * Return the containing group for the share. If a group was actually
* passed in, we don't want a parent so return NULL.
*/
@@ -1728,7 +1988,7 @@ sa_update_config(sa_handle_t handle)
/*
* get_node_attr(node, tag)
*
- * Get the speficied tag(attribute) if it exists on the node. This is
+ * Get the specified tag(attribute) if it exists on the node. This is
* used internally by a number of attribute oriented functions.
*/
@@ -1746,7 +2006,7 @@ get_node_attr(void *nodehdl, char *tag)
/*
* get_node_attr(node, tag)
*
- * Set the speficied tag(attribute) to the specified value This is
+ * Set the specified tag(attribute) to the specified value This is
* used internally by a number of attribute oriented functions. It
* doesn't update the repository, only the internal document state.
*/
@@ -1792,6 +2052,14 @@ sa_set_group_attr(sa_group_t group, char *tag, char *value)
char *groupname;
sa_handle_impl_t impl_handle;
+ /*
+ * ZFS group/subgroup doesn't need the handle so shortcut.
+ */
+ if (sa_group_is_zfs(group)) {
+ set_node_attr((void *)group, tag, value);
+ return (SA_OK);
+ }
+
impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
if (impl_handle != NULL) {
groupname = sa_get_group_attr(group, "name");
@@ -1833,47 +2101,15 @@ sa_get_share_attr(sa_share_t share, char *tag)
}
/*
- * sa_get_resource(group, resource)
- *
- * Search all the shares in the speified group for a share with a
- * resource name matching the one specified.
- *
- * In the future, it may be advantageous to allow group to be NULL and
- * search all groups but that isn't needed at present.
- */
-
-sa_share_t
-sa_get_resource(sa_group_t group, char *resource)
-{
- sa_share_t share = NULL;
- char *name = NULL;
-
- if (resource != NULL) {
- for (share = sa_get_share(group, NULL); share != NULL;
- share = sa_get_next_share(share)) {
- name = sa_get_share_attr(share, "resource");
- if (name != NULL) {
- if (strcmp(name, resource) == 0)
- break;
- sa_free_attr_string(name);
- name = NULL;
- }
- }
- if (name != NULL)
- sa_free_attr_string(name);
- }
- return ((sa_share_t)share);
-}
-
-/*
* _sa_set_share_description(share, description)
*
- * Add a description tag with text contents to the specified share.
- * A separate XML tag is used rather than a property.
+ * Add a description tag with text contents to the specified share. A
+ * separate XML tag is used rather than a property. This can also be
+ * used with resources.
*/
xmlNodePtr
-_sa_set_share_description(sa_share_t share, char *content)
+_sa_set_share_description(void *share, char *content)
{
xmlNodePtr node;
node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
@@ -2205,7 +2441,6 @@ sa_set_share_description(sa_share_t share, char *content)
break;
}
}
- group = sa_get_parent_group(share);
/* no existing description but want to add */
if (node == NULL && content != NULL) {
/* add a description */
@@ -2218,7 +2453,8 @@ sa_set_share_description(sa_share_t share, char *content)
xmlUnlinkNode(node);
xmlFreeNode(node);
}
- if (group != NULL && is_persistent((sa_group_t)share)) {
+ group = sa_get_parent_group(share);
+ if (group != NULL && sa_is_persistent(share)) {
sa_handle_impl_t impl_handle;
impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
if (impl_handle != NULL) {
@@ -2269,7 +2505,7 @@ sa_get_share_description(sa_share_t share)
}
}
if (node != NULL) {
- description = xmlNodeGetContent((xmlNodePtr)share);
+ description = xmlNodeGetContent(node);
fixproblemchars((char *)description);
}
return ((char *)description);
@@ -2299,12 +2535,44 @@ sa_create_optionset(sa_group_t group, char *proto)
{
sa_optionset_t optionset;
sa_group_t parent = group;
+ sa_share_t share = NULL;
+ int err = SA_OK;
+ char *id = NULL;
optionset = sa_get_optionset(group, proto);
if (optionset != NULL) {
/* can't have a duplicate protocol */
optionset = NULL;
} else {
+ /*
+ * Account for resource names being slightly
+ * different.
+ */
+ if (sa_is_share(group)) {
+ /*
+ * Transient shares do not have an "id" so not an
+ * error to not find one.
+ */
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ } else if (sa_is_resource(group)) {
+ share = sa_get_resource_parent(
+ (sa_resource_t)group);
+ id = sa_get_resource_attr(share, "id");
+
+ /* id can be NULL if the group is transient (ZFS) */
+ if (id == NULL && sa_is_persistent(group))
+ err = SA_NO_MEMORY;
+ }
+ if (err == SA_NO_MEMORY) {
+ /*
+ * Couldn't get the id for the share or
+ * resource. While this could be a
+ * configuration issue, it is most likely an
+ * out of memory. In any case, fail the create.
+ */
+ return (NULL);
+ }
+
optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
NULL, (xmlChar *)"optionset", NULL);
/*
@@ -2314,38 +2582,44 @@ sa_create_optionset(sa_group_t group, char *proto)
if (optionset != NULL) {
char oname[SA_STRSIZE];
char *groupname;
- char *id = NULL;
- if (sa_is_share(group))
+ /*
+ * Need to get parent group in all cases, but also get
+ * the share if this is a resource.
+ */
+ if (sa_is_share(group)) {
parent = sa_get_parent_group((sa_share_t)group);
+ } else if (sa_is_resource(group)) {
+ share = sa_get_resource_parent(
+ (sa_resource_t)group);
+ parent = sa_get_parent_group(share);
+ }
sa_set_optionset_attr(optionset, "type", proto);
- if (sa_is_share(group)) {
- id = sa_get_share_attr((sa_share_t)group, "id");
- }
(void) sa_optionset_name(optionset, oname,
sizeof (oname), id);
groupname = sa_get_group_attr(parent, "name");
- if (groupname != NULL && is_persistent(group)) {
+ if (groupname != NULL && sa_is_persistent(group)) {
sa_handle_impl_t impl_handle;
- impl_handle = (sa_handle_impl_t)
- sa_find_group_handle(group);
+ impl_handle =
+ (sa_handle_impl_t)sa_find_group_handle(
+ group);
assert(impl_handle != NULL);
if (impl_handle != NULL) {
(void) sa_get_instance(
- impl_handle->scfhandle,
- groupname);
+ impl_handle->scfhandle, groupname);
(void) sa_create_pgroup(
impl_handle->scfhandle, oname);
}
}
if (groupname != NULL)
sa_free_attr_string(groupname);
- if (id != NULL)
- sa_free_attr_string(id);
}
}
+
+ if (id != NULL)
+ sa_free_attr_string(id);
return (optionset);
}
@@ -2388,7 +2662,7 @@ sa_get_optionset_parent(sa_optionset_t optionset)
*
* In order to avoid making multiple updates to a ZFS share when
* setting properties, the share attribute "changed" will be set to
- * true when a property is added or modifed. When done adding
+ * true when a property is added or modified. When done adding
* properties, we can then detect that an update is needed. We then
* clear the state here to detect additional changes.
*/
@@ -2470,7 +2744,7 @@ sa_commit_properties(sa_optionset_t optionset, int clear)
/*
* sa_destroy_optionset(optionset)
*
- * Remove the optionset from its group. Update the repostory to
+ * Remove the optionset from its group. Update the repository to
* reflect this change.
*/
@@ -2486,9 +2760,16 @@ sa_destroy_optionset(sa_optionset_t optionset)
/* now delete the prop group */
group = sa_get_optionset_parent(optionset);
- if (group != NULL && sa_is_share(group)) {
- ispersist = is_persistent(group);
- id = sa_get_share_attr((sa_share_t)group, "id");
+ if (group != NULL) {
+ if (sa_is_resource(group)) {
+ sa_resource_t resource = group;
+ sa_share_t share = sa_get_resource_parent(resource);
+ group = sa_get_parent_group(share);
+ id = sa_get_share_attr(share, "id");
+ } else if (sa_is_share(group)) {
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ }
+ ispersist = sa_is_persistent(group);
}
if (ispersist) {
sa_handle_impl_t impl_handle;
@@ -2559,7 +2840,7 @@ sa_create_security(sa_group_t group, char *sectype, char *proto)
sa_set_security_attr(security, "sectype", sectype);
(void) sa_security_name(security, oname,
sizeof (oname), id);
- if (groupname != NULL && is_persistent(group)) {
+ if (groupname != NULL && sa_is_persistent(group)) {
sa_handle_impl_t impl_handle;
impl_handle =
(sa_handle_impl_t)sa_find_group_handle(
@@ -2603,7 +2884,7 @@ sa_destroy_security(sa_security_t security)
if (group != NULL && !iszfs) {
if (sa_is_share(group))
- ispersist = is_persistent(group);
+ ispersist = sa_is_persistent(group);
id = sa_get_share_attr((sa_share_t)group, "id");
}
if (ispersist) {
@@ -2666,7 +2947,6 @@ is_nodetype(void *node, char *type)
return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
}
-
/*
* add_or_update()
*
@@ -2721,12 +3001,12 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
int opttype; /* 1 == optionset, 0 == security */
char *id = NULL;
int iszfs = 0;
- int isshare = 0;
sa_group_t parent = NULL;
+ sa_share_t share = NULL;
sa_handle_impl_t impl_handle;
scfutilhandle_t *scf_handle;
- if (!is_persistent(group)) {
+ if (!sa_is_persistent(group)) {
/*
* if the group/share is not persistent we don't need
* to do anything here
@@ -2742,12 +3022,20 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
entry = scf_entry_create(scf_handle->handle);
opttype = is_nodetype((void *)optionset, "optionset");
+ /*
+ * Check for share vs. resource since they need slightly
+ * different treatment given the hierarchy.
+ */
if (valstr != NULL && entry != NULL) {
if (sa_is_share(group)) {
- isshare = 1;
parent = sa_get_parent_group(group);
+ share = (sa_share_t)group;
if (parent != NULL)
iszfs = is_zfs_group(parent);
+ } else if (sa_is_resource(group)) {
+ share = sa_get_parent_group(group);
+ if (share != NULL)
+ parent = sa_get_parent_group(share);
} else {
iszfs = is_zfs_group(group);
}
@@ -2755,15 +3043,13 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
if (scf_handle->trans == NULL) {
char oname[SA_STRSIZE];
char *groupname = NULL;
- if (isshare) {
- if (parent != NULL) {
+ if (share != NULL) {
+ if (parent != NULL)
groupname =
sa_get_group_attr(parent,
"name");
- }
- id =
- sa_get_share_attr((sa_share_t)group,
- "id");
+ id = sa_get_share_attr(
+ (sa_share_t)share, "id");
} else {
groupname = sa_get_group_attr(group,
"name");
@@ -2870,14 +3156,22 @@ sa_add_property(void *object, sa_property_t property)
sa_free_attr_string(proto);
parent = sa_get_parent_group(object);
- if (!is_persistent(parent)) {
+ if (!sa_is_persistent(parent))
return (ret);
- }
- if (sa_is_share(parent))
+ if (sa_is_resource(parent)) {
+ /*
+ * Resources are children of share. Need to go up two
+ * levels to find the group but the parent needs to be
+ * the share at this point in order to get the "id".
+ */
+ parent = sa_get_parent_group(parent);
group = sa_get_parent_group(parent);
- else
+ } else if (sa_is_share(parent)) {
+ group = sa_get_parent_group(parent);
+ } else {
group = parent;
+ }
if (property == NULL) {
ret = SA_NO_MEMORY;
@@ -3110,7 +3404,7 @@ sa_set_protocol_property(sa_property_t prop, char *value)
/*
* sa_add_protocol_property(propset, prop)
*
- * Add a new property to the protocol sepcific property set.
+ * Add a new property to the protocol specific property set.
*/
int
@@ -3128,7 +3422,7 @@ sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
/*
* sa_create_protocol_properties(proto)
*
- * Create a protocol specifity property set.
+ * Create a protocol specific property set.
*/
sa_protocol_properties_t
@@ -3141,3 +3435,583 @@ sa_create_protocol_properties(char *proto)
xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
return (node);
}
+
+/*
+ * sa_get_share_resource(share, resource)
+ *
+ * Get the named resource from the share, if it exists. If resource is
+ * NULL, get the first resource.
+ */
+
+sa_resource_t
+sa_get_share_resource(sa_share_t share, char *resource)
+{
+ xmlNodePtr node = NULL;
+ xmlChar *name;
+
+ if (share != NULL) {
+ for (node = ((xmlNodePtr)share)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) {
+ if (resource == NULL) {
+ /*
+ * We are looking for the first
+ * resource node and not a names
+ * resource.
+ */
+ break;
+ } else {
+ /* is it the correct share? */
+ name = xmlGetProp(node,
+ (xmlChar *)"name");
+ if (name != NULL &&
+ xmlStrcasecmp(name,
+ (xmlChar *)resource) == 0) {
+ xmlFree(name);
+ break;
+ }
+ xmlFree(name);
+ }
+ }
+ }
+ }
+ return ((sa_resource_t)node);
+}
+
+/*
+ * sa_get_next_resource(resource)
+ * Return the next share following the specified share
+ * from the internal list of shares. Returns NULL if there
+ * are no more shares. The list is relative to the same
+ * group.
+ */
+sa_share_t
+sa_get_next_resource(sa_resource_t resource)
+{
+ xmlNodePtr node = NULL;
+
+ if (resource != NULL) {
+ for (node = ((xmlNodePtr)resource)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
+ break;
+ }
+ }
+ return ((sa_share_t)node);
+}
+
+/*
+ * _sa_get_next_resource_index(share)
+ *
+ * get the next resource index number (one greater then current largest)
+ */
+
+static int
+_sa_get_next_resource_index(sa_share_t share)
+{
+ sa_resource_t resource;
+ int index = 0;
+ char *id;
+
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource)) {
+ id = get_node_attr((void *)resource, "id");
+ if (id != NULL) {
+ int val;
+ val = atoi(id);
+ if (val > index)
+ index = val;
+ sa_free_attr_string(id);
+ }
+ }
+ return (index + 1);
+}
+
+
+/*
+ * sa_add_resource(share, resource, persist, &err)
+ *
+ * Adds a new resource name associated with share. The resource name
+ * must be unique in the system and will be case insensitive (eventually).
+ */
+
+sa_resource_t
+sa_add_resource(sa_share_t share, char *resource, int persist, int *error)
+{
+ xmlNodePtr node;
+ int err = SA_OK;
+ sa_resource_t res;
+ sa_group_t group;
+ sa_handle_t handle;
+ char istring[8]; /* just big enough for an integer value */
+ int index;
+
+ group = sa_get_parent_group(share);
+ handle = sa_find_group_handle(group);
+ res = sa_find_resource(handle, resource);
+ if (res != NULL) {
+ err = SA_DUPLICATE_NAME;
+ res = NULL;
+ } else {
+ node = xmlNewChild((xmlNodePtr)share, NULL,
+ (xmlChar *)"resource", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name",
+ (xmlChar *)resource);
+ xmlSetProp(node, (xmlChar *)"type", persist ?
+ (xmlChar *)"persist" : (xmlChar *)"transient");
+ if (persist != SA_SHARE_TRANSIENT) {
+ index = _sa_get_next_resource_index(share);
+ (void) snprintf(istring, sizeof (istring), "%d",
+ index);
+ xmlSetProp(node, (xmlChar *)"id",
+ (xmlChar *)istring);
+ if (!sa_group_is_zfs(group) &&
+ sa_is_persistent((sa_group_t)share)) {
+ /* ZFS doesn't use resource names */
+ sa_handle_impl_t ihandle;
+ ihandle = (sa_handle_impl_t)
+ sa_find_group_handle(
+ group);
+ if (ihandle != NULL)
+ err = sa_commit_share(
+ ihandle->scfhandle, group,
+ share);
+ else
+ err = SA_SYSTEM_ERR;
+ }
+ }
+ }
+ }
+ if (error != NULL)
+ *error = err;
+ return ((sa_resource_t)node);
+}
+
+/*
+ * sa_remove_resource(resource)
+ *
+ * Remove the resource name from the share (and the system)
+ */
+
+int
+sa_remove_resource(sa_resource_t resource)
+{
+ sa_share_t share;
+ sa_group_t group;
+ char *type;
+ int ret = SA_OK;
+ int transient = 0;
+
+ share = sa_get_resource_parent(resource);
+ type = sa_get_share_attr(share, "type");
+ group = sa_get_parent_group(share);
+
+
+ if (type != NULL) {
+ if (strcmp(type, "persist") != 0)
+ transient = 1;
+ sa_free_attr_string(type);
+ }
+
+ /* Remove from the share */
+ xmlUnlinkNode((xmlNode *)resource);
+ xmlFreeNode((xmlNode *)resource);
+
+ /* only do SMF action if permanent and not ZFS */
+ if (!transient && !sa_group_is_zfs(group)) {
+ sa_handle_impl_t ihandle;
+ ihandle = (sa_handle_impl_t)sa_find_group_handle(group);
+ if (ihandle != NULL)
+ ret = sa_commit_share(ihandle->scfhandle, group, share);
+ else
+ ret = SA_SYSTEM_ERR;
+ }
+ return (ret);
+}
+
+/*
+ * proto_resource_rename(handle, group, resource, newname)
+ *
+ * Helper function for sa_rename_resource that notifies the protocol
+ * of a resource name change prior to a config repository update.
+ */
+static int
+proto_rename_resource(sa_handle_t handle, sa_group_t group,
+ sa_resource_t resource, char *newname)
+{
+ sa_optionset_t optionset;
+ int ret = SA_OK;
+ int err;
+
+ for (optionset = sa_get_optionset(group, NULL);
+ optionset != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+ char *type;
+ type = sa_get_optionset_attr(optionset, "type");
+ if (type != NULL) {
+ err = sa_proto_rename_resource(handle, type, resource,
+ newname);
+ if (err != SA_OK)
+ ret = err;
+ sa_free_attr_string(type);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_rename_resource(resource, newname)
+ *
+ * Rename the resource to the new name, if it is unique.
+ */
+
+int
+sa_rename_resource(sa_resource_t resource, char *newname)
+{
+ sa_share_t share;
+ sa_group_t group = NULL;
+ sa_resource_t target;
+ int ret = SA_CONFIG_ERR;
+ sa_handle_t handle = NULL;
+
+ share = sa_get_resource_parent(resource);
+ if (share == NULL)
+ return (ret);
+
+ group = sa_get_parent_group(share);
+ if (group == NULL)
+ return (ret);
+
+ handle = (sa_handle_impl_t)sa_find_group_handle(group);
+ if (handle == NULL)
+ return (ret);
+
+ target = sa_find_resource(handle, newname);
+ if (target != NULL) {
+ ret = SA_DUPLICATE_NAME;
+ } else {
+ /*
+ * Everything appears to be valid at this
+ * point. Change the name of the active share and then
+ * update the share in the appropriate repository.
+ */
+ ret = proto_rename_resource(handle, group, resource, newname);
+ set_node_attr(resource, "name", newname);
+ if (!sa_group_is_zfs(group) &&
+ sa_is_persistent((sa_group_t)share)) {
+ sa_handle_impl_t ihandle = (sa_handle_impl_t)handle;
+ ret = sa_commit_share(ihandle->scfhandle, group,
+ share);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_get_resource_attr(resource, tag)
+ *
+ * Get the named attribute of the resource. "name" and "id" are
+ * currently defined. NULL if tag not defined.
+ */
+
+char *
+sa_get_resource_attr(sa_resource_t resource, char *tag)
+{
+ return (get_node_attr((void *)resource, tag));
+}
+
+/*
+ * sa_set_resource_attr(resource, tag, value)
+ *
+ * Get the named attribute of the resource. "name" and "id" are
+ * currently defined. NULL if tag not defined. Currently we don't do
+ * much, but additional checking may be needed in the future.
+ */
+
+int
+sa_set_resource_attr(sa_resource_t resource, char *tag, char *value)
+{
+ set_node_attr((void *)resource, tag, value);
+ return (SA_OK);
+}
+
+/*
+ * sa_get_resource_parent(resource_t)
+ *
+ * Returns the share associated with the resource.
+ */
+
+sa_share_t
+sa_get_resource_parent(sa_resource_t resource)
+{
+ sa_share_t share = NULL;
+
+ if (resource != NULL)
+ share = (sa_share_t)((xmlNodePtr)resource)->parent;
+ return (share);
+}
+
+/*
+ * find_resource(group, name)
+ *
+ * Find the resource within the group.
+ */
+
+static sa_resource_t
+find_resource(sa_group_t group, char *resname)
+{
+ sa_share_t share;
+ sa_resource_t resource = NULL;
+ char *name;
+
+ /* Iterate over all the shares and resources in the group. */
+ for (share = sa_get_share(group, NULL);
+ share != NULL && resource == NULL;
+ share = sa_get_next_share(share)) {
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource)) {
+ name = sa_get_resource_attr(resource, "name");
+ if (name != NULL && xmlStrcasecmp((xmlChar*)name,
+ (xmlChar*)resname) == 0) {
+ sa_free_attr_string(name);
+ break;
+ }
+ if (name != NULL) {
+ sa_free_attr_string(name);
+ }
+ }
+ }
+ return (resource);
+}
+
+/*
+ * sa_find_resource(name)
+ *
+ * Find the named resource in the system.
+ */
+
+sa_resource_t
+sa_find_resource(sa_handle_t handle, char *name)
+{
+ sa_group_t group;
+ sa_group_t zgroup;
+ sa_resource_t resource = NULL;
+
+ /*
+ * Iterate over all groups and zfs subgroups and check for
+ * resource name in them.
+ */
+ for (group = sa_get_group(handle, NULL); group != NULL;
+ group = sa_get_next_group(group)) {
+
+ if (is_zfs_group(group)) {
+ for (zgroup =
+ (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
+ (xmlChar *)"group");
+ zgroup != NULL && resource == NULL;
+ zgroup = sa_get_next_group(zgroup)) {
+ resource = find_resource(zgroup, name);
+ }
+ } else {
+ resource = find_resource(group, name);
+ }
+ if (resource != NULL)
+ break;
+ }
+ return (resource);
+}
+
+/*
+ * sa_get_resource(group, resource)
+ *
+ * Search all the shares in the specified group for a share with a
+ * resource name matching the one specified.
+ *
+ * In the future, it may be advantageous to allow group to be NULL and
+ * search all groups but that isn't needed at present.
+ */
+
+sa_resource_t
+sa_get_resource(sa_group_t group, char *resource)
+{
+ sa_share_t share = NULL;
+ sa_resource_t res = NULL;
+
+ if (resource != NULL) {
+ for (share = sa_get_share(group, NULL);
+ share != NULL && res == NULL;
+ share = sa_get_next_share(share)) {
+ res = sa_get_share_resource(share, resource);
+ }
+ }
+ return (res);
+}
+
+/*
+ * sa_enable_resource, protocol)
+ * Disable the specified share to the specified protocol.
+ * If protocol is NULL, then all protocols.
+ */
+int
+sa_enable_resource(sa_resource_t resource, char *protocol)
+{
+ int ret = SA_OK;
+ char **protocols;
+ int numproto;
+
+ if (protocol != NULL) {
+ ret = sa_proto_share_resource(protocol, resource);
+ } else {
+ /* need to do all protocols */
+ if ((numproto = sa_get_protocols(&protocols)) >= 0) {
+ int i, err;
+ for (i = 0; i < numproto; i++) {
+ err = sa_proto_share_resource(
+ protocols[i], resource);
+ if (err != SA_OK)
+ ret = err;
+ }
+ free(protocols);
+ }
+ }
+ if (ret == SA_OK)
+ (void) sa_set_resource_attr(resource, "shared", NULL);
+
+ return (ret);
+}
+
+/*
+ * sa_disable_resource(resource, protocol)
+ *
+ * Disable the specified share for the specified protocol. If
+ * protocol is NULL, then all protocols. If the underlying
+ * protocol doesn't implement disable at the resource level, we
+ * disable at the share level.
+ */
+int
+sa_disable_resource(sa_resource_t resource, char *protocol)
+{
+ int ret = SA_OK;
+ char **protocols;
+ int numproto;
+
+ if (protocol != NULL) {
+ ret = sa_proto_unshare_resource(protocol, resource);
+ if (ret == SA_NOT_IMPLEMENTED) {
+ sa_share_t parent;
+ /*
+ * The protocol doesn't implement unshare
+ * resource. That implies that resource names are
+ * simple aliases for this protocol so we need to
+ * unshare the share.
+ */
+ parent = sa_get_resource_parent(resource);
+ if (parent != NULL)
+ ret = sa_disable_share(parent, protocol);
+ else
+ ret = SA_CONFIG_ERR;
+ }
+ } else {
+ /* need to do all protocols */
+ if ((numproto = sa_get_protocols(&protocols)) >= 0) {
+ int i, err;
+ for (i = 0; i < numproto; i++) {
+ err = sa_proto_unshare_resource(protocols[i],
+ resource);
+ if (err == SA_NOT_SUPPORTED) {
+ sa_share_t parent;
+ parent = sa_get_resource_parent(
+ resource);
+ if (parent != NULL)
+ err = sa_disable_share(parent,
+ protocols[i]);
+ else
+ err = SA_CONFIG_ERR;
+ }
+ if (err != SA_OK)
+ ret = err;
+ }
+ free(protocols);
+ }
+ }
+ if (ret == SA_OK)
+ (void) sa_set_resource_attr(resource, "shared", NULL);
+
+ return (ret);
+}
+
+/*
+ * sa_set_resource_description(resource, content)
+ *
+ * Set the description of share to content.
+ */
+
+int
+sa_set_resource_description(sa_resource_t resource, char *content)
+{
+ xmlNodePtr node;
+ sa_group_t group;
+ sa_share_t share;
+ int ret = SA_OK;
+
+ for (node = ((xmlNodePtr)resource)->children;
+ node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
+ break;
+ }
+ }
+
+ /* no existing description but want to add */
+ if (node == NULL && content != NULL) {
+ /* add a description */
+ node = _sa_set_share_description(resource, content);
+ } else if (node != NULL && content != NULL) {
+ /* update a description */
+ xmlNodeSetContent(node, (xmlChar *)content);
+ } else if (node != NULL && content == NULL) {
+ /* remove an existing description */
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ }
+ share = sa_get_resource_parent(resource);
+ group = sa_get_parent_group(share);
+ if (group != NULL && sa_is_persistent(share)) {
+ sa_handle_impl_t impl_handle;
+ impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
+ if (impl_handle != NULL)
+ ret = sa_commit_share(impl_handle->scfhandle,
+ group, share);
+ else
+ ret = SA_SYSTEM_ERR;
+ }
+ return (ret);
+}
+
+/*
+ * sa_get_resource_description(share)
+ *
+ * Return the description text for the specified share if it
+ * exists. NULL if no description exists.
+ */
+
+char *
+sa_get_resource_description(sa_resource_t resource)
+{
+ xmlChar *description = NULL;
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)resource)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"description") == 0)
+ break;
+ }
+ if (node != NULL) {
+ description = xmlNodeGetContent(node);
+ fixproblemchars((char *)description);
+ }
+ return ((char *)description);
+}
diff --git a/usr/src/lib/libshare/common/libshare.h b/usr/src/lib/libshare/common/libshare.h
index c78b0822c6..bdac6a6d64 100644
--- a/usr/src/lib/libshare/common/libshare.h
+++ b/usr/src/lib/libshare/common/libshare.h
@@ -37,6 +37,8 @@
extern "C" {
#endif
+#include <sys/types.h>
+
/*
* Basic datatypes for most functions
*/
@@ -46,6 +48,7 @@ typedef void *sa_property_t;
typedef void *sa_optionset_t;
typedef void *sa_security_t;
typedef void *sa_protocol_properties_t;
+typedef void *sa_resource_t;
typedef void *sa_handle_t; /* opaque handle to access core functions */
@@ -77,6 +80,11 @@ typedef void *sa_handle_t; /* opaque handle to access core functions */
#define SA_NOT_SUPPORTED 21 /* operation not supported for proto */
#define SA_PROP_SHARE_ONLY 22 /* property valid on share only */
#define SA_NOT_SHARED 23 /* path is not shared */
+#define SA_NO_SUCH_RESOURCE 24 /* resource not found */
+#define SA_RESOURCE_REQUIRED 25 /* resource name is required */
+#define SA_MULTIPLE_ERROR 26 /* multiple protocols reported error */
+#define SA_PATH_IS_SUBDIR 27 /* check_path found path is subdir */
+#define SA_PATH_IS_PARENTDIR 28 /* check_path found path is parent */
/* API Initialization */
#define SA_INIT_SHARE_API 0x0001 /* init share specific interface */
@@ -90,8 +98,9 @@ typedef void *sa_handle_t; /* opaque handle to access core functions */
*/
#define SA_MAX_NAME_LEN 100 /* must fit service instance name */
+#define SA_MAX_RESOURCE_NAME 255 /* Maximum length of resource name */
-/* Used in calls to sa_add_share() */
+/* Used in calls to sa_add_share() and sa_add_resource() */
#define SA_SHARE_TRANSIENT 0 /* shared but not across reboot */
#define SA_SHARE_LEGACY 1 /* share is in dfstab only */
#define SA_SHARE_PERMANENT 2 /* share goes to repository */
@@ -105,6 +114,16 @@ typedef void *sa_handle_t; /* opaque handle to access core functions */
#define SA_RBAC_VALUE "solaris.smf.value.shares"
/*
+ * Feature set bit definitions
+ */
+
+#define SA_FEATURE_NONE 0x0000 /* no feature flags set */
+#define SA_FEATURE_RESOURCE 0x0001 /* resource names are required */
+#define SA_FEATURE_DFSTAB 0x0002 /* need to manage in dfstab */
+#define SA_FEATURE_ALLOWSUBDIRS 0x0004 /* allow subdirs to be shared */
+#define SA_FEATURE_ALLOWPARDIRS 0x0008 /* allow parent dirs to be shared */
+
+/*
* legacy files
*/
@@ -143,7 +162,6 @@ extern int sa_check_path(sa_group_t, char *, int);
extern int sa_move_share(sa_group_t, sa_share_t);
extern int sa_remove_share(sa_share_t);
extern sa_share_t sa_get_share(sa_group_t, char *);
-extern sa_share_t sa_get_resource(sa_group_t, char *);
extern sa_share_t sa_find_share(sa_handle_t, char *);
extern sa_share_t sa_get_next_share(sa_share_t);
extern char *sa_get_share_attr(sa_share_t, char *);
@@ -152,9 +170,26 @@ extern sa_group_t sa_get_parent_group(sa_share_t);
extern int sa_set_share_attr(sa_share_t, char *, char *);
extern int sa_set_share_description(sa_share_t, char *);
extern int sa_enable_share(sa_group_t, char *);
-extern int sa_disable_share(sa_group_t, char *);
+extern int sa_disable_share(sa_share_t, char *);
extern int sa_is_share(void *);
+/* resource name related */
+extern sa_resource_t sa_find_resource(sa_handle_t, char *);
+extern sa_resource_t sa_get_resource(sa_group_t, char *);
+extern sa_resource_t sa_get_next_resource(sa_resource_t);
+extern sa_share_t sa_get_resource_parent(sa_resource_t);
+extern sa_resource_t sa_get_share_resource(sa_share_t, char *);
+extern sa_resource_t sa_add_resource(sa_share_t, char *, int, int *);
+extern int sa_remove_resource(sa_resource_t);
+extern char *sa_get_resource_attr(sa_resource_t, char *);
+extern int sa_set_resource_attr(sa_resource_t, char *, char *);
+extern int sa_set_resource_description(sa_resource_t, char *);
+extern char *sa_get_resource_description(sa_resource_t);
+extern int sa_enable_resource(sa_resource_t, char *);
+extern int sa_disable_resource(sa_resource_t, char *);
+extern int sa_rename_resource(sa_resource_t, char *);
+extern void sa_fix_resource_name(char *);
+
/* data structure free calls */
extern void sa_free_attr_string(char *);
extern void sa_free_share_description(char *);
@@ -179,6 +214,7 @@ extern int sa_update_property(sa_property_t, char *);
extern int sa_remove_property(sa_property_t);
extern int sa_commit_properties(sa_optionset_t, int);
extern int sa_valid_property(void *, char *, sa_property_t);
+extern int sa_is_persistent(void *);
/* security control */
extern sa_security_t sa_get_security(sa_group_t, char *, char *);
@@ -196,6 +232,7 @@ extern int sa_parse_legacy_options(sa_group_t, char *, char *);
extern char *sa_proto_legacy_format(char *, sa_group_t, int);
extern int sa_is_security(char *, char *);
extern sa_protocol_properties_t sa_proto_get_properties(char *);
+extern uint64_t sa_proto_get_featureset(char *);
extern sa_property_t sa_get_protocol_property(sa_protocol_properties_t, char *);
extern sa_property_t sa_get_next_protocol_property(sa_property_t);
extern int sa_set_protocol_property(sa_property_t, char *);
@@ -206,9 +243,12 @@ extern int sa_add_protocol_property(sa_protocol_properties_t, sa_property_t);
extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t);
extern int sa_proto_valid_space(char *, char *);
extern char *sa_proto_space_alias(char *, char *);
+extern int sa_proto_get_transients(sa_handle_t, char *);
+extern int sa_proto_notify_resource(sa_resource_t, char *);
+extern int sa_proto_change_notify(sa_share_t, char *);
/* handle legacy (dfstab/sharetab) files */
-extern int sa_delete_legacy(sa_share_t);
+extern int sa_delete_legacy(sa_share_t, char *);
extern int sa_update_legacy(sa_share_t, char *);
extern int sa_update_sharetab(sa_share_t, char *);
extern int sa_delete_sharetab(char *, char *);
diff --git a/usr/src/lib/libshare/common/libshare_impl.h b/usr/src/lib/libshare/common/libshare_impl.h
index a454e3ef75..ab661e432f 100644
--- a/usr/src/lib/libshare/common/libshare_impl.h
+++ b/usr/src/lib/libshare/common/libshare_impl.h
@@ -72,6 +72,13 @@ struct sa_plugin_ops {
char *(*sa_space_alias)(char *);
int (*sa_update_legacy)(sa_share_t);
int (*sa_delete_legacy)(sa_share_t);
+ int (*sa_change_notify)(sa_share_t);
+ int (*sa_enable_resource)(sa_resource_t);
+ int (*sa_disable_resource)(sa_resource_t);
+ uint64_t (*sa_features)(void);
+ int (*sa_get_transient_shares)(sa_handle_t); /* add transients */
+ int (*sa_notify_resource)(sa_resource_t);
+ int (*sa_rename_resource)(sa_handle_t, sa_resource_t, char *);
int (*sa_run_command)(int, int, char **); /* proto specific */
int (*sa_command_help)();
};
@@ -97,6 +104,8 @@ extern int sa_proto_unshare(sa_share_t, char *, char *);
extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t);
extern int sa_proto_security_prop(char *, char *);
extern int sa_proto_legacy_opts(char *, sa_group_t, char *);
+extern int sa_proto_share_resource(char *, sa_resource_t);
+extern int sa_proto_unshare_resource(char *, sa_resource_t);
/* internal utility functions */
extern sa_optionset_t sa_get_derived_optionset(sa_group_t, char *, int);
@@ -121,7 +130,7 @@ extern void sa_emptyshare(struct share *sh);
/* ZFS functions */
extern int sa_get_zfs_shares(sa_handle_t, char *);
extern int sa_zfs_update(sa_share_t);
-extern int sa_share_zfs(sa_share_t, char *, share_t *, void *, boolean_t);
+extern int sa_share_zfs(sa_share_t, char *, share_t *, void *, zfs_share_op_t);
extern int sa_sharetab_fill_zfs(sa_share_t share, struct share *sh,
char *proto);
@@ -131,6 +140,8 @@ extern void proto_plugin_fini();
extern int sa_proto_set_property(char *, sa_property_t);
extern int sa_proto_delete_legacy(char *, sa_share_t);
extern int sa_proto_update_legacy(char *, sa_share_t);
+extern int sa_proto_rename_resource(sa_handle_t, char *,
+ sa_resource_t, char *);
#define PL_TYPE_PROPERTY 0
#define PL_TYPE_SECURITY 1
diff --git a/usr/src/lib/libshare/common/libshare_zfs.c b/usr/src/lib/libshare/common/libshare_zfs.c
index 1cfbcbe72a..e0ee84ef8a 100644
--- a/usr/src/lib/libshare/common/libshare_zfs.c
+++ b/usr/src/lib/libshare/common/libshare_zfs.c
@@ -36,7 +36,7 @@
#include <sys/mnttab.h>
#include <sys/mntent.h>
-extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
+extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t);
extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
extern char *sa_fstype(char *);
extern void set_node_attr(void *, char *, char *);
@@ -109,7 +109,7 @@ sa_zfs_fini(sa_handle_impl_t impl_handle)
/*
* get_one_filesystem(zfs_handle_t, data)
*
- * an interator function called while iterating through the ZFS
+ * an iterator function called while iterating through the ZFS
* root. It accumulates into an array of file system handles that can
* be used to derive info about those file systems.
*
@@ -390,7 +390,7 @@ sa_zfs_is_shared(sa_handle_t sahandle, char *path)
}
/*
- * find_or_create_group(groupname, proto, *err)
+ * find_or_create_group(handle, groupname, proto, *err)
*
* While walking the ZFS tree, we need to add shares to a defined
* group. If the group doesn't exist, create it first, making sure it
@@ -424,19 +424,8 @@ find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
if (group != NULL) {
if (proto != NULL) {
optionset = sa_get_optionset(group, proto);
- if (optionset == NULL) {
+ if (optionset == NULL)
optionset = sa_create_optionset(group, proto);
- } else {
- char **protolist;
- int numprotos, i;
- numprotos = sa_get_protocols(&protolist);
- for (i = 0; i < numprotos; i++) {
- optionset = sa_create_optionset(group,
- protolist[i]);
- }
- if (protolist != NULL)
- free(protolist);
- }
}
}
if (err != NULL)
@@ -457,8 +446,8 @@ find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
*/
static sa_group_t
-find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname,
- char *optstring, int *err)
+find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, char *proto,
+ char *optstring, int *err)
{
sa_group_t group = NULL;
sa_group_t zfs;
@@ -494,30 +483,58 @@ find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname,
options = strdup(optstring);
if (options != NULL) {
*err = sa_parse_legacy_options(group,
- options, "nfs");
+ options, proto);
+
+ /* If no optionset, add one */
+ if (sa_get_optionset(group, proto) ==
+ NULL)
+ (void) sa_create_optionset(
+ group, proto);
free(options);
} else {
*err = SA_NO_MEMORY;
}
}
+ } else if (proto != NULL && strcmp(proto, "smb") == 0) {
+ *err = SA_PROP_SHARE_ONLY;
}
}
return (group);
}
/*
+ * zfs_construct_resource(share, name, base, dataset)
+ *
+ * Add a resource to the share using name as a template. If name ==
+ * NULL, then construct a name based on the dataset value.
+ * name.
+ */
+static void
+zfs_construct_resource(sa_share_t share, char *dataset)
+{
+ char buff[SA_MAX_RESOURCE_NAME + 1];
+ int ret = SA_OK;
+
+ (void) snprintf(buff, SA_MAX_RESOURCE_NAME, "%s", dataset);
+ sa_fix_resource_name(buff);
+ (void) sa_add_resource(share, buff, SA_SHARE_TRANSIENT, &ret);
+}
+
+/*
* zfs_inherited(handle, source, sourcestr)
*
- * handle case of inherited sharenfs. Pulled out of sa_get_zfs_shares
+ * handle case of inherited share{nfs,smb}. Pulled out of sa_get_zfs_shares
* for readability.
*/
static int
zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
- char *shareopts, char *mountpoint)
+ char *shareopts, char *mountpoint, char *proto, char *dataset)
{
int doshopt = 0;
int err = SA_OK;
sa_group_t group;
+ sa_resource_t resource;
+ uint64_t features;
/*
* Need to find the "real" parent sub-group. It may not be
@@ -525,11 +542,18 @@ zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
* variable. The real parent not mounted can occur if
* "canmount=off and sharenfs=on".
*/
- group = find_or_create_zfs_subgroup(handle, sourcestr, shareopts,
- &doshopt);
+ group = find_or_create_zfs_subgroup(handle, sourcestr, proto,
+ shareopts, &doshopt);
if (group != NULL) {
- share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT,
- &err);
+ /*
+ * We may need the first share for resource
+ * prototype. We only care about it if it has a
+ * resource that sets a prefix value.
+ */
+ if (share == NULL)
+ share = _sa_add_share(group, mountpoint,
+ SA_SHARE_TRANSIENT, &err,
+ (uint64_t)SA_FEATURE_NONE);
/*
* some options may only be on shares. If the opt
* string contains one of those, we put it just on the
@@ -539,10 +563,27 @@ zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
char *options;
options = strdup(shareopts);
if (options != NULL) {
+ set_node_attr(share, "dataset", dataset);
err = sa_parse_legacy_options(share, options,
- "nfs");
+ proto);
+ set_node_attr(share, "dataset", NULL);
free(options);
}
+ if (sa_get_optionset(group, proto) == NULL)
+ (void) sa_create_optionset(group, proto);
+ }
+ features = sa_proto_get_featureset(proto);
+ if (share != NULL && features & SA_FEATURE_RESOURCE) {
+ /*
+ * We have a share and the protocol requires
+ * that at least one resource exist (probably
+ * SMB). We need to make sure that there is at
+ * least one.
+ */
+ resource = sa_get_share_resource(share, NULL);
+ if (resource == NULL) {
+ zfs_construct_resource(share, dataset);
+ }
}
} else {
err = SA_NO_MEMORY;
@@ -557,45 +598,60 @@ zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
* of sa_get_zfs_shares for readability.
*/
static int
-zfs_notinherited(sa_group_t group, char *mountpoint, char *shareopts)
+zfs_notinherited(sa_group_t group, sa_share_t share, char *mountpoint,
+ char *shareopts, char *proto, char *dataset)
{
int err = SA_OK;
- sa_share_t share;
- char *options;
+ sa_resource_t resource;
+ uint64_t features;
set_node_attr(group, "zfs", "true");
- share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, &err);
+ if (share == NULL)
+ share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT,
+ &err, (uint64_t)SA_FEATURE_NONE);
if (err == SA_OK) {
if (strcmp(shareopts, "on") == 0)
- shareopts = "rw";
-
- options = strdup(shareopts);
- if (options != NULL) {
- err = sa_parse_legacy_options(group, options,
- "nfs");
- free(options);
- }
- if (err == SA_PROP_SHARE_ONLY) {
- /*
- * Same as above, some properties may
- * only be on shares, but due to the
- * ZFS sub-groups being artificial, we
- * sometimes get this and have to deal
- * with it. We do it by attempting to
- * put it on the share.
- */
+ shareopts = "";
+ if (shareopts != NULL) {
+ char *options;
options = strdup(shareopts);
if (options != NULL) {
- err = sa_parse_legacy_options(share,
- options, "nfs");
+ err = sa_parse_legacy_options(group, options,
+ proto);
free(options);
}
+ if (err == SA_PROP_SHARE_ONLY) {
+ /*
+ * Same as above, some properties may
+ * only be on shares, but due to the
+ * ZFS sub-groups being artificial, we
+ * sometimes get this and have to deal
+ * with it. We do it by attempting to
+ * put it on the share.
+ */
+ options = strdup(shareopts);
+ if (options != NULL) {
+ err = sa_parse_legacy_options(share,
+ options, proto);
+ free(options);
+ }
+ }
+ /* unmark the share's changed state */
+ set_node_attr(share, "changed", NULL);
+ }
+ features = sa_proto_get_featureset(proto);
+ if (share != NULL && features & SA_FEATURE_RESOURCE) {
+ /*
+ * We have a share and the protocol requires
+ * that at least one resource exist (probably
+ * SMB). We need to make sure that there is at
+ * least one.
+ */
+ resource = sa_get_share_resource(share, NULL);
+ if (resource == NULL) {
+ zfs_construct_resource(share, dataset);
+ }
}
- /* Mark as the defining node of the subgroup */
- set_node_attr(share, "subgroup", "true");
-
- /* unmark the share's changed state */
- set_node_attr(share, "changed", NULL);
}
return (err);
}
@@ -619,6 +675,46 @@ zfs_grp_error(int err)
}
/*
+ * zfs_process_share(handle, share, mountpoint, proto, source,
+ * shareopts, sourcestr)
+ *
+ * Creates the subgroup, if necessary and adds shares and adds shares
+ * and properties.
+ */
+static int
+zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
+ char *mountpoint, char *proto, zprop_source_t source, char *shareopts,
+ char *sourcestr, char *dataset)
+{
+ int err = SA_OK;
+
+ if (source & ZPROP_SRC_INHERITED) {
+ err = zfs_inherited(handle, share, sourcestr, shareopts,
+ mountpoint, proto, dataset);
+ } else {
+ group = find_or_create_zfs_subgroup(handle, dataset, proto,
+ shareopts, &err);
+ if (group == NULL) {
+ static int err = 0;
+ /*
+ * there is a problem, but we can't do
+ * anything about it at this point so we issue
+ * a warning an move on.
+ */
+ zfs_grp_error(err);
+ err = 1;
+ }
+ set_node_attr(group, "zfs", "true");
+ /*
+ * Add share with local opts via zfs_notinherited.
+ */
+ err = zfs_notinherited(group, share, mountpoint, shareopts,
+ proto, dataset);
+ }
+ return (err);
+}
+
+/*
* sa_get_zfs_shares(handle, groupname)
*
* Walk the mnttab for all zfs mounts and determine which are
@@ -652,7 +748,7 @@ sa_get_zfs_shares(sa_handle_t handle, char *groupname)
if (zfs_libhandle == NULL)
return (SA_SYSTEM_ERR);
- zfsgroup = find_or_create_group(handle, groupname, "nfs", &err);
+ zfsgroup = find_or_create_group(handle, groupname, NULL, &err);
if (zfsgroup == NULL)
return (legacy);
@@ -666,6 +762,7 @@ sa_get_zfs_shares(sa_handle_t handle, char *groupname)
group = zfsgroup;
for (i = 0; i < count; i++) {
char *dataset;
+ int foundnfs = 0;
source = ZPROP_SRC_ALL;
/* If no mountpoint, skip. */
@@ -694,8 +791,9 @@ sa_get_zfs_shares(sa_handle_t handle, char *groupname)
ZFS_MAXPROPLEN, B_FALSE) == 0 &&
strcmp(shareopts, "off") != 0) {
/* it is shared so add to list */
- share = sa_find_share(handle, mountpoint);
err = SA_OK;
+ foundnfs = 1;
+ share = sa_find_share(handle, mountpoint);
if (share != NULL) {
/*
* A zfs file system had been shared
@@ -711,36 +809,36 @@ sa_get_zfs_shares(sa_handle_t handle, char *groupname)
share = NULL;
}
if (err == SA_OK) {
- if (source & ZPROP_SRC_INHERITED) {
- err = zfs_inherited(handle,
- share, sourcestr,
- shareopts, mountpoint);
- } else {
- group = _sa_create_zfs_group(
- zfsgroup, dataset);
- if (group == NULL) {
- static int err = 0;
- /*
- * there is a problem,
- * but we can't do
- * anything about it
- * at this point so we
- * issue a warning an
- * move on.
- */
- zfs_grp_error(err);
- err = 1;
- continue;
- }
- set_node_attr(group, "zfs",
- "true");
- /*
- * Add share with local opts via
- * zfs_notinherited.
- */
- err = zfs_notinherited(group,
- mountpoint, shareopts);
- }
+ err = zfs_process_share(handle, group,
+ share, mountpoint, "nfs", source,
+ shareopts, sourcestr, dataset);
+ }
+ }
+ if (zfs_prop_get(zlist[i], ZFS_PROP_SHARESMB, shareopts,
+ sizeof (shareopts), &source, sourcestr,
+ ZFS_MAXPROPLEN, B_FALSE) == 0 &&
+ strcmp(shareopts, "off") != 0) {
+ /* it is shared so add to list */
+ err = SA_OK;
+ share = sa_find_share(handle, mountpoint);
+ if (share != NULL && !foundnfs) {
+ /*
+ * A zfs file system had been shared
+ * through traditional methods
+ * (share/dfstab or added to a non-zfs
+ * group. Now it has been added to a
+ * ZFS group via the zfs
+ * command. Remove from previous
+ * config and setup with current
+ * options.
+ */
+ err = sa_remove_share(share);
+ share = NULL;
+ }
+ if (err == SA_OK) {
+ err = zfs_process_share(handle, group,
+ share, mountpoint, "smb", source,
+ shareopts, sourcestr, dataset);
}
}
}
@@ -811,6 +909,122 @@ sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
}
/*
+ * add_resources(share, opt)
+ *
+ * Add resource properties to those in "opt". Resources are prefixed
+ * with name=resourcename.
+ */
+static char *
+add_resources(sa_share_t share, char *opt)
+{
+ char *newopt = NULL;
+ char *propstr;
+ sa_resource_t resource;
+
+ newopt = strdup(opt);
+ if (newopt == NULL)
+ return (newopt);
+
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource)) {
+ char *name;
+ size_t size;
+
+ name = sa_get_resource_attr(resource, "name");
+ if (name == NULL) {
+ free(newopt);
+ return (NULL);
+ }
+ size = strlen(name) + strlen(opt) + sizeof ("name=") + 1;
+ newopt = calloc(1, size);
+ if (newopt != NULL)
+ (void) snprintf(newopt, size, "%s,name=%s", opt, name);
+ free(opt);
+ opt = newopt;
+ propstr = sa_proto_legacy_format("smb", resource, 0);
+ if (propstr == NULL) {
+ free(opt);
+ return (NULL);
+ }
+ size = strlen(propstr) + strlen(opt) + 2;
+ newopt = calloc(1, size);
+ if (newopt != NULL)
+ (void) snprintf(newopt, size, "%s,%s", opt, propstr);
+ free(opt);
+ opt = newopt;
+ }
+ return (opt);
+}
+
+/*
+ * sa_zfs_set_sharesmb(group, path, on)
+ *
+ * Update the "sharesmb" property on the path. If on is true, then set
+ * to the properties on the group or "on" if no properties are
+ * defined. Set to "off" if on is false.
+ */
+
+int
+sa_zfs_set_sharesmb(sa_group_t group, char *path, int on)
+{
+ int ret = SA_NOT_IMPLEMENTED;
+ char *command;
+ sa_share_t share;
+
+ /* In case SMB not enabled */
+ if (sa_get_optionset(group, "smb") == NULL)
+ return (SA_NOT_SUPPORTED);
+
+ command = malloc(ZFS_MAXPROPLEN * 2);
+ if (command != NULL) {
+ char *opts = NULL;
+ char *dataset = NULL;
+ FILE *pfile;
+ sa_handle_impl_t impl_handle;
+
+ if (on) {
+ char *newopt;
+
+ share = sa_get_share(group, NULL);
+ opts = sa_proto_legacy_format("smb", share, 1);
+ if (opts != NULL && strlen(opts) == 0) {
+ free(opts);
+ opts = strdup("on");
+ }
+ newopt = add_resources(opts, share);
+ free(opts);
+ opts = newopt;
+ }
+
+ impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
+ assert(impl_handle != NULL);
+ if (impl_handle != NULL)
+ dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
+ else
+ ret = SA_SYSTEM_ERR;
+
+ if (dataset != NULL) {
+ (void) snprintf(command, ZFS_MAXPROPLEN * 2,
+ "echo %s set sharesmb=\"%s\" %s", COMMAND,
+ opts != NULL ? opts : "off", dataset);
+ pfile = popen(command, "r");
+ if (pfile != NULL) {
+ ret = pclose(pfile);
+ if (ret != 0)
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ if (opts != NULL)
+ free(opts);
+ if (dataset != NULL)
+ free(dataset);
+ free(command);
+ }
+ return (ret);
+}
+
+/*
* sa_zfs_update(group)
*
* call back to ZFS to update the share if necessary.
@@ -986,7 +1200,7 @@ sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto)
int
sa_share_zfs(sa_share_t share, char *path, share_t *sh,
- void *exportdata, boolean_t on)
+ void *exportdata, zfs_share_op_t operation)
{
libzfs_handle_t *libhandle;
sa_group_t group;
@@ -1057,7 +1271,7 @@ sa_share_zfs(sa_share_t share, char *path, share_t *sh,
sh->sh_size += j;
SMAX(i, j);
err = zfs_deleg_share_nfs(libhandle, dataset, path,
- exportdata, sh, i, on);
+ exportdata, sh, i, operation);
libzfs_fini(libhandle);
}
free(dataset);
diff --git a/usr/src/lib/libshare/common/libsharecore.c b/usr/src/lib/libshare/common/libsharecore.c
index 503a424cc6..2e39594fc3 100644
--- a/usr/src/lib/libshare/common/libsharecore.c
+++ b/usr/src/lib/libshare/common/libsharecore.c
@@ -51,6 +51,7 @@
#include <sys/param.h>
#include <signal.h>
#include <libintl.h>
+#include <dirent.h>
#include <sharefs/share.h>
#include "sharetab.h"
@@ -88,7 +89,7 @@ extern char *get_token(char *);
static void dfs_free_list(xfs_sharelist_t *);
/* prototypes */
void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
-extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
+extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t);
extern sa_group_t _sa_create_group(sa_handle_impl_t, char *);
static void outdfstab(FILE *, xfs_sharelist_t *);
extern int _sa_remove_optionset(sa_optionset_t);
@@ -563,13 +564,13 @@ sa_comment_line(char *line, char *err)
}
/*
- * sa_delete_legacy(share)
+ * sa_delete_legacy(share, protocol)
*
* Delete the specified share from the legacy config file.
*/
int
-sa_delete_legacy(sa_share_t share)
+sa_delete_legacy(sa_share_t share, char *protocol)
{
FILE *dfstab;
int err;
@@ -580,39 +581,54 @@ sa_delete_legacy(sa_share_t share)
sa_group_t parent;
sigset_t old;
+ /*
+ * Protect against shares that don't have paths. This is not
+ * really an error at this point.
+ */
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL)
+ return (ret);
+
dfstab = open_dfstab(SA_LEGACY_DFSTAB);
if (dfstab != NULL) {
(void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
sablocksigs(&old);
- path = sa_get_share_attr(share, "path");
parent = sa_get_parent_group(share);
if (parent != NULL) {
(void) lockf(fileno(dfstab), F_LOCK, 0);
list = getdfstab(dfstab);
rewind(dfstab);
- for (optionset = sa_get_optionset(parent, NULL);
- optionset != NULL;
- optionset = sa_get_next_optionset(optionset)) {
- char *proto = sa_get_optionset_attr(optionset,
- "type");
- if (list != NULL && proto != NULL)
+ if (protocol != NULL) {
+ if (list != NULL)
list = remdfsentry(list, path,
- proto);
- if (proto == NULL)
- ret = SA_NO_MEMORY;
- /*
- * May want to only do the dfstab if
- * this call returns NOT IMPLEMENTED
- * but it shouldn't hurt.
- */
- if (ret == SA_OK) {
- err = sa_proto_delete_legacy(proto,
- share);
- if (err != SA_NOT_IMPLEMENTED)
- ret = err;
+ protocol);
+ } else {
+ for (optionset = sa_get_optionset(parent, NULL);
+ optionset != NULL;
+ optionset =
+ sa_get_next_optionset(optionset)) {
+ char *proto = sa_get_optionset_attr(
+ optionset, "type");
+
+ if (list != NULL && proto != NULL)
+ list = remdfsentry(list, path,
+ proto);
+ if (proto == NULL)
+ ret = SA_NO_MEMORY;
+ /*
+ * may want to only do the dfstab if
+ * this call returns NOT IMPLEMENTED
+ * but it shouldn't hurt.
+ */
+ if (ret == SA_OK) {
+ err = sa_proto_delete_legacy(
+ proto, share);
+ if (err != SA_NOT_IMPLEMENTED)
+ ret = err;
+ }
+ if (proto != NULL)
+ sa_free_attr_string(proto);
}
- if (proto != NULL)
- sa_free_attr_string(proto);
}
outdfstab(dfstab, list);
if (list != NULL)
@@ -623,13 +639,16 @@ sa_delete_legacy(sa_share_t share)
(void) fsync(fileno(dfstab));
saunblocksigs(&old);
(void) fclose(dfstab);
- sa_free_attr_string(path);
} else {
if (errno == EACCES || errno == EPERM)
ret = SA_NO_PERMISSION;
else
ret = SA_CONFIG_ERR;
}
+
+ if (path != NULL)
+ sa_free_attr_string(path);
+
return (ret);
}
@@ -639,7 +658,7 @@ sa_delete_legacy(sa_share_t share)
* There is an assumption that dfstab will be the most common form of
* legacy configuration file for shares, but not the only one. Because
* of that, dfstab handling is done in the main code with calls to
- * this function and protocol specific calls to deal with formating
+ * this function and protocol specific calls to deal with formatting
* options into dfstab/share compatible syntax. Since not everything
* will be dfstab, there is a provision for calling a protocol
* specific plugin interface that allows the protocol plugin to do its
@@ -655,10 +674,16 @@ sa_update_legacy(sa_share_t share, char *proto)
char *path;
sigset_t old;
char *persist;
+ uint64_t features;
ret = sa_proto_update_legacy(proto, share);
if (ret != SA_NOT_IMPLEMENTED)
return (ret);
+
+ features = sa_proto_get_featureset(proto);
+ if (!(features & SA_FEATURE_DFSTAB))
+ return (ret);
+
/* do the dfstab format */
persist = sa_get_share_attr(share, "type");
/*
@@ -748,6 +773,21 @@ sa_is_share(void *object)
}
return (0);
}
+/*
+ * sa_is_resource(object)
+ *
+ * returns true of the object is of type "share".
+ */
+
+int
+sa_is_resource(void *object)
+{
+ if (object != NULL) {
+ if (strcmp((char *)((xmlNodePtr)object)->name, "resource") == 0)
+ return (1);
+ }
+ return (0);
+}
/*
* _sa_remove_property(property)
@@ -1392,6 +1432,7 @@ parse_sharetab(sa_handle_t handle)
sa_group_t lgroup;
char *groupname;
int legacy = 0;
+ char shareopts[MAXNAMLEN];
list = get_share_list(&err);
if (list == NULL)
@@ -1410,7 +1451,9 @@ parse_sharetab(sa_handle_t handle)
* share with no arguments.
*/
set_node_attr(share, "shared", "true");
- set_node_attr(share, "shareopts", tmplist->options);
+ (void) snprintf(shareopts, MAXNAMLEN, "shareopts-%s",
+ tmplist->fstype);
+ set_node_attr(share, shareopts, tmplist->options);
continue;
}
@@ -1426,9 +1469,9 @@ parse_sharetab(sa_handle_t handle)
*groupname++ = '\0';
group = sa_get_group(handle, groupname);
if (group != NULL) {
- share = _sa_add_share(group,
- tmplist->path,
- SA_SHARE_TRANSIENT, &err);
+ share = _sa_add_share(group, tmplist->path,
+ SA_SHARE_TRANSIENT, &err,
+ (uint64_t)SA_FEATURE_NONE);
} else {
/*
* While this case shouldn't
@@ -1447,7 +1490,7 @@ parse_sharetab(sa_handle_t handle)
*/
share = _sa_add_share(lgroup,
tmplist->path, SA_SHARE_TRANSIENT,
- &err);
+ &err, (uint64_t)SA_FEATURE_NONE);
}
} else {
if (sa_zfs_is_shared(handle, tmplist->path)) {
@@ -1472,11 +1515,12 @@ parse_sharetab(sa_handle_t handle)
if (group != NULL) {
share = _sa_add_share(group,
tmplist->path, SA_SHARE_TRANSIENT,
- &err);
+ &err, (uint64_t)SA_FEATURE_NONE);
}
} else {
share = _sa_add_share(lgroup, tmplist->path,
- SA_SHARE_TRANSIENT, &err);
+ SA_SHARE_TRANSIENT, &err,
+ (uint64_t)SA_FEATURE_NONE);
}
}
if (share == NULL)
@@ -1517,12 +1561,22 @@ int
gettransients(sa_handle_impl_t ihandle, xmlNodePtr *root)
{
int legacy = 0;
+ int numproto;
+ char **protocols = NULL;
+ int i;
if (root != NULL) {
if (*root == NULL)
*root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
- if (*root != NULL)
+ if (*root != NULL) {
legacy = parse_sharetab(ihandle);
+ numproto = sa_get_protocols(&protocols);
+ for (i = 0; i < numproto; i++)
+ legacy |= sa_proto_get_transients(
+ (sa_handle_t)ihandle, protocols[i]);
+ if (protocols != NULL)
+ free(protocols);
+ }
}
return (legacy);
}
@@ -1630,7 +1684,7 @@ sa_free_fstype(char *type)
* Work backward to the top of the share object tree and start
* copying protocol specific optionsets into a newly created
* optionset that doesn't have a parent (it will be freed
- * later). This provides for the property inheritence model. That
+ * later). This provides for the property inheritance model. That
* is, properties closer to the share take precedence over group
* level. This also provides for groups of groups in the future.
*/
@@ -1719,7 +1773,7 @@ sa_free_derived_optionset(sa_optionset_t optionset)
* Find all the security types set for this object. This is
* preliminary to getting a derived security set. The return value is an
* optionset containg properties which are the sectype values found by
- * walking up the XML document struture. The returned optionset
+ * walking up the XML document structure. The returned optionset
* is a derived optionset.
*
* If hier is 0, only look at object. If non-zero, walk up the tree.
@@ -1885,6 +1939,19 @@ sa_fillshare(sa_share_t share, char *proto, struct share *sh)
sa_group_t group;
char *buff;
char *zfs;
+ sa_resource_t resource;
+ char *rsrcname = NULL;
+ char *defprop;
+
+ /*
+ * We only want to deal with the path level shares for the
+ * sharetab file. If a resource, get the parent.
+ */
+ if (sa_is_resource(share)) {
+ resource = (sa_resource_t)share;
+ share = sa_get_resource_parent(resource);
+ rsrcname = sa_get_resource_attr(resource, "name");
+ }
group = sa_get_parent_group(share);
if (group != NULL) {
@@ -1912,41 +1979,48 @@ sa_fillshare(sa_share_t share, char *proto, struct share *sh)
sa_free_attr_string(value);
}
- value = sa_get_share_attr(share, "resource");
- if (value != NULL || groupname != NULL) {
+ if (rsrcname != NULL || groupname != NULL) {
int len = 0;
- if (value != NULL)
- len += strlen(value);
+ if (rsrcname != NULL)
+ len += strlen(rsrcname);
if (groupname != NULL)
len += strlen(groupname);
len += 3; /* worst case */
buff = malloc(len);
(void) snprintf(buff, len, "%s%s%s",
- (value != NULL && strlen(value) > 0) ? value : "-",
+ (rsrcname != NULL &&
+ strlen(rsrcname) > 0) ? rsrcname : "-",
groupname != NULL ? "@" : "",
groupname != NULL ? groupname : "");
sh->sh_res = buff;
- if (value != NULL)
- sa_free_attr_string(value);
- if (groupname != NULL) {
+ if (rsrcname != NULL)
+ sa_free_attr_string(rsrcname);
+ if (groupname != NULL)
sa_free_attr_string(groupname);
- groupname = NULL;
- }
} else {
sh->sh_res = strdup("-");
}
+ /*
+ * Get correct default prop string. NFS uses "rw", others use
+ * "".
+ */
+ if (strcmp(proto, "nfs") != 0)
+ defprop = "\"\"";
+ else
+ defprop = "rw";
+
sh->sh_fstype = strdup(proto);
value = sa_proto_legacy_format(proto, share, 1);
if (value != NULL) {
if (strlen(value) > 0)
sh->sh_opts = strdup(value);
else
- sh->sh_opts = strdup("rw");
+ sh->sh_opts = strdup(defprop);
free(value);
} else {
- sh->sh_opts = strdup("rw");
+ sh->sh_opts = strdup(defprop);
}
value = sa_get_share_description(share);
@@ -2041,3 +2115,67 @@ sa_delete_sharetab(char *path, char *proto)
return (ret);
}
+/*
+ * sa_fix_resource_name(path)
+ *
+ * change all illegal characters to something else. For now, all get
+ * converted to '_' and the leading '/' is stripped off. This is used
+ * to construct an resource name (SMB share name) that is valid.
+ * Caller must pass a valid path.
+ */
+void
+sa_fix_resource_name(char *path)
+{
+ char *cp;
+ size_t len;
+
+ assert(path != NULL);
+
+ /* make sure we are appropriate length */
+ cp = path;
+ if (*cp == '/')
+ cp++; /* skip leading slash */
+ while (cp != NULL && strlen(cp) > SA_MAX_RESOURCE_NAME) {
+ cp = strchr(cp, '/');
+ if (cp != NULL)
+ cp++;
+ }
+ /* two cases - cp == NULL and cp is substring of path */
+ if (cp == NULL) {
+ /* just take last SA_MAX_RESOURCE_NAME chars */
+ len = 1 + strlen(path) - SA_MAX_RESOURCE_NAME;
+ (void) memmove(path, path + len, SA_MAX_RESOURCE_NAME);
+ path[SA_MAX_RESOURCE_NAME] = '\0';
+ } else {
+ len = strlen(cp) + 1;
+ (void) memmove(path, cp, len);
+ }
+
+ /*
+ * Don't want any of the characters that are not allowed
+ * in an SMB share name. Replace them with '_'.
+ */
+ while (*path) {
+ switch (*path) {
+ case '/':
+ case '"':
+ case '\\':
+ case '[':
+ case ']':
+ case ':':
+ case '|':
+ case '<':
+ case '>':
+ case '+':
+ case ';':
+ case ',':
+ case '?':
+ case '*':
+ case '=':
+ case '\t':
+ *path = '_';
+ break;
+ }
+ path++;
+ }
+}
diff --git a/usr/src/lib/libshare/common/mapfile-vers b/usr/src/lib/libshare/common/mapfile-vers
index 317623b3b4..f7dfde5730 100644
--- a/usr/src/lib/libshare/common/mapfile-vers
+++ b/usr/src/lib/libshare/common/mapfile-vers
@@ -106,9 +106,28 @@ SUNWprivate {
sa_delete_legacy;
sa_free_derived_security;
sa_enable_share;
+ sa_enable_resource;
sa_create_group;
sa_valid_protocol;
sa_find_group_handle;
+ sa_add_resource;
+ sa_remove_resource;
+ sa_rename_resource;
+ sa_get_resource;
+ sa_get_next_resource;
+ sa_get_resource_attr;
+ sa_set_resource_attr;
+ sa_get_share_resource;
+ sa_get_resource_parent;
+ sa_find_resource;
+ sa_proto_change_notify;
+ sa_proto_notify_resource;
+ sa_disable_resource;
+ sa_proto_get_featureset;
+ sa_is_persistent;
+ sa_set_resource_description;
+ sa_get_resource_description;
+ sa_fix_resource_name;
local:
*;
};
diff --git a/usr/src/lib/libshare/common/plugin.c b/usr/src/lib/libshare/common/plugin.c
index 4079e24ff7..08856a8951 100644
--- a/usr/src/lib/libshare/common/plugin.c
+++ b/usr/src/lib/libshare/common/plugin.c
@@ -64,7 +64,7 @@ void proto_plugin_fini();
* /usr/lib/fs/nfs/libshare_nfs.so. The protocol specific directory
* would have a modules with name libshare_<proto>.so. If one is
* found, initialize it and add to the internal list of
- * protocols. These are used for protocol specifici operations.
+ * protocols. These are used for protocol specific operations.
*/
int
@@ -242,9 +242,9 @@ sa_proto_share(char *proto, sa_share_t share)
}
/*
- * sa_proto_unshare(proto, path)
+ * sa_proto_unshare(proto, share)
*
- * Deactivate (unshare) the path for this protocol.
+ * Deactivate (unshare) the share for this protocol.
*/
int
@@ -259,6 +259,52 @@ sa_proto_unshare(sa_share_t share, char *proto, char *path)
}
/*
+ * sa_proto_share_resource(char *proto, sa_resource_t resource)
+ *
+ * For protocols that actually enable at the resource level, do the
+ * protocol specific resource enable. If it doesn't, return an error.
+ * Note that the resource functions are optional so can return
+ * SA_NOT_SUPPORTED.
+ */
+
+int
+sa_proto_share_resource(char *proto, sa_resource_t resource)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_INVALID_PROTOCOL;
+
+ if (ops != NULL) {
+ if (ops->sa_enable_resource != NULL)
+ ret = ops->sa_enable_resource(resource);
+ else
+ ret = SA_NOT_SUPPORTED;
+ }
+ return (ret);
+}
+
+/*
+ * sa_proto_unshare_resource(char *proto, sa_resource_t resource)
+ *
+ * For protocols that actually disable at the resource level, do the
+ * protocol specific resource disable. If it doesn't, return an error.
+ */
+
+int
+sa_proto_unshare_resource(char *proto, sa_resource_t resource)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_INVALID_PROTOCOL;
+
+ if (ops != NULL) {
+ if (ops->sa_disable_resource != NULL)
+ ret = ops->sa_disable_resource(resource);
+ else
+ ret = SA_NOT_SUPPORTED;
+ }
+ return (ret);
+}
+
+/*
* sa_proto_valid_prop(proto, prop, opt)
*
* Check to see if the specified prop is valid for this protocol.
@@ -395,7 +441,7 @@ sa_proto_get_properties(char *proto)
/*
* sa_proto_set_property(proto, prop)
*
- * Update the protocol specifiec property.
+ * Update the protocol specific property.
*/
int
@@ -467,16 +513,127 @@ int
sa_proto_delete_legacy(char *proto, sa_share_t share)
{
struct sa_plugin_ops *ops = find_protocol(proto);
- int ret = SA_OK;
+ int ret = SA_NOT_IMPLEMENTED;
if (ops != NULL) {
if (ops->sa_delete_legacy != NULL)
ret = ops->sa_delete_legacy(share);
- } else {
- if (proto != NULL)
- ret = SA_NOT_IMPLEMENTED;
- else
+ } else if (proto == NULL) {
+ ret = SA_INVALID_PROTOCOL;
+ }
+ return (ret);
+}
+
+/*
+ * sa_proto_change_notify(share, char *protocol)
+ *
+ * Notify the protocol that a change has been made to the share
+ */
+
+int
+sa_proto_change_notify(sa_share_t share, char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_NOT_IMPLEMENTED;
+
+ if (ops != NULL) {
+ if (ops->sa_change_notify != NULL)
+ ret = ops->sa_change_notify(share);
+ } else if (proto == NULL) {
ret = SA_INVALID_PROTOCOL;
}
return (ret);
}
+
+/*
+ * sa_proto_notify_resource(resource, char *protocol)
+ *
+ * Notify the protocol that a change has been made to the share
+ */
+
+int
+sa_proto_notify_resource(sa_resource_t resource, char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_NOT_IMPLEMENTED;
+
+ if (ops != NULL) {
+ if (ops->sa_notify_resource != NULL)
+ ret = ops->sa_notify_resource(resource);
+ } else if (proto == NULL) {
+ ret = SA_INVALID_PROTOCOL;
+ }
+ return (ret);
+}
+
+/*
+ * sa_proto_get_featureset(protocol)
+ *
+ * Get bitmask of defined features of the protocol. These are
+ * primarily things like SA_FEATURE_RESOURCE (shares are by resource
+ * name rather than path) and other operational features that affect
+ * behavior.
+ */
+
+uint64_t
+sa_proto_get_featureset(char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ uint64_t ret = 0;
+
+ if (ops != NULL) {
+ if (ops->sa_features != NULL)
+ ret = ops->sa_features();
+ }
+ /* if not implemented, zero is valid */
+ return (ret);
+}
+
+/*
+ * sa_proto_get_transients(sa_handle_t)
+ *
+ * Called to get any protocol specific transient shares. NFS doesn't
+ * use this since the info is in sharetab which is processed as a
+ * common transient store.
+ *
+ * The protocol plugin should verify that the share isn't in the
+ * repository and then add it as a transient.
+ *
+ * Not having an entry is not a problem. It returns 0 in that case.
+ */
+
+int
+sa_proto_get_transients(sa_handle_t handle, char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = 0;
+
+ if (ops != NULL) {
+ if (ops->sa_get_transient_shares != NULL)
+ ret = ops->sa_get_transient_shares(handle);
+ }
+ return (ret);
+}
+
+/*
+ * sa_proto_rename_resource(sa_handle_t, proto, sa_resource_t, newname)
+ *
+ * Protocols may need to know when a resource has changed names in
+ * order to notify clients. This must be done "before" the name in the
+ * resource has been changed. Not being implemented is not a problem.
+ */
+
+int
+sa_proto_rename_resource(sa_handle_t handle, char *proto,
+ sa_resource_t resource, char *newname)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_OK;
+
+ if (ops != NULL) {
+ if (ops->sa_rename_resource != NULL)
+ ret = ops->sa_rename_resource(handle, resource,
+ newname);
+ }
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/scfutil.c b/usr/src/lib/libshare/common/scfutil.c
index 05421d605b..344d3729bb 100644
--- a/usr/src/lib/libshare/common/scfutil.c
+++ b/usr/src/lib/libshare/common/scfutil.c
@@ -35,6 +35,7 @@
#include "libshare_impl.h"
#include "scfutil.h"
#include <string.h>
+#include <ctype.h>
#include <errno.h>
#include <uuid/uuid.h>
#include <sys/param.h>
@@ -396,7 +397,8 @@ out:
static char *share_attr[] = {
"path",
"id",
- "resource",
+ "drive-letter",
+ "exclude",
NULL,
};
@@ -411,6 +413,52 @@ is_share_attr(char *name)
}
/*
+ * _sa_make_resource(node, valuestr)
+ *
+ * Make a resource node on the share node. The valusestr will either
+ * be old format (SMF acceptable string) or new format (pretty much an
+ * arbitrary string with "nnn:" prefixing in order to persist
+ * mapping). The input valuestr will get modified in place. This is
+ * only used in SMF repository parsing. A possible third field will be
+ * a "description" string.
+ */
+
+static void
+_sa_make_resource(xmlNodePtr node, char *valuestr)
+{
+ char *idx;
+ char *name;
+ char *description = NULL;
+
+ idx = valuestr;
+ name = strchr(valuestr, ':');
+ if (name == NULL) {
+ /* this is old form so give an index of "0" */
+ idx = "0";
+ name = valuestr;
+ } else {
+ /* NUL the ':' and move past it */
+ *name++ = '\0';
+ /* There could also be a description string */
+ description = strchr(name, ':');
+ if (description != NULL)
+ *description++ = '\0';
+ }
+ node = xmlNewChild(node, NULL, (xmlChar *)"resource", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)name);
+ xmlSetProp(node, (xmlChar *)"id", (xmlChar *)idx);
+ /* SMF values are always persistent */
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist");
+ if (description != NULL && strlen(description) > 0) {
+ (void) xmlNewChild(node, NULL, (xmlChar *)"description",
+ (xmlChar *)description);
+ }
+ }
+}
+
+
+/*
* sa_share_from_pgroup
*
* Extract the share definition from the share property group. We do
@@ -490,34 +538,70 @@ sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
vallen) >= 0) {
ret = SA_OK;
}
+ } else if (strcmp(name, "resource") == 0) {
+ ret = SA_OK;
}
}
- if (ret == SA_OK) {
+ if (ret != SA_OK)
+ continue;
+ /*
+ * Check that we have the "path" property in
+ * name. The string in name will always be nul
+ * terminated if scf_property_get_name()
+ * succeeded.
+ */
+ if (strcmp(name, "path") == 0)
+ have_path = 1;
+ if (is_share_attr(name)) {
/*
- * Check that we have the "path" property in
- * name. The string in name will always be nul
- * terminated if scf_property_get_name()
- * succeeded.
+ * If a share attr, then simple -
+ * usually path and id name
*/
- if (strcmp(name, "path") == 0)
- have_path = 1;
- if (is_share_attr(name)) {
- /*
- * If a share attr, then simple -
- * usually path and resource name
- */
- xmlSetProp(node, (xmlChar *)name,
- (xmlChar *)valuestr);
- } else {
- if (strcmp(name, "description") == 0) {
- /* We have a description node */
- xmlNodePtr desc;
- desc = xmlNewChild(node, NULL,
- (xmlChar *)"description", NULL);
- if (desc != NULL)
- xmlNodeSetContent(desc,
- (xmlChar *)valuestr);
+ xmlSetProp(node, (xmlChar *)name,
+ (xmlChar *)valuestr);
+ } else if (strcmp(name, "resource") == 0) {
+ /*
+ * Resource names handled differently since
+ * there can be multiple on each share. The
+ * "resource" id must be preserved since this
+ * will be used by some protocols in mapping
+ * "property spaces" to names and is always
+ * used to create SMF property groups specific
+ * to resources. CIFS needs this. The first
+ * value is present so add and then loop for
+ * any additional. Since this is new and
+ * previous values may exist, handle
+ * conversions.
+ */
+ scf_iter_t *viter;
+ viter = scf_iter_create(handle->handle);
+ if (viter != NULL &&
+ scf_iter_property_values(viter, prop) == 0) {
+ while (scf_iter_next_value(viter, value) > 0) {
+ /* Have a value so process it */
+ if (scf_value_get_ustring(value,
+ valuestr, vallen) >= 0) {
+ /* have a ustring */
+ _sa_make_resource(node,
+ valuestr);
+ } else if (scf_value_get_astring(value,
+ valuestr, vallen) >= 0) {
+ /* have an astring */
+ _sa_make_resource(node,
+ valuestr);
+ }
}
+ scf_iter_destroy(viter);
+ }
+ } else {
+ if (strcmp(name, "description") == 0) {
+ /* We have a description node */
+ xmlNodePtr desc;
+ desc = xmlNewChild(node, NULL,
+ (xmlChar *)"description", NULL);
+ if (desc != NULL)
+ xmlNodeSetContent(desc,
+ (xmlChar *)valuestr);
}
}
}
@@ -583,7 +667,34 @@ find_share_by_id(sa_handle_t handle, char *shareid)
}
/*
- * sa_share_props_from_pgroup(root, handle, pg, id)
+ * find_resource_by_index(share, index)
+ *
+ * Search the resource records on the share for the id index.
+ */
+static sa_resource_t
+find_resource_by_index(sa_share_t share, char *index)
+{
+ sa_resource_t resource;
+ sa_resource_t found = NULL;
+ char *id;
+
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL && found == NULL;
+ resource = sa_get_next_resource(resource)) {
+ id = (char *)xmlGetProp((xmlNodePtr)resource, (xmlChar *)"id");
+ if (id != NULL) {
+ if (strcmp(id, index) == 0) {
+ /* found it so save in "found" */
+ found = resource;
+ }
+ sa_free_attr_string(id);
+ }
+ }
+ return (found);
+}
+
+/*
+ * sa_share_props_from_pgroup(root, handle, pg, id, sahandle)
*
* Extract share properties from the SMF property group. More sanity
* checks are done and the share object is created. We ignore some
@@ -639,6 +750,7 @@ sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
/* not a valid proto (null) */
return (ret);
}
+
sectype = strchr(proto, '_');
if (sectype != NULL)
*sectype++ = '\0';
@@ -664,10 +776,35 @@ sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
if (sectype == NULL)
node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
else {
- node = xmlNewChild(root, NULL, (xmlChar *)"security", NULL);
- if (node != NULL)
- xmlSetProp(node, (xmlChar *)"sectype",
- (xmlChar *)sectype);
+ if (isdigit((int)*sectype)) {
+ sa_resource_t resource;
+ /*
+ * If sectype[0] is a digit, then it is an index into
+ * the resource names. We need to find a resource
+ * record and then get the properties into an
+ * optionset. The optionset becomes the "node" and the
+ * rest is hung off of the share.
+ */
+ resource = find_resource_by_index(share, sectype);
+ if (resource != NULL) {
+ node = xmlNewChild(resource, NULL,
+ (xmlChar *)"optionset", NULL);
+ } else {
+ /* this shouldn't happen */
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ /*
+ * If not a digit, then it is a security type
+ * (alternate option space). Security types start with
+ * an alphabetic.
+ */
+ node = xmlNewChild(root, NULL, (xmlChar *)"security",
+ NULL);
+ if (node != NULL)
+ xmlSetProp(node, (xmlChar *)"sectype",
+ (xmlChar *)sectype);
+ }
}
if (node == NULL) {
ret = SA_NO_MEMORY;
@@ -685,7 +822,7 @@ sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
if (iter == NULL || value == NULL || prop == NULL || name == NULL)
goto out;
- /* Iterate over the share pg properties */
+ /* iterate over the share pg properties */
if (scf_iter_pg_properties(iter, pg) == 0) {
while (scf_iter_next_property(iter, prop) > 0) {
ret = SA_SYSTEM_ERR; /* assume the worst */
@@ -897,7 +1034,7 @@ out:
* sa_extract_defaults(root, handle, instance)
*
* Local function to find the default properties that live in the
- * default instance's "operation" proprerty group.
+ * default instance's "operation" property group.
*/
static void
@@ -946,7 +1083,7 @@ out:
/*
- * sa_get_config(handle, root, doc, sahandlec)
+ * sa_get_config(handle, root, doc, sahandle)
*
* Walk the SMF repository for /network/shares/group and find all the
* instances. These become group names. Then add the XML structure
@@ -1276,6 +1413,147 @@ sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
}
/*
+ * check_resource(share)
+ *
+ * Check to see if share has any persistent resources. We don't want
+ * to save if they are all transient.
+ */
+static int
+check_resource(sa_share_t share)
+{
+ sa_resource_t resource;
+ int ret = B_FALSE;
+
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL && ret == B_FALSE;
+ resource = sa_get_next_resource(resource)) {
+ char *type;
+ type = sa_get_resource_attr(resource, "type");
+ if (type != NULL) {
+ if (strcmp(type, "transient") != 0) {
+ ret = B_TRUE;
+ }
+ sa_free_attr_string(type);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_set_resource_property(handle, prop, value)
+ *
+ * set a property transaction entry into the pending SMF
+ * transaction. We don't want to include any transient resources
+ */
+
+static int
+sa_set_resource_property(scfutilhandle_t *handle, sa_share_t share)
+{
+ int ret = SA_OK;
+ scf_value_t *value;
+ scf_transaction_entry_t *entry;
+ sa_resource_t resource;
+ char *valstr;
+ char *idstr;
+ char *description;
+ char *propstr = NULL;
+ size_t strsize;
+
+ /* don't bother if no persistent resources */
+ if (check_resource(share) == B_FALSE)
+ return (ret);
+
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ entry = scf_entry_create(handle->handle);
+ if (entry == NULL)
+ return (SA_SYSTEM_ERR);
+
+ if (scf_transaction_property_change(handle->trans, entry,
+ "resource", SCF_TYPE_ASTRING) != 0 &&
+ scf_transaction_property_new(handle->trans, entry,
+ "resource", SCF_TYPE_ASTRING) != 0) {
+ scf_entry_destroy(entry);
+ return (SA_SYSTEM_ERR);
+
+ }
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource)) {
+ value = scf_value_create(handle->handle);
+ if (value == NULL) {
+ ret = SA_NO_MEMORY;
+ break;
+ }
+ /* Get size of complete string */
+ valstr = sa_get_resource_attr(resource, "name");
+ idstr = sa_get_resource_attr(resource, "id");
+ description = sa_get_resource_description(resource);
+ strsize = (valstr != NULL) ? strlen(valstr) : 0;
+ strsize += (idstr != NULL) ? strlen(idstr) : 0;
+ strsize += (description != NULL) ? strlen(description) : 0;
+ if (strsize > 0) {
+ strsize += 3; /* add nul and ':' */
+ propstr = (char *)malloc(strsize);
+ if (propstr == NULL) {
+ scf_value_destroy(value);
+ ret = SA_NO_MEMORY;
+ goto err;
+ }
+ if (idstr == NULL)
+ (void) snprintf(propstr, strsize, "%s",
+ valstr ? valstr : "");
+ else
+ (void) snprintf(propstr, strsize, "%s:%s:%s",
+ idstr ? idstr : "", valstr ? valstr : "",
+ description ? description : "");
+ if (scf_value_set_astring(value, propstr) != 0) {
+ ret = SA_SYSTEM_ERR;
+ free(propstr);
+ scf_value_destroy(value);
+ break;
+ }
+ if (scf_entry_add_value(entry, value) != 0) {
+ ret = SA_SYSTEM_ERR;
+ free(propstr);
+ scf_value_destroy(value);
+ break;
+ }
+ /* the value is in the transaction */
+ value = NULL;
+ free(propstr);
+ }
+err:
+ if (valstr != NULL)
+ sa_free_attr_string(valstr);
+ if (idstr != NULL)
+ sa_free_attr_string(idstr);
+ if (description != NULL)
+ sa_free_share_description(description);
+ }
+ /* the entry is in the transaction */
+ entry = NULL;
+
+ if (ret == SA_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SA_NO_PERMISSION;
+ break;
+ }
+ }
+ /*
+ * cleanup if there were any errors that didn't leave
+ * these values where they would be cleaned up later.
+ */
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+
+ return (ret);
+}
+
+/*
* sa_commit_share(handle, group, share)
*
* Commit this share to the repository.
@@ -1288,7 +1566,6 @@ sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
int ret = SA_OK;
char *groupname;
char *name;
- char *resource;
char *description;
char *sharename;
ssize_t proplen;
@@ -1371,15 +1648,35 @@ sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
}
}
if (ret == SA_OK) {
- resource = sa_get_share_attr(share,
- "resource");
- if (resource != NULL) {
+ name = sa_get_share_attr(share, "drive-letter");
+ if (name != NULL) {
+ /* A drive letter may exist for SMB */
+ ret = sa_set_property(handle,
+ "drive-letter", name);
+ sa_free_attr_string(name);
+ }
+ }
+ if (ret == SA_OK) {
+ name = sa_get_share_attr(share, "exclude");
+ if (name != NULL) {
+ /*
+ * In special cases need to
+ * exclude proto enable.
+ */
ret = sa_set_property(handle,
- "resource", resource);
- sa_free_attr_string(resource);
+ "exclude", name);
+ sa_free_attr_string(name);
}
}
if (ret == SA_OK) {
+ /*
+ * If there are resource names, bundle them up
+ * and save appropriately.
+ */
+ ret = sa_set_resource_property(handle, share);
+ }
+
+ if (ret == SA_OK) {
description = sa_get_share_description(share);
if (description != NULL) {
ret = sa_set_property(handle,
@@ -1414,6 +1711,49 @@ sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
}
/*
+ * remove_resources(handle, share, shareid)
+ *
+ * If the share has resources, remove all of them and their
+ * optionsets.
+ */
+static int
+remove_resources(scfutilhandle_t *handle, sa_share_t share, char *shareid)
+{
+ sa_resource_t resource;
+ sa_optionset_t opt;
+ char *proto;
+ char *id;
+ ssize_t proplen;
+ char *propstring;
+ int ret = SA_OK;
+
+ proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ propstring = malloc(proplen);
+ if (propstring == NULL)
+ return (SA_NO_MEMORY);
+
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL; resource = sa_get_next_resource(resource)) {
+ id = sa_get_resource_attr(resource, "id");
+ if (id == NULL)
+ continue;
+ for (opt = sa_get_optionset(resource, NULL);
+ opt != NULL; opt = sa_get_next_optionset(resource)) {
+ proto = sa_get_optionset_attr(opt, "type");
+ if (proto != NULL) {
+ (void) snprintf(propstring, proplen,
+ "%s_%s_%s", shareid, proto, id);
+ ret = sa_delete_pgroup(handle, propstring);
+ sa_free_attr_string(proto);
+ }
+ }
+ sa_free_attr_string(id);
+ }
+ free(propstring);
+ return (ret);
+}
+
+/*
* sa_delete_share(handle, group, share)
*
* Remove the specified share from the group (and service instance).
@@ -1444,6 +1784,8 @@ sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
}
ret = sa_get_instance(handle, groupname);
if (ret == SA_OK) {
+ /* If a share has resources, remove them */
+ ret = remove_resources(handle, share, shareid);
/* If a share has properties, remove them */
ret = sa_delete_pgroup(handle, shareid);
for (opt = sa_get_optionset(share, NULL);
diff --git a/usr/src/lib/libshare/nfs/libshare_nfs.c b/usr/src/lib/libshare/nfs/libshare_nfs.c
index a7426ab841..0a3175de70 100644
--- a/usr/src/lib/libshare/nfs/libshare_nfs.c
+++ b/usr/src/lib/libshare/nfs/libshare_nfs.c
@@ -73,6 +73,7 @@ static int nfs_set_proto_prop(sa_property_t);
static sa_protocol_properties_t nfs_get_proto_set();
static char *nfs_get_status();
static char *nfs_space_alias(char *);
+static uint64_t nfs_features();
/*
* ops vector that provides the protocol specific info and operations
@@ -95,7 +96,14 @@ struct sa_plugin_ops sa_plugin_ops = {
nfs_get_proto_set,
nfs_get_status,
nfs_space_alias,
- NULL,
+ NULL, /* update_legacy */
+ NULL, /* delete_legacy */
+ NULL, /* change_notify */
+ NULL, /* enable_resource */
+ NULL, /* disable_resource */
+ nfs_features,
+ NULL, /* transient shares */
+ NULL, /* notify resource */
NULL
};
@@ -543,13 +551,16 @@ nfs_parse_legacy_options(sa_group_t group, char *options)
optionset = sa_create_optionset(group, "nfs");
} else {
/*
- * have an existing optionset so we need to compare
- * options in order to detect errors. For now, we
- * assume that the first optionset is the correct one
- * and the others will be the same. This needs to be
- * fixed before the final code is ready.
+ * Have an existing optionset . Ideally, we would need
+ * to compare options in order to detect errors. For
+ * now, we assume that the first optionset is the
+ * correct one and the others will be the same. An
+ * empty optionset is the same as no optionset so we
+ * don't want to exit in that case. Getting an empty
+ * optionset can occur with ZFS property checking.
*/
- return (ret);
+ if (sa_get_property(optionset, NULL) != NULL)
+ return (ret);
}
if (strcmp(options, SHOPT_RW) == 0) {
@@ -839,7 +850,7 @@ fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset)
/*
* since options may be set/reset multiple times, always do an
* explicit set or clear of the option. This allows defaults
- * to be set and then the protocol specifici to override.
+ * to be set and then the protocol specific to override.
*/
name = sa_get_property_attr(option, "type");
@@ -1804,7 +1815,8 @@ nfs_enable_share(sa_share_t share)
ea.uex = &export;
sa_sharetab_fill_zfs(share, &sh, "nfs");
- err = sa_share_zfs(share, path, &sh, &ea, B_TRUE);
+ err = sa_share_zfs(share, path, &sh,
+ &ea, ZFS_SHARE_NFS);
sa_emptyshare(&sh);
}
} else {
@@ -1909,7 +1921,8 @@ nfs_disable_share(sa_share_t share, char *path)
sh.sh_path = path;
sh.sh_fstype = "nfs";
- err = sa_share_zfs(share, path, &sh, &ea, B_FALSE);
+ err = sa_share_zfs(share, path, &sh,
+ &ea, ZFS_UNSHARE_NFS);
} else
err = exportfs(path, NULL);
if (err < 0) {
@@ -2802,7 +2815,7 @@ nfs_minmax_check(int index, int value)
/*
* nfs_validate_proto_prop(index, name, value)
*
- * Verify that the property specifed by name can take the new
+ * Verify that the property specified by name can take the new
* value. This is a sanity check to prevent bad values getting into
* the default files. All values need to be checked against what is
* allowed by their defined type. If a type isn't explicitly defined
@@ -2967,3 +2980,15 @@ nfs_space_alias(char *space)
}
return (strdup(name));
}
+
+/*
+ * nfs_features()
+ *
+ * Return a mask of the features required.
+ */
+
+static uint64_t
+nfs_features()
+{
+ return ((uint64_t)SA_FEATURE_DFSTAB);
+}
diff --git a/usr/src/lib/libshare/smb/Makefile b/usr/src/lib/libshare/smb/Makefile
new file mode 100644
index 0000000000..d5f3df5233
--- /dev/null
+++ b/usr/src/lib/libshare/smb/Makefile
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../../Makefile.lib
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+MSGFILES = libshare_smb.c
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../Makefile.targ
+include ../../../Makefile.msg.targ
diff --git a/usr/src/lib/libshare/smb/Makefile.com b/usr/src/lib/libshare/smb/Makefile.com
new file mode 100644
index 0000000000..997f47daef
--- /dev/null
+++ b/usr/src/lib/libshare/smb/Makefile.com
@@ -0,0 +1,88 @@
+#
+# 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
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY = libshare_smb.a
+VERS = .1
+SMBMLSVC_DIR = $(SRC)/lib/smbsrv/libmlsvc/common
+SMBBASE_DIR = $(SRC)/lib/smbsrv/libsmb/common
+SMBCOMMON_DIR = $(SRC)/common/smbsrv
+
+LIBOBJS = libshare_smb.o smb_share_doorclnt.o
+SMBCOMMON_OBJ = smb_share_door_decode.o smb_common_door_decode.o
+SMBBASE_OBJ = smb_cfg.o smb_scfutil.o smb_door_client.o
+SMBMLSVC_OBJ = smb_share_util.o
+OBJECTS = $(LIBOBJS) $(SMBCOMMON_OBJ) $(SMBBASE_OBJ) $(SMBMLSVC_OBJ)
+
+include ../../../Makefile.lib
+
+ROOTLIBDIR = $(ROOT)/usr/lib/fs/smb
+ROOTLIBDIR64 = $(ROOT)/usr/lib/fs/smb/$(MACH64)
+
+LIBSRCS = $(LIBOBJS:%.o=$(SRCDIR)/%.c)
+lintcheck := SRCS = $(LIBSRCS)
+
+LIBS = $(DYNLIB)
+LDLIBS += -lshare -lnsl -lscf -lumem -lc
+all install := LDLIBS += -lxml2
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_REENTRANT -I/usr/include/libxml2 \
+ -I$(SRCDIR)/../common
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all
+
+lint: lintcheck
+
+pics/smb_door_client.o: $(SMBBASE_DIR)/smb_door_client.c
+ $(COMPILE.c) -o $@ $(SMBBASE_DIR)/smb_door_client.c
+ $(POST_PROCESS_O)
+
+pics/smb_share_door_decode.o: $(SMBCOMMON_DIR)/smb_share_door_decode.c
+ $(COMPILE.c) -o $@ $(SMBCOMMON_DIR)/smb_share_door_decode.c
+ $(POST_PROCESS_O)
+
+pics/smb_common_door_decode.o: $(SMBCOMMON_DIR)/smb_common_door_decode.c
+ $(COMPILE.c) -o $@ $(SMBCOMMON_DIR)/smb_common_door_decode.c
+ $(POST_PROCESS_O)
+
+pics/smb_cfg.o: $(SMBBASE_DIR)/smb_cfg.c
+ $(COMPILE.c) -o $@ $(SMBBASE_DIR)/smb_cfg.c
+ $(POST_PROCESS_O)
+
+pics/smb_scfutil.o: $(SMBBASE_DIR)/smb_scfutil.c
+ $(COMPILE.c) -o $@ $(SMBBASE_DIR)/smb_scfutil.c
+ $(POST_PROCESS_O)
+
+pics/smb_share_util.o: $(SMBMLSVC_DIR)/smb_share_util.c
+ $(COMPILE.c) -o $@ $(SMBMLSVC_DIR)/smb_share_util.c
+ $(POST_PROCESS_O)
+
+include ../../../Makefile.targ
diff --git a/usr/src/lib/libshare/smb/amd64/Makefile b/usr/src/lib/libshare/smb/amd64/Makefile
new file mode 100644
index 0000000000..90b1730d23
--- /dev/null
+++ b/usr/src/lib/libshare/smb/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
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libshare/smb/i386/Makefile b/usr/src/lib/libshare/smb/i386/Makefile
new file mode 100644
index 0000000000..543238473c
--- /dev/null
+++ b/usr/src/lib/libshare/smb/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
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/libshare/smb/libshare_smb.c b/usr/src/lib/libshare/smb/libshare_smb.c
new file mode 100644
index 0000000000..7d705e0369
--- /dev/null
+++ b/usr/src/lib/libshare/smb/libshare_smb.c
@@ -0,0 +1,1641 @@
+/*
+ * 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 specific functions
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <zone.h>
+#include <errno.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include <pwd.h>
+#include <limits.h>
+#include <libscf.h>
+#include <strings.h>
+#include "libshare_smb.h"
+#include <rpcsvc/daemon_utils.h>
+#include <smbsrv/lmshare.h>
+#include <smbsrv/lmshare_door.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/libsmb.h>
+
+/* internal functions */
+static int smb_share_init(void);
+static void smb_share_fini(void);
+static int smb_enable_share(sa_share_t);
+static int smb_share_changed(sa_share_t);
+static int smb_resource_changed(sa_resource_t);
+static int smb_rename_resource(sa_handle_t, sa_resource_t, char *);
+static int smb_disable_share(sa_share_t share, char *);
+static int smb_validate_property(sa_property_t, sa_optionset_t);
+static int smb_set_proto_prop(sa_property_t);
+static sa_protocol_properties_t smb_get_proto_set(void);
+static char *smb_get_status(void);
+static int smb_parse_optstring(sa_group_t, char *);
+static char *smb_format_options(sa_group_t, int);
+
+static int smb_enable_service(void);
+
+static int range_check_validator(int, char *);
+static int range_check_validator_zero_ok(int, char *);
+static int string_length_check_validator(int, char *);
+static int true_false_validator(int, char *);
+static int ip_address_validator_empty_ok(int, char *);
+static int ip_address_csv_list_validator_empty_ok(int, char *);
+static int ipc_mode_validator(int, char *);
+static int path_validator(int, char *);
+
+static int smb_enable_resource(sa_resource_t);
+static int smb_disable_resource(sa_resource_t);
+static uint64_t smb_share_features(void);
+static int smb_list_transient(sa_handle_t);
+
+/* size of basic format allocation */
+#define OPT_CHUNK 1024
+
+/*
+ * Indexes of entries in smb_proto_options table.
+ * Changes to smb_proto_options table may require
+ * an update to these values.
+ */
+#define PROTO_OPT_WINS1 6
+#define PROTO_OPT_WINS_EXCLUDE 8
+
+
+/*
+ * ops vector that provides the protocol specific info and operations
+ * for share management.
+ */
+
+struct sa_plugin_ops sa_plugin_ops = {
+ SA_PLUGIN_VERSION,
+ SMB_PROTOCOL_NAME,
+ smb_share_init,
+ smb_share_fini,
+ smb_enable_share,
+ smb_disable_share,
+ smb_validate_property,
+ NULL,
+ NULL,
+ smb_parse_optstring,
+ smb_format_options,
+ smb_set_proto_prop,
+ smb_get_proto_set,
+ smb_get_status,
+ NULL,
+ NULL,
+ NULL,
+ smb_share_changed,
+ smb_enable_resource,
+ smb_disable_resource,
+ smb_share_features,
+ smb_list_transient,
+ smb_resource_changed,
+ smb_rename_resource,
+ NULL,
+ NULL
+};
+
+/*
+ * option definitions. Make sure to keep the #define for the option
+ * index just before the entry it is the index for. Changing the order
+ * can cause breakage.
+ */
+
+struct option_defs optdefs[] = {
+ {SHOPT_AD_CONTAINER, OPT_TYPE_STRING},
+ {SHOPT_NAME, OPT_TYPE_NAME},
+ {NULL, NULL},
+};
+
+/*
+ * findopt(name)
+ *
+ * Lookup option "name" in the option table and return the table
+ * index.
+ */
+
+static int
+findopt(char *name)
+{
+ int i;
+ if (name != NULL) {
+ for (i = 0; optdefs[i].tag != NULL; i++) {
+ if (strcmp(optdefs[i].tag, name) == 0)
+ return (i);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * is_a_number(number)
+ *
+ * is the string a number in one of the forms we want to use?
+ */
+
+static int
+is_a_number(char *number)
+{
+ int ret = 1;
+ int hex = 0;
+
+ if (strncmp(number, "0x", 2) == 0) {
+ number += 2;
+ hex = 1;
+ } else if (*number == '-') {
+ number++; /* skip the minus */
+ }
+
+ while (ret == 1 && *number != '\0') {
+ if (hex) {
+ ret = isxdigit(*number++);
+ } else {
+ ret = isdigit(*number++);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * validresource(name)
+ *
+ * Check that name only has valid characters in it. The current valid
+ * set are the printable characters but not including:
+ * " / \ [ ] : | < > + ; , ? * = \t
+ * Note that space is included and there is a maximum length.
+ */
+static int
+validresource(const char *name)
+{
+ const char *cp;
+ size_t len;
+
+ if (name == NULL)
+ return (B_FALSE);
+
+ len = strlen(name);
+ if (len == 0 || len > SA_MAX_RESOURCE_NAME)
+ return (B_FALSE);
+
+ if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
+ return (B_FALSE);
+ }
+
+ for (cp = name; *cp != '\0'; cp++)
+ if (iscntrl(*cp))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+/*
+ * smb_isonline()
+ *
+ * Determine if the SMF service instance is in the online state or
+ * not. A number of operations depend on this state.
+ */
+static boolean_t
+smb_isonline(void)
+{
+ char *str;
+ boolean_t ret = B_FALSE;
+
+ if ((str = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI)) != NULL) {
+ ret = (strcmp(str, SCF_STATE_STRING_ONLINE) == 0);
+ free(str);
+ }
+ return (ret);
+}
+
+/*
+ * smb_enable_share tells the implementation that it is to enable the share.
+ * This entails converting the path and options into the appropriate ioctl
+ * calls. It is assumed that all error checking of paths, etc. were
+ * done earlier.
+ */
+static int
+smb_enable_share(sa_share_t share)
+{
+ char *path;
+ char *rname;
+ lmshare_info_t si;
+ sa_resource_t resource;
+ boolean_t iszfs;
+ boolean_t privileged;
+ int err = SA_OK;
+ priv_set_t *priv_effective;
+ boolean_t online;
+
+ priv_effective = priv_allocset();
+ (void) getppriv(PRIV_EFFECTIVE, priv_effective);
+ privileged = (priv_isfullset(priv_effective) == B_TRUE);
+ priv_freeset(priv_effective);
+
+ /* get the path since it is important in several places */
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL)
+ return (SA_NO_SUCH_PATH);
+
+ online = smb_isonline();
+
+ iszfs = sa_path_is_zfs(path);
+
+ if (iszfs) {
+
+ if (privileged == B_FALSE && !online) {
+
+ if (!online) {
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "SMB: Cannot share remove "
+ "file system: %s\n"), path);
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "SMB: Service needs to be enabled "
+ "by a privileged user\n"));
+ err = SA_NO_PERMISSION;
+ errno = EPERM;
+ }
+ if (err) {
+ sa_free_attr_string(path);
+ return (err);
+ }
+
+ }
+ }
+
+ if (privileged == B_TRUE && !online) {
+ err = smb_enable_service();
+ if (err != SA_OK) {
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "SMB: Unable to enable service\n"));
+ /*
+ * For now, it is OK to not be able to enable
+ * the service.
+ */
+ if (err == SA_BUSY)
+ err = SA_OK;
+ } else {
+ online = B_TRUE;
+ }
+ }
+
+ /*
+ * Don't bother trying to start shares if the service isn't
+ * running.
+ */
+ if (!online)
+ goto done;
+
+ /* Each share can have multiple resources */
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource)) {
+ sa_optionset_t opts;
+ bzero(&si, sizeof (lmshare_info_t));
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL) {
+ sa_free_attr_string(path);
+ return (SA_NO_SUCH_RESOURCE);
+ }
+
+ opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
+ smb_build_lmshare_info(rname, path, opts, &si);
+ sa_free_attr_string(rname);
+
+ sa_free_derived_optionset(opts);
+ if (!iszfs) {
+ err = lmshrd_add(&si);
+ } else {
+ share_t sh;
+
+ sa_sharetab_fill_zfs(share, &sh, "smb");
+ err = sa_share_zfs(share, (char *)path, &sh,
+ &si, ZFS_SHARE_SMB);
+
+ sa_emptyshare(&sh);
+ }
+ }
+ if (!iszfs)
+ (void) sa_update_sharetab(share, "smb");
+done:
+ sa_free_attr_string(path);
+
+ return (err == NERR_DuplicateShare ? 0 : err);
+}
+
+/*
+ * This is the share for CIFS all shares have resource names.
+ * Enable tells the smb server to update its hash. If it fails
+ * because smb server is down, we just ignore as smb server loads
+ * the resources from sharemanager at startup.
+ */
+
+static int
+smb_enable_resource(sa_resource_t resource)
+{
+ char *path;
+ char *rname;
+ sa_optionset_t opts;
+ sa_share_t share;
+ lmshare_info_t si;
+ int ret;
+
+ share = sa_get_resource_parent(resource);
+ if (share == NULL)
+ return (SA_NO_SUCH_PATH);
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL)
+ return (SA_SYSTEM_ERR);
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL) {
+ sa_free_attr_string(path);
+ return (SA_NO_SUCH_RESOURCE);
+ }
+
+ ret = smb_enable_service();
+
+ if (!smb_isonline()) {
+ ret = SA_OK;
+ goto done;
+ }
+
+ opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
+ smb_build_lmshare_info(rname, path, opts, &si);
+ sa_free_attr_string(path);
+ sa_free_attr_string(rname);
+ sa_free_derived_optionset(opts);
+ if (lmshrd_add(&si) != NERR_Success)
+ return (SA_NOT_SHARED);
+ (void) sa_update_sharetab(share, "smb");
+
+done:
+ return (ret);
+}
+
+/*
+ * Remove it from smb server hash.
+ */
+static int
+smb_disable_resource(sa_resource_t resource)
+{
+ char *rname;
+ DWORD res;
+ sa_share_t share;
+
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL)
+ return (SA_NO_SUCH_RESOURCE);
+
+ if (smb_isonline()) {
+ res = lmshrd_delete(rname);
+ if (res != NERR_Success) {
+ sa_free_attr_string(rname);
+ return (SA_CONFIG_ERR);
+ }
+ sa_free_attr_string(rname);
+ rname = NULL;
+ }
+ share = sa_get_resource_parent(resource);
+ if (share != NULL) {
+ rname = sa_get_share_attr(share, "path");
+ if (rname != NULL) {
+ (void) sa_delete_sharetab(rname, "smb");
+ sa_free_attr_string(rname);
+ rname = NULL;
+ }
+ }
+ if (rname != NULL)
+ sa_free_attr_string(rname);
+ /*
+ * Always return OK as smb/server may be down and
+ * Shares will be picked up when loaded.
+ */
+ return (SA_OK);
+}
+
+/*
+ * smb_share_changed(sa_share_t share)
+ *
+ * The specified share has changed.
+ */
+static int
+smb_share_changed(sa_share_t share)
+{
+ char *path;
+ sa_resource_t resource;
+
+ /* get the path since it is important in several places */
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL)
+ return (SA_NO_SUCH_PATH);
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource))
+ (void) smb_resource_changed(resource);
+
+ sa_free_attr_string(path);
+
+ return (SA_OK);
+}
+
+/*
+ * smb_resource_changed(sa_resource_t resource)
+ *
+ * The specified resource has changed.
+ */
+static int
+smb_resource_changed(sa_resource_t resource)
+{
+ DWORD res;
+ lmshare_info_t si;
+ lmshare_info_t new_si;
+ char *rname, *path;
+ sa_optionset_t opts;
+ sa_share_t share;
+
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL)
+ return (SA_NO_SUCH_RESOURCE);
+
+ share = sa_get_resource_parent(resource);
+ if (share == NULL) {
+ sa_free_attr_string(rname);
+ return (SA_CONFIG_ERR);
+ }
+
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL) {
+ sa_free_attr_string(rname);
+ return (SA_NO_SUCH_PATH);
+ }
+
+ if (!smb_isonline()) {
+ sa_free_attr_string(rname);
+ return (SA_OK);
+ }
+
+ /* Update the share cache in smb/server */
+ res = lmshrd_getinfo(rname, &si);
+ if (res != NERR_Success) {
+ sa_free_attr_string(path);
+ sa_free_attr_string(rname);
+ return (SA_CONFIG_ERR);
+ }
+
+ opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
+ smb_build_lmshare_info(rname, path, opts, &new_si);
+ sa_free_derived_optionset(opts);
+ sa_free_attr_string(path);
+ sa_free_attr_string(rname);
+
+ /*
+ * Update all fields from sa_share_t
+ * Get derived values.
+ */
+ if (lmshrd_setinfo(&new_si) != LMSHR_DOOR_SRV_SUCCESS)
+ return (SA_CONFIG_ERR);
+ return (smb_enable_service());
+}
+
+/*
+ * smb_disable_share(sa_share_t share)
+ *
+ * Unshare the specified share.
+ */
+static int
+smb_disable_share(sa_share_t share, char *path)
+{
+ char *rname;
+ sa_resource_t resource;
+ boolean_t iszfs;
+ int err = SA_OK;
+
+ iszfs = sa_path_is_zfs(path);
+ if (!smb_isonline())
+ goto done;
+
+ 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) {
+ continue;
+ }
+ if (!iszfs) {
+ err = lmshrd_delete(rname);
+ switch (err) {
+ case NERR_NetNameNotFound:
+ case NERR_Success:
+ err = SA_OK;
+ break;
+ default:
+ err = SA_CONFIG_ERR;
+ break;
+ }
+ } else {
+ share_t sh;
+
+ sa_sharetab_fill_zfs(share, &sh, "smb");
+ err = sa_share_zfs(share, (char *)path, &sh,
+ rname, ZFS_UNSHARE_SMB);
+ sa_emptyshare(&sh);
+ }
+ sa_free_attr_string(rname);
+ }
+done:
+ if (!iszfs)
+ (void) sa_delete_sharetab(path, "smb");
+ return (err);
+}
+
+/*
+ * smb_validate_property(property, parent)
+ *
+ * Check that the property has a legitimate value for its type.
+ */
+
+static int
+smb_validate_property(sa_property_t property, sa_optionset_t parent)
+{
+ int ret = SA_OK;
+ char *propname;
+ int optindex;
+ sa_group_t parent_group;
+ char *value;
+
+ propname = sa_get_property_attr(property, "type");
+
+ if ((optindex = findopt(propname)) < 0)
+ ret = SA_NO_SUCH_PROP;
+
+ /* need to validate value range here as well */
+ if (ret == SA_OK) {
+ parent_group = sa_get_parent_group((sa_share_t)parent);
+ if (optdefs[optindex].share && !sa_is_share(parent_group))
+ ret = SA_PROP_SHARE_ONLY;
+ }
+ if (ret != SA_OK) {
+ if (propname != NULL)
+ sa_free_attr_string(propname);
+ return (ret);
+ }
+
+ value = sa_get_property_attr(property, "value");
+ if (value != NULL) {
+ /* first basic type checking */
+ switch (optdefs[optindex].type) {
+ case OPT_TYPE_NUMBER:
+ /* check that the value is all digits */
+ if (!is_a_number(value))
+ ret = SA_BAD_VALUE;
+ break;
+ case OPT_TYPE_BOOLEAN:
+ if (strlen(value) == 0 ||
+ strcasecmp(value, "true") == 0 ||
+ strcmp(value, "1") == 0 ||
+ strcasecmp(value, "false") == 0 ||
+ strcmp(value, "0") == 0) {
+ ret = SA_OK;
+ } else {
+ ret = SA_BAD_VALUE;
+ }
+ break;
+ case OPT_TYPE_NAME:
+ /*
+ * Make sure no invalid characters
+ */
+ if (validresource(value) == B_FALSE)
+ ret = SA_BAD_VALUE;
+ break;
+ case OPT_TYPE_STRING:
+ /* whatever is here should be ok */
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (value != NULL)
+ sa_free_attr_string(value);
+ if (ret == SA_OK && optdefs[optindex].check != NULL)
+ /* do the property specific check */
+ ret = optdefs[optindex].check(property);
+
+ if (propname != NULL)
+ sa_free_attr_string(propname);
+ return (ret);
+}
+
+/*
+ * Protocol management functions
+ *
+ * properties defined in the default files are defined in
+ * proto_option_defs for parsing and validation.
+ */
+
+struct smb_proto_option_defs {
+ char *name; /* display name -- remove protocol identifier */
+ int smb_index;
+ int32_t minval;
+ int32_t maxval; /* In case of length of string this should be max */
+ int (*validator)(int, char *);
+ int32_t refresh;
+} smb_proto_options[] = {
+ { SMB_CD_SYS_CMNT,
+ SMB_CI_SYS_CMNT, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_MAX_WORKERS,
+ SMB_CI_MAX_WORKERS, 64, 1024, range_check_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_NBSCOPE,
+ SMB_CI_NBSCOPE, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_RDR_IPCMODE,
+ SMB_CI_RDR_IPCMODE, 0, 0, ipc_mode_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_LM_LEVEL,
+ SMB_CI_LM_LEVEL, 2, 5, range_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_KEEPALIVE,
+ SMB_CI_KEEPALIVE, 20, 5400, range_check_validator_zero_ok,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_WINS_SRV1,
+ SMB_CI_WINS_SRV1, 0, MAX_VALUE_BUFLEN,
+ ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
+ { SMB_CD_WINS_SRV2,
+ SMB_CI_WINS_SRV2, 0, MAX_VALUE_BUFLEN,
+ ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
+ { SMB_CD_WINS_EXCL,
+ SMB_CI_WINS_EXCL, 0, MAX_VALUE_BUFLEN,
+ ip_address_csv_list_validator_empty_ok, SMB_REFRESH_REFRESH},
+ { SMB_CD_SIGNING_ENABLE,
+ SMB_CI_SIGNING_ENABLE, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_SIGNING_REQD,
+ SMB_CI_SIGNING_REQD, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_RESTRICT_ANON,
+ SMB_CI_RESTRICT_ANON, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_DOMAIN_SRV,
+ SMB_CI_DOMAIN_SRV, 0, MAX_VALUE_BUFLEN,
+ ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_ENABLE,
+ SMB_CI_ADS_ENABLE, 0, 0, true_false_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_USER,
+ SMB_CI_ADS_USER, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_USER_CONTAINER,
+ SMB_CI_ADS_USER_CONTAINER, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_DOMAIN,
+ SMB_CI_ADS_DOMAIN, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_PASSWD,
+ SMB_CI_ADS_PASSWD, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_IPLOOKUP,
+ SMB_CI_ADS_IPLOOKUP, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_SITE,
+ SMB_CI_ADS_SITE, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_DYNDNS_ENABLE,
+ SMB_CI_DYNDNS_ENABLE, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_DYNDNS_RETRY_SEC,
+ SMB_CI_DYNDNS_RETRY_SEC, 0, 20, range_check_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_DYNDNS_RETRY_COUNT,
+ SMB_CI_DYNDNS_RETRY_COUNT, 3, 5, range_check_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_AUTOHOME_MAP,
+ SMB_CI_AUTOHOME_MAP, 0, MAX_VALUE_BUFLEN,
+ path_validator},
+ {NULL, -1, 0, 0, NULL}
+};
+
+/*
+ * Check the range of value as int range.
+ */
+static int
+range_check_validator(int index, char *value)
+{
+ int ret = SA_OK;
+
+ if (!is_a_number(value)) {
+ ret = SA_BAD_VALUE;
+ } else {
+ int val;
+ val = strtoul(value, NULL, 0);
+ if (val < smb_proto_options[index].minval ||
+ val > smb_proto_options[index].maxval)
+ ret = SA_BAD_VALUE;
+ }
+ return (ret);
+}
+
+/*
+ * Check the range of value as int range.
+ */
+static int
+range_check_validator_zero_ok(int index, char *value)
+{
+ int ret = SA_OK;
+
+ if (!is_a_number(value)) {
+ ret = SA_BAD_VALUE;
+ } else {
+ int val;
+ val = strtoul(value, NULL, 0);
+ if (val == 0)
+ ret = SA_OK;
+ else {
+ if (val < smb_proto_options[index].minval ||
+ val > smb_proto_options[index].maxval)
+ ret = SA_BAD_VALUE;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Check the length of the string
+ */
+static int
+string_length_check_validator(int index, char *value)
+{
+ int ret = SA_OK;
+
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+ if (strlen(value) > smb_proto_options[index].maxval)
+ ret = SA_BAD_VALUE;
+ return (ret);
+}
+
+/*
+ * Check yes/no
+ */
+/*ARGSUSED*/
+static int
+true_false_validator(int index, char *value)
+{
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+ if ((strcasecmp(value, "true") == 0) ||
+ (strcasecmp(value, "false") == 0))
+ return (SA_OK);
+ return (SA_BAD_VALUE);
+}
+
+/*
+ * Check IP address.
+ */
+/*ARGSUSED*/
+static int
+ip_address_validator_empty_ok(int index, char *value)
+{
+ char sbytes[16];
+ int len;
+
+ if (value == NULL)
+ return (SA_OK);
+ len = strlen(value);
+ if (len == 0)
+ return (SA_OK);
+ if (inet_pton(AF_INET, value, (void *)sbytes) != 1)
+ return (SA_BAD_VALUE);
+
+ return (SA_OK);
+}
+
+/*
+ * Check IP address list
+ */
+/*ARGSUSED*/
+static int
+ip_address_csv_list_validator_empty_ok(int index, char *value)
+{
+ char sbytes[16];
+ char *ip, *tmp, *ctx;
+
+ if (value == NULL || *value == '\0')
+ return (SA_OK);
+
+ if (strlen(value) > MAX_VALUE_BUFLEN)
+ return (SA_BAD_VALUE);
+
+ if ((tmp = strdup(value)) == NULL)
+ return (SA_NO_MEMORY);
+
+ ip = strtok_r(tmp, ",", &ctx);
+ while (ip) {
+ if (strlen(ip) == 0) {
+ free(tmp);
+ return (SA_BAD_VALUE);
+ }
+ if (*ip != 0) {
+ if (inet_pton(AF_INET, ip,
+ (void *)sbytes) != 1) {
+ free(tmp);
+ return (SA_BAD_VALUE);
+ }
+ }
+ ip = strtok_r(0, ",", &ctx);
+ }
+
+ free(tmp);
+ return (SA_OK);
+}
+
+/*
+ * Check IPC mode
+ */
+/*ARGSUSED*/
+static int
+ipc_mode_validator(int index, char *value)
+{
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+ if (strcasecmp(value, "anon") == 0)
+ return (SA_OK);
+ if (strcasecmp(value, "auth") == 0)
+ return (SA_OK);
+ return (SA_BAD_VALUE);
+}
+
+/*
+ * Check path
+ */
+/*ARGSUSED*/
+static int
+path_validator(int index, char *value)
+{
+ struct stat buffer;
+ int fd, status;
+
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+
+ fd = open(value, O_RDONLY);
+ if (fd < 0)
+ return (SA_BAD_VALUE);
+
+ status = fstat(fd, &buffer);
+ (void) close(fd);
+
+ if (status < 0)
+ return (SA_BAD_VALUE);
+
+ if (buffer.st_mode & S_IFDIR)
+ return (SA_OK);
+ return (SA_BAD_VALUE);
+}
+
+/*
+ * the protoset holds the defined options so we don't have to read
+ * them multiple times
+ */
+static sa_protocol_properties_t protoset;
+
+static int
+findprotoopt(char *name)
+{
+ int i;
+ for (i = 0; smb_proto_options[i].name != NULL; i++) {
+ if (strcasecmp(smb_proto_options[i].name, name) == 0)
+ return (i);
+ }
+ return (-1);
+}
+
+/*
+ * smb_load_proto_properties()
+ *
+ * read the smb config values from SMF.
+ */
+
+static int
+smb_load_proto_properties()
+{
+ sa_property_t prop;
+ int index;
+ char *value;
+
+ protoset = sa_create_protocol_properties(SMB_PROTOCOL_NAME);
+ if (protoset == NULL)
+ return (SA_NO_MEMORY);
+
+ if (smb_config_load() != 0)
+ return (SA_CONFIG_ERR);
+ for (index = 0; smb_proto_options[index].name != NULL; index++) {
+ value = smb_config_getenv(smb_proto_options[index].smb_index);
+ prop = sa_create_property(
+ smb_proto_options[index].name, value);
+ (void) sa_add_protocol_property(protoset, prop);
+ }
+ return (SA_OK);
+}
+
+/*
+ * smb_share_init()
+ *
+ * Initialize the smb plugin.
+ */
+
+static int
+smb_share_init(void)
+{
+ int ret = SA_OK;
+
+ if (sa_plugin_ops.sa_init != smb_share_init)
+ return (SA_SYSTEM_ERR);
+
+ if (smb_load_proto_properties() != SA_OK)
+ return (SA_SYSTEM_ERR);
+
+ return (ret);
+}
+
+/*
+ * smb_share_fini()
+ *
+ */
+static void
+smb_share_fini(void)
+{
+ xmlFreeNode(protoset);
+ protoset = NULL;
+}
+
+/*
+ * smb_get_proto_set()
+ *
+ * Return an optionset with all the protocol specific properties in
+ * it.
+ */
+static sa_protocol_properties_t
+smb_get_proto_set(void)
+{
+ return (protoset);
+}
+
+/*
+ * How long to wait for service to come online
+ */
+#define WAIT_FOR_SERVICE 15
+
+/*
+ * smb_enable_service()
+ *
+ */
+static int
+smb_enable_service(void)
+{
+ int i;
+ int ret = SA_OK;
+
+ if (!smb_isonline()) {
+ if (smf_enable_instance(SMBD_DEFAULT_INSTANCE_FMRI, 0) != 0) {
+ (void) fprintf(stderr,
+ dgettext(TEXT_DOMAIN,
+ "%s failed to restart: %s\n"),
+ scf_strerror(scf_error()));
+ return (SA_CONFIG_ERR);
+ }
+
+ /* Wait for service to come online */
+ for (i = 0; i < WAIT_FOR_SERVICE; i++) {
+ if (smb_isonline()) {
+ ret = SA_OK;
+ break;
+ } else {
+ ret = SA_BUSY;
+ (void) sleep(1);
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * smb_validate_proto_prop(index, name, value)
+ *
+ * Verify that the property specified by name can take the new
+ * value. This is a sanity check to prevent bad values getting into
+ * the default files.
+ */
+static int
+smb_validate_proto_prop(int index, char *name, char *value)
+{
+ if ((name == NULL) || (index < 0))
+ return (SA_BAD_VALUE);
+
+ if (smb_proto_options[index].validator == NULL)
+ return (SA_OK);
+
+ if (smb_proto_options[index].validator(index, value) == SA_OK)
+ return (SA_OK);
+ return (SA_BAD_VALUE);
+}
+
+/*
+ * smb_set_proto_prop(prop)
+ *
+ * check that prop is valid.
+ */
+/*ARGSUSED*/
+static int
+smb_set_proto_prop(sa_property_t prop)
+{
+ int ret = SA_OK;
+ char *name;
+ char *value;
+ int index = -1;
+
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (name != NULL && value != NULL) {
+ index = findprotoopt(name);
+ if (index >= 0) {
+ /* should test for valid value */
+ ret = smb_validate_proto_prop(index, name, value);
+ if (ret == SA_OK) {
+ /* Save to SMF */
+ smb_config_setenv(
+ smb_proto_options[index].smb_index, value);
+ /*
+ * Specialized refresh mechanisms can
+ * be flagged in the proto_options and
+ * processed here.
+ */
+ if (smb_proto_options[index].refresh &
+ SMB_REFRESH_REFRESH)
+ (void) smf_refresh_instance(
+ SMBD_DEFAULT_INSTANCE_FMRI);
+ else if (smb_proto_options[index].refresh &
+ SMB_REFRESH_RESTART)
+ (void) smf_restart_instance(
+ SMBD_DEFAULT_INSTANCE_FMRI);
+ }
+ }
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+
+ return (ret);
+}
+
+/*
+ * smb_get_status()
+ *
+ * What is the current status of the smbd? We use the SMF state here.
+ * Caller must free the returned value.
+ */
+
+static char *
+smb_get_status(void)
+{
+ char *state = NULL;
+ state = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI);
+ return (state != NULL ? state : "-");
+}
+
+/*
+ * This protocol plugin require resource names
+ */
+static uint64_t
+smb_share_features(void)
+{
+ return (SA_FEATURE_RESOURCE | SA_FEATURE_ALLOWSUBDIRS |
+ SA_FEATURE_ALLOWPARDIRS);
+}
+
+/*
+ * This should be used to convert lmshare_info to sa_resource_t
+ * Should only be needed to build temp shares/resources to be
+ * supplied to sharemanager to display temp shares.
+ */
+static int
+smb_build_tmp_sa_resource(sa_handle_t handle, lmshare_info_t *si)
+{
+ int err;
+ sa_share_t share;
+ sa_group_t group;
+ sa_resource_t resource;
+
+ if (si == NULL)
+ return (SA_INVALID_NAME);
+
+ /*
+ * First determine if the "share path" is already shared
+ * somewhere. If it is, we have to use it as the authority on
+ * where the transient share lives so will use it's parent
+ * group. If it doesn't exist, it needs to land in "smb".
+ */
+
+ share = sa_find_share(handle, si->directory);
+ if (share != NULL) {
+ group = sa_get_parent_group(share);
+ } else {
+ group = smb_get_smb_share_group(handle);
+ if (group == NULL)
+ return (SA_NO_SUCH_GROUP);
+ share = sa_get_share(group, si->directory);
+ if (share == NULL) {
+ share = sa_add_share(group, si->directory,
+ SA_SHARE_TRANSIENT, &err);
+ if (share == NULL)
+ return (SA_NO_SUCH_PATH);
+ }
+ }
+
+ /*
+ * Now handle the resource. Make sure that the resource is
+ * transient and added to the share.
+ */
+ resource = sa_get_share_resource(share, si->share_name);
+ if (resource == NULL) {
+ resource = sa_add_resource(share,
+ si->share_name, SA_SHARE_TRANSIENT, &err);
+ if (resource == NULL)
+ return (SA_NO_SUCH_RESOURCE);
+ }
+
+ /* set resource attributes now */
+ (void) sa_set_resource_attr(resource, "description", si->comment);
+ (void) sa_set_resource_attr(resource, SHOPT_AD_CONTAINER,
+ si->container);
+
+ return (SA_OK);
+}
+
+/*
+ * Return smb transient shares. Note that we really want to look at
+ * all current shares from SMB in order to determine this. Transient
+ * shares should be those that don't appear in either the SMF or ZFS
+ * configurations. Those that are in the repositories will be
+ * filtered out by smb_build_tmp_sa_resource.
+ */
+static int
+smb_list_transient(sa_handle_t handle)
+{
+ int i, offset, num;
+ lmshare_list_t list;
+ int res;
+
+ num = lmshrd_num_shares();
+ if (num <= 0)
+ return (SA_OK);
+ offset = 0;
+ while (lmshrd_list(offset, &list) != NERR_InternalError) {
+ if (list.no == 0)
+ break;
+ for (i = 0; i < list.no; i++) {
+ res = smb_build_tmp_sa_resource(handle,
+ &(list.smbshr[i]));
+ if (res != SA_OK)
+ return (res);
+ }
+ offset += list.no;
+ }
+
+ return (SA_OK);
+}
+
+/*
+ * fix_resource_name(share, name, prefix)
+ *
+ * Construct a name where the ZFS dataset has the prefix replaced with "name".
+ */
+static char *
+fix_resource_name(sa_share_t share, char *name, char *prefix)
+{
+ char *dataset = NULL;
+ char *newname = NULL;
+ size_t psize;
+ size_t nsize;
+
+ dataset = sa_get_share_attr(share, "dataset");
+
+ if (dataset != NULL && strcmp(dataset, prefix) != 0) {
+ psize = strlen(prefix);
+ if (strncmp(dataset, prefix, psize) == 0) {
+ /* need string plus ',' and NULL */
+ nsize = (strlen(dataset) - psize) + strlen(name) + 2;
+ newname = calloc(nsize, 1);
+ if (newname != NULL) {
+ (void) snprintf(newname, nsize, "%s%s", name,
+ dataset + psize);
+ sa_fix_resource_name(newname);
+ }
+ sa_free_attr_string(dataset);
+ return (newname);
+ }
+ }
+ if (dataset != NULL)
+ sa_free_attr_string(dataset);
+ return (strdup(name));
+}
+
+/*
+ * smb_parse_optstring(group, options)
+ *
+ * parse a compact option string into individual options. This allows
+ * ZFS sharesmb and sharemgr "share" command to work. group can be a
+ * group, a share or a resource.
+ */
+static int
+smb_parse_optstring(sa_group_t group, char *options)
+{
+ char *dup;
+ char *base;
+ char *lasts;
+ char *token;
+ sa_optionset_t optionset;
+ sa_group_t parent = NULL;
+ sa_resource_t resource = NULL;
+ int iszfs = 0;
+ int persist = 0;
+ int need_optionset = 0;
+ int ret = SA_OK;
+ sa_property_t prop;
+
+ /*
+ * In order to not attempt to change ZFS properties unless
+ * absolutely necessary, we never do it in the legacy parsing
+ * so we need to keep track of this.
+ */
+ if (sa_is_share(group)) {
+ char *zfs;
+
+ parent = sa_get_parent_group(group);
+ if (parent != NULL) {
+ zfs = sa_get_group_attr(parent, "zfs");
+ if (zfs != NULL) {
+ sa_free_attr_string(zfs);
+ iszfs = 1;
+ }
+ }
+ } else {
+ iszfs = sa_group_is_zfs(group);
+ /*
+ * If a ZFS group, then we need to see if a resource
+ * name is being set. If so, bail with
+ * SA_PROP_SHARE_ONLY, so we come back in with a share
+ * instead of a group.
+ */
+ if (strncmp(options, "name=", sizeof ("name=") - 1) == 0 ||
+ strstr(options, ",name=") != NULL) {
+ return (SA_PROP_SHARE_ONLY);
+ }
+ }
+
+ /* do we have an existing optionset? */
+ optionset = sa_get_optionset(group, "smb");
+ if (optionset == NULL) {
+ /* didn't find existing optionset so create one */
+ optionset = sa_create_optionset(group, "smb");
+ if (optionset == NULL)
+ return (SA_NO_MEMORY);
+ } else {
+ /*
+ * If an optionset already exists, we've come through
+ * twice so ignore the second time.
+ */
+ return (ret);
+ }
+
+ /* We need a copy of options for the next part. */
+ dup = strdup(options);
+ if (dup == NULL)
+ return (SA_NO_MEMORY);
+
+ /*
+ * SMB properties are straightforward and are strings,
+ * integers or booleans. Properties are separated by
+ * commas. It will be necessary to parse quotes due to some
+ * strings not having a restricted characters set.
+ *
+ * Note that names will create a resource. For now, if there
+ * is a set of properties "before" the first name="", those
+ * properties will be placed on the group.
+ */
+ persist = sa_is_persistent(group);
+ base = dup;
+ token = dup;
+ lasts = NULL;
+ while (token != NULL && ret == SA_OK) {
+ ret = SA_OK;
+ token = strtok_r(base, ",", &lasts);
+ base = NULL;
+ if (token != NULL) {
+ char *value;
+ /*
+ * All SMB properties have values so there
+ * MUST be an '=' character. If it doesn't,
+ * it is a syntax error.
+ */
+ value = strchr(token, '=');
+ if (value != NULL) {
+ *value++ = '\0';
+ } else {
+ ret = SA_SYNTAX_ERR;
+ break;
+ }
+ /*
+ * We may need to handle a "name" property
+ * that is a ZFS imposed resource name. Each
+ * name would trigger getting a new "resource"
+ * to put properties on. For now, assume no
+ * "name" property for special handling.
+ */
+
+ if (strcmp(token, "name") == 0) {
+ char *prefix;
+ char *name = NULL;
+ /*
+ * We have a name, so now work on the
+ * resource level. We have a "share"
+ * in "group" due to the caller having
+ * added it. If we are called with a
+ * group, the check for group/share
+ * at the beginning of this function
+ * will bail out the parse if there is a
+ * "name" but no share.
+ */
+ if (!iszfs) {
+ ret = SA_SYNTAX_ERR;
+ break;
+ }
+ /*
+ * Make sure the parent group has the
+ * "prefix" property since we will
+ * need to use this for constructing
+ * inherited name= values.
+ */
+ prefix = sa_get_group_attr(parent, "prefix");
+ if (prefix == NULL) {
+ prefix = sa_get_group_attr(parent,
+ "name");
+ if (prefix != NULL) {
+ (void) sa_set_group_attr(parent,
+ "prefix", prefix);
+ }
+ }
+ name = fix_resource_name((sa_share_t)group,
+ value, prefix);
+ if (name != NULL) {
+ resource = sa_add_resource(
+ (sa_share_t)group, name,
+ SA_SHARE_TRANSIENT, &ret);
+ sa_free_attr_string(name);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (prefix != NULL)
+ sa_free_attr_string(prefix);
+
+ /* A resource level optionset is needed */
+
+ need_optionset = 1;
+ if (resource == NULL) {
+ ret = SA_NO_MEMORY;
+ break;
+ }
+ continue;
+ }
+
+ if (need_optionset) {
+ optionset = sa_create_optionset(resource,
+ "smb");
+ need_optionset = 0;
+ }
+
+ prop = sa_create_property(token, value);
+ if (prop == NULL)
+ ret = SA_NO_MEMORY;
+ else
+ ret = sa_add_property(optionset, prop);
+ if (ret != SA_OK)
+ break;
+ if (!iszfs)
+ ret = sa_commit_properties(optionset, !persist);
+ }
+ }
+ free(dup);
+ return (ret);
+}
+
+/*
+ * smb_sprint_option(rbuff, rbuffsize, incr, prop, sep)
+ *
+ * provides a mechanism to format SMB properties into legacy output
+ * format. If the buffer would overflow, it is reallocated and grown
+ * as appropriate. Special cases of converting internal form of values
+ * to those used by "share" are done. this function does one property
+ * at a time.
+ */
+
+static void
+smb_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
+ sa_property_t prop, int sep)
+{
+ char *name;
+ char *value;
+ int curlen;
+ char *buff = *rbuff;
+ size_t buffsize = *rbuffsize;
+
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (buff != NULL)
+ curlen = strlen(buff);
+ else
+ curlen = 0;
+ if (name != NULL) {
+ int len;
+ len = strlen(name) + sep;
+
+ /*
+ * A future RFE would be to replace this with more
+ * generic code and to possibly handle more types.
+ *
+ * For now, everything else is treated as a string. If
+ * we get any properties that aren't exactly
+ * name/value pairs, we may need to
+ * interpret/transform.
+ */
+ if (value != NULL)
+ len += 1 + strlen(value);
+
+ while (buffsize <= (curlen + len)) {
+ /* need more room */
+ buffsize += incr;
+ buff = realloc(buff, buffsize);
+ *rbuff = buff;
+ *rbuffsize = buffsize;
+ if (buff == NULL) {
+ /* realloc failed so free everything */
+ if (*rbuff != NULL)
+ free(*rbuff);
+ goto err;
+ }
+ }
+ if (buff == NULL)
+ goto err;
+ (void) snprintf(buff + curlen, buffsize - curlen,
+ "%s%s=%s", sep ? "," : "",
+ name, value != NULL ? value : "\"\"");
+
+ }
+err:
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+}
+
+/*
+ * smb_format_resource_options(resource, hier)
+ *
+ * format all the options on the group into a flattened option
+ * string. If hier is non-zero, walk up the tree to get inherited
+ * options.
+ */
+
+static char *
+smb_format_options(sa_group_t group, int hier)
+{
+ sa_optionset_t options = NULL;
+ sa_property_t prop;
+ int sep = 0;
+ char *buff;
+ size_t buffsize;
+
+
+ buff = malloc(OPT_CHUNK);
+ if (buff == NULL)
+ return (NULL);
+
+ buff[0] = '\0';
+ buffsize = OPT_CHUNK;
+
+ /*
+ * We may have a an optionset relative to this item. format
+ * these if we find them and then add any security definitions.
+ */
+
+ options = sa_get_derived_optionset(group, "smb", hier);
+
+ /*
+ * do the default set first but skip any option that is also
+ * in the protocol specific optionset.
+ */
+ if (options != NULL) {
+ for (prop = sa_get_property(options, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ /*
+ * use this one since we skipped any
+ * of these that were also in
+ * optdefault
+ */
+ smb_sprint_option(&buff, &buffsize, OPT_CHUNK,
+ prop, sep);
+ if (buff == NULL) {
+ /*
+ * buff could become NULL if there
+ * isn't enough memory for
+ * smb_sprint_option to realloc()
+ * as necessary. We can't really
+ * do anything about it at this
+ * point so we return NULL. The
+ * caller should handle the
+ * failure.
+ */
+ if (options != NULL)
+ sa_free_derived_optionset(
+ options);
+ return (buff);
+ }
+ sep = 1;
+ }
+ }
+
+ if (options != NULL)
+ sa_free_derived_optionset(options);
+ return (buff);
+}
+
+/*
+ * smb_rename_resource(resource, newname)
+ *
+ * Change the current exported name of the resource to newname.
+ */
+/*ARGSUSED*/
+int
+smb_rename_resource(sa_handle_t handle, sa_resource_t resource, char *newname)
+{
+ int ret = SA_OK;
+ int err;
+ char *oldname;
+
+ oldname = sa_get_resource_attr(resource, "name");
+ if (oldname == NULL)
+ return (SA_NO_SUCH_RESOURCE);
+
+ err = lmshrd_rename(oldname, newname);
+
+ /* improve error values somewhat */
+ switch (err) {
+ case NERR_Success:
+ break;
+ case NERR_InternalError:
+ ret = SA_SYSTEM_ERR;
+ break;
+ case NERR_DuplicateShare:
+ ret = SA_DUPLICATE_NAME;
+ break;
+ default:
+ ret = SA_CONFIG_ERR;
+ break;
+ }
+
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/smb/libshare_smb.h b/usr/src/lib/libshare/smb/libshare_smb.h
new file mode 100644
index 0000000000..2d86c9ed32
--- /dev/null
+++ b/usr/src/lib/libshare/smb/libshare_smb.h
@@ -0,0 +1,75 @@
+/*
+ * 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"
+
+/*
+ * basic API declarations for share management
+ */
+
+#ifndef _LIBSHARE_SMB_H
+#define _LIBSHARE_SMB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <smbsrv/lmshare.h>
+
+/*
+ * defined options types. These should be in a file rather than
+ * compiled in. Until there is a plugin mechanism to add new types,
+ * this is sufficient.
+ */
+#define OPT_TYPE_ANY 0
+#define OPT_TYPE_STRING 1
+#define OPT_TYPE_BOOLEAN 2
+#define OPT_TYPE_NUMBER 3
+#define OPT_TYPE_PATH 4
+#define OPT_TYPE_PROTOCOL 5
+#define OPT_TYPE_NAME 6
+
+struct option_defs {
+ char *tag;
+ int type;
+ int share; /* share only option */
+ int (*check)(char *);
+};
+
+/*
+ * Sharectl property refresh types. Bit mask to indicate which type(s)
+ * of refresh might be needed on the service(s).
+ */
+
+#define SMB_REFRESH_RESTART 0x0001 /* restart smb/server */
+#define SMB_REFRESH_REFRESH 0x0002 /* refresh smb/server */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSHARE_SMB_H */
diff --git a/usr/src/lib/libshare/smb/mapfile-vers b/usr/src/lib/libshare/smb/mapfile-vers
new file mode 100644
index 0000000000..18c216bf6b
--- /dev/null
+++ b/usr/src/lib/libshare/smb/mapfile-vers
@@ -0,0 +1,34 @@
+#
+# 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_1.1 {
+ global:
+ sa_plugin_ops;
+ local:
+ *;
+};
+
diff --git a/usr/src/lib/libshare/smb/smb_share_doorclnt.c b/usr/src/lib/libshare/smb/smb_share_doorclnt.c
new file mode 100644
index 0000000000..0ebf61b7a9
--- /dev/null
+++ b/usr/src/lib/libshare/smb/smb_share_doorclnt.c
@@ -0,0 +1,951 @@
+/*
+ * 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 LanMan share management.
+ */
+
+#include <syslog.h>
+#include <door.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+
+#include <smbsrv/libsmb.h>
+
+#include <smbsrv/lmshare.h>
+#include <smbsrv/lmerr.h>
+#include <smbsrv/lmshare_door.h>
+#include <smbsrv/cifs.h>
+
+int lmshrd_fildes = -1;
+
+char *lmshrd_desc[] = {
+ "",
+ "LmshrdOpenIter",
+ "LmshrdCloseIter",
+ "LmshrdIterate",
+ "LmshrdNumShares",
+ "LmshrdDelete",
+ "LmshrdRename",
+ "LmshrdGetinfo",
+ "LmshrdAdd",
+ "LmshrdSetinfo",
+ "LmshrdExists",
+ "LmshrdIsSpecial",
+ "LmshrdIsRestricted",
+ "LmshrdIsAdmin",
+ "LmshrdIsValid",
+ "LmshrdIsDir",
+ "LmshrdList",
+ "LmshrdListTrans",
+ "LmshrdNumTrans",
+ "N/A",
+ 0
+};
+
+/*
+ * Returns 0 on success. Otherwise, -1.
+ */
+static int
+lmshrd_door_open(int opcode)
+{
+ int rc = 0;
+
+ if (lmshrd_fildes == -1 &&
+ (lmshrd_fildes = open(LMSHR_DOOR_NAME, O_RDONLY)) < 0) {
+ syslog(LOG_DEBUG, "%s: open %s failed %s", lmshrd_desc[opcode],
+ LMSHR_DOOR_NAME, strerror(errno));
+ rc = -1;
+ }
+ return (rc);
+}
+
+/*
+ * Return 0 upon success. Otherwise, -1.
+ */
+static int
+lmshrd_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 LMSHR_DOOR_SRV_SUCCESS:
+ rc = 0;
+ break;
+
+ case LMSHR_DOOR_SRV_ERROR:
+ err = smb_dr_get_uint32(dec_ctx);
+ syslog(LOG_ERR, "%s: Encountered door server error %s",
+ lmshrd_desc[opcode], strerror(err));
+ break;
+
+ default:
+ syslog(LOG_ERR, "%s: Unknown door server status",
+ lmshrd_desc[opcode]);
+ }
+
+ if (rc != 0) {
+ if ((err = smb_dr_decode_finish(dec_ctx)) != 0)
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(err));
+ }
+
+ return (rc);
+}
+
+uint64_t
+lmshrd_open_iterator(int mode)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ unsigned int status = 0;
+ uint64_t lmshr_iter = 0;
+ int opcode = LMSHR_DOOR_OPEN_ITERATOR;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (lmshr_iter);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (lmshr_iter);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_int32(enc_ctx, mode);
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (lmshr_iter);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (lmshr_iter);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (lmshr_iter);
+ }
+
+ lmshr_iter = smb_dr_get_lmshr_iterator(dec_ctx);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (lmshr_iter);
+ }
+
+ (void) free(buf);
+ return (lmshr_iter);
+}
+
+
+DWORD
+lmshrd_close_iterator(uint64_t iterator)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ unsigned int status = 0;
+ int opcode = LMSHR_DOOR_CLOSE_ITERATOR;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (NERR_InternalError);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_lmshr_iterator(enc_ctx, iterator);
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+ return (NERR_Success);
+}
+
+DWORD
+lmshrd_iterate(uint64_t iterator, lmshare_info_t *si)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ unsigned int status = 0;
+ int opcode = LMSHR_DOOR_ITERATE;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (NERR_InternalError);
+
+ bzero(si, sizeof (lmshare_info_t));
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_lmshr_iterator(enc_ctx, iterator);
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ smb_dr_get_lmshare(dec_ctx, si);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+ return (NERR_Success);
+}
+
+DWORD
+lmshrd_list(int offset, lmshare_list_t *list)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = LMSHR_DOOR_LIST;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+
+ if (!buf)
+ return (NERR_InternalError);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_int32(enc_ctx, offset);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ rc = smb_dr_get_uint32(dec_ctx);
+ smb_dr_get_lmshr_list(dec_ctx, list);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+
+ return (rc);
+}
+
+int
+lmshrd_num_shares(void)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ unsigned int status = 0;
+ DWORD num_shares;
+ int opcode = LMSHR_DOOR_NUM_SHARES;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (-1);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (-1);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, LMSHR_DOOR_NUM_SHARES);
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (-1);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (-1);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (-1);
+ }
+
+ num_shares = smb_dr_get_uint32(dec_ctx);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (-1);
+ }
+
+ (void) free(buf);
+ return (num_shares);
+}
+
+DWORD
+lmshrd_delete(char *share_name)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = LMSHR_DOOR_DELETE;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (NERR_InternalError);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, LMSHR_DOOR_DELETE);
+ smb_dr_put_string(enc_ctx, share_name);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ rc = smb_dr_get_uint32(dec_ctx);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+ return (rc);
+
+}
+
+DWORD
+lmshrd_rename(char *from, char *to)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = LMSHR_DOOR_RENAME;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (NERR_InternalError);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, LMSHR_DOOR_RENAME);
+ smb_dr_put_string(enc_ctx, from);
+ smb_dr_put_string(enc_ctx, to);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ rc = smb_dr_get_uint32(dec_ctx);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+ return (rc);
+}
+
+DWORD
+lmshrd_getinfo(char *share_name, lmshare_info_t *si)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = LMSHR_DOOR_GETINFO;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (NERR_InternalError);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, LMSHR_DOOR_GETINFO);
+ smb_dr_put_string(enc_ctx, share_name);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ rc = smb_dr_get_uint32(dec_ctx);
+ smb_dr_get_lmshare(dec_ctx, si);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+ return (rc);
+}
+
+DWORD
+lmshrd_add(lmshare_info_t *si)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = LMSHR_DOOR_ADD;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (NERR_InternalError);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_lmshare(enc_ctx, si);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ rc = smb_dr_get_uint32(dec_ctx);
+ smb_dr_get_lmshare(dec_ctx, si);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+ return (rc);
+}
+
+DWORD
+lmshrd_setinfo(lmshare_info_t *si)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status;
+ DWORD rc;
+ int opcode = LMSHR_DOOR_SETINFO;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (NERR_InternalError);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_lmshare(enc_ctx, si);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ rc = smb_dr_get_uint32(dec_ctx);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+ return (rc);
+}
+
+static int
+lmshrd_check(char *share_name, int opcode)
+{
+ door_arg_t arg;
+ char *buf;
+ unsigned int used;
+ smb_dr_ctx_t *dec_ctx;
+ smb_dr_ctx_t *enc_ctx;
+ int status, rc;
+
+ if (lmshrd_door_open(opcode) == -1)
+ return (NERR_InternalError);
+
+ buf = malloc(LMSHR_DOOR_SIZE);
+ if (!buf)
+ return (NERR_InternalError);
+
+ enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE);
+ smb_dr_put_uint32(enc_ctx, opcode);
+ smb_dr_put_string(enc_ctx, share_name);
+
+ if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
+ syslog(LOG_ERR, "%s: Encode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ arg.data_ptr = buf;
+ arg.data_size = used;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = buf;
+ arg.rsize = LMSHR_DOOR_SIZE;
+
+ if (door_call(lmshrd_fildes, &arg) < 0) {
+ syslog(LOG_DEBUG, "%s: Door call failed %s",
+ lmshrd_desc[opcode], strerror(errno));
+ (void) free(buf);
+ lmshrd_fildes = -1;
+ return (NERR_InternalError);
+ }
+
+ dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
+ if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) {
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ rc = smb_dr_get_int32(dec_ctx);
+ if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
+ syslog(LOG_ERR, "%s: Decode error %s",
+ lmshrd_desc[opcode], strerror(status));
+ (void) free(buf);
+ return (NERR_InternalError);
+ }
+
+ (void) free(buf);
+ return (rc);
+}
+
+int
+lmshrd_exists(char *share_name)
+{
+ return (lmshrd_check(share_name, LMSHR_DOOR_EXISTS));
+}
+
+int
+lmshrd_is_special(char *share_name)
+{
+ return (lmshrd_check(share_name, LMSHR_DOOR_IS_SPECIAL));
+}
+
+int
+lmshrd_is_restricted(char *share_name)
+{
+ return (lmshrd_check(share_name, LMSHR_DOOR_IS_RESTRICTED));
+}
+
+int
+lmshrd_is_admin(char *share_name)
+{
+ return (lmshrd_check(share_name, LMSHR_DOOR_IS_ADMIN));
+}
+
+int
+lmshrd_is_valid(char *share_name)
+{
+ return (lmshrd_check(share_name, LMSHR_DOOR_IS_VALID));
+}
+
+int
+lmshrd_is_dir(char *path)
+{
+ return (lmshrd_check(path, LMSHR_DOOR_IS_DIR));
+}
+
+static char *
+lmshare_decode_type(unsigned int 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");
+ };
+}
+
+
+static void
+lmshare_loginfo(FILE *fp, lmshare_info_t *si)
+{
+ if (!si) {
+ return;
+ }
+
+ (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(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);
+}
+
+int
+lmshrd_dump_hash(char *logfname)
+{
+ lmshare_info_t si;
+ uint64_t it;
+ FILE *fp;
+
+ if ((logfname == 0) || (*logfname == 0))
+ fp = stdout;
+ else {
+ fp = fopen(logfname, "w");
+ if (fp == 0) {
+ syslog(LOG_WARNING, "LmshareDump [%s]:"
+ " cannot create logfile", logfname);
+ syslog(LOG_WARNING, "LmshareDump:"
+ " output will be written on screen");
+ }
+ }
+
+ it = lmshrd_open_iterator(LMSHRM_PERM);
+ if (it == NULL) {
+ syslog(LOG_ERR, "LmshareDump: resource shortage");
+ if (fp && fp != stdout) {
+ (void) fclose(fp);
+ }
+ return (1);
+ }
+
+ if (lmshrd_iterate(it, &si) != NERR_Success) {
+ syslog(LOG_ERR, "LmshareDump: Iterator iterate failed");
+ if (fp && fp != stdout) {
+ (void) fclose(fp);
+ }
+ return (1);
+ }
+ while (*si.share_name != 0) {
+ lmshare_loginfo(fp, &si);
+ if (lmshrd_iterate(it, &si) != NERR_Success) {
+ syslog(LOG_ERR, "LmshareDump: Iterator iterate failed");
+ if (fp && fp != stdout) {
+ (void) fclose(fp);
+ }
+ return (1);
+ }
+ }
+
+ if (lmshrd_close_iterator(it) != NERR_Success) {
+ syslog(LOG_ERR, "LmshareDump: Iterator close failed");
+ if (fp && fp != stdout) {
+ (void) fclose(fp);
+ }
+ return (1);
+ }
+ if (fp && fp != stdout) {
+ (void) fclose(fp);
+ }
+ return (0);
+}
diff --git a/usr/src/lib/libshare/smb/sparc/Makefile b/usr/src/lib/libshare/smb/sparc/Makefile
new file mode 100644
index 0000000000..543238473c
--- /dev/null
+++ b/usr/src/lib/libshare/smb/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
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/libshare/smb/sparcv9/Makefile b/usr/src/lib/libshare/smb/sparcv9/Makefile
new file mode 100644
index 0000000000..90b1730d23
--- /dev/null
+++ b/usr/src/lib/libshare/smb/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
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libsldap/common/ns_writes.c b/usr/src/lib/libsldap/common/ns_writes.c
index 6c39272eba..4ac8c16c13 100644
--- a/usr/src/lib/libsldap/common/ns_writes.c
+++ b/usr/src/lib/libsldap/common/ns_writes.c
@@ -86,7 +86,7 @@ replace_mapped_attr_in_dn(
*new_dn = NULL;
/*
- * seperate dn into individual componets
+ * separate dn into individual componets
* e.g.
* "automountKey=user_01" , "automountMapName_test=auto_home", ...
*/
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index 73bd80d201..265d22e66b 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -110,6 +110,8 @@ enum {
EZFS_BADPERMSET, /* invalid permission set name */
EZFS_NODELEGATION, /* delegated administration is disabled */
EZFS_PERMRDONLY, /* pemissions are readonly */
+ EZFS_UNSHARESMBFAILED, /* failed to unshare over smb */
+ EZFS_SHARESMBFAILED, /* failed to share over smb */
EZFS_UNKNOWN
};
@@ -320,7 +322,6 @@ extern const char *zfs_get_name(const zfs_handle_t *);
/*
* zfs dataset property management
*/
-extern int zfs_prop_valid_for_type(zfs_prop_t, int);
extern const char *zfs_prop_default_string(zfs_prop_t);
extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
extern const char *zfs_prop_column_name(zfs_prop_t);
@@ -459,15 +460,22 @@ extern int zfs_unshare(zfs_handle_t *);
* Protocol-specific share support functions.
*/
extern boolean_t zfs_is_shared_nfs(zfs_handle_t *, char **);
+extern boolean_t zfs_is_shared_smb(zfs_handle_t *, char **);
extern int zfs_share_nfs(zfs_handle_t *);
+extern int zfs_share_smb(zfs_handle_t *);
+extern int zfs_shareall(zfs_handle_t *);
extern int zfs_unshare_nfs(zfs_handle_t *, const char *);
+extern int zfs_unshare_smb(zfs_handle_t *, const char *);
extern int zfs_unshareall_nfs(zfs_handle_t *);
+extern int zfs_unshareall_smb(zfs_handle_t *);
+extern int zfs_unshareall_bypath(zfs_handle_t *, const char *);
+extern int zfs_unshareall(zfs_handle_t *);
extern boolean_t zfs_is_shared_iscsi(zfs_handle_t *);
extern int zfs_share_iscsi(zfs_handle_t *);
extern int zfs_unshare_iscsi(zfs_handle_t *);
extern int zfs_iscsi_perm_check(libzfs_handle_t *, char *, ucred_t *);
extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *,
- void *, void *, int, boolean_t);
+ void *, void *, int, zfs_share_op_t);
/*
* When dealing with nvlists, verify() is extremely useful
diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c
index 9ceefdc3b1..2b53f7d983 100644
--- a/usr/src/lib/libzfs/common/libzfs_changelist.c
+++ b/usr/src/lib/libzfs/common/libzfs_changelist.c
@@ -71,6 +71,7 @@ typedef struct prop_changenode {
struct prop_changelist {
zfs_prop_t cl_prop;
zfs_prop_t cl_realprop;
+ zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
uu_list_pool_t *cl_pool;
uu_list_t *cl_list;
boolean_t cl_waslegacy;
@@ -185,6 +186,7 @@ changelist_postfix(prop_changelist_t *clp)
cn = uu_list_prev(clp->cl_list, cn)) {
boolean_t sharenfs;
+ boolean_t sharesmb;
/*
* If we are in the global zone, but this dataset is exported
@@ -222,14 +224,18 @@ changelist_postfix(prop_changelist_t *clp)
/*
* Remount if previously mounted or mountpoint was legacy,
- * or sharenfs property is set.
+ * or sharenfs or sharesmb property is set.
*/
sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
shareopts, sizeof (shareopts), NULL, NULL, 0,
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
- if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs) &&
- !zfs_is_mounted(cn->cn_handle, NULL) &&
+ sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
+ shareopts, sizeof (shareopts), NULL, NULL, 0,
+ B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
+
+ if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs ||
+ sharesmb) && !zfs_is_mounted(cn->cn_handle, NULL) &&
zfs_mount(cn->cn_handle, NULL, 0) != 0)
ret = -1;
@@ -237,11 +243,16 @@ changelist_postfix(prop_changelist_t *clp)
* We always re-share even if the filesystem is currently
* shared, so that we can adopt any new options.
*/
- if (cn->cn_shared || clp->cl_waslegacy || sharenfs) {
+ if (cn->cn_shared || clp->cl_waslegacy ||
+ sharenfs || sharesmb) {
if (sharenfs)
ret = zfs_share_nfs(cn->cn_handle);
else
ret = zfs_unshare_nfs(cn->cn_handle, NULL);
+ if (sharesmb)
+ ret = zfs_share_smb(cn->cn_handle);
+ else
+ ret = zfs_unshare_smb(cn->cn_handle, NULL);
}
}
@@ -302,21 +313,22 @@ changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
}
/*
- * Given a gathered changelist for the 'sharenfs' property, unshare all the
- * datasets in the list.
+ * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
+ * unshare all the datasets in the list.
*/
int
-changelist_unshare(prop_changelist_t *clp)
+changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
{
prop_changenode_t *cn;
int ret = 0;
- if (clp->cl_prop != ZFS_PROP_SHARENFS)
+ if (clp->cl_prop != ZFS_PROP_SHARENFS &&
+ clp->cl_prop != ZFS_PROP_SHARESMB)
return (0);
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
- if (zfs_unshare_nfs(cn->cn_handle, NULL) != 0)
+ if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
ret = -1;
}
@@ -386,6 +398,7 @@ change_one(zfs_handle_t *zhp, void *data)
char where[64];
prop_changenode_t *cn;
zprop_source_t sourcetype;
+ zprop_source_t share_sourcetype;
/*
* We only want to unmount/unshare those filesystems that may inherit
@@ -405,9 +418,25 @@ change_one(zfs_handle_t *zhp, void *data)
return (0);
}
+ /*
+ * If we are "watching" sharenfs or sharesmb
+ * then check out the companion property which is tracked
+ * in cl_shareprop
+ */
+ if (clp->cl_shareprop != ZPROP_INVAL &&
+ zfs_prop_get(zhp, clp->cl_shareprop, property,
+ sizeof (property), &share_sourcetype, where, sizeof (where),
+ B_FALSE) != 0) {
+ zfs_close(zhp);
+ return (0);
+ }
+
if (clp->cl_alldependents || clp->cl_allchildren ||
sourcetype == ZPROP_SRC_DEFAULT ||
- sourcetype == ZPROP_SRC_INHERITED) {
+ sourcetype == ZPROP_SRC_INHERITED ||
+ (clp->cl_shareprop != ZPROP_INVAL &&
+ (share_sourcetype == ZPROP_SRC_DEFAULT ||
+ share_sourcetype == ZPROP_SRC_INHERITED))) {
if ((cn = zfs_alloc(zfs_get_handle(zhp),
sizeof (prop_changenode_t))) == NULL) {
zfs_close(zhp);
@@ -507,7 +536,8 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
* order, regardless of their position in the hierarchy.
*/
if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
- prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
+ prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
+ prop == ZFS_PROP_SHARESMB) {
compare = compare_mountpoints;
clp->cl_sorted = B_TRUE;
}
@@ -561,9 +591,19 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
clp->cl_prop != ZFS_PROP_SHARENFS &&
+ clp->cl_prop != ZFS_PROP_SHARESMB &&
clp->cl_prop != ZFS_PROP_SHAREISCSI)
return (clp);
+ /*
+ * If watching SHARENFS or SHARESMB then
+ * also watch its companion property.
+ */
+ if (clp->cl_prop == ZFS_PROP_SHARENFS)
+ clp->cl_shareprop = ZFS_PROP_SHARESMB;
+ else if (clp->cl_prop == ZFS_PROP_SHARESMB)
+ clp->cl_shareprop = ZFS_PROP_SHARENFS;
+
if (clp->cl_alldependents) {
if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
changelist_free(clp);
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 04a37032ea..db912e7f20 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -51,6 +51,7 @@
#include <sys/spa.h>
#include <sys/zio.h>
#include <sys/zap.h>
+#include <sys/zfs_i18n.h>
#include <libzfs.h>
#include "zfs_namecheck.h"
@@ -376,7 +377,7 @@ top:
if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0)
goto top;
/*
- * If we can sucessfully destroy it, pretend that it
+ * If we can successfully destroy it, pretend that it
* never existed.
*/
if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) {
@@ -481,6 +482,8 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
char *strval;
zfs_prop_t prop;
nvlist_t *ret;
+ int chosen_normal = -1;
+ int chosen_utf = -1;
if (type == ZFS_TYPE_SNAPSHOT) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -545,7 +548,7 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
}
if (zfs_prop_readonly(prop) &&
- (prop != ZFS_PROP_VOLBLOCKSIZE || zhp != NULL)) {
+ (!zfs_prop_setonce(prop) || zhp != NULL)) {
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "'%s' is readonly"),
propname);
@@ -636,19 +639,23 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
/*FALLTHRU*/
+ case ZFS_PROP_SHARESMB:
case ZFS_PROP_SHARENFS:
/*
- * For the mountpoint and sharenfs properties, check if
- * it can be set in a global/non-global zone based on
+ * For the mountpoint and sharenfs or sharesmb
+ * properties, check if it can be set in a
+ * global/non-global zone based on
* the zoned property value:
*
* global zone non-global zone
* --------------------------------------------------
* zoned=on mountpoint (no) mountpoint (yes)
* sharenfs (no) sharenfs (no)
+ * sharesmb (no) sharesmb (no)
*
* zoned=off mountpoint (yes) N/A
* sharenfs (yes)
+ * sharesmb (yes)
*/
if (zoned) {
if (getzoneid() == GLOBAL_ZONEID) {
@@ -659,7 +666,8 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
(void) zfs_error(hdl, EZFS_ZONED,
errbuf);
goto error;
- } else if (prop == ZFS_PROP_SHARENFS) {
+ } else if (prop == ZFS_PROP_SHARENFS ||
+ prop == ZFS_PROP_SHARESMB) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"'%s' cannot be set in "
"a non-global zone"), propname);
@@ -684,17 +692,24 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
* property. Now we want to make sure that the
* property value is valid if it is sharenfs.
*/
- if (prop == ZFS_PROP_SHARENFS &&
+ if ((prop == ZFS_PROP_SHARENFS ||
+ prop == ZFS_PROP_SHARESMB) &&
strcmp(strval, "on") != 0 &&
strcmp(strval, "off") != 0) {
+ zfs_share_proto_t proto;
+
+ if (prop == ZFS_PROP_SHARESMB)
+ proto = PROTO_SMB;
+ else
+ proto = PROTO_NFS;
/*
- * Must be an NFS option string so
- * init the libshare in order to
- * enable the parser and then parse
- * the options. We use the control API
- * since we don't care about the
- * current configuration and don't
+ * Must be an valid sharing protocol
+ * option string so init the libshare
+ * in order to enable the parser and
+ * then parse the options. We use the
+ * control API since we don't care about
+ * the current configuration and don't
* want the overhead of loading it
* until we actually do something.
*/
@@ -714,7 +729,7 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
goto error;
}
- if (zfs_parse_options(strval, "nfs") != SA_OK) {
+ if (zfs_parse_options(strval, proto) != SA_OK) {
/*
* There was an error in parsing so
* deal with it by issuing an error
@@ -734,6 +749,12 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
}
break;
+ case ZFS_PROP_UTF8ONLY:
+ chosen_utf = (int)intval;
+ break;
+ case ZFS_PROP_NORMALIZE:
+ chosen_normal = (int)intval;
+ break;
}
/*
@@ -786,6 +807,27 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
}
/*
+ * If normalization was chosen, but no UTF8 choice was made,
+ * enforce rejection of non-UTF8 names.
+ *
+ * If normalization was chosen, but rejecting non-UTF8 names
+ * was explicitly not chosen, it is an error.
+ */
+ if (chosen_normal > ZFS_NORMALIZE_NONE && chosen_utf < 0) {
+ if (nvlist_add_uint64(ret,
+ zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
+ (void) no_memory(hdl);
+ goto error;
+ }
+ } else if (chosen_normal > ZFS_NORMALIZE_NONE && chosen_utf == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be set 'on' if normalization chosen"),
+ zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ /*
* If this is an existing volume, and someone is setting the volsize,
* make sure that it matches the reservation, or add it if necessary.
*/
@@ -941,7 +983,7 @@ zfs_perms_add_who_nvlist(nvlist_t *who_nvp, uint64_t whoid, void *whostr,
* whostr may be null for everyone or create perms.
* who_type: is the type of entry in whostr. Typically this will be
* ZFS_DELEG_WHO_UNKNOWN.
- * perms: comman separated list of permissions. May be null if user
+ * perms: common separated list of permissions. May be null if user
* is requested to remove permissions by who.
* inherit: Specifies the inheritance of the permissions. Will be either
* ZFS_DELEG_PERM_LOCAL and/or ZFS_DELEG_PERM_DESCENDENT.
@@ -1833,6 +1875,11 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
mntopt_on = MNTOPT_XATTR;
mntopt_off = MNTOPT_NOXATTR;
break;
+
+ case ZFS_PROP_NBMAND:
+ mntopt_on = MNTOPT_NBMAND;
+ mntopt_off = MNTOPT_NONBMAND;
+ break;
}
/*
@@ -1871,6 +1918,7 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
case ZFS_PROP_READONLY:
case ZFS_PROP_SETUID:
case ZFS_PROP_XATTR:
+ case ZFS_PROP_NBMAND:
*val = getprop_uint64(zhp, prop, source);
if (hasmntopt(&mnt, mntopt_on) && !*val) {
@@ -4177,7 +4225,7 @@ zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred)
int
zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
- void *export, void *sharetab, int sharemax, boolean_t share_on)
+ void *export, void *sharetab, int sharemax, zfs_share_op_t operation)
{
zfs_cmd_t zc = { 0 };
int error;
@@ -4186,7 +4234,7 @@ zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
- zc.zc_share.z_sharetype = share_on;
+ zc.zc_share.z_sharetype = operation;
zc.zc_share.z_sharemax = sharemax;
error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index 19a7590cee..cfc03791dd 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -90,6 +90,23 @@ struct zpool_handle {
diskaddr_t zpool_start_block;
};
+typedef enum {
+ PROTO_NFS = 0,
+ PROTO_SMB = 1,
+ PROTO_END = 2
+} zfs_share_proto_t;
+
+/*
+ * The following can be used as a bitmask and any new values
+ * added must preserve that capability.
+ */
+typedef enum {
+ SHARED_NOT_SHARED = 0x0,
+ SHARED_ISCSI = 0x1,
+ SHARED_NFS = 0x2,
+ SHARED_SMB = 0x4
+} zfs_share_type_t;
+
int zfs_error(libzfs_handle_t *, int, const char *);
int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...);
void zfs_error_aux(libzfs_handle_t *, const char *, ...);
@@ -127,7 +144,7 @@ void changelist_rename(prop_changelist_t *, const char *, const char *);
void changelist_remove(zfs_handle_t *, prop_changelist_t *);
void changelist_free(prop_changelist_t *);
prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int);
-int changelist_unshare(prop_changelist_t *);
+int changelist_unshare(prop_changelist_t *, zfs_share_proto_t *);
int changelist_haszonedchild(prop_changelist_t *);
void remove_mountpoint(zfs_handle_t *);
@@ -148,8 +165,10 @@ void namespace_clear(libzfs_handle_t *);
extern int zfs_init_libshare(libzfs_handle_t *, int);
extern void zfs_uninit_libshare(libzfs_handle_t *);
-extern int zfs_parse_options(char *, char *);
+extern int zfs_parse_options(char *, zfs_share_proto_t);
+extern int zfs_unshare_proto(zfs_handle_t *zhp,
+ const char *, zfs_share_proto_t *);
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libzfs/common/libzfs_mount.c b/usr/src/lib/libzfs/common/libzfs_mount.c
index d7dd227bc8..6810f7efdc 100644
--- a/usr/src/lib/libzfs/common/libzfs_mount.c
+++ b/usr/src/lib/libzfs/common/libzfs_mount.c
@@ -45,11 +45,17 @@
* zfs_unshare()
*
* zfs_is_shared_nfs()
- * zfs_share_nfs()
- * zfs_unshare_nfs()
- * zfs_unshareall_nfs()
+ * zfs_is_shared_smb()
* zfs_is_shared_iscsi()
+ * zfs_share_proto()
+ * zfs_shareall();
* zfs_share_iscsi()
+ * zfs_unshare_nfs()
+ * zfs_unshare_smb()
+ * zfs_unshareall_nfs()
+ * zfs_unshareall_smb()
+ * zfs_unshareall()
+ * zfs_unshareall_bypath()
* zfs_unshare_iscsi()
*
* The following functions are available for pool consumers, and will
@@ -82,11 +88,46 @@
#include <sys/systeminfo.h>
#define MAXISALEN 257 /* based on sysinfo(2) man page */
+static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
+zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
+ zfs_share_proto_t);
+
static int (*iscsitgt_zfs_share)(const char *);
static int (*iscsitgt_zfs_unshare)(const char *);
static int (*iscsitgt_zfs_is_shared)(const char *);
static int (*iscsitgt_svc_online)();
+/*
+ * The share protocols table must be in the same order as the zfs_share_prot_t
+ * enum in libzfs_impl.h
+ */
+typedef struct {
+ zfs_prop_t p_prop;
+ char *p_name;
+ int p_share_err;
+ int p_unshare_err;
+} proto_table_t;
+
+proto_table_t proto_table[PROTO_END] = {
+ {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED},
+ {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED},
+};
+
+zfs_share_proto_t nfs_only[] = {
+ PROTO_NFS,
+ PROTO_END
+};
+
+zfs_share_proto_t smb_only[] = {
+ PROTO_SMB,
+ PROTO_END
+};
+zfs_share_proto_t share_all_proto[] = {
+ PROTO_NFS,
+ PROTO_SMB,
+ PROTO_END
+};
+
#pragma init(zfs_iscsi_init)
static void
zfs_iscsi_init(void)
@@ -111,29 +152,54 @@ zfs_iscsi_init(void)
}
/*
- * Search the sharetab for the given mountpoint, returning true if it is found.
+ * Search the sharetab for the given mountpoint and protocol, returning
+ * a zfs_share_type_t value.
*/
-static boolean_t
-is_shared(libzfs_handle_t *hdl, const char *mountpoint)
+static zfs_share_type_t
+is_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto)
{
char buf[MAXPATHLEN], *tab;
+ char *ptr;
if (hdl->libzfs_sharetab == NULL)
- return (0);
+ return (SHARED_NOT_SHARED);
(void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
/* the mountpoint is the first entry on each line */
- if ((tab = strchr(buf, '\t')) != NULL) {
+ if ((tab = strchr(buf, '\t')) == NULL)
+ continue;
+
+ *tab = '\0';
+ if (strcmp(buf, mountpoint) == 0) {
+ /*
+ * the protocol field is the third field
+ * skip over second field
+ */
+ ptr = ++tab;
+ if ((tab = strchr(ptr, '\t')) == NULL)
+ continue;
+ ptr = ++tab;
+ if ((tab = strchr(ptr, '\t')) == NULL)
+ continue;
*tab = '\0';
- if (strcmp(buf, mountpoint) == 0)
- return (B_TRUE);
+ if (strcmp(ptr,
+ proto_table[proto].p_name) == 0) {
+ switch (proto) {
+ case PROTO_NFS:
+ return (SHARED_NFS);
+ case PROTO_SMB:
+ return (SHARED_SMB);
+ default:
+ return (0);
+ }
+ }
}
}
- return (B_FALSE);
+ return (SHARED_NOT_SHARED);
}
/*
@@ -349,12 +415,12 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
/*
* Unshare and unmount the filesystem
*/
- if (zfs_unshare_nfs(zhp, mntpt) != 0)
+ if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0)
return (-1);
if (unmount_one(zhp->zfs_hdl, mntpt, flags) != 0) {
free(mntpt);
- (void) zfs_share_nfs(zhp);
+ (void) zfs_shareall(zhp);
return (-1);
}
free(mntpt);
@@ -387,10 +453,17 @@ zfs_unmountall(zfs_handle_t *zhp, int flags)
boolean_t
zfs_is_shared(zfs_handle_t *zhp)
{
+ zfs_share_type_t rc = 0;
+ zfs_share_proto_t *curr_proto;
+
if (ZFS_IS_VOLUME(zhp))
return (zfs_is_shared_iscsi(zhp));
- return (zfs_is_shared_nfs(zhp, NULL));
+ for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
+ curr_proto++)
+ rc |= zfs_is_shared_proto(zhp, NULL, *curr_proto);
+
+ return (rc ? B_TRUE : B_FALSE);
}
int
@@ -399,7 +472,7 @@ zfs_share(zfs_handle_t *zhp)
if (ZFS_IS_VOLUME(zhp))
return (zfs_share_iscsi(zhp));
- return (zfs_share_nfs(zhp));
+ return (zfs_share_proto(zhp, share_all_proto));
}
int
@@ -408,32 +481,47 @@ zfs_unshare(zfs_handle_t *zhp)
if (ZFS_IS_VOLUME(zhp))
return (zfs_unshare_iscsi(zhp));
- return (zfs_unshare_nfs(zhp, NULL));
+ return (zfs_unshareall(zhp));
}
/*
* Check to see if the filesystem is currently shared.
*/
-boolean_t
-zfs_is_shared_nfs(zfs_handle_t *zhp, char **where)
+zfs_share_type_t
+zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto)
{
char *mountpoint;
+ zfs_share_type_t rc;
if (!zfs_is_mounted(zhp, &mountpoint))
- return (B_FALSE);
+ return (SHARED_NOT_SHARED);
- if (is_shared(zhp->zfs_hdl, mountpoint)) {
+ if (rc = is_shared(zhp->zfs_hdl, mountpoint, proto)) {
if (where != NULL)
*where = mountpoint;
else
free(mountpoint);
- return (B_TRUE);
+ return (rc);
} else {
free(mountpoint);
- return (B_FALSE);
+ return (SHARED_NOT_SHARED);
}
}
+boolean_t
+zfs_is_shared_nfs(zfs_handle_t *zhp, char **where)
+{
+ return (zfs_is_shared_proto(zhp, where,
+ PROTO_NFS) != SHARED_NOT_SHARED);
+}
+
+boolean_t
+zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
+{
+ return (zfs_is_shared_proto(zhp, where,
+ PROTO_SMB) != SHARED_NOT_SHARED);
+}
+
/*
* Make sure things will work if libshare isn't installed by using
* wrapper functions that check to see that the pointers to functions
@@ -552,12 +640,13 @@ zfs_uninit_libshare(libzfs_handle_t *zhandle)
*/
int
-zfs_parse_options(char *options, char *proto)
+zfs_parse_options(char *options, zfs_share_proto_t proto)
{
int ret;
if (_sa_parse_legacy_options != NULL)
- ret = _sa_parse_legacy_options(NULL, options, proto);
+ ret = _sa_parse_legacy_options(NULL, options,
+ proto_table[proto].p_name);
else
ret = SA_CONFIG_ERR;
return (ret);
@@ -609,74 +698,102 @@ zfs_sa_disable_share(sa_share_t share, char *proto)
}
/*
- * Share the given filesystem according to the options in 'sharenfs'. We rely
+ * Share the given filesystem according to the options in the specified
+ * protocol specific properties (sharenfs, sharesmb). We rely
* on "libshare" to the dirty work for us.
*/
-int
-zfs_share_nfs(zfs_handle_t *zhp)
+static int
+zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
{
char mountpoint[ZFS_MAXPROPLEN];
char shareopts[ZFS_MAXPROPLEN];
libzfs_handle_t *hdl = zhp->zfs_hdl;
sa_share_t share;
+ zfs_share_proto_t *curr_proto;
int ret;
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
return (0);
- /*
- * Return success if there are no share options.
- */
- if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts),
- NULL, NULL, 0, B_FALSE) != 0 ||
- strcmp(shareopts, "off") == 0)
- return (0);
-
- /*
- * If the 'zoned' property is set, then zfs_is_mountable() will have
- * already bailed out if we are in the global zone. But local
- * zones cannot be NFS servers, so we ignore it for local zones as well.
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
- return (0);
-
if ((ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) {
(void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
zfs_get_name(zhp), _sa_errorstr(ret));
return (-1);
}
- share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint);
- if (share != NULL) {
- int err;
- err = zfs_sa_enable_share(share, "nfs");
- if (err != SA_OK) {
- (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
+
+ for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
+ /*
+ * Return success if there are no share options.
+ */
+ if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
+ shareopts, sizeof (shareopts), NULL, NULL,
+ 0, B_FALSE) != 0 || strcmp(shareopts, "off") == 0)
+ continue;
+
+ /*
+ * If the 'zoned' property is set, then zfs_is_mountable()
+ * will have already bailed out if we are in the global zone.
+ * But local zones cannot be NFS servers, so we ignore it for
+ * local zones as well.
+ */
+ if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
+ continue;
+
+ share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint);
+ if (share != NULL) {
+ int err;
+ err = zfs_sa_enable_share(share,
+ proto_table[*curr_proto].p_name);
+ if (err != SA_OK) {
+ (void) zfs_error_fmt(hdl,
+ proto_table[*curr_proto].p_share_err,
+ dgettext(TEXT_DOMAIN, "cannot share '%s'"),
+ zfs_get_name(zhp));
+ return (-1);
+ }
+ } else {
+ (void) zfs_error_fmt(hdl,
+ proto_table[*curr_proto].p_share_err,
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
zfs_get_name(zhp));
return (-1);
}
- } else {
- (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
- return (-1);
- }
+ }
return (0);
}
+
+int
+zfs_share_nfs(zfs_handle_t *zhp)
+{
+ return (zfs_share_proto(zhp, nfs_only));
+}
+
+int
+zfs_share_smb(zfs_handle_t *zhp)
+{
+ return (zfs_share_proto(zhp, smb_only));
+}
+
+int
+zfs_shareall(zfs_handle_t *zhp)
+{
+ return (zfs_share_proto(zhp, share_all_proto));
+}
+
/*
* Unshare a filesystem by mountpoint.
*/
static int
-unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
+unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
+ zfs_share_proto_t proto)
{
sa_share_t share;
int err;
char *mntpt;
-
/*
* Mountpoint could get trashed if libshare calls getmntany
* which id does during API initialization, so strdup the
@@ -696,7 +813,7 @@ unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
free(mntpt); /* don't need the copy anymore */
if (share != NULL) {
- err = zfs_sa_disable_share(share, "nfs");
+ err = zfs_sa_disable_share(share, proto_table[proto].p_name);
if (err != SA_OK) {
return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
@@ -714,7 +831,8 @@ unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
* Unshare the given filesystem.
*/
int
-zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
+zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
+ zfs_share_proto_t *proto)
{
struct mnttab search = { 0 }, entry;
char *mntpt = NULL;
@@ -724,19 +842,25 @@ zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
search.mnt_fstype = MNTTYPE_ZFS;
rewind(zhp->zfs_hdl->libzfs_mnttab);
if (mountpoint != NULL)
- mountpoint = mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint);
+ mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint);
if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
+ zfs_share_proto_t *curr_proto;
if (mountpoint == NULL)
- mountpoint = entry.mnt_mountp;
+ mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
- if (is_shared(zhp->zfs_hdl, mountpoint) &&
- unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) {
- if (mntpt != NULL)
- free(mntpt);
- return (-1);
+ for (curr_proto = proto; *curr_proto != PROTO_END;
+ curr_proto++) {
+
+ if (is_shared(zhp->zfs_hdl, mntpt, *curr_proto) &&
+ unshare_one(zhp->zfs_hdl, zhp->zfs_name,
+ mntpt, *curr_proto) != 0) {
+ if (mntpt != NULL)
+ free(mntpt);
+ return (-1);
+ }
}
}
if (mntpt != NULL)
@@ -745,11 +869,23 @@ zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
return (0);
}
+int
+zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
+{
+ return (zfs_unshare_proto(zhp, mountpoint, nfs_only));
+}
+
+int
+zfs_unshare_smb(zfs_handle_t *zhp, const char *mountpoint)
+{
+ return (zfs_unshare_proto(zhp, mountpoint, smb_only));
+}
+
/*
- * Same as zfs_unmountall(), but for NFS unshares.
+ * Same as zfs_unmountall(), but for NFS and SMB unshares.
*/
int
-zfs_unshareall_nfs(zfs_handle_t *zhp)
+zfs_unshareall_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
{
prop_changelist_t *clp;
int ret;
@@ -758,12 +894,36 @@ zfs_unshareall_nfs(zfs_handle_t *zhp)
if (clp == NULL)
return (-1);
- ret = changelist_unshare(clp);
+ ret = changelist_unshare(clp, proto);
changelist_free(clp);
return (ret);
}
+int
+zfs_unshareall_nfs(zfs_handle_t *zhp)
+{
+ return (zfs_unshareall_proto(zhp, nfs_only));
+}
+
+int
+zfs_unshareall_smb(zfs_handle_t *zhp)
+{
+ return (zfs_unshareall_proto(zhp, smb_only));
+}
+
+int
+zfs_unshareall(zfs_handle_t *zhp)
+{
+ return (zfs_unshareall_proto(zhp, share_all_proto));
+}
+
+int
+zfs_unshareall_bypath(zfs_handle_t *zhp, const char *mountpoint)
+{
+ return (zfs_unshare_proto(zhp, mountpoint, share_all_proto));
+}
+
/*
* Remove the mountpoint associated with the current dataset, if necessary.
* We only remove the underlying directory if:
@@ -805,7 +965,7 @@ zfs_is_shared_iscsi(zfs_handle_t *zhp)
* If iscsi deamon isn't running then we aren't shared
*/
if (iscsitgt_svc_online && iscsitgt_svc_online() == 1)
- return (0);
+ return (B_FALSE);
else
return (iscsitgt_zfs_is_shared != NULL &&
iscsitgt_zfs_is_shared(zhp->zfs_name) != 0);
@@ -853,7 +1013,7 @@ zfs_unshare_iscsi(zfs_handle_t *zhp)
/*
* Return if the volume is not shared
*/
- if (!zfs_is_shared_iscsi(zhp))
+ if (zfs_is_shared_iscsi(zhp) != SHARED_ISCSI)
return (0);
/*
@@ -1143,9 +1303,14 @@ zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
* Walk through and first unshare everything.
*/
for (i = 0; i < used; i++) {
- if (is_shared(hdl, mountpoints[i]) &&
- unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0)
- goto out;
+ zfs_share_proto_t *curr_proto;
+ for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
+ curr_proto++) {
+ if (is_shared(hdl, mountpoints[i], *curr_proto) &&
+ unshare_one(hdl, mountpoints[i],
+ mountpoints[i], *curr_proto) != 0)
+ goto out;
+ }
}
/*
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index d446b32091..8a1ad6d656 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -136,6 +136,10 @@ libzfs_error_description(libzfs_handle_t *hdl)
return (dgettext(TEXT_DOMAIN, "unshare(1M) failed"));
case EZFS_SHARENFSFAILED:
return (dgettext(TEXT_DOMAIN, "share(1M) failed"));
+ case EZFS_UNSHARESMBFAILED:
+ return (dgettext(TEXT_DOMAIN, "smb remove share failed"));
+ case EZFS_SHARESMBFAILED:
+ return (dgettext(TEXT_DOMAIN, "smb add share failed"));
case EZFS_ISCSISVCUNAVAIL:
return (dgettext(TEXT_DOMAIN,
"iscsitgt service need to be enabled by "
diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers
index 1ca0da3453..d7d68f8708 100644
--- a/usr/src/lib/libzfs/common/mapfile-vers
+++ b/usr/src/lib/libzfs/common/mapfile-vers
@@ -53,6 +53,7 @@ SUNWprivate_1.1 {
zfs_is_shared;
zfs_is_shared_iscsi;
zfs_is_shared_nfs;
+ zfs_is_shared_smb;
zfs_iter_children;
zfs_iter_dependents;
zfs_iter_filesystems;
@@ -92,7 +93,9 @@ SUNWprivate_1.1 {
zfs_rollback;
zfs_send;
zfs_share;
+ zfs_shareall;
zfs_share_nfs;
+ zfs_share_smb;
zfs_share_iscsi;
zfs_snapshot;
zfs_type_to_name;
@@ -101,7 +104,11 @@ SUNWprivate_1.1 {
zfs_unshare;
zfs_unshare_iscsi;
zfs_unshare_nfs;
+ zfs_unshare_smb;
+ zfs_unshareall;
+ zfs_unshareall_bypath;
zfs_unshareall_nfs;
+ zfs_unshareall_smb;
zpool_add;
zpool_clear;
zpool_close;
diff --git a/usr/src/lib/libzpool/common/kernel.c b/usr/src/lib/libzpool/common/kernel.c
index 12219b72a9..e8ce11b2a5 100644
--- a/usr/src/lib/libzpool/common/kernel.c
+++ b/usr/src/lib/libzpool/common/kernel.c
@@ -389,9 +389,10 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
return (0);
}
+/*ARGSUSED*/
int
vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2,
- int x3, vnode_t *startvp)
+ int x3, vnode_t *startvp, int fd)
{
char *realpath = umem_alloc(strlen(path) + 2, UMEM_NOFAIL);
int ret;
@@ -399,6 +400,7 @@ vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2,
ASSERT(startvp == rootdir);
(void) sprintf(realpath, "/%s", path);
+ /* fd ignored for now, need if want to simulate nbmand support */
ret = vn_open(realpath, x1, flags, mode, vpp, x2, x3);
umem_free(realpath, strlen(path) + 2);
@@ -622,7 +624,8 @@ kobj_open_file(char *name)
vnode_t *vp;
/* set vp as the _fd field of the file */
- if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir) != 0)
+ if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir,
+ -1) != 0)
return ((void *)-1UL);
file = umem_zalloc(sizeof (struct _buf), UMEM_NOFAIL);
@@ -814,6 +817,14 @@ z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen,
return (ret);
}
+/*ARGSUSED*/
+size_t u8_textprep_str(char *i, size_t *il, char *o, size_t *ol, int nf,
+ size_t vers, int *err)
+{
+ *err = EINVAL;
+ return ((size_t)-1);
+}
+
uid_t
crgetuid(cred_t *cr)
{
diff --git a/usr/src/lib/libzpool/common/sys/zfs_context.h b/usr/src/lib/libzpool/common/sys/zfs_context.h
index 628a9c25a4..7825bfcb08 100644
--- a/usr/src/lib/libzpool/common/sys/zfs_context.h
+++ b/usr/src/lib/libzpool/common/sys/zfs_context.h
@@ -322,6 +322,9 @@ extern void taskq_destroy(taskq_t *);
extern void taskq_wait(taskq_t *);
extern int taskq_member(taskq_t *, void *);
+#define XVA_MAPSIZE 3
+#define XVA_MAGIC 0x78766174
+
/*
* vnodes
*/
@@ -331,41 +334,79 @@ typedef struct vnode {
char *v_path;
} vnode_t;
+
+typedef struct xoptattr {
+ timestruc_t xoa_createtime; /* Create time of file */
+ uint8_t xoa_archive;
+ uint8_t xoa_system;
+ uint8_t xoa_readonly;
+ uint8_t xoa_hidden;
+ uint8_t xoa_nounlink;
+ uint8_t xoa_immutable;
+ uint8_t xoa_appendonly;
+ uint8_t xoa_nodump;
+ uint8_t xoa_settable;
+ uint8_t xoa_opaque;
+ uint8_t xoa_av_quarantined;
+ uint8_t xoa_av_modified;
+} xoptattr_t;
+
typedef struct vattr {
uint_t va_mask; /* bit-mask of attributes */
u_offset_t va_size; /* file size in bytes */
} vattr_t;
-#define AT_TYPE 0x0001
-#define AT_MODE 0x0002
-#define AT_UID 0x0004
-#define AT_GID 0x0008
-#define AT_FSID 0x0010
-#define AT_NODEID 0x0020
-#define AT_NLINK 0x0040
-#define AT_SIZE 0x0080
-#define AT_ATIME 0x0100
-#define AT_MTIME 0x0200
-#define AT_CTIME 0x0400
-#define AT_RDEV 0x0800
-#define AT_BLKSIZE 0x1000
-#define AT_NBLOCKS 0x2000
-#define AT_SEQ 0x8000
+
+typedef struct xvattr {
+ vattr_t xva_vattr; /* Embedded vattr structure */
+ uint32_t xva_magic; /* Magic Number */
+ uint32_t xva_mapsize; /* Size of attr bitmap (32-bit words) */
+ uint32_t *xva_rtnattrmapp; /* Ptr to xva_rtnattrmap[] */
+ uint32_t xva_reqattrmap[XVA_MAPSIZE]; /* Requested attrs */
+ uint32_t xva_rtnattrmap[XVA_MAPSIZE]; /* Returned attrs */
+ xoptattr_t xva_xoptattrs; /* Optional attributes */
+} xvattr_t;
+
+typedef struct vsecattr {
+ uint_t vsa_mask; /* See below */
+ int vsa_aclcnt; /* ACL entry count */
+ void *vsa_aclentp; /* pointer to ACL entries */
+ int vsa_dfaclcnt; /* default ACL entry count */
+ void *vsa_dfaclentp; /* pointer to default ACL entries */
+ size_t vsa_aclentsz; /* ACE size in bytes of vsa_aclentp */
+} vsecattr_t;
+
+#define AT_TYPE 0x00001
+#define AT_MODE 0x00002
+#define AT_UID 0x00004
+#define AT_GID 0x00008
+#define AT_FSID 0x00010
+#define AT_NODEID 0x00020
+#define AT_NLINK 0x00040
+#define AT_SIZE 0x00080
+#define AT_ATIME 0x00100
+#define AT_MTIME 0x00200
+#define AT_CTIME 0x00400
+#define AT_RDEV 0x00800
+#define AT_BLKSIZE 0x01000
+#define AT_NBLOCKS 0x02000
+#define AT_SEQ 0x08000
+#define AT_XVATTR 0x10000
#define CRCREAT 0
-#define VOP_CLOSE(vp, f, c, o, cr) 0
-#define VOP_PUTPAGE(vp, of, sz, fl, cr) 0
-#define VOP_GETATTR(vp, vap, fl, cr) ((vap)->va_size = (vp)->v_size, 0)
+#define VOP_CLOSE(vp, f, c, o, cr, ct) 0
+#define VOP_PUTPAGE(vp, of, sz, fl, cr, ct) 0
+#define VOP_GETATTR(vp, vap, fl, cr, ct) ((vap)->va_size = (vp)->v_size, 0)
-#define VOP_FSYNC(vp, f, cr) fsync((vp)->v_fd)
+#define VOP_FSYNC(vp, f, cr, ct) fsync((vp)->v_fd)
#define VN_RELE(vp) vn_close(vp)
extern int vn_open(char *path, int x1, int oflags, int mode, vnode_t **vpp,
int x2, int x3);
extern int vn_openat(char *path, int x1, int oflags, int mode, vnode_t **vpp,
- int x2, int x3, vnode_t *vp);
+ int x2, int x3, vnode_t *vp, int fd);
extern int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len,
offset_t offset, int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp);
extern void vn_close(vnode_t *vp);
@@ -453,6 +494,21 @@ struct bootstat {
uint64_t st_size;
};
+typedef struct ace_object {
+ uid_t a_who;
+ uint32_t a_access_mask;
+ uint16_t a_flags;
+ uint16_t a_type;
+ uint8_t a_obj_type[16];
+ uint8_t a_inherit_obj_type[16];
+} ace_object_t;
+
+
+#define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05
+#define ACE_ACCESS_DENIED_OBJECT_ACE_TYPE 0x06
+#define ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07
+#define ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08
+
extern struct _buf *kobj_open_file(char *name);
extern int kobj_read_file(struct _buf *file, char *buf, unsigned size,
unsigned off);
@@ -464,6 +520,25 @@ extern int zfs_secpolicy_rename_perms(const char *from, const char *to,
extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
extern zoneid_t getzoneid(void);
+/*
+ * UTF-8 text preparation functions and their macros.
+ * (sunddi.h)
+ */
+#define U8_STRCMP_CS 0x00000001
+#define U8_STRCMP_CI_UPPER 0x00000002
+#define U8_STRCMP_CI_LOWER 0x00000004
+
+#define U8_TEXTPREP_TOUPPER U8_STRCMP_CI_UPPER
+#define U8_TEXTPREP_TOLOWER U8_STRCMP_CI_LOWER
+#define U8_TEXTPREP_IGNORE_NULL 0x00010000
+
+#define U8_UNICODE_320 (0)
+#define U8_UNICODE_500 (1)
+#define U8_UNICODE_LATEST U8_UNICODE_500
+
+extern size_t u8_textprep_str(char *, size_t *, char *, size_t *, int, size_t,
+ int *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/pam_modules/Makefile b/usr/src/lib/pam_modules/Makefile
index c5692283be..0dce8eb7b1 100644
--- a/usr/src/lib/pam_modules/Makefile
+++ b/usr/src/lib/pam_modules/Makefile
@@ -49,7 +49,8 @@ SUBDIRS = \
unix_auth \
unix_account \
unix_cred \
- unix_session
+ unix_session \
+ smb
$(CLOSED_BUILD)SUBDIRS += \
$(CLOSED)/lib/pam_modules/smartcard
diff --git a/usr/src/lib/pam_modules/smb/Makefile b/usr/src/lib/pam_modules/smb/Makefile
new file mode 100644
index 0000000000..ea50c3ae1f
--- /dev/null
+++ b/usr/src/lib/pam_modules/smb/Makefile
@@ -0,0 +1,56 @@
+#
+# 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
+
+TEXT_DOMAIN= SUNW_OST_SYSOSPAM
+POFILE= smb_passwd.po
+MSGFILES= smb_passwd.c
+
+SUBDIRS= $(MACH)
+#$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+_msg: $(MSGDOMAINPOFILE)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
+include ../../Makefile.targ
diff --git a/usr/src/lib/pam_modules/smb/Makefile.com b/usr/src/lib/pam_modules/smb/Makefile.com
new file mode 100644
index 0000000000..8afe5b8232
--- /dev/null
+++ b/usr/src/lib/pam_modules/smb/Makefile.com
@@ -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"
+#
+
+LIBRARY= pam_smb_passwd.a
+VERS= .1
+OBJECTS= smb_passwd.o
+
+include ../../Makefile.pam_modules
+
+LDLIBS += -lpam -lc
+LDLIBS += -L$(ROOT)/usr/lib/smbsrv -lsmb
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/pam_modules/smb/amd64/Makefile b/usr/src/lib/pam_modules/smb/amd64/Makefile
new file mode 100644
index 0000000000..8b0fed8b9f
--- /dev/null
+++ b/usr/src/lib/pam_modules/smb/amd64/Makefile
@@ -0,0 +1,35 @@
+#
+# 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
+
+DYNFLAGS += -R/usr/lib/smbsrv/$(MACH64)
+DYNFLAGS += $(ROOT)/usr/lib/$(MACH64)/passwdutil.so.1
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/pam_modules/smb/i386/Makefile b/usr/src/lib/pam_modules/smb/i386/Makefile
new file mode 100644
index 0000000000..5be2ca90df
--- /dev/null
+++ b/usr/src/lib/pam_modules/smb/i386/Makefile
@@ -0,0 +1,34 @@
+#
+# 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
+
+DYNFLAGS += -R/usr/lib/smbsrv
+DYNFLAGS += $(ROOT)/usr/lib/passwdutil.so.1
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/pam_modules/smb/mapfile-vers b/usr/src/lib/pam_modules/smb/mapfile-vers
new file mode 100644
index 0000000000..2a7c980a33
--- /dev/null
+++ b/usr/src/lib/pam_modules/smb/mapfile-vers
@@ -0,0 +1,33 @@
+#
+# 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"
+#
+
+SUNW_1.1 {
+ global:
+ pam_sm_chauthtok;
+ local:
+ *;
+};
diff --git a/usr/src/lib/pam_modules/smb/smb_passwd.c b/usr/src/lib/pam_modules/smb/smb_passwd.c
new file mode 100644
index 0000000000..9167d093a6
--- /dev/null
+++ b/usr/src/lib/pam_modules/smb/smb_passwd.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"
+
+#include <sys/types.h>
+#include <sys/varargs.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#include <security/pam_impl.h>
+
+#include <libintl.h>
+#include <passwdutil.h>
+
+#include <smbsrv/libsmb.h>
+
+/*PRINTFLIKE3*/
+static void
+error(boolean_t nowarn, pam_handle_t *pamh, char *fmt, ...)
+{
+ va_list ap;
+ char message[PAM_MAX_MSG_SIZE];
+
+ if (nowarn)
+ return;
+
+ va_start(ap, fmt);
+ (void) vsnprintf(message, sizeof (message), fmt, ap);
+ (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, &message,
+ NULL);
+ va_end(ap);
+}
+
+/*PRINTFLIKE3*/
+static void
+info(boolean_t nowarn, pam_handle_t *pamh, char *fmt, ...)
+{
+ va_list ap;
+ char message[PAM_MAX_MSG_SIZE];
+
+ if (nowarn)
+ return;
+
+ va_start(ap, fmt);
+ (void) vsnprintf(message, sizeof (message), fmt, ap);
+ (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, &message,
+ NULL);
+ va_end(ap);
+}
+
+int
+pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ boolean_t debug = B_FALSE;
+ boolean_t nowarn = B_FALSE;
+ pwu_repository_t files_rep;
+ char *user, *local_user;
+ char *newpw;
+ char *service;
+ int privileged;
+ int res;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (strcmp(argv[i], "debug") == 0)
+ debug = B_TRUE;
+ else if (strcmp(argv[i], "nowarn") == 0)
+ nowarn = B_TRUE;
+ }
+
+ if ((flags & PAM_PRELIM_CHECK) != 0)
+ return (PAM_IGNORE);
+
+ if ((flags & PAM_UPDATE_AUTHTOK) == 0)
+ return (PAM_SYSTEM_ERR);
+
+ if ((flags & PAM_SILENT) != 0)
+ nowarn = B_TRUE;
+
+ if (debug)
+ __pam_log(LOG_AUTH | LOG_DEBUG,
+ "pam_smb_passwd: storing authtok");
+
+ (void) pam_get_item(pamh, PAM_SERVICE, (void **)&service);
+ (void) pam_get_item(pamh, PAM_USER, (void **)&user);
+
+ if (user == NULL || *user == '\0') {
+ __pam_log(LOG_AUTH | LOG_ERR,
+ "pam_smb_passwd: username is empty");
+ return (PAM_USER_UNKNOWN);
+ }
+
+ (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&newpw);
+ if (newpw == NULL) {
+ /*
+ * A module on the stack has removed PAM_AUTHTOK. We fail
+ */
+ return (PAM_AUTHTOK_ERR);
+ }
+
+ /* Check to see if this is a local user */
+ files_rep.type = "files";
+ files_rep.scope = NULL;
+ files_rep.scope_len = 0;
+ res = __user_to_authenticate(user, &files_rep, &local_user,
+ &privileged);
+ if (res != PWU_SUCCESS) {
+ switch (res) {
+ case PWU_NOT_FOUND:
+ /* if not a local user, ignore */
+ if (debug) {
+ __pam_log(LOG_AUTH | LOG_DEBUG,
+ "pam_smb_passwd: %s is not local", user);
+ }
+ return (PAM_IGNORE);
+ case PWU_DENIED:
+ return (PAM_PERM_DENIED);
+ }
+ return (PAM_SYSTEM_ERR);
+ }
+
+ res = smb_pwd_setpasswd(user, newpw);
+
+ /*
+ * now map the various return states to user messages
+ * and PAM return codes.
+ */
+ switch (res) {
+ case SMB_PWE_SUCCESS:
+ info(nowarn, pamh, dgettext(TEXT_DOMAIN,
+ "%s: SMB password successfully changed for %s"),
+ service, user);
+ return (PAM_SUCCESS);
+
+ case SMB_PWE_STAT_FAILED:
+ __pam_log(LOG_AUTH | LOG_ERR,
+ "%s: stat of SMB password file failed", service);
+ return (PAM_SYSTEM_ERR);
+
+ case SMB_PWE_OPEN_FAILED:
+ case SMB_PWE_WRITE_FAILED:
+ case SMB_PWE_CLOSE_FAILED:
+ case SMB_PWE_UPDATE_FAILED:
+ error(nowarn, pamh, dgettext(TEXT_DOMAIN,
+ "%s: Unexpected failure. SMB password database unchanged."),
+ service);
+ return (PAM_SYSTEM_ERR);
+
+ case SMB_PWE_BUSY:
+ error(nowarn, pamh, dgettext(TEXT_DOMAIN,
+ "%s: SMB password database busy. Try again later."),
+ service);
+
+ return (PAM_AUTHTOK_LOCK_BUSY);
+
+ case SMB_PWE_USER_UNKNOWN:
+ error(nowarn, pamh, dgettext(TEXT_DOMAIN,
+ "%s: %s does not exist."), service, user);
+ return (PAM_USER_UNKNOWN);
+
+ case SMB_PWE_USER_DISABLE:
+ error(nowarn, pamh, dgettext(TEXT_DOMAIN,
+ "%s: %s is disable. SMB password database unchanged."),
+ service, user);
+ return (PAM_IGNORE);
+
+ case SMB_PWE_DENIED:
+ return (PAM_PERM_DENIED);
+
+ default:
+ res = PAM_SYSTEM_ERR;
+ break;
+ }
+
+ return (res);
+}
diff --git a/usr/src/lib/pam_modules/smb/sparc/Makefile b/usr/src/lib/pam_modules/smb/sparc/Makefile
new file mode 100644
index 0000000000..5be2ca90df
--- /dev/null
+++ b/usr/src/lib/pam_modules/smb/sparc/Makefile
@@ -0,0 +1,34 @@
+#
+# 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
+
+DYNFLAGS += -R/usr/lib/smbsrv
+DYNFLAGS += $(ROOT)/usr/lib/passwdutil.so.1
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/pam_modules/smb/sparcv9/Makefile b/usr/src/lib/pam_modules/smb/sparcv9/Makefile
new file mode 100644
index 0000000000..8b0fed8b9f
--- /dev/null
+++ b/usr/src/lib/pam_modules/smb/sparcv9/Makefile
@@ -0,0 +1,35 @@
+#
+# 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
+
+DYNFLAGS += -R/usr/lib/smbsrv/$(MACH64)
+DYNFLAGS += $(ROOT)/usr/lib/$(MACH64)/passwdutil.so.1
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
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)