summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
authorGordon Ross <gwr@nexenta.com>2014-06-05 14:30:31 -0400
committerGordon Ross <gwr@nexenta.com>2015-10-26 10:17:47 -0400
commitb3700b074e637f8c6991b70754c88a2cfffb246b (patch)
treec979fb7c426aec884413fae889fab8356ca9ef17 /usr/src/lib
parented81dd52230eff1a7c7625caad21af232c36f6cb (diff)
downloadillumos-joyent-b3700b074e637f8c6991b70754c88a2cfffb246b.tar.gz
6352 Updated DC locator for SMB and idmap
Portions contributed by: Matt Barden <Matt.Barden@nexenta.com> Portions contributed by: Kevin Crowe <kevin.crowe@nexenta.com> Portions contributed by: Alek Pinchuk <alek@nexenta.com> Reviewed by: Bayard Bell <bayard.bell@nexenta.com> Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com> Reviewed by: Rick McNeal <rick.mcneal@nexenta.com> Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com> Reviewed by: Tony Nguyen <tony.nguyen@nexenta.com> Approved by: Robert Mustacchi <rm@joyent.com>
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/Makefile6
-rw-r--r--usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c20
-rw-r--r--usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c85
-rw-r--r--usr/src/lib/libads/Makefile78
-rw-r--r--usr/src/lib/libads/Makefile.com62
-rw-r--r--usr/src/lib/libads/amd64/Makefile31
-rw-r--r--usr/src/lib/libads/common/ads_priv.x107
-rw-r--r--usr/src/lib/libads/common/dsgetdc.c162
-rw-r--r--usr/src/lib/libads/common/dsgetdc.h161
-rw-r--r--usr/src/lib/libads/common/llib-lads31
-rw-r--r--usr/src/lib/libads/common/mapfile-vers54
-rw-r--r--usr/src/lib/libads/common/poke.c58
-rw-r--r--usr/src/lib/libads/i386/Makefile30
-rw-r--r--usr/src/lib/libads/sparc/Makefile30
-rw-r--r--usr/src/lib/libads/sparcv9/Makefile31
-rw-r--r--usr/src/lib/libadutils/Makefile.com13
-rw-r--r--usr/src/lib/libadutils/common/addisc.c1238
-rw-r--r--usr/src/lib/libadutils/common/addisc.h55
-rw-r--r--usr/src/lib/libadutils/common/addisc_impl.h130
-rw-r--r--usr/src/lib/libadutils/common/adutils_impl.h2
-rw-r--r--usr/src/lib/libadutils/common/ldap_ping.c677
-rw-r--r--usr/src/lib/libadutils/common/mapfile-vers10
-rw-r--r--usr/src/lib/libadutils/common/srv_query.c612
-rw-r--r--usr/src/lib/libkrb5/Makefile8
-rw-r--r--usr/src/lib/libldap5/mapfile-vers3
-rw-r--r--usr/src/lib/libresolv2/common/resolv/res_send.c26
-rw-r--r--usr/src/lib/nsswitch/ad/common/ad_common.c4
-rw-r--r--usr/src/lib/nsswitch/ad/common/ad_common.h3
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/Makefile.com2
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/dfs.c9
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c4
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h11
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/lsalib.c155
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/lsalib.h8
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c64
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers2
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h15
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c81
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c464
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c5
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c223
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c16
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c15
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/samlib.c14
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c4
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c5
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c15
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/libsmb.h23
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/mapfile-vers2
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_cfg.c37
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_domain.c41
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c47
-rw-r--r--usr/src/lib/smbsrv/libsmbns/Makefile.com3
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/libsmbns.h40
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/mapfile-vers4
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c1075
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c21
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h2
-rw-r--r--usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c13
59 files changed, 4138 insertions, 2009 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 544a04dd33..56b90369f2 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -23,8 +23,8 @@
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
# Copyright (c) 2013 Gary Mills
-# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
# Copyright (c) 2015 Gary Mills
include ../Makefile.master
@@ -248,6 +248,7 @@ SUBDIRS += \
libshare \
libsqlite \
libidmap \
+ libads \
libadutils \
libipmi \
libexacct/demo \
@@ -376,6 +377,7 @@ i386_MSGSUBDIRS= libfdisk
HDRSUBDIRS= \
auditd_plugins \
+ libads \
libast \
libbrand \
libbsm \
@@ -661,7 +663,7 @@ libtsalarm: libpcp
smbsrv: libsocket libnsl libmd libxnet libpthread librt \
libshare libidmap pkcs11 libsqlite libcryptoutil \
libreparse libcmdutils libresolv libsmbfs libuuid \
- libfakekernel
+ libfakekernel libads
libv12n: libds libuuid
libvrrpadm: libsocket libdladm libscf
libvscan: libscf
diff --git a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c
index c94bf33608..c4034223e8 100644
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c
@@ -31,6 +31,8 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include "k5-int.h"
@@ -138,6 +140,18 @@ krb5_get_default_realm(krb5_context context, char **lrealm)
if (!context || (context->magic != KV5M_CONTEXT))
return KV5M_CONTEXT;
+ /*
+ * Solaris Kerberos: (illumos)
+ * Another way to provide the default realm.
+ */
+ if (!context->default_realm) {
+ if ((realm = getenv("KRB5_DEFAULT_REALM")) != NULL) {
+ context->default_realm = strdup(realm);
+ if (context->default_realm == NULL)
+ return ENOMEM;
+ }
+ }
+
if (!context->default_realm) {
context->default_realm = 0;
if (context->profile != 0) {
@@ -192,11 +206,13 @@ krb5_get_default_realm(krb5_context context, char **lrealm)
}
} else
#endif /* KRB5_DNS_LOOKUP */
- {
+ if (getenv("MS_INTEROP") == NULL) {
/*
* Solaris Kerberos:
- * Try to find a realm based on one of the local IP addresses
+ * Try to find a realm based on one of the local IP addresses.
+ * Don't do this for AD, which often does _not_ support any
+ * DNS reverse lookup, making these queries take forever.
*/
(void) krb5int_foreach_localaddr(context,
krb5int_address_get_realm, 0, 0);
diff --git a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c
index 94662ca8e6..38bb3c1363 100644
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -71,6 +72,10 @@
#include <syslog.h>
#include <locale.h>
+#if USE_DLOPEN
+#include <dlfcn.h>
+#endif
+
/* for old Unixes and friends ... */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
@@ -446,6 +451,72 @@ module_locate_server (krb5_context ctx, const krb5_data *realm,
return 0;
}
+/* XXX - move to locate_plugin.h? */
+typedef krb5_error_code (*krb5_lookup_func)(
+ void *,
+ enum locate_service_type svc, const char *realm,
+ int socktype, int family,
+ int (*cbfunc)(void *,int,struct sockaddr *),
+ void *cbdata);
+
+/*
+ * Solaris Kerberos (illumos)
+ *
+ * Allow main programs to provide an override function for _locate_server,
+ * named _krb5_override_service_locator(). If that function is found in
+ * the main program, it's called like a service locator plugin function.
+ * If it returns KRB5_PLUGIN_NO_HANDLE, continue with other _locate_server
+ * functions. If it returns anything else (zero or some other error),
+ * that return is "final" (no other _locate_server functions are called).
+ * This mechanism is used by programs like "idmapd" that want to completely
+ * control service location.
+ */
+static krb5_error_code
+override_locate_server (krb5_context ctx, const krb5_data *realm,
+ struct addrlist *addrlist,
+ enum locate_service_type svc, int socktype, int family)
+{
+ struct module_callback_data cbdata = { 0, };
+ krb5_error_code code;
+ void *dlh;
+ krb5_lookup_func lookup_func;
+
+ Tprintf("in override_locate_server\n");
+ cbdata.lp = addrlist;
+
+ if ((dlh = dlopen(0, RTLD_FIRST | RTLD_LAZY)) == NULL) {
+ Tprintf("dlopen failed\n");
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ lookup_func = (krb5_lookup_func) dlsym(
+ dlh, "_krb5_override_service_locator");
+ dlclose(dlh);
+ if (lookup_func == NULL) {
+ Tprintf("dlsym failed\n");
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ code = lookup_func(ctx, svc, realm->data, socktype, family,
+ module_callback, &cbdata);
+ if (code == KRB5_PLUGIN_NO_HANDLE) {
+ Tprintf("override lookup routine returned KRB5_PLUGIN_NO_HANDLE\n");
+ return code;
+ }
+ if (code != 0) {
+ /* Module encountered an actual error. */
+ Tprintf("override lookup routine returned error %d: %s\n",
+ code, error_message(code));
+ return code;
+ }
+
+ /* Got something back, yippee. */
+ Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist);
+ print_addrlist(addrlist);
+
+ return 0;
+}
+/* Solaris Kerberos (illumos) */
+
static krb5_error_code
prof_locate_server (krb5_context context, const krb5_data *realm,
char ***hostlist,
@@ -799,6 +870,20 @@ krb5int_locate_server (krb5_context context, const krb5_data *realm,
*addrlist = al;
+ /*
+ * Solaris Kerberos (illumos)
+ * Allow main programs to override _locate_server()
+ */
+ code = override_locate_server(context, realm, &al, svc, socktype, family);
+ if (code != KRB5_PLUGIN_NO_HANDLE) {
+ if (code == 0)
+ *addrlist = al;
+ else if (al.space)
+ free_list (&al);
+ return (code);
+ }
+ /* Solaris Kerberos (illumos) */
+
code = module_locate_server(context, realm, &al, svc, socktype, family);
Tprintf("module_locate_server returns %d\n", code);
if (code == KRB5_PLUGIN_NO_HANDLE) {
diff --git a/usr/src/lib/libads/Makefile b/usr/src/lib/libads/Makefile
new file mode 100644
index 0000000000..41b86fab1d
--- /dev/null
+++ b/usr/src/lib/libads/Makefile
@@ -0,0 +1,78 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
+
+include $(SRC)/lib/Makefile.lib
+
+HDRS= dsgetdc.h
+HDRDIR= common
+
+ROOTHDRDIR= $(ROOT)/usr/include/ads
+ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%)
+
+# ISA targets
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+DERIVED_FILES= \
+ common/ads_priv.h \
+ common/adspriv_xdr.c
+
+RPCGENFLAGS = -CMN
+
+CLEANFILES += $(DERIVED_FILES)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all: $(DERIVED_FILES) .WAIT $(SUBDIRS)
+
+install: all .WAIT $(SUBDIRS)
+
+install_h: $(DERIVED_FILES) $(ROOTHDRS)
+
+clean clobber lint: $(SUBDIRS)
+
+check: $(CHECKHDRS)
+
+common/ads_priv.h: $(HDRDIR)/ads_priv.x
+ $(RPCGEN) $(RPCGENFLAGS) -h $(HDRDIR)/ads_priv.x > $@
+
+common/adspriv_xdr.c: $(HDRDIR)/ads_priv.x
+ $(RPCGEN) $(RPCGENFLAGS) -c $(HDRDIR)/ads_priv.x > $@
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libads/Makefile.com b/usr/src/lib/libads/Makefile.com
new file mode 100644
index 0000000000..1e1d7d968d
--- /dev/null
+++ b/usr/src/lib/libads/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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
+
+LIBRARY = libads.a
+VERS = .1
+LINT_OBJECTS = dsgetdc.o poke.o
+OBJECTS = $(LINT_OBJECTS) adspriv_xdr.o
+
+include ../../Makefile.lib
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lnsl -lc
+SRCDIR = ../common
+$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC)
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_REENTRANT -I$(SRCDIR) -I..
+# CPPFLAGS += -I$(SRC)/lib/libldap5/include/ldap
+
+CERRWARN += -_gcc=-Wno-type-limits
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-unused-variable
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint := OBJECTS = $(LINT_OBJECTS)
+
+lint: lintcheck
+
+#LINTFLAGS += -erroff=E_CONSTANT_CONDITION
+#LINTFLAGS64 += -erroff=E_CONSTANT_CONDITION
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libads/amd64/Makefile b/usr/src/lib/libads/amd64/Makefile
new file mode 100644
index 0000000000..6ad55a66d4
--- /dev/null
+++ b/usr/src/lib/libads/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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libads/common/ads_priv.x b/usr/src/lib/libads/common/ads_priv.x
new file mode 100644
index 0000000000..5bcba2033b
--- /dev/null
+++ b/usr/src/lib/libads/common/ads_priv.x
@@ -0,0 +1,107 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+%/*
+% * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+% */
+
+%/*
+% * from ads_priv.x
+% * Active Directory Services (ADS) Private interface between
+% * libads and the ADS deamon. (RPC over doors)
+% */
+
+#ifdef RPC_HDR
+%/*
+% * Declarations for the ADS API
+% */
+
+#elif RPC_SVC
+%
+%/*
+% * Server side stubs for the ADS API
+% */
+%
+#elif RPC_CLNT
+%
+%/*
+% * Client side stubs for the ADS API
+% */
+%
+#elif RPC_XDR
+%/*
+% * XDR routines for the ADS API
+% */
+#endif
+
+const ADSPRIV_MAX_XFER = 16384;
+const ADSPRIV_GUID_LEN = 16;
+const ADSPRIV_SOCKADDR_LEN = 256;
+const ADSPRIV_STR_MAX = 256;
+
+typedef opaque adspriv_guid[ADSPRIV_GUID_LEN];
+typedef opaque adspriv_sockaddr[ADSPRIV_SOCKADDR_LEN];
+
+/*
+ * Structure returned from DsGetDcName
+ * NB: Keep same as DOMAIN_CONTROLLER_INFO
+ */
+struct adspriv_dcinfo {
+ string dci_DcName<ADSPRIV_STR_MAX>;
+ string dci_DcAddr<ADSPRIV_STR_MAX>;
+ unsigned int dci_AddrType;
+ adspriv_guid dci_guid;
+ string dci_DomainName<ADSPRIV_STR_MAX>;
+ string dci_DnsForestName<ADSPRIV_STR_MAX>;
+ unsigned int dci_Flags;
+ string dci_DcSiteName<ADSPRIV_STR_MAX>;
+ string dci_ClientSiteName<ADSPRIV_STR_MAX>;
+ adspriv_sockaddr dci_sockaddr;
+};
+
+/*
+ * DsForceRediscovery args
+ */
+struct DsForceRediscoveryArgs {
+ unsigned int Flags;
+ string DomainName<ADSPRIV_STR_MAX>;
+};
+
+/*
+ * DsGetDcName args, result
+ */
+struct DsGetDcNameArgs {
+ string ComputerName<ADSPRIV_STR_MAX>;
+ string DomainName<ADSPRIV_STR_MAX>;
+ string DomainGuid<ADSPRIV_STR_MAX>;
+ string SiteName<ADSPRIV_STR_MAX>;
+ unsigned int Flags;
+};
+
+union DsGetDcNameRes switch (int status) {
+case 0:
+ adspriv_dcinfo res0;
+default:
+ void;
+};
+
+program ADSPRIV_PROGRAM {
+ version ADSPRIV_V1 {
+ void
+ ADSPRIV_NULL(void) = 0;
+
+ int
+ ADSPRIV_ForceRediscovery(DsForceRediscoveryArgs) = 1;
+
+ DsGetDcNameRes
+ ADSPRIV_GetDcName(DsGetDcNameArgs) = 2;
+ } = 1;
+} = 100001;
diff --git a/usr/src/lib/libads/common/dsgetdc.c b/usr/src/lib/libads/common/dsgetdc.c
new file mode 100644
index 0000000000..a8b45707b9
--- /dev/null
+++ b/usr/src/lib/libads/common/dsgetdc.c
@@ -0,0 +1,162 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * MS-compatible Directory Server Discovery API, DsGetDC...()
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <smb/nterror.h>
+#include <smb/ntstatus.h>
+#include <arpa/inet.h>
+#include "dsgetdc.h"
+#include "ads_priv.h"
+#include <assert.h>
+
+#define DSGETDC_VALID_FLAGS ( \
+ DS_FORCE_REDISCOVERY | \
+ DS_DIRECTORY_SERVICE_REQUIRED | \
+ DS_DIRECTORY_SERVICE_PREFERRED | \
+ DS_GC_SERVER_REQUIRED | \
+ DS_PDC_REQUIRED | \
+ DS_BACKGROUND_ONLY | \
+ DS_IP_REQUIRED | \
+ DS_KDC_REQUIRED | \
+ DS_TIMESERV_REQUIRED | \
+ DS_WRITABLE_REQUIRED | \
+ DS_GOOD_TIMESERV_PREFERRED | \
+ DS_AVOID_SELF | \
+ DS_ONLY_LDAP_NEEDED | \
+ DS_IS_FLAT_NAME | \
+ DS_IS_DNS_NAME | \
+ DS_RETURN_FLAT_NAME | \
+ DS_RETURN_DNS_NAME)
+
+static struct timeval TIMEOUT = { 15, 0 };
+
+/*
+ * The Windows version of this would return a single allocation,
+ * where any strings pointed to in the returned structure would be
+ * stored in space following the top-level returned structure.
+ * This allows NetApiBufferFree() to be the same as free().
+ *
+ * However, we don't have an easy way to do that right now, so
+ * the dcinfo returned here will be free'd with DsFreeDcInfo().
+ */
+uint32_t
+_DsGetDcName(const char *ComputerName,
+ const char *DomainName, const struct uuid *DomainGuid,
+ const char *SiteName, uint32_t Flags,
+ DOMAIN_CONTROLLER_INFO **dcinfo)
+{
+ DsGetDcNameArgs args;
+ DsGetDcNameRes res;
+ CLIENT *clnt = NULL;
+ enum clnt_stat clstat;
+
+ *dcinfo = NULL;
+ (void) memset(&args, 0, sizeof (args));
+ (void) memset(&res, 0, sizeof (res));
+
+ /*
+ * Later check for over constrained optional args here,
+ * and return (ERROR_INVALID_PARAMETER);
+ */
+
+ if (Flags & ~DSGETDC_VALID_FLAGS)
+ return (ERROR_INVALID_FLAGS);
+
+ /*
+ * Call the ADS deamon.
+ */
+ clnt = clnt_door_create(ADSPRIV_PROGRAM, ADSPRIV_V1, ADSPRIV_MAX_XFER);
+ if (clnt == NULL)
+ return (RPC_S_NOT_LISTENING);
+
+ args.ComputerName = (char *)ComputerName;
+ args.DomainName = (char *)DomainName;
+ if (DomainGuid != NULL)
+ (void) memcpy(&args.DomainGuid, DomainGuid,
+ sizeof (args.DomainGuid));
+ args.SiteName = (char *)SiteName;
+ args.Flags = Flags;
+
+ clstat = clnt_call(clnt, ADSPRIV_GetDcName,
+ (xdrproc_t)xdr_DsGetDcNameArgs, (caddr_t)&args,
+ (xdrproc_t)xdr_DsGetDcNameRes, (caddr_t)&res, TIMEOUT);
+
+ clnt_destroy(clnt);
+ if (clstat != RPC_SUCCESS)
+ return (RPC_S_CALL_FAILED);
+ if (res.status != 0)
+ return (res.status);
+
+ *dcinfo = malloc(sizeof (**dcinfo));
+ if (*dcinfo == NULL)
+ return (ERROR_NOT_ENOUGH_MEMORY);
+
+ /*
+ * We have taken pains to make these two the same.
+ * DOMAIN_CONTROLLER_INFO / struct adspriv_dcinfo
+ */
+ /* LINTED E_TRUE_LOGICAL_EXPR */
+ assert(sizeof (**dcinfo) == sizeof (res.DsGetDcNameRes_u.res0));
+ (void) memcpy(*dcinfo, &res.DsGetDcNameRes_u.res0, sizeof (**dcinfo));
+
+ /*
+ * NB: Do NOT xdr_free the result, because we're
+ * returning a copy of it to the caller.
+ */
+ return (0);
+}
+
+int
+DsGetDcName(const char *ComputerName,
+ const char *DomainName, const struct uuid *DomainGuid,
+ const char *SiteName, uint32_t Flags,
+ DOMAIN_CONTROLLER_INFO **dcinfo)
+{
+ uint32_t status;
+ int rc;
+
+ status = _DsGetDcName(ComputerName, DomainName, DomainGuid,
+ SiteName, Flags, dcinfo);
+
+ switch (status) {
+ case 0:
+ rc = 0;
+ break;
+ case NT_STATUS_NO_SUCH_DOMAIN: /* Specified domain unknown */
+ case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
+ case NT_STATUS_CANT_WAIT: /* or gave up waiting. */
+ case NT_STATUS_INVALID_SERVER_STATE: /* not in domain mode. */
+ rc = ERROR_NO_SUCH_DOMAIN;
+ break;
+ default:
+ rc = ERROR_INTERNAL_ERROR;
+ break;
+ }
+ return (rc);
+}
+
+void
+DsFreeDcInfo(DOMAIN_CONTROLLER_INFO *dci)
+{
+ if (dci != NULL) {
+ xdr_free(xdr_DsGetDcNameRes, (char *)dci);
+ free(dci);
+ }
+}
diff --git a/usr/src/lib/libads/common/dsgetdc.h b/usr/src/lib/libads/common/dsgetdc.h
new file mode 100644
index 0000000000..175b2cce65
--- /dev/null
+++ b/usr/src/lib/libads/common/dsgetdc.h
@@ -0,0 +1,161 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Declarations intentionally similar to the MSDN SDK file
+ * winsdk/Include/DsGetDC.h
+ */
+
+
+#ifndef _ADS_DSGETDC_H
+#define _ADS_DSGETDC_H
+
+#include <sys/types.h>
+#include <sys/uuid.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Flags to passed to DsGetDcName
+ */
+
+#define DS_FORCE_REDISCOVERY 0x00000001
+
+#define DS_DIRECTORY_SERVICE_REQUIRED 0x00000010
+#define DS_DIRECTORY_SERVICE_PREFERRED 0x00000020
+#define DS_GC_SERVER_REQUIRED 0x00000040
+#define DS_PDC_REQUIRED 0x00000080
+#define DS_BACKGROUND_ONLY 0x00000100
+#define DS_IP_REQUIRED 0x00000200
+#define DS_KDC_REQUIRED 0x00000400
+#define DS_TIMESERV_REQUIRED 0x00000800
+#define DS_WRITABLE_REQUIRED 0x00001000
+#define DS_GOOD_TIMESERV_PREFERRED 0x00002000
+#define DS_AVOID_SELF 0x00004000
+#define DS_ONLY_LDAP_NEEDED 0x00008000
+
+
+#define DS_IS_FLAT_NAME 0x00010000
+#define DS_IS_DNS_NAME 0x00020000
+
+#define DS_RETURN_DNS_NAME 0x40000000
+#define DS_RETURN_FLAT_NAME 0x80000000
+
+/*
+ * Structure returned from DsGetDcName
+ * NB: Keep same as adspriv_dcinfo
+ */
+
+typedef struct _DOMAIN_CONTROLLER_INFO {
+ char *DomainControllerName;
+ char *DomainControllerAddress;
+ uint32_t DomainControllerAddressType;
+ uuid_t DomainGuid;
+ char *DomainName;
+ char *DnsForestName;
+ uint32_t Flags;
+ char *DcSiteName;
+ char *ClientSiteName;
+ uint8_t _sockaddr[256];
+} DOMAIN_CONTROLLER_INFO, *PDOMAIN_CONTROLLER_INFO;
+
+/*
+ * Values for DomainControllerAddressType
+ */
+
+#define DS_INET_ADDRESS 1
+#define DS_NETBIOS_ADDRESS 2
+
+/*
+ * Values for returned Flags
+ */
+
+#define DS_PDC_FLAG 0x00000001 /* DC is PDC of Domain */
+#define DS_GC_FLAG 0x00000004 /* DC is a GC of forest */
+#define DS_LDAP_FLAG 0x00000008 /* supports an LDAP server */
+#define DS_DS_FLAG 0x00000010 /* supports a DS and is a */
+ /* Domain Controller */
+#define DS_KDC_FLAG 0x00000020 /* is running KDC service */
+#define DS_TIMESERV_FLAG 0x00000040 /* is running time service */
+#define DS_CLOSEST_FLAG 0x00000080 /* DC is in closest site */
+ /* to the client */
+#define DS_WRITABLE_FLAG 0x00000100 /* DC has a writable DS */
+#define DS_GOOD_TIMESERV_FLAG 0x00000200 /* is running time service */
+ /* (and has clock hardware) */
+#define DS_NDNC_FLAG 0x00000400 /* DomainName is non-domain */
+ /* NC serviced by the */
+ /* LDAP server */
+#define DS_PING_FLAGS 0x0000FFFF /* Flags returned on ping */
+
+#define DS_DNS_CONTROLLER_FLAG 0x20000000 /* DC Name is a DNS name */
+#define DS_DNS_DOMAIN_FLAG 0x40000000 /* DomainName is a DNS name */
+#define DS_DNS_FOREST_FLAG 0x80000000 /* ForestName is a DNS name */
+
+
+/*
+ * Function Prototypes
+ */
+
+/* Offial API. Returns an NT error number. */
+extern int
+DsGetDcName(const char *ComputerName,
+ const char *DomainName, const struct uuid *DomainGuid,
+ const char *SiteName, uint32_t Flags,
+ DOMAIN_CONTROLLER_INFO **dcinfo);
+
+/* internal version of above - returns a detailed NT status */
+extern uint32_t
+_DsGetDcName(const char *ComputerName,
+ const char *DomainName, const struct uuid *DomainGuid,
+ const char *SiteName, uint32_t Flags,
+ DOMAIN_CONTROLLER_INFO **dcinfo);
+
+extern int
+DsGetSiteName(
+ const char *ComputerName,
+ char **SiteName);
+
+/*
+ * XXX: Others from DsGetDc.h we may want later:
+ * DsValidateSubnetName()
+ * DsAddressToSiteNames()
+ * DsAddressToSiteNamesEx()
+ * DsEnumerateDomainTrusts()
+ * DsGetForestTrustInformation()
+ * DsGetDcSiteCoverage()
+ * DsDeregisterDnsHostRecords()
+ * DsGetDcOpen(), DsGetDcNext(), DsGetDcClose()
+ */
+
+/*
+ * Until we can easily allocate a DC Info as one big hunk.
+ * This will free a DC Info returned by DsGetDcName().
+ */
+extern void
+DsFreeDcInfo(DOMAIN_CONTROLLER_INFO *);
+
+/*
+ * Internal function to force DC Rediscovery.
+ */
+extern int
+_DsForceRediscovery(char *domain, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ADS_DSGETDC_H */
diff --git a/usr/src/lib/libads/common/llib-lads b/usr/src/lib/libads/common/llib-lads
new file mode 100644
index 0000000000..7cdc5c1188
--- /dev/null
+++ b/usr/src/lib/libads/common/llib-lads
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include "dsgetdc.h"
diff --git a/usr/src/lib/libads/common/mapfile-vers b/usr/src/lib/libads/common/mapfile-vers
new file mode 100644
index 0000000000..9773b4d315
--- /dev/null
+++ b/usr/src/lib/libads/common/mapfile-vers
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL 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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate {
+ global:
+ _DsForceRediscovery;
+ _DsGetDcName;
+ DsFreeDcInfo;
+ DsGetDcName;
+ xdr_adspriv_dcinfo;
+ xdr_DsForceRediscoveryArgs;
+ xdr_DsGetDcNameArgs;
+ xdr_DsGetDcNameRes;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libads/common/poke.c b/usr/src/lib/libads/common/poke.c
new file mode 100644
index 0000000000..3c3b45d24f
--- /dev/null
+++ b/usr/src/lib/libads/common/poke.c
@@ -0,0 +1,58 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Private API to force DC Rediscovery.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <smb/nterror.h>
+#include <arpa/inet.h>
+#include "dsgetdc.h"
+#include "ads_priv.h"
+#include <assert.h>
+
+static struct timeval TIMEOUT = { 15, 0 };
+
+int
+_DsForceRediscovery(char *domain, int flags)
+{
+ DsForceRediscoveryArgs args;
+ CLIENT *clnt = NULL;
+ enum clnt_stat clstat;
+ int res;
+
+ (void) memset(&args, 0, sizeof (args));
+ args.Flags = flags;
+ args.DomainName = domain;
+
+ /*
+ * Call the ADS deamon.
+ */
+ clnt = clnt_door_create(ADSPRIV_PROGRAM, ADSPRIV_V1, ADSPRIV_MAX_XFER);
+ if (clnt == NULL)
+ return (RPC_S_NOT_LISTENING);
+
+ clstat = clnt_call(clnt, ADSPRIV_ForceRediscovery,
+ (xdrproc_t)xdr_DsForceRediscoveryArgs, (caddr_t)&args,
+ (xdrproc_t)xdr_int, (caddr_t)&res, TIMEOUT);
+
+ clnt_destroy(clnt);
+ if (clstat != RPC_SUCCESS)
+ return (RPC_S_CALL_FAILED);
+
+ return (res);
+}
diff --git a/usr/src/lib/libads/i386/Makefile b/usr/src/lib/libads/i386/Makefile
new file mode 100644
index 0000000000..3cf14ec6d8
--- /dev/null
+++ b/usr/src/lib/libads/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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libads/sparc/Makefile b/usr/src/lib/libads/sparc/Makefile
new file mode 100644
index 0000000000..3cf14ec6d8
--- /dev/null
+++ b/usr/src/lib/libads/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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libads/sparcv9/Makefile b/usr/src/lib/libads/sparcv9/Makefile
new file mode 100644
index 0000000000..6ad55a66d4
--- /dev/null
+++ b/usr/src/lib/libads/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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libadutils/Makefile.com b/usr/src/lib/libadutils/Makefile.com
index 8776b44c86..67014cb75e 100644
--- a/usr/src/lib/libadutils/Makefile.com
+++ b/usr/src/lib/libadutils/Makefile.com
@@ -22,11 +22,13 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
LIBRARY = libadutils.a
VERS = .1
-OBJECTS = adutils.o addisc.o adutils_threadfuncs.o
-LINT_OBJECTS = adutils.o addisc.o adutils_threadfuncs.o
+OBJECTS = adutils.o addisc.o adutils_threadfuncs.o \
+ ldap_ping.o srv_query.o
include ../../Makefile.lib
@@ -34,18 +36,17 @@ C99MODE= -xc99=%all
C99LMODE= -Xc99=%all
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS += -lc -lldap -lresolv -lsocket -lnsl
+LDLIBS += -lldap -lresolv -lsocket -lnsl -lc
SRCDIR = ../common
$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC)
CFLAGS += $(CCVERBOSE)
-CPPFLAGS += -D_REENTRANT -I$(SRCDIR) -I$(SRC)/lib/libldap5/include/ldap
+CPPFLAGS += -D_REENTRANT -I$(SRCDIR)
+CPPFLAGS += -I$(SRC)/lib/libldap5/include/ldap
CERRWARN += -_gcc=-Wno-type-limits
CERRWARN += -_gcc=-Wno-uninitialized
-lint := OBJECTS = $(LINT_OBJECTS)
-
.KEEP_STATE:
all: $(LIBS)
diff --git a/usr/src/lib/libadutils/common/addisc.c b/usr/src/lib/libadutils/common/addisc.c
index a0bbfe1c0f..f6ac98d11a 100644
--- a/usr/src/lib/libadutils/common/addisc.c
+++ b/usr/src/lib/libadutils/common/addisc.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -83,12 +84,10 @@
#include <assert.h>
#include <stdlib.h>
#include <net/if.h>
-#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <netinet/in.h>
-#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
@@ -96,11 +95,14 @@
#include <ctype.h>
#include <errno.h>
#include <ldap.h>
+#include <note.h>
#include <sasl/sasl.h>
#include <sys/u8_textprep.h>
#include <syslog.h>
+#include <uuid/uuid.h>
+#include <ads/dsgetdc.h>
#include "adutils_impl.h"
-#include "addisc.h"
+#include "addisc_impl.h"
/*
* These set some sanity policies for discovery. After a discovery
@@ -113,70 +115,10 @@
#define MINIMUM_TTL (5 * 60)
#define MAXIMUM_TTL (20 * 60)
-enum ad_item_state {
- AD_STATE_INVALID = 0, /* The value is not valid */
- AD_STATE_FIXED, /* The value was fixed by caller */
- AD_STATE_AUTO /* The value is auto discovered */
- };
-
-enum ad_data_type {
- AD_STRING = 123,
- AD_DIRECTORY,
- AD_DOMAINS_IN_FOREST,
- AD_TRUSTED_DOMAINS
- };
-
-
-typedef struct ad_subnet {
- char subnet[24];
-} ad_subnet_t;
-
-
-typedef struct ad_item {
- enum ad_item_state state;
- enum ad_data_type type;
- void *value;
- time_t expires;
- unsigned int version; /* Version is only changed */
- /* if the value changes */
-#define PARAM1 0
-#define PARAM2 1
- int param_version[2];
- /* These holds the version of */
- /* dependents so that a dependent */
- /* change can be detected */
-} ad_item_t;
-
-typedef struct ad_disc {
- struct __res_state res_state;
- int res_ninitted;
- ad_subnet_t *subnets;
- boolean_t subnets_changed;
- time_t subnets_last_check;
- time_t expires_not_before;
- time_t expires_not_after;
- ad_item_t domain_name; /* DNS hostname string */
- ad_item_t domain_controller; /* Directory hostname and */
- /* port array */
- ad_item_t site_name; /* String */
- ad_item_t forest_name; /* DNS forestname string */
- ad_item_t global_catalog; /* Directory hostname and */
- /* port array */
- ad_item_t domains_in_forest; /* DNS domainname and SID */
- /* array */
- ad_item_t trusted_domains; /* DNS domainname and trust */
- /* direction array */
- /* Site specfic versions */
- ad_item_t site_domain_controller; /* Directory hostname and */
- /* port array */
- ad_item_t site_global_catalog; /* Directory hostname and */
- /* port array */
- int debug[AD_DEBUG_MAX+1]; /* Debug levels */
-} ad_disc;
-
#define DNS_MAX_NAME NS_MAXDNAME
+#define GC_PORT 3268
/* SRV RR names for various queries */
#define LDAP_SRV_HEAD "_ldap._tcp."
@@ -194,8 +136,23 @@ typedef struct ad_disc {
* We try res_ninit() whenever we don't have one. res_ninit() fails if
* idmapd is running before the network is up!
*/
-#define DO_RES_NINIT(ctx) if (!(ctx)->res_ninitted) \
- (ctx)->res_ninitted = (res_ninit(&ctx->res_state) != -1)
+#define DO_RES_NINIT(ctx) \
+ if (!(ctx)->res_ninitted) \
+ (void) do_res_ninit(ctx)
+
+#define DO_GETNAMEINFO(b, l, s) \
+ if (ad_disc_getnameinfo(b, l, s) != 0) \
+ (void) strlcpy(b, "?", l)
+
+#define DEBUG1STATUS(ctx, ...) do { \
+ if (DBG(DISC, 1)) \
+ logger(LOG_DEBUG, __VA_ARGS__); \
+ if (ctx->status_fp) { \
+ (void) fprintf(ctx->status_fp, __VA_ARGS__); \
+ (void) fprintf(ctx->status_fp, "\n"); \
+ } \
+ _NOTE(CONSTCOND) \
+} while (0)
#define is_fixed(item) \
((item)->state == AD_STATE_FIXED)
@@ -203,13 +160,65 @@ typedef struct ad_disc {
#define is_changed(item, num, param) \
((item)->param_version[num] != (param)->version)
+void * uuid_dup(void *);
+
+static ad_item_t *validate_SiteName(ad_disc_t ctx);
+static ad_item_t *validate_PreferredDC(ad_disc_t ctx);
+
/*
* Function definitions
*/
-static ad_item_t *
-validate_SiteName(ad_disc_t ctx);
+static int
+do_res_ninit(ad_disc_t ctx)
+{
+ int rc;
+
+ rc = res_ninit(&ctx->res_state);
+ if (rc != 0)
+ return (rc);
+ ctx->res_ninitted = 1;
+ /*
+ * The SRV records returnd by AD can be larger than 512 bytes,
+ * so we'd like to use TCP for those searches. Unfortunately,
+ * the TCP connect timeout seen by the resolver is very long
+ * (more than a couple minutes) and we can't wait that long.
+ * Don't do use TCP until we can override the timeout.
+ *
+ * Note that some queries will try TCP anyway.
+ */
+#if 0
+ ctx->res_state.options |= RES_USEVC;
+#endif
+ return (0);
+}
+
+/*
+ * Private getnameinfo(3socket) variant tailored to our needs.
+ */
+int
+ad_disc_getnameinfo(char *obuf, int olen, struct sockaddr_storage *ss)
+{
+ struct sockaddr *sa;
+ int eai, slen;
+
+ sa = (void *)ss;
+ switch (sa->sa_family) {
+ case AF_INET:
+ slen = sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ slen = sizeof (struct sockaddr_in6);
+ break;
+ default:
+ return (EAI_FAMILY);
+ }
+
+ eai = getnameinfo(sa, slen, obuf, olen, NULL, 0, NI_NUMERICHOST);
+
+ return (eai);
+}
static void
update_version(ad_item_t *item, int num, ad_item_t *param)
@@ -240,6 +249,8 @@ update_item(ad_item_t *item, void *value, enum ad_item_state state,
if (item->value != NULL && value != NULL) {
if ((item->type == AD_STRING &&
strcmp(item->value, value) != 0) ||
+ (item->type == AD_UUID &&
+ ad_disc_compare_uuid(item->value, value) != 0)||
(item->type == AD_DIRECTORY &&
ad_disc_compare_ds(item->value, value) != 0)||
(item->type == AD_DOMAINS_IN_FOREST &&
@@ -262,10 +273,29 @@ update_item(ad_item_t *item, void *value, enum ad_item_state state,
item->expires = time(NULL) + ttl;
}
+/* Compare UUIDs */
+int
+ad_disc_compare_uuid(uuid_t *u1, uuid_t *u2)
+{
+ int rc;
+
+ rc = memcmp(u1, u2, UUID_LEN);
+ return (rc);
+}
+
+void *
+uuid_dup(void *src)
+{
+ void *dst;
+ dst = malloc(UUID_LEN);
+ if (dst != NULL)
+ (void) memcpy(dst, src, UUID_LEN);
+ return (dst);
+}
/* Compare DS lists */
int
-ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2)
+ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2)
{
int i, j;
int num_ds1;
@@ -298,17 +328,17 @@ ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2)
/* Copy a list of DSs */
-static idmap_ad_disc_ds_t *
-ds_dup(const idmap_ad_disc_ds_t *srv)
+static ad_disc_ds_t *
+ds_dup(const ad_disc_ds_t *srv)
{
int i;
int size;
- idmap_ad_disc_ds_t *new = NULL;
+ ad_disc_ds_t *new = NULL;
for (i = 0; srv[i].host[0] != '\0'; i++)
continue;
- size = (i + 1) * sizeof (idmap_ad_disc_ds_t);
+ size = (i + 1) * sizeof (ad_disc_ds_t);
new = malloc(size);
if (new != NULL)
(void) memcpy(new, srv, size);
@@ -603,237 +633,13 @@ DN_to_DNS(const char *dn_name)
}
-/* Make a list of subnet object DNs from a list of subnets */
-static char **
-subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn)
-{
- char **results;
- int i, j;
-
- for (i = 0; subnets[i].subnet[0] != '\0'; i++)
- continue;
-
- results = calloc(i + 1, sizeof (char *));
- if (results == NULL)
- return (NULL);
-
- for (i = 0; subnets[i].subnet[0] != '\0'; i++) {
- (void) asprintf(&results[i], "CN=%s,CN=Subnets,CN=Sites,%s",
- subnets[i].subnet, base_dn);
- if (results[i] == NULL) {
- for (j = 0; j < i; j++)
- free(results[j]);
- free(results);
- return (NULL);
- }
- }
-
- return (results);
-}
-
-
-/* Compare SRC RRs; used with qsort() */
-static int
-srvcmp(idmap_ad_disc_ds_t *s1, idmap_ad_disc_ds_t *s2)
-{
- if (s1->priority < s2->priority)
- return (1);
- else if (s1->priority > s2->priority)
- return (-1);
-
- if (s1->weight < s2->weight)
- return (1);
- else if (s1->weight > s2->weight)
- return (-1);
-
- return (0);
-}
-
-
-/*
- * Query or search the SRV RRs for a given name.
- *
- * If name == NULL then search (as in res_nsearch(3RESOLV), honoring any
- * search list/option), else query (as in res_nquery(3RESOLV)).
- *
- * The output TTL will be the one of the SRV RR with the lowest TTL.
- */
-idmap_ad_disc_ds_t *
-srv_query(res_state state, const char *svc_name, const char *dname,
- char **rrname, uint32_t *ttl)
-{
- idmap_ad_disc_ds_t *srv;
- idmap_ad_disc_ds_t *srv_res = NULL;
- union {
- HEADER hdr;
- uchar_t buf[NS_MAXMSG];
- } msg;
- int len, cnt, qdcount, ancount;
- uchar_t *ptr, *eom;
- uchar_t *end;
- uint16_t type;
- /* LINTED E_FUNC_SET_NOT_USED */
- uint16_t class;
- uint32_t rttl;
- uint16_t size;
- char namebuf[NS_MAXDNAME];
-
- if (state == NULL)
- return (NULL);
-
- /* Set negative result TTL */
- *ttl = 5 * 60;
-
- /* 1. query necessary resource records */
-
- /* Search, querydomain or query */
- if (rrname != NULL) {
- *rrname = NULL;
- if (DBG(DNS, 1)) {
- logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'",
- svc_name);
- }
- len = res_nsearch(state, svc_name, C_IN, T_SRV,
- msg.buf, sizeof (msg.buf));
- if (len < 0) {
- if (DBG(DNS, 0)) {
- logger(LOG_DEBUG,
- "DNS search for '%s' failed (%s)",
- svc_name, hstrerror(state->res_h_errno));
- }
- return (NULL);
- }
- } else if (dname != NULL) {
- if (DBG(DNS, 1)) {
- logger(LOG_DEBUG, "Looking for SRV RRs '%s.%s' ",
- svc_name, dname);
- }
-
- len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV,
- msg.buf, sizeof (msg.buf));
-
- if (len < 0) {
- if (DBG(DNS, 0)) {
- logger(LOG_DEBUG, "DNS: %s.%s: %s",
- svc_name, dname,
- hstrerror(state->res_h_errno));
- }
- return (NULL);
- }
- }
-
- if (len > sizeof (msg.buf)) {
- logger(LOG_ERR,
- "DNS query %ib message doesn't fit into %ib buffer",
- len, sizeof (msg.buf));
- return (NULL);
- }
-
- /* 2. parse the reply, skip header and question sections */
-
- ptr = msg.buf + sizeof (msg.hdr);
- eom = msg.buf + len;
- qdcount = ntohs(msg.hdr.qdcount);
- ancount = ntohs(msg.hdr.ancount);
-
- for (cnt = qdcount; cnt > 0; --cnt) {
- if ((len = dn_skipname(ptr, eom)) < 0) {
- logger(LOG_ERR, "DNS query invalid message format");
- return (NULL);
- }
- ptr += len + QFIXEDSZ;
- }
-
- /* 3. walk through the answer section */
-
- srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t));
- if (srv_res == NULL) {
- logger(LOG_ERR, "Out of memory");
- return (NULL);
- }
-
- *ttl = (uint32_t)-1;
-
- for (srv = srv_res, cnt = ancount;
- cnt > 0; --cnt, srv++) {
-
- len = dn_expand(msg.buf, eom, ptr, namebuf,
- sizeof (namebuf));
- if (len < 0) {
- logger(LOG_ERR, "DNS query invalid message format");
- goto err;
- }
- if (rrname != NULL && *rrname == NULL) {
- *rrname = strdup(namebuf);
- if (*rrname == NULL) {
- logger(LOG_ERR, "Out of memory");
- goto err;
- }
- }
- ptr += len;
- NS_GET16(type, ptr);
- NS_GET16(class, ptr);
- NS_GET32(rttl, ptr);
- NS_GET16(size, ptr);
- if ((end = ptr + size) > eom) {
- logger(LOG_ERR, "DNS query invalid message format");
- goto err;
- }
-
- if (type != T_SRV) {
- ptr = end;
- continue;
- }
-
- NS_GET16(srv->priority, ptr);
- NS_GET16(srv->weight, ptr);
- NS_GET16(srv->port, ptr);
- len = dn_expand(msg.buf, eom, ptr, srv->host,
- sizeof (srv->host));
- if (len < 0) {
- logger(LOG_ERR, "DNS query invalid SRV record");
- goto err;
- }
-
- if (rttl < *ttl)
- *ttl = rttl;
-
- if (DBG(DNS, 1)) {
- logger(LOG_DEBUG, " %s", namebuf);
- logger(LOG_DEBUG,
- " ttl=%d pri=%d weight=%d %s:%d",
- rttl, srv->priority, srv->weight,
- srv->host, srv->port);
- }
-
- /* 3. move ptr to the end of current record */
-
- ptr = end;
- }
-
- if (ancount > 1)
- qsort(srv_res, ancount, sizeof (*srv_res),
- (int (*)(const void *, const void *))srvcmp);
-
- return (srv_res);
-
-err:
- free(srv_res);
- if (rrname != NULL) {
- free(*rrname);
- *rrname = NULL;
- }
- return (NULL);
-}
-
-
/*
* A utility function to bind to a Directory server
*/
static
LDAP *
-ldap_lookup_init(idmap_ad_disc_ds_t *ds)
+ldap_lookup_init(ad_disc_ds_t *ds)
{
int i;
int rc, ldversion;
@@ -844,6 +650,11 @@ ldap_lookup_init(idmap_ad_disc_ds_t *ds)
LDAP *ld = NULL;
for (i = 0; ds[i].host[0] != '\0'; i++) {
+ if (DBG(LDAP, 2)) {
+ logger(LOG_DEBUG, "adutils: ldap_lookup_init, host %s",
+ ds[i].host);
+ }
+
ld = ldap_init(ds[i].host, ds[i].port);
if (ld == NULL) {
if (DBG(LDAP, 1)) {
@@ -896,60 +707,6 @@ ldap_lookup_init(idmap_ad_disc_ds_t *ds)
/*
- * A utility function to get the value of some attribute of one of one
- * or more AD LDAP objects named by the dn_list; first found one wins.
- */
-static char *
-ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers,
- char **dn_list, char *attr)
-{
- int i;
- int rc;
- int scope = LDAP_SCOPE_BASE;
- char *attrs[2];
- LDAPMessage *results = NULL;
- LDAPMessage *entry;
- char **values = NULL;
- char *val = NULL;
-
- attrs[0] = attr;
- attrs[1] = NULL;
-
- if (*ld == NULL)
- *ld = ldap_lookup_init(domainControllers);
-
- if (*ld == NULL)
- return (NULL);
-
- for (i = 0; dn_list[i] != NULL; i++) {
- rc = ldap_search_s(*ld, dn_list[i], scope,
- "(objectclass=*)", attrs, 0, &results);
- if (rc == LDAP_SUCCESS) {
- for (entry = ldap_first_entry(*ld, results);
- entry != NULL && values == NULL;
- entry = ldap_next_entry(*ld, entry)) {
- values = ldap_get_values(
- *ld, entry, attr);
- }
-
- if (values != NULL) {
- (void) ldap_msgfree(results);
- val = strdup(values[0]);
- ldap_value_free(values);
- return (val);
- }
- }
- if (results != NULL) {
- (void) ldap_msgfree(results);
- results = NULL;
- }
- }
-
- return (NULL);
-}
-
-
-/*
* Lookup the trusted domains in the global catalog.
*
* Returns:
@@ -958,7 +715,7 @@ ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers,
* NULL an error occured
*/
ad_disc_trusteddomains_t *
-ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog,
+ldap_lookup_trusted_domains(LDAP **ld, ad_disc_ds_t *globalCatalog,
char *base_dn)
{
int scope = LDAP_SCOPE_SUBTREE;
@@ -978,8 +735,10 @@ ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog,
if (*ld == NULL)
*ld = ldap_lookup_init(globalCatalog);
- if (*ld == NULL)
+ if (*ld == NULL) {
+ logger(LOG_ERR, "adutils: ldap_lookup_init failed");
return (NULL);
+ }
attrs[0] = "trustPartner";
attrs[1] = "trustDirection";
@@ -1039,6 +798,9 @@ ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog,
trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t));
if (DBG(DISC, 1))
logger(LOG_DEBUG, " not found");
+ } else {
+ if (DBG(DISC, 1))
+ logger(LOG_DEBUG, " rc=%d", rc);
}
if (results != NULL)
(void) ldap_msgfree(results);
@@ -1051,7 +813,7 @@ ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog,
* This functions finds all the domains in a forest.
*/
ad_disc_domainsinforest_t *
-ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs)
+ldap_lookup_domains_in_forest(LDAP **ld, ad_disc_ds_t *globalCatalogs)
{
static char *attrs[] = {
"objectSid",
@@ -1064,27 +826,34 @@ ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs)
int nresults;
ad_disc_domainsinforest_t *domains = NULL;
- if (DBG(DISC, 2))
+ if (DBG(DISC, 1))
logger(LOG_DEBUG, "Looking for domains in forest...");
if (*ld == NULL)
*ld = ldap_lookup_init(globalCatalogs);
- if (*ld == NULL)
+ if (*ld == NULL) {
+ logger(LOG_ERR, "adutils: ldap_lookup_init failed");
return (NULL);
+ }
/* Find domains */
rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE,
"(objectClass=Domain)", attrs, 0, &result);
+ if (rc != LDAP_SUCCESS) {
+ logger(LOG_ERR, "adutils: ldap_search, rc=%d", rc);
+ goto err;
+ }
if (DBG(DISC, 1))
logger(LOG_DEBUG, "Domains in forest:");
- if (rc != LDAP_SUCCESS)
- goto err;
nresults = ldap_count_entries(*ld, result);
domains = calloc(nresults + 1, sizeof (*domains));
- if (domains == NULL)
+ if (domains == NULL) {
+ if (DBG(DISC, 1))
+ logger(LOG_DEBUG, " (nomem)");
goto err;
+ }
for (entry = ldap_first_entry(*ld, result);
entry != NULL;
@@ -1162,7 +931,9 @@ ad_disc_init(void)
DO_RES_NINIT(ctx);
ctx->domain_name.type = AD_STRING;
+ ctx->domain_guid.type = AD_UUID;
ctx->domain_controller.type = AD_DIRECTORY;
+ ctx->preferred_dc.type = AD_DIRECTORY;
ctx->site_name.type = AD_STRING;
ctx->forest_name.type = AD_STRING;
ctx->global_catalog.type = AD_DIRECTORY;
@@ -1189,9 +960,15 @@ ad_disc_fini(ad_disc_t ctx)
if (ctx->domain_name.value != NULL)
free(ctx->domain_name.value);
+ if (ctx->domain_guid.value != NULL)
+ free(ctx->domain_guid.value);
+
if (ctx->domain_controller.value != NULL)
free(ctx->domain_controller.value);
+ if (ctx->preferred_dc.value != NULL)
+ free(ctx->preferred_dc.value);
+
if (ctx->site_name.value != NULL)
free(ctx->site_name.value);
@@ -1220,17 +997,25 @@ ad_disc_fini(ad_disc_t ctx)
void
ad_disc_refresh(ad_disc_t ctx)
{
- if (ctx->res_ninitted)
+ if (ctx->res_ninitted) {
res_ndestroy(&ctx->res_state);
+ ctx->res_ninitted = 0;
+ }
(void) memset(&ctx->res_state, 0, sizeof (ctx->res_state));
- ctx->res_ninitted = res_ninit(&ctx->res_state) != -1;
+ DO_RES_NINIT(ctx);
if (ctx->domain_name.state == AD_STATE_AUTO)
ctx->domain_name.state = AD_STATE_INVALID;
+ if (ctx->domain_guid.state == AD_STATE_AUTO)
+ ctx->domain_guid.state = AD_STATE_INVALID;
+
if (ctx->domain_controller.state == AD_STATE_AUTO)
ctx->domain_controller.state = AD_STATE_INVALID;
+ if (ctx->preferred_dc.state == AD_STATE_AUTO)
+ ctx->preferred_dc.state = AD_STATE_INVALID;
+
if (ctx->site_name.state == AD_STATE_AUTO)
ctx->site_name.state = AD_STATE_INVALID;
@@ -1270,15 +1055,77 @@ ad_disc_done(ad_disc_t ctx)
ctx->expires_not_after = now + MAXIMUM_TTL;
}
+static void
+log_cds(ad_disc_t ctx, ad_disc_cds_t *cds)
+{
+ char buf[INET6_ADDRSTRLEN];
+ struct addrinfo *ai;
+
+ if (!DBG(DISC, 1) && ctx->status_fp == NULL)
+ return;
+
+ DEBUG1STATUS(ctx, "Candidate servers:");
+ if (cds->cds_ds.host[0] == '\0') {
+ DEBUG1STATUS(ctx, " (empty list)");
+ return;
+ }
+
+ while (cds->cds_ds.host[0] != '\0') {
+
+ DEBUG1STATUS(ctx, " %s p=%d w=%d",
+ cds->cds_ds.host,
+ cds->cds_ds.priority,
+ cds->cds_ds.weight);
+
+ ai = cds->cds_ai;
+ if (ai == NULL) {
+ DEBUG1STATUS(ctx, " (no address)");
+ }
+ while (ai != NULL) {
+ int eai;
+
+ eai = getnameinfo(ai->ai_addr, ai->ai_addrlen,
+ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST);
+ if (eai != 0)
+ (void) strlcpy(buf, "?", sizeof (buf));
+
+ DEBUG1STATUS(ctx, " %s", buf);
+ ai = ai->ai_next;
+ }
+ cds++;
+ }
+}
+
+static void
+log_ds(ad_disc_t ctx, ad_disc_ds_t *ds)
+{
+ char buf[INET6_ADDRSTRLEN];
+
+ if (!DBG(DISC, 1) && ctx->status_fp == NULL)
+ return;
+
+ DEBUG1STATUS(ctx, "Responding servers:");
+ if (ds->host[0] == '\0') {
+ DEBUG1STATUS(ctx, " (empty list)");
+ return;
+ }
+
+ while (ds->host[0] != '\0') {
+
+ DEBUG1STATUS(ctx, " %s", ds->host);
+ DO_GETNAMEINFO(buf, sizeof (buf), &ds->addr);
+ DEBUG1STATUS(ctx, " %s", buf);
+
+ ds++;
+ }
+}
/* Discover joined Active Directory domainName */
static ad_item_t *
validate_DomainName(ad_disc_t ctx)
{
- idmap_ad_disc_ds_t *domain_controller = NULL;
char *dname, *srvname;
- uint32_t ttl = 0;
- int len;
+ int len, rc;
if (is_valid(&ctx->domain_name))
return (&ctx->domain_name);
@@ -1286,23 +1133,21 @@ validate_DomainName(ad_disc_t ctx)
/* Try to find our domain by searching for DCs for it */
DO_RES_NINIT(ctx);
- if (DBG(DISC, 2))
+ if (DBG(DISC, 1))
logger(LOG_DEBUG, "Looking for our AD domain name...");
- domain_controller = srv_query(&ctx->res_state,
- LDAP_SRV_HEAD DC_SRV_TAIL,
- ctx->domain_name.value, &srvname, &ttl);
+ rc = srv_getdom(&ctx->res_state,
+ LDAP_SRV_HEAD DC_SRV_TAIL, &srvname);
/*
* If we can't find DCs by via res_nsearch() then there's no
* point in trying anything else to discover the AD domain name.
*/
- if (domain_controller == NULL) {
+ if (rc < 0) {
if (DBG(DISC, 1))
logger(LOG_DEBUG, "Can't find our domain name.");
return (NULL);
}
- free(domain_controller);
/*
* We have the FQDN of the SRV RR name, so now we extract the
* domainname suffix from it.
@@ -1324,7 +1169,14 @@ validate_DomainName(ad_disc_t ctx)
if (DBG(DISC, 1))
logger(LOG_DEBUG, "Our domain name: %s", dname);
- update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl);
+
+ /*
+ * There is no "time to live" on the discovered domain,
+ * so passing zero as TTL here, making it non-expiring.
+ * Note that current consumers do not auto-discover the
+ * domain name, though a future installer could.
+ */
+ update_item(&ctx->domain_name, dname, AD_STATE_AUTO, 0);
return (&ctx->domain_name);
}
@@ -1354,12 +1206,16 @@ ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered)
static ad_item_t *
validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
{
- uint32_t ttl = 0;
- idmap_ad_disc_ds_t *domain_controller = NULL;
+ ad_disc_ds_t *dc = NULL;
+ ad_disc_cds_t *cdc = NULL;
boolean_t validate_global = B_FALSE;
boolean_t validate_site = B_FALSE;
ad_item_t *domain_name_item;
+ char *domain_name;
ad_item_t *site_name_item = NULL;
+ char *site_name;
+ ad_item_t *prefer_dc_item;
+ ad_disc_ds_t *prefer_dc = NULL;
/* If the values is fixed there will not be a site specific version */
if (is_fixed(&ctx->domain_controller))
@@ -1368,12 +1224,17 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
domain_name_item = validate_DomainName(ctx);
if (domain_name_item == NULL)
return (NULL);
+ domain_name = (char *)domain_name_item->value;
+
+ /* Get (optional) preferred DC. */
+ prefer_dc_item = validate_PreferredDC(ctx);
+ if (prefer_dc_item != NULL)
+ prefer_dc = prefer_dc_item->value;
if (req == AD_DISC_GLOBAL)
validate_global = B_TRUE;
else {
- site_name_item = validate_SiteName(ctx);
- if (site_name_item != NULL)
+ if (is_fixed(&ctx->site_name))
validate_site = B_TRUE;
else if (req == AD_DISC_PREFER_SITE)
validate_global = B_TRUE;
@@ -1383,43 +1244,44 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
if (!is_valid(&ctx->domain_controller) ||
is_changed(&ctx->domain_controller, PARAM1,
domain_name_item)) {
- if (DBG(DISC, 2)) {
- logger(LOG_DEBUG, "Looking for DCs for %s",
- domain_name_item->value);
- }
+
/*
* Lookup DNS SRV RR named
* _ldap._tcp.dc._msdcs.<DomainName>
*/
+ DEBUG1STATUS(ctx, "DNS SRV query, dom=%s",
+ domain_name);
DO_RES_NINIT(ctx);
- domain_controller = srv_query(&ctx->res_state,
+ cdc = srv_query(&ctx->res_state,
LDAP_SRV_HEAD DC_SRV_TAIL,
- domain_name_item->value, NULL, &ttl);
+ domain_name, prefer_dc);
- if (DBG(DISC, 1)) {
- logger(LOG_DEBUG, "DCs for %s:",
- domain_name_item->value);
- }
- if (domain_controller == NULL) {
- if (DBG(DISC, 1))
- logger(LOG_DEBUG, " not found");
+ if (cdc == NULL) {
+ DEBUG1STATUS(ctx, "(no DNS response)");
return (NULL);
}
+ log_cds(ctx, cdc);
- if (DBG(DISC, 1)) {
- int i;
-
- for (i = 0;
- domain_controller[i].host[0] != '\0';
- i++) {
- logger(LOG_DEBUG, " %s:%d",
- domain_controller[i].host,
- domain_controller[i].port);
- }
+ /*
+ * Filter out unresponsive servers, and
+ * save the domain info we get back.
+ */
+ dc = ldap_ping(
+ ctx,
+ cdc,
+ domain_name,
+ DS_DS_FLAG);
+ srv_free(cdc);
+ cdc = NULL;
+
+ if (dc == NULL) {
+ DEBUG1STATUS(ctx, "(no LDAP response)");
+ return (NULL);
}
+ log_ds(ctx, dc);
- update_item(&ctx->domain_controller, domain_controller,
- AD_STATE_AUTO, ttl);
+ update_item(&ctx->domain_controller, dc,
+ AD_STATE_AUTO, dc->ttl);
update_version(&ctx->domain_controller, PARAM1,
domain_name_item);
}
@@ -1427,54 +1289,55 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
}
if (validate_site) {
+ site_name_item = &ctx->site_name;
+ site_name = (char *)site_name_item->value;
+
if (!is_valid(&ctx->site_domain_controller) ||
is_changed(&ctx->site_domain_controller, PARAM1,
domain_name_item) ||
is_changed(&ctx->site_domain_controller, PARAM2,
site_name_item)) {
char rr_name[DNS_MAX_NAME];
- if (DBG(DISC, 2)) {
- logger(LOG_DEBUG,
- "Looking for DCs for %s in %s",
- domain_name_item->value,
- site_name_item->value);
- }
+
/*
* Lookup DNS SRV RR named
* _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName>
*/
+ DEBUG1STATUS(ctx, "DNS SRV query, dom=%s, site=%s",
+ domain_name, site_name);
(void) snprintf(rr_name, sizeof (rr_name),
LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL,
- site_name_item->value);
+ site_name);
DO_RES_NINIT(ctx);
- domain_controller = srv_query(&ctx->res_state, rr_name,
- domain_name_item->value, NULL, &ttl);
- if (DBG(DISC, 1)) {
- logger(LOG_DEBUG,
- "DCs for %s in %s",
- domain_name_item->value,
- site_name_item->value);
- }
- if (domain_controller == NULL) {
- if (DBG(DISC, 1))
- logger(LOG_DEBUG, " not found");
+ cdc = srv_query(&ctx->res_state, rr_name,
+ domain_name, prefer_dc);
+
+ if (cdc == NULL) {
+ DEBUG1STATUS(ctx, "(no DNS response)");
return (NULL);
}
+ log_cds(ctx, cdc);
- if (DBG(DISC, 1)) {
- int i;
-
- for (i = 0;
- domain_controller[i].host[0] != '\0';
- i++) {
- logger(LOG_DEBUG, " %s:%d",
- domain_controller[i].host,
- domain_controller[i].port);
- }
+ /*
+ * Filter out unresponsive servers, and
+ * save the domain info we get back.
+ */
+ dc = ldap_ping(
+ ctx,
+ cdc,
+ domain_name,
+ DS_DS_FLAG);
+ srv_free(cdc);
+ cdc = NULL;
+
+ if (dc == NULL) {
+ DEBUG1STATUS(ctx, "(no LDAP response)");
+ return (NULL);
}
+ log_ds(ctx, dc);
- update_item(&ctx->site_domain_controller,
- domain_controller, AD_STATE_AUTO, ttl);
+ update_item(&ctx->site_domain_controller, dc,
+ AD_STATE_AUTO, dc->ttl);
update_version(&ctx->site_domain_controller, PARAM1,
domain_name_item);
update_version(&ctx->site_domain_controller, PARAM2,
@@ -1485,12 +1348,12 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
return (NULL);
}
-idmap_ad_disc_ds_t *
+ad_disc_ds_t *
ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req,
- boolean_t *auto_discovered)
+ boolean_t *auto_discovered)
{
ad_item_t *domain_controller_item;
- idmap_ad_disc_ds_t *domain_controller = NULL;
+ ad_disc_ds_t *domain_controller = NULL;
domain_controller_item = validate_DomainController(ctx, req);
@@ -1506,161 +1369,68 @@ ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req,
}
-/* Discover site name (for multi-homed systems the first one found wins) */
+/*
+ * Discover the Domain GUID
+ * This info comes from validate_DomainController()
+ */
static ad_item_t *
-validate_SiteName(ad_disc_t ctx)
+validate_DomainGUID(ad_disc_t ctx)
{
- LDAP *ld = NULL;
- ad_subnet_t *subnets = NULL;
- char **dn_subnets = NULL;
- char *dn_root[2];
- char *config_naming_context = NULL;
- char *site_object = NULL;
- char *site_name = NULL;
- char *forest_name;
- int len;
- boolean_t update_required = B_FALSE;
ad_item_t *domain_controller_item;
- if (is_fixed(&ctx->site_name))
- return (&ctx->site_name);
+ if (is_fixed(&ctx->domain_guid))
+ return (&ctx->domain_guid);
- /* Can't rely on site-specific DCs */
domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
if (domain_controller_item == NULL)
return (NULL);
- if (!is_valid(&ctx->site_name) ||
- is_changed(&ctx->site_name, PARAM1, domain_controller_item) ||
- ctx->subnets == NULL || ctx->subnets_changed) {
- subnets = find_subnets();
- ctx->subnets_last_check = time(NULL);
- update_required = B_TRUE;
- } else if (ctx->subnets_last_check + 60 < time(NULL)) {
- /* NEEDSWORK magic constant 60 above */
- subnets = find_subnets();
- ctx->subnets_last_check = time(NULL);
- if (cmpsubnets(ctx->subnets, subnets) != 0)
- update_required = B_TRUE;
- }
-
- if (!update_required) {
- free(subnets);
- return (&ctx->site_name);
- }
-
- if (subnets == NULL)
+ if (!is_valid(&ctx->domain_guid))
return (NULL);
- dn_root[0] = "";
- dn_root[1] = NULL;
+ return (&ctx->domain_guid);
+}
- if (DBG(DISC, 1))
- logger(LOG_DEBUG, "Getting site name");
- config_naming_context = ldap_lookup_entry_attr(
- &ld, ctx->domain_controller.value,
- dn_root, "configurationNamingContext");
- if (config_naming_context == NULL)
- goto out;
- /*
- * configurationNamingContext also provides the Forest
- * Name.
- */
- if (!is_fixed(&ctx->forest_name)) {
- /*
- * The configurationNamingContext should be of
- * form:
- * CN=Configuration,<DNforestName>
- * Remove the first part and convert to DNS form
- * (replace ",DC=" with ".")
- */
- char *str = "CN=Configuration,";
- int len = strlen(str);
- if (strncasecmp(config_naming_context, str, len) == 0) {
- forest_name = DN_to_DNS(config_naming_context + len);
- if (DBG(DISC, 1)) {
- logger(LOG_DEBUG, " forest: %s",
- forest_name);
- }
- update_item(&ctx->forest_name, forest_name,
- AD_STATE_AUTO, 0);
- update_version(&ctx->forest_name, PARAM1,
- domain_controller_item);
- }
- }
+uchar_t *
+ad_disc_get_DomainGUID(ad_disc_t ctx, boolean_t *auto_discovered)
+{
+ ad_item_t *domain_guid_item;
+ uchar_t *domain_guid = NULL;
- if (DBG(DISC, 2))
- logger(LOG_DEBUG, " CNC: %s", config_naming_context);
+ domain_guid_item = validate_DomainGUID(ctx);
+ if (domain_guid_item != NULL) {
+ domain_guid = uuid_dup(domain_guid_item->value);
+ if (auto_discovered != NULL)
+ *auto_discovered =
+ (domain_guid_item->state == AD_STATE_AUTO);
+ } else if (auto_discovered != NULL)
+ *auto_discovered = B_FALSE;
- if (DBG(DISC, 2)) {
- int i;
- logger(LOG_DEBUG, " Looking for sites for subnets:");
- for (i = 0; subnets[i].subnet[0] != '\0'; i++) {
- logger(LOG_DEBUG, " %s", subnets[i].subnet);
- }
- }
+ return (domain_guid);
+}
- dn_subnets = subnets_to_DNs(subnets, config_naming_context);
- if (dn_subnets == NULL)
- goto out;
-
- site_object = ldap_lookup_entry_attr(
- &ld, domain_controller_item->value,
- dn_subnets, "siteobject");
- if (site_object != NULL) {
- /*
- * The site object should be of the form
- * CN=<site>,CN=Sites,CN=Configuration,
- * <DN Domain>
- */
- if (DBG(DISC, 2))
- logger(LOG_DEBUG, " Site object: %s", site_object);
- if (strncasecmp(site_object, "CN=", 3) == 0) {
- for (len = 0; site_object[len + 3] != ','; len++)
- ;
- site_name = malloc(len + 1);
- (void) strncpy(site_name, &site_object[3], len);
- site_name[len] = '\0';
- if (DBG(DISC, 1)) {
- logger(LOG_DEBUG, " Site name \"%s\"",
- site_name);
- }
- update_item(&ctx->site_name, site_name,
- AD_STATE_AUTO, 0);
- update_version(&ctx->site_name, PARAM1,
- domain_controller_item);
- }
- }
- if (ctx->subnets != NULL) {
- free(ctx->subnets);
- ctx->subnets = NULL;
- }
- ctx->subnets = subnets;
- subnets = NULL;
- ctx->subnets_changed = B_FALSE;
+/*
+ * Discover site name (for multi-homed systems the first one found wins)
+ * This info comes from validate_DomainController()
+ */
+static ad_item_t *
+validate_SiteName(ad_disc_t ctx)
+{
+ ad_item_t *domain_controller_item;
-out:
- if (ld != NULL)
- (void) ldap_unbind(ld);
+ if (is_fixed(&ctx->site_name))
+ return (&ctx->site_name);
- if (dn_subnets != NULL) {
- int i;
- for (i = 0; dn_subnets[i] != NULL; i++)
- free(dn_subnets[i]);
- free(dn_subnets);
- }
- if (config_naming_context != NULL)
- free(config_naming_context);
- if (site_object != NULL)
- free(site_object);
+ domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
+ if (domain_controller_item == NULL)
+ return (NULL);
- free(subnets);
- if (site_name == NULL)
+ if (!is_valid(&ctx->site_name))
return (NULL);
- return (&ctx->site_name);
+ return (&ctx->site_name);
}
@@ -1684,69 +1454,25 @@ ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered)
-/* Discover forest name */
+/*
+ * Discover forest name
+ * This info comes from validate_DomainController()
+ */
static ad_item_t *
validate_ForestName(ad_disc_t ctx)
{
- LDAP *ld = NULL;
- char *config_naming_context;
- char *forest_name = NULL;
- char *dn_list[2];
ad_item_t *domain_controller_item;
if (is_fixed(&ctx->forest_name))
return (&ctx->forest_name);
- /*
- * We may not have a site name yet, so we won't rely on
- * site-specific DCs. (But maybe we could replace
- * validate_ForestName() with validate_siteName()?)
- */
+
domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
if (domain_controller_item == NULL)
return (NULL);
- if (!is_valid(&ctx->forest_name) ||
- is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) {
-
- dn_list[0] = "";
- dn_list[1] = NULL;
- if (DBG(DISC, 1))
- logger(LOG_DEBUG, "Getting forest name");
- config_naming_context = ldap_lookup_entry_attr(
- &ld, ctx->domain_controller.value,
- dn_list, "configurationNamingContext");
- if (config_naming_context != NULL) {
- /*
- * The configurationNamingContext should be of
- * form:
- * CN=Configuration,<DNforestName>
- * Remove the first part and convert to DNS form
- * (replace ",DC=" with ".")
- */
- char *str = "CN=Configuration,";
- int len = strlen(str);
- if (strncasecmp(config_naming_context, str, len) == 0) {
- forest_name = DN_to_DNS(
- config_naming_context + len);
- }
- free(config_naming_context);
- }
- if (ld != NULL)
- (void) ldap_unbind(ld);
-
- if (forest_name == NULL) {
- if (DBG(DISC, 1))
- logger(LOG_DEBUG, " not found");
- return (NULL);
- }
-
- if (DBG(DISC, 1))
- logger(LOG_DEBUG, " %s", forest_name);
+ if (!is_valid(&ctx->forest_name))
+ return (NULL);
- update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0);
- update_version(&ctx->forest_name, PARAM1,
- domain_controller_item);
- }
return (&ctx->forest_name);
}
@@ -1775,12 +1501,15 @@ ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered)
static ad_item_t *
validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
{
- idmap_ad_disc_ds_t *global_catalog = NULL;
- uint32_t ttl = 0;
+ ad_disc_ds_t *gc = NULL;
+ ad_disc_cds_t *cgc = NULL;
boolean_t validate_global = B_FALSE;
boolean_t validate_site = B_FALSE;
+ ad_item_t *dc_item;
ad_item_t *forest_name_item;
ad_item_t *site_name_item;
+ char *forest_name;
+ char *site_name;
/* If the values is fixed there will not be a site specific version */
if (is_fixed(&ctx->global_catalog))
@@ -1789,12 +1518,12 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
forest_name_item = validate_ForestName(ctx);
if (forest_name_item == NULL)
return (NULL);
+ forest_name = (char *)forest_name_item->value;
if (req == AD_DISC_GLOBAL)
validate_global = B_TRUE;
else {
- site_name_item = validate_SiteName(ctx);
- if (site_name_item != NULL)
+ if (is_fixed(&ctx->site_name))
validate_site = B_TRUE;
else if (req == AD_DISC_PREFER_SITE)
validate_global = B_TRUE;
@@ -1804,40 +1533,63 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
if (!is_valid(&ctx->global_catalog) ||
is_changed(&ctx->global_catalog, PARAM1,
forest_name_item)) {
+
/*
- * Lookup DNS SRV RR named
+ * See if our DC is also a GC.
+ */
+ dc_item = validate_DomainController(ctx, req);
+ if (dc_item != NULL) {
+ ad_disc_ds_t *ds = dc_item->value;
+ if ((ds->flags & DS_GC_FLAG) != 0) {
+ DEBUG1STATUS(ctx,
+ "DC is also a GC for %s",
+ forest_name);
+ gc = ds_dup(ds);
+ if (gc != NULL) {
+ gc->port = GC_PORT;
+ goto update_global;
+ }
+ }
+ }
+
+ /*
+ * Lookup DNS SRV RR named:
* _ldap._tcp.gc._msdcs.<ForestName>
*/
+ DEBUG1STATUS(ctx, "DNS SRV query, forest=%s",
+ forest_name);
DO_RES_NINIT(ctx);
- global_catalog =
- srv_query(&ctx->res_state,
+ cgc = srv_query(&ctx->res_state,
LDAP_SRV_HEAD GC_SRV_TAIL,
- ctx->forest_name.value, NULL, &ttl);
+ forest_name, NULL);
- if (DBG(DISC, 1)) {
- logger(LOG_DEBUG,
- "GC servers for %s:",
- ctx->forest_name.value);
- }
- if (global_catalog == NULL) {
- if (DBG(DISC, 1))
- logger(LOG_DEBUG, " not found");
+ if (cgc == NULL) {
+ DEBUG1STATUS(ctx, "(no DNS response)");
return (NULL);
}
+ log_cds(ctx, cgc);
- if (DBG(DISC, 1)) {
- int i;
- for (i = 0;
- global_catalog[i].host[0] != '\0';
- i++) {
- logger(LOG_DEBUG, " %s:%d",
- global_catalog[i].host,
- global_catalog[i].port);
- }
+ /*
+ * Filter out unresponsive servers, and
+ * save the domain info we get back.
+ */
+ gc = ldap_ping(
+ NULL,
+ cgc,
+ forest_name,
+ DS_GC_FLAG);
+ srv_free(cgc);
+ cgc = NULL;
+
+ if (gc == NULL) {
+ DEBUG1STATUS(ctx, "(no LDAP response)");
+ return (NULL);
}
+ log_ds(ctx, gc);
- update_item(&ctx->global_catalog, global_catalog,
- AD_STATE_AUTO, ttl);
+ update_global:
+ update_item(&ctx->global_catalog, gc,
+ AD_STATE_AUTO, gc->ttl);
update_version(&ctx->global_catalog, PARAM1,
forest_name_item);
}
@@ -1845,51 +1597,75 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
}
if (validate_site) {
+ site_name_item = &ctx->site_name;
+ site_name = (char *)site_name_item->value;
+
if (!is_valid(&ctx->site_global_catalog) ||
is_changed(&ctx->site_global_catalog, PARAM1,
forest_name_item) ||
is_changed(&ctx->site_global_catalog, PARAM2,
site_name_item)) {
- char rr_name[DNS_MAX_NAME];
+ char rr_name[DNS_MAX_NAME];
+
+ /*
+ * See if our DC is also a GC.
+ */
+ dc_item = validate_DomainController(ctx, req);
+ if (dc_item != NULL) {
+ ad_disc_ds_t *ds = dc_item->value;
+ if ((ds->flags & DS_GC_FLAG) != 0) {
+ DEBUG1STATUS(ctx,
+ "DC is also a GC for %s in %s",
+ forest_name, site_name);
+ gc = ds_dup(ds);
+ if (gc != NULL) {
+ gc->port = GC_PORT;
+ goto update_site;
+ }
+ }
+ }
/*
* Lookup DNS SRV RR named:
* _ldap._tcp.<siteName>._sites.gc.
* _msdcs.<ForestName>
*/
- (void) snprintf(rr_name,
- sizeof (rr_name),
+ DEBUG1STATUS(ctx, "DNS SRV query, forest=%s, site=%s",
+ forest_name, site_name);
+ (void) snprintf(rr_name, sizeof (rr_name),
LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL,
- ctx->site_name.value);
+ site_name);
DO_RES_NINIT(ctx);
- global_catalog = srv_query(&ctx->res_state, rr_name,
- ctx->forest_name.value, NULL, &ttl);
+ cgc = srv_query(&ctx->res_state, rr_name,
+ forest_name, NULL);
- if (DBG(DISC, 1)) {
- logger(LOG_DEBUG,
- "GC servers for %s in %s",
- ctx->forest_name.value,
- ctx->site_name.value);
- }
- if (global_catalog == NULL) {
- if (DBG(DISC, 1))
- logger(LOG_DEBUG, " not found");
+ if (cgc == NULL) {
+ DEBUG1STATUS(ctx, "(no DNS response)");
return (NULL);
}
+ log_cds(ctx, cgc);
- if (DBG(DISC, 1)) {
- int i;
- for (i = 0;
- global_catalog[i].host[0] != '\0';
- i++) {
- logger(LOG_DEBUG, " %s:%d",
- global_catalog[i].host,
- global_catalog[i].port);
- }
+ /*
+ * Filter out unresponsive servers, and
+ * save the domain info we get back.
+ */
+ gc = ldap_ping(
+ NULL,
+ cgc,
+ forest_name,
+ DS_GC_FLAG);
+ srv_free(cgc);
+ cgc = NULL;
+
+ if (gc == NULL) {
+ DEBUG1STATUS(ctx, "(no LDAP response)");
+ return (NULL);
}
+ log_ds(ctx, gc);
- update_item(&ctx->site_global_catalog, global_catalog,
- AD_STATE_AUTO, ttl);
+ update_site:
+ update_item(&ctx->site_global_catalog, gc,
+ AD_STATE_AUTO, gc->ttl);
update_version(&ctx->site_global_catalog, PARAM1,
forest_name_item);
update_version(&ctx->site_global_catalog, PARAM2,
@@ -1901,11 +1677,11 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
}
-idmap_ad_disc_ds_t *
+ad_disc_ds_t *
ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req,
boolean_t *auto_discovered)
{
- idmap_ad_disc_ds_t *global_catalog = NULL;
+ ad_disc_ds_t *global_catalog = NULL;
ad_item_t *global_catalog_item;
global_catalog_item = validate_GlobalCatalog(ctx, req);
@@ -2059,6 +1835,33 @@ ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered)
return (domains_in_forest);
}
+static ad_item_t *
+validate_PreferredDC(ad_disc_t ctx)
+{
+ if (is_valid(&ctx->preferred_dc))
+ return (&ctx->preferred_dc);
+
+ return (NULL);
+}
+
+ad_disc_ds_t *
+ad_disc_get_PreferredDC(ad_disc_t ctx, boolean_t *auto_discovered)
+{
+ ad_disc_ds_t *preferred_dc = NULL;
+ ad_item_t *preferred_dc_item;
+
+ preferred_dc_item = validate_PreferredDC(ctx);
+
+ if (preferred_dc_item != NULL) {
+ preferred_dc = ds_dup(preferred_dc_item->value);
+ if (auto_discovered != NULL)
+ *auto_discovered =
+ (preferred_dc_item->state == AD_STATE_AUTO);
+ } else if (auto_discovered != NULL)
+ *auto_discovered = B_FALSE;
+
+ return (preferred_dc);
+}
@@ -2077,12 +1880,40 @@ ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName)
return (0);
}
+int
+ad_disc_set_DomainGUID(ad_disc_t ctx, uchar_t *u)
+{
+ char *domain_guid = NULL;
+ if (u != NULL) {
+ domain_guid = uuid_dup(u);
+ if (domain_guid == NULL)
+ return (-1);
+ update_item(&ctx->domain_guid, domain_guid,
+ AD_STATE_FIXED, 0);
+ } else if (ctx->domain_guid.state == AD_STATE_FIXED)
+ ctx->domain_guid.state = AD_STATE_INVALID;
+ return (0);
+}
+
+void
+auto_set_DomainGUID(ad_disc_t ctx, uchar_t *u)
+{
+ char *domain_guid = NULL;
+
+ if (is_fixed(&ctx->domain_guid))
+ return;
+
+ domain_guid = uuid_dup(u);
+ if (domain_guid == NULL)
+ return;
+ update_item(&ctx->domain_guid, domain_guid, AD_STATE_AUTO, 0);
+}
int
ad_disc_set_DomainController(ad_disc_t ctx,
- const idmap_ad_disc_ds_t *domainController)
+ const ad_disc_ds_t *domainController)
{
- idmap_ad_disc_ds_t *domain_controller = NULL;
+ ad_disc_ds_t *domain_controller = NULL;
if (domainController != NULL) {
domain_controller = ds_dup(domainController);
if (domain_controller == NULL)
@@ -2094,7 +1925,6 @@ ad_disc_set_DomainController(ad_disc_t ctx,
return (0);
}
-
int
ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName)
{
@@ -2109,6 +1939,20 @@ ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName)
return (0);
}
+void
+auto_set_SiteName(ad_disc_t ctx, char *siteName)
+{
+ char *site_name = NULL;
+
+ if (is_fixed(&ctx->site_name))
+ return;
+
+ site_name = strdup(siteName);
+ if (site_name == NULL)
+ return;
+ update_item(&ctx->site_name, site_name, AD_STATE_AUTO, 0);
+}
+
int
ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName)
{
@@ -2124,11 +1968,25 @@ ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName)
return (0);
}
+void
+auto_set_ForestName(ad_disc_t ctx, char *forestName)
+{
+ char *forest_name = NULL;
+
+ if (is_fixed(&ctx->forest_name))
+ return;
+
+ forest_name = strdup(forestName);
+ if (forest_name == NULL)
+ return;
+ update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0);
+}
+
int
ad_disc_set_GlobalCatalog(ad_disc_t ctx,
- const idmap_ad_disc_ds_t *globalCatalog)
+ const ad_disc_ds_t *globalCatalog)
{
- idmap_ad_disc_ds_t *global_catalog = NULL;
+ ad_disc_ds_t *global_catalog = NULL;
if (globalCatalog != NULL) {
global_catalog = ds_dup(globalCatalog);
if (global_catalog == NULL)
@@ -2140,6 +1998,27 @@ ad_disc_set_GlobalCatalog(ad_disc_t ctx,
return (0);
}
+int
+ad_disc_set_PreferredDC(ad_disc_t ctx, const ad_disc_ds_t *pref_dc)
+{
+ ad_disc_ds_t *new_pref_dc = NULL;
+ if (pref_dc != NULL) {
+ new_pref_dc = ds_dup(pref_dc);
+ if (new_pref_dc == NULL)
+ return (-1);
+ update_item(&ctx->preferred_dc, new_pref_dc,
+ AD_STATE_FIXED, 0);
+ } else if (ctx->preferred_dc.state == AD_STATE_FIXED)
+ ctx->preferred_dc.state = AD_STATE_INVALID;
+ return (0);
+}
+
+void
+ad_disc_set_StatusFP(ad_disc_t ctx, struct __FILE_TAG *fp)
+{
+ ctx->status_fp = fp;
+}
+
int
ad_disc_unset(ad_disc_t ctx)
@@ -2150,6 +2029,9 @@ ad_disc_unset(ad_disc_t ctx)
if (ctx->domain_controller.state == AD_STATE_FIXED)
ctx->domain_controller.state = AD_STATE_INVALID;
+ if (ctx->preferred_dc.state == AD_STATE_FIXED)
+ ctx->preferred_dc.state = AD_STATE_INVALID;
+
if (ctx->site_name.state == AD_STATE_FIXED)
ctx->site_name.state = AD_STATE_INVALID;
diff --git a/usr/src/lib/libadutils/common/addisc.h b/usr/src/lib/libadutils/common/addisc.h
index 786e79a66d..b3a0b44cc3 100644
--- a/usr/src/lib/libadutils/common/addisc.h
+++ b/usr/src/lib/libadutils/common/addisc.h
@@ -21,12 +21,14 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _ADINFO_H
#define _ADINFO_H
-#include <rpcsvc/idmap_prot.h>
+#include <sys/socket.h>
+#include <sys/uuid.h>
#include "libadutils.h"
@@ -41,6 +43,7 @@ extern "C" {
*/
#define MAXSTRSID 185
#define MAXDOMAINNAME 256
+#define AD_DISC_MAXHOSTNAME 256
typedef struct ad_disc *ad_disc_t;
@@ -66,6 +69,24 @@ enum ad_disc_req {
AD_DISC_GLOBAL /* Request global version */
};
+/*
+ * First four members of this are like idmap_ad_disc_ds_t
+ * (for compatiblity) until that can be eliminated.
+ * See PROP_DOMAIN_CONTROLLER in idmapd/server.c
+ */
+typedef struct ad_disc_ds {
+ /* Keep these first four in sync with idmap_ad_disc_ds_t */
+ int port;
+ int priority;
+ int weight;
+ char host[AD_DISC_MAXHOSTNAME];
+ /* Members after this are private and free to change. */
+ char site[AD_DISC_MAXHOSTNAME];
+ struct sockaddr_storage addr;
+ uint32_t flags;
+ uint32_t ttl;
+} ad_disc_ds_t;
+
ad_disc_t ad_disc_init(void);
void ad_disc_fini(ad_disc_t);
@@ -76,17 +97,23 @@ void ad_disc_fini(ad_disc_t);
char *
ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered);
-idmap_ad_disc_ds_t *
+uchar_t *
+ad_disc_get_DomainGUID(ad_disc_t ctx, boolean_t *auto_discovered);
+
+ad_disc_ds_t *
ad_disc_get_DomainController(ad_disc_t ctx,
enum ad_disc_req req, boolean_t *auto_discovered);
+ad_disc_ds_t *
+ad_disc_get_PreferredDC(ad_disc_t ctx, boolean_t *auto_discovered);
+
char *
ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered);
char *
ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered);
-idmap_ad_disc_ds_t *
+ad_disc_ds_t *
ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req,
boolean_t *auto_discovered);
@@ -105,8 +132,13 @@ int
ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName);
int
+ad_disc_set_DomainGUID(ad_disc_t ctx, uchar_t *u);
+
+int
ad_disc_set_DomainController(ad_disc_t ctx,
- const idmap_ad_disc_ds_t *domainController);
+ const ad_disc_ds_t *domainController);
+int
+ad_disc_set_PreferredDC(ad_disc_t ctx, const ad_disc_ds_t *dc);
int
ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName);
@@ -116,8 +148,17 @@ ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName);
int
ad_disc_set_GlobalCatalog(ad_disc_t ctx,
- const idmap_ad_disc_ds_t *globalCatalog);
+ const ad_disc_ds_t *globalCatalog);
+
+/*
+ * This function sets a FILE * on which this library will write
+ * progress information during DC Location.
+ */
+void
+ad_disc_set_StatusFP(ad_disc_t ctx, struct __FILE_TAG *);
+int
+ad_disc_getnameinfo(char *, int, struct sockaddr_storage *);
/*
* This routine forces all auto discovery item to be recomputed
@@ -140,7 +181,9 @@ boolean_t ad_disc_SubnetChanged(ad_disc_t);
/* This routine returns the Time To Live for auto discovered items */
int ad_disc_get_TTL(ad_disc_t);
-int ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2);
+int ad_disc_compare_uuid(uuid_t *u1, uuid_t *u2);
+
+int ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2);
int ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1,
ad_disc_trusteddomains_t *td2);
diff --git a/usr/src/lib/libadutils/common/addisc_impl.h b/usr/src/lib/libadutils/common/addisc_impl.h
new file mode 100644
index 0000000000..ce26ce256b
--- /dev/null
+++ b/usr/src/lib/libadutils/common/addisc_impl.h
@@ -0,0 +1,130 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#ifndef _ADDISC_IMPL_H
+#define _ADDISC_IMPL_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <resolv.h>
+#include <ldap.h>
+#include <pthread.h>
+#include "addisc.h"
+#include "libadutils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum ad_item_state {
+ AD_STATE_INVALID = 0, /* The value is not valid */
+ AD_STATE_FIXED, /* The value was fixed by caller */
+ AD_STATE_AUTO /* The value is auto discovered */
+ };
+
+enum ad_data_type {
+ AD_STRING = 123,
+ AD_UUID,
+ AD_DIRECTORY,
+ AD_DOMAINS_IN_FOREST,
+ AD_TRUSTED_DOMAINS
+ };
+
+
+typedef struct ad_subnet {
+ char subnet[24];
+} ad_subnet_t;
+
+
+typedef struct ad_item {
+ enum ad_item_state state;
+ enum ad_data_type type;
+ void *value;
+ time_t expires;
+ unsigned int version; /* Version is only changed */
+ /* if the value changes */
+#define PARAM1 0
+#define PARAM2 1
+ int param_version[2];
+ /* These holds the version of */
+ /* dependents so that a dependent */
+ /* change can be detected */
+} ad_item_t;
+
+typedef struct ad_disc {
+ struct __res_state res_state;
+ int res_ninitted;
+ ad_subnet_t *subnets;
+ boolean_t subnets_changed;
+ time_t subnets_last_check;
+ time_t expires_not_before;
+ time_t expires_not_after;
+ ad_item_t domain_name; /* DNS hostname string */
+ ad_item_t domain_guid; /* Domain UUID (binary) */
+ ad_item_t domain_controller; /* Directory hostname and */
+ /* port array */
+ ad_item_t preferred_dc;
+ ad_item_t site_name; /* String */
+ ad_item_t forest_name; /* DNS forestname string */
+ ad_item_t global_catalog; /* Directory hostname and */
+ /* port array */
+ ad_item_t domains_in_forest; /* DNS domainname and SID */
+ /* array */
+ ad_item_t trusted_domains; /* DNS domainname and trust */
+ /* direction array */
+ /* Site specfic versions */
+ ad_item_t site_domain_controller; /* Directory hostname and */
+ /* port array */
+ ad_item_t site_global_catalog; /* Directory hostname and */
+ /* port array */
+ /* Optional FILE * for DC Location status. */
+ struct __FILE_TAG *status_fp;
+
+ int debug[AD_DEBUG_MAX+1]; /* Debug levels */
+} ad_disc;
+
+/* Candidate Directory Servers (CDS) */
+typedef struct ad_disc_cds {
+ struct ad_disc_ds cds_ds;
+ struct addrinfo *cds_ai;
+} ad_disc_cds_t;
+
+ad_disc_ds_t *ldap_ping(ad_disc_t, ad_disc_cds_t *, char *, int);
+
+int srv_getdom(res_state, const char *, char **);
+ad_disc_cds_t *srv_query(res_state, const char *, const char *,
+ ad_disc_ds_t *);
+void srv_free(ad_disc_cds_t *);
+
+void auto_set_DomainGUID(ad_disc_t, uchar_t *);
+void auto_set_ForestName(ad_disc_t, char *);
+void auto_set_SiteName(ad_disc_t, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ADDISC_IMPL_H */
diff --git a/usr/src/lib/libadutils/common/adutils_impl.h b/usr/src/lib/libadutils/common/adutils_impl.h
index f82282ff97..d5d41a6768 100644
--- a/usr/src/lib/libadutils/common/adutils_impl.h
+++ b/usr/src/lib/libadutils/common/adutils_impl.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _ADUTILS_IMPL_H
@@ -31,7 +32,6 @@
#include <ldap.h>
#include <pthread.h>
#include "addisc.h"
-#include <rpcsvc/idmap_prot.h>
#include "libadutils.h"
#ifdef __cplusplus
diff --git a/usr/src/lib/libadutils/common/ldap_ping.c b/usr/src/lib/libadutils/common/ldap_ping.c
new file mode 100644
index 0000000000..5008372d74
--- /dev/null
+++ b/usr/src/lib/libadutils/common/ldap_ping.c
@@ -0,0 +1,677 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <net/if.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+#include <ldap.h>
+#include <lber.h>
+#include <syslog.h>
+#include "adutils_impl.h"
+#include "addisc_impl.h"
+
+#define LDAP_PORT 389
+
+#define NETLOGON_ATTR_NAME "NetLogon"
+#define NETLOGON_NT_VERSION_1 0x00000001
+#define NETLOGON_NT_VERSION_5 0x00000002
+#define NETLOGON_NT_VERSION_5EX 0x00000004
+#define NETLOGON_NT_VERSION_5EX_WITH_IP 0x00000008
+#define NETLOGON_NT_VERSION_WITH_CLOSEST_SITE 0x00000010
+#define NETLOGON_NT_VERSION_AVOID_NT4EMUL 0x01000000
+
+typedef enum {
+ OPCODE = 0,
+ SBZ,
+ FLAGS,
+ DOMAIN_GUID,
+ FOREST_NAME,
+ DNS_DOMAIN_NAME,
+ DNS_HOST_NAME,
+ NET_DOMAIN_NAME,
+ NET_COMP_NAME,
+ USER_NAME,
+ DC_SITE_NAME,
+ CLIENT_SITE_NAME,
+ SOCKADDR_SIZE,
+ SOCKADDR,
+ NEXT_CLOSEST_SITE_NAME,
+ NTVER,
+ LM_NT_TOKEN,
+ LM_20_TOKEN
+} field_5ex_t;
+
+struct _berelement {
+ char *ber_buf;
+ char *ber_ptr;
+ char *ber_end;
+};
+
+extern int ldap_put_filter(BerElement *ber, char *);
+static void send_to_cds(ad_disc_cds_t *, char *, size_t, int);
+static ad_disc_cds_t *find_cds_by_addr(ad_disc_cds_t *, struct sockaddr_in6 *);
+static boolean_t addrmatch(struct addrinfo *, struct sockaddr_in6 *);
+static void save_ai(ad_disc_cds_t *, struct addrinfo *);
+
+static void
+cldap_escape_le64(char *buf, uint64_t val, int bytes)
+{
+ char *p = buf;
+
+ while (bytes != 0) {
+ p += sprintf(p, "\\%.2x", (uint8_t)(val & 0xff));
+ val >>= 8;
+ bytes--;
+ }
+ *p = '\0';
+}
+
+/*
+ * Construct CLDAPMessage PDU for NetLogon search request.
+ *
+ * CLDAPMessage ::= SEQUENCE {
+ * messageID MessageID,
+ * protocolOp searchRequest SearchRequest;
+ * }
+ *
+ * SearchRequest ::=
+ * [APPLICATION 3] SEQUENCE {
+ * baseObject LDAPDN,
+ * scope ENUMERATED {
+ * baseObject (0),
+ * singleLevel (1),
+ * wholeSubtree (2)
+ * },
+ * derefAliases ENUMERATED {
+ * neverDerefAliases (0),
+ * derefInSearching (1),
+ * derefFindingBaseObj (2),
+ * derefAlways (3)
+ * },
+ * sizeLimit INTEGER (0 .. MaxInt),
+ * timeLimit INTEGER (0 .. MaxInt),
+ * attrsOnly BOOLEAN,
+ * filter Filter,
+ * attributes SEQUENCE OF AttributeType
+ * }
+ */
+BerElement *
+cldap_build_request(const char *dname,
+ const char *host, uint32_t ntver, uint16_t msgid)
+{
+ BerElement *ber;
+ int len = 0;
+ char *basedn = "";
+ int scope = LDAP_SCOPE_BASE, deref = LDAP_DEREF_NEVER,
+ sizelimit = 0, timelimit = 0, attrsonly = 0;
+ char filter[512];
+ char ntver_esc[13];
+ char *p, *pend;
+
+ /*
+ * Construct search filter in LDAP format.
+ */
+ p = filter;
+ pend = p + sizeof (filter);
+
+ len = snprintf(p, pend - p, "(&(DnsDomain=%s)", dname);
+ if (len >= (pend - p))
+ goto fail;
+ p += len;
+
+ if (host != NULL) {
+ len = snprintf(p, (pend - p), "(Host=%s)", host);
+ if (len >= (pend - p))
+ goto fail;
+ p += len;
+ }
+
+ if (ntver != 0) {
+ /*
+ * Format NtVer as little-endian with LDAPv3 escapes.
+ */
+ cldap_escape_le64(ntver_esc, ntver, sizeof (ntver));
+ len = snprintf(p, (pend - p), "(NtVer=%s)", ntver_esc);
+ if (len >= (pend - p))
+ goto fail;
+ p += len;
+ }
+
+ len = snprintf(p, pend - p, ")");
+ if (len >= (pend - p))
+ goto fail;
+ p += len;
+
+ /*
+ * Encode CLDAPMessage and beginning of SearchRequest sequence.
+ */
+
+ if ((ber = ber_alloc()) == NULL)
+ goto fail;
+
+ if (ber_printf(ber, "{it{seeiib", msgid,
+ LDAP_REQ_SEARCH, basedn, scope, deref,
+ sizelimit, timelimit, attrsonly) < 0)
+ goto fail;
+
+ /*
+ * Encode Filter sequence.
+ */
+ if (ldap_put_filter(ber, filter) < 0)
+ goto fail;
+ /*
+ * Encode attribute and close Filter and SearchRequest sequences.
+ */
+ if (ber_printf(ber, "{s}}}", NETLOGON_ATTR_NAME) < 0)
+ goto fail;
+
+ /*
+ * Success
+ */
+ return (ber);
+
+fail:
+ if (ber != NULL)
+ ber_free(ber, 1);
+ return (NULL);
+}
+
+/*
+ * Parse incoming search responses and attribute to correct hosts.
+ *
+ * CLDAPMessage ::= SEQUENCE {
+ * messageID MessageID,
+ * searchResponse SEQUENCE OF
+ * SearchResponse;
+ * }
+ *
+ * SearchResponse ::=
+ * CHOICE {
+ * entry [APPLICATION 4] SEQUENCE {
+ * objectName LDAPDN,
+ * attributes SEQUENCE OF SEQUENCE {
+ * AttributeType,
+ * SET OF
+ * AttributeValue
+ * }
+ * },
+ * resultCode [APPLICATION 5] LDAPResult
+ * }
+ */
+
+static int
+decode_name(uchar_t *base, uchar_t *cp, char *str)
+{
+ uchar_t *tmp = NULL, *st = cp;
+ uint8_t len;
+
+ /*
+ * there should probably be some boundary checks on str && cp
+ * maybe pass in strlen && msglen ?
+ */
+ while (*cp != 0) {
+ if (*cp == 0xc0) {
+ if (tmp == NULL)
+ tmp = cp + 2;
+ cp = base + *(cp + 1);
+ }
+ for (len = *cp++; len > 0; len--)
+ *str++ = *cp++;
+ *str++ = '.';
+ }
+ if (cp != st)
+ *(str-1) = '\0';
+ else
+ *str = '\0';
+
+ return ((tmp == NULL ? cp + 1 : tmp) - st);
+}
+
+static int
+cldap_parse(ad_disc_t ctx, ad_disc_cds_t *cds, BerElement *ber)
+{
+ ad_disc_ds_t *dc = &cds->cds_ds;
+ uchar_t *base = NULL, *cp = NULL;
+ char val[512]; /* how big should val be? */
+ int l, msgid, rc = 0;
+ uint16_t opcode;
+ field_5ex_t f = OPCODE;
+
+ /*
+ * Later, compare msgid's/some validation?
+ */
+
+ if (ber_scanf(ber, "{i{x{{x[la", &msgid, &l, &cp) == LBER_ERROR) {
+ rc = 1;
+ goto out;
+ }
+
+ for (base = cp; ((cp - base) < l) && (f <= LM_20_TOKEN); f++) {
+ val[0] = '\0';
+ switch (f) {
+ case OPCODE:
+ /* opcode = *(uint16_t *)cp; */
+ /* cp +=2; */
+ opcode = *cp++;
+ opcode |= (*cp++ << 8);
+ break;
+ case SBZ:
+ cp += 2;
+ break;
+ case FLAGS:
+ /* dci->Flags = *(uint32_t *)cp; */
+ /* cp +=4; */
+ dc->flags = *cp++;
+ dc->flags |= (*cp++ << 8);
+ dc->flags |= (*cp++ << 16);
+ dc->flags |= (*cp++ << 26);
+ break;
+ case DOMAIN_GUID:
+ if (ctx != NULL)
+ auto_set_DomainGUID(ctx, cp);
+ cp += 16;
+ break;
+ case FOREST_NAME:
+ cp += decode_name(base, cp, val);
+ if (ctx != NULL)
+ auto_set_ForestName(ctx, val);
+ break;
+ case DNS_DOMAIN_NAME:
+ /*
+ * We always have this already.
+ * (Could validate it here.)
+ */
+ cp += decode_name(base, cp, val);
+ break;
+ case DNS_HOST_NAME:
+ cp += decode_name(base, cp, val);
+ if (0 != strcasecmp(val, dc->host)) {
+ logger(LOG_ERR, "DC name %s != %s?",
+ val, dc->host);
+ }
+ break;
+ case NET_DOMAIN_NAME:
+ /*
+ * This is the "Flat" domain name.
+ * (i.e. the NetBIOS name)
+ * ignore for now.
+ */
+ cp += decode_name(base, cp, val);
+ break;
+ case NET_COMP_NAME:
+ /* not needed */
+ cp += decode_name(base, cp, val);
+ break;
+ case USER_NAME:
+ /* not needed */
+ cp += decode_name(base, cp, val);
+ break;
+ case DC_SITE_NAME:
+ cp += decode_name(base, cp, val);
+ (void) strlcpy(dc->site, val, sizeof (dc->site));
+ break;
+ case CLIENT_SITE_NAME:
+ cp += decode_name(base, cp, val);
+ if (ctx != NULL)
+ auto_set_SiteName(ctx, val);
+ break;
+ /*
+ * These are all possible, but we don't really care about them.
+ * Sockaddr_size && sockaddr might be useful at some point
+ */
+ case SOCKADDR_SIZE:
+ case SOCKADDR:
+ case NEXT_CLOSEST_SITE_NAME:
+ case NTVER:
+ case LM_NT_TOKEN:
+ case LM_20_TOKEN:
+ break;
+ default:
+ rc = 3;
+ goto out;
+ }
+ }
+
+out:
+ if (base)
+ free(base);
+ else if (cp)
+ free(cp);
+ return (rc);
+}
+
+
+/*
+ * Filter out unresponsive servers, and save the domain info
+ * returned by the "LDAP ping" in the returned object.
+ * If ctx != NULL, this is a query for a DC, in which case we
+ * also save the Domain GUID, Site name, and Forest name as
+ * "auto" (discovered) values in the ctx.
+ *
+ * Only return the "winner". (We only want one DC/GC)
+ */
+ad_disc_ds_t *
+ldap_ping(ad_disc_t ctx, ad_disc_cds_t *dclist, char *dname, int reqflags)
+{
+ struct sockaddr_in6 addr6;
+ socklen_t addrlen;
+ struct pollfd pingchk;
+ ad_disc_cds_t *send_ds;
+ ad_disc_cds_t *recv_ds = NULL;
+ ad_disc_ds_t *ret_ds = NULL;
+ BerElement *req = NULL;
+ BerElement *res = NULL;
+ struct _berelement *be, *rbe;
+ size_t be_len, rbe_len;
+ int fd = -1;
+ int tries = 3;
+ int waitsec;
+ int r;
+ uint16_t msgid;
+
+ /* One plus a null entry. */
+ ret_ds = calloc(2, sizeof (ad_disc_ds_t));
+ if (ret_ds == NULL)
+ goto fail;
+
+ if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) < 0)
+ goto fail;
+
+ (void) memset(&addr6, 0, sizeof (addr6));
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_addr = in6addr_any;
+ if (bind(fd, (struct sockaddr *)&addr6, sizeof (addr6)) < 0)
+ goto fail;
+
+ /*
+ * semi-unique msgid...
+ */
+ msgid = gethrtime() & 0xffff;
+
+ /*
+ * Is ntver right? It certainly works on w2k8... If others are needed,
+ * that might require changes to cldap_parse
+ */
+ req = cldap_build_request(dname, NULL,
+ NETLOGON_NT_VERSION_5EX, msgid);
+ if (req == NULL)
+ goto fail;
+ be = (struct _berelement *)req;
+ be_len = be->ber_end - be->ber_buf;
+
+ if ((res = ber_alloc()) == NULL)
+ goto fail;
+ rbe = (struct _berelement *)res;
+ rbe_len = rbe->ber_end - rbe->ber_buf;
+
+ pingchk.fd = fd;
+ pingchk.events = POLLIN;
+ pingchk.revents = 0;
+
+try_again:
+ send_ds = dclist;
+ waitsec = 5;
+ while (recv_ds == NULL && waitsec > 0) {
+
+ /*
+ * If there is another candidate, send to it.
+ */
+ if (send_ds->cds_ds.host[0] != '\0') {
+ send_to_cds(send_ds, be->ber_buf, be_len, fd);
+ send_ds++;
+
+ /*
+ * Wait 1/10 sec. before the next send.
+ */
+ r = poll(&pingchk, 1, 100);
+#if 0 /* DEBUG */
+ /* Drop all responses 1st pass. */
+ if (waitsec == 5)
+ r = 0;
+#endif
+ } else {
+ /*
+ * No more candidates to "ping", so
+ * just wait a sec for responses.
+ */
+ r = poll(&pingchk, 1, 1000);
+ if (r == 0)
+ --waitsec;
+ }
+
+ if (r > 0) {
+ /*
+ * Got a response.
+ */
+ (void) memset(&addr6, 0, addrlen = sizeof (addr6));
+ r = recvfrom(fd, rbe->ber_buf, rbe_len, 0,
+ (struct sockaddr *)&addr6, &addrlen);
+
+ recv_ds = find_cds_by_addr(dclist, &addr6);
+ if (recv_ds == NULL)
+ continue;
+
+ (void) cldap_parse(ctx, recv_ds, res);
+ if ((recv_ds->cds_ds.flags & reqflags) != reqflags) {
+ logger(LOG_ERR, "Skip %s"
+ "due to flags 0x%X",
+ recv_ds->cds_ds.host,
+ recv_ds->cds_ds.flags);
+ recv_ds = NULL;
+ }
+ }
+ }
+
+ if (recv_ds == NULL) {
+ if (--tries <= 0)
+ goto fail;
+ goto try_again;
+ }
+
+ (void) memcpy(ret_ds, recv_ds, sizeof (*ret_ds));
+
+ ber_free(res, 1);
+ ber_free(req, 1);
+ (void) close(fd);
+ return (ret_ds);
+
+fail:
+ ber_free(res, 1);
+ ber_free(req, 1);
+ (void) close(fd);
+ free(ret_ds);
+ return (NULL);
+}
+
+/*
+ * Attempt a send of the LDAP request to all known addresses
+ * for this candidate server.
+ */
+static void
+send_to_cds(ad_disc_cds_t *send_cds, char *ber_buf, size_t be_len, int fd)
+{
+ struct sockaddr_in6 addr6;
+ struct addrinfo *ai;
+ int err;
+
+ if (DBG(DISC, 2)) {
+ logger(LOG_DEBUG, "send to: %s", send_cds->cds_ds.host);
+ }
+
+ for (ai = send_cds->cds_ai; ai != NULL; ai = ai->ai_next) {
+
+ /*
+ * Build the "to" address.
+ */
+ (void) memset(&addr6, 0, sizeof (addr6));
+ if (ai->ai_family == AF_INET6) {
+ (void) memcpy(&addr6, ai->ai_addr, sizeof (addr6));
+ } else if (ai->ai_family == AF_INET) {
+ struct sockaddr_in *sin =
+ (void *)ai->ai_addr;
+ addr6.sin6_family = AF_INET6;
+ IN6_INADDR_TO_V4MAPPED(&sin->sin_addr,
+ &addr6.sin6_addr);
+ } else {
+ continue;
+ }
+ addr6.sin6_port = htons(LDAP_PORT);
+
+ /*
+ * Send the "ping" to this address.
+ */
+ err = sendto(fd, ber_buf, be_len, 0,
+ (struct sockaddr *)&addr6, sizeof (addr6));
+ err = (err < 0) ? errno : 0;
+
+ if (DBG(DISC, 2)) {
+ char abuf[INET6_ADDRSTRLEN];
+ const char *pa;
+
+ pa = inet_ntop(AF_INET6,
+ &addr6.sin6_addr,
+ abuf, sizeof (abuf));
+ logger(LOG_ERR, " > %s rc=%d",
+ pa ? pa : "?", err);
+ }
+ }
+}
+
+/*
+ * We have a response from some address. Find the candidate with
+ * this address. In case a candidate had multiple addresses, we
+ * keep track of which the response came from.
+ */
+static ad_disc_cds_t *
+find_cds_by_addr(ad_disc_cds_t *dclist, struct sockaddr_in6 *sin6from)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ ad_disc_cds_t *ds;
+ struct addrinfo *ai;
+ int eai;
+
+ if (DBG(DISC, 1)) {
+ eai = getnameinfo((void *)sin6from, sizeof (*sin6from),
+ abuf, sizeof (abuf), NULL, 0, NI_NUMERICHOST);
+ if (eai != 0)
+ (void) strlcpy(abuf, "?", sizeof (abuf));
+ logger(LOG_DEBUG, "LDAP ping resp: addr=%s", abuf);
+ }
+
+ /*
+ * Find the DS this response came from.
+ * (don't accept unexpected responses)
+ */
+ for (ds = dclist; ds->cds_ds.host[0] != '\0'; ds++) {
+ ai = ds->cds_ai;
+ while (ai != NULL) {
+ if (addrmatch(ai, sin6from))
+ goto found;
+ ai = ai->ai_next;
+ }
+ }
+ if (DBG(DISC, 1)) {
+ logger(LOG_DEBUG, " (unexpected)");
+ }
+ return (NULL);
+
+found:
+ if (DBG(DISC, 2)) {
+ logger(LOG_DEBUG, " from %s", ds->cds_ds.host);
+ }
+ save_ai(ds, ai);
+ return (ds);
+}
+
+static boolean_t
+addrmatch(struct addrinfo *ai, struct sockaddr_in6 *sin6from)
+{
+
+ /*
+ * Note: on a GC query, the ds->addr port numbers are
+ * the GC port, and our from addr has the LDAP port.
+ * Just compare the IP addresses.
+ */
+
+ if (ai->ai_family == AF_INET6) {
+ struct sockaddr_in6 *sin6p = (void *)ai->ai_addr;
+
+ if (!memcmp(&sin6from->sin6_addr, &sin6p->sin6_addr,
+ sizeof (struct in6_addr)))
+ return (B_TRUE);
+ }
+
+ if (ai->ai_family == AF_INET) {
+ struct in6_addr in6;
+ struct sockaddr_in *sin4p = (void *)ai->ai_addr;
+
+ IN6_INADDR_TO_V4MAPPED(&sin4p->sin_addr, &in6);
+ if (!memcmp(&sin6from->sin6_addr, &in6,
+ sizeof (struct in6_addr)))
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+static void
+save_ai(ad_disc_cds_t *cds, struct addrinfo *ai)
+{
+ ad_disc_ds_t *ds = &cds->cds_ds;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ /*
+ * If this DS already saw a response, keep the first
+ * address from which we received a response.
+ */
+ if (ds->addr.ss_family != 0) {
+ if (DBG(DISC, 2))
+ logger(LOG_DEBUG, "already have an address");
+ return;
+ }
+
+ switch (ai->ai_family) {
+ case AF_INET:
+ sin = (void *)&ds->addr;
+ (void) memcpy(sin, ai->ai_addr, sizeof (*sin));
+ sin->sin_port = htons(ds->port);
+ break;
+
+ case AF_INET6:
+ sin6 = (void *)&ds->addr;
+ (void) memcpy(sin6, ai->ai_addr, sizeof (*sin6));
+ sin6->sin6_port = htons(ds->port);
+ break;
+
+ default:
+ logger(LOG_ERR, "bad AF %d", ai->ai_family);
+ }
+}
diff --git a/usr/src/lib/libadutils/common/mapfile-vers b/usr/src/lib/libadutils/common/mapfile-vers
index a05bcb4aff..2d029e3ce5 100644
--- a/usr/src/lib/libadutils/common/mapfile-vers
+++ b/usr/src/lib/libadutils/common/mapfile-vers
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
#
@@ -66,6 +67,8 @@ SYMBOL_VERSION SUNWprivate {
ad_disc_init;
ad_disc_get_DomainName;
ad_disc_set_DomainName;
+ ad_disc_get_DomainGUID;
+ ad_disc_set_DomainGUID;
ad_disc_compare_ds;
ad_disc_compare_trusteddomains;
ad_disc_compare_domainsinforest;
@@ -78,9 +81,14 @@ SYMBOL_VERSION SUNWprivate {
ad_disc_get_ForestName;
ad_disc_get_DomainController;
ad_disc_set_DomainController;
+ ad_disc_get_PreferredDC;
+ ad_disc_set_PreferredDC;
+ ad_disc_get_SiteName;
ad_disc_set_SiteName;
ad_disc_refresh;
- ad_disc_get_SiteName;
+ ad_disc_unset;
+ ad_disc_getnameinfo;
+ ad_disc_set_StatusFP;
ad_disc_get_TrustedDomains;
ad_disc_get_DomainsInForest;
domain_eq;
diff --git a/usr/src/lib/libadutils/common/srv_query.c b/usr/src/lib/libadutils/common/srv_query.c
new file mode 100644
index 0000000000..aa7a833421
--- /dev/null
+++ b/usr/src/lib/libadutils/common/srv_query.c
@@ -0,0 +1,612 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * DNS query helper functions for addisc.c
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+#include <ldap.h>
+#include <sasl/sasl.h>
+#include <sys/u8_textprep.h>
+#include <syslog.h>
+#include <uuid/uuid.h>
+#include <ads/dsgetdc.h>
+#include "adutils_impl.h"
+#include "addisc_impl.h"
+
+static void save_addr(ad_disc_cds_t *, sa_family_t, uchar_t *, size_t);
+static struct addrinfo *make_addrinfo(sa_family_t, uchar_t *, size_t);
+
+static void do_getaddrinfo(ad_disc_cds_t *);
+static ad_disc_cds_t *srv_parse(uchar_t *, int, int *, int *);
+static void add_preferred(ad_disc_cds_t *, ad_disc_ds_t *, int *, int);
+static void get_addresses(ad_disc_cds_t *, int);
+
+/*
+ * Simplified version of srv_query() for domain auto-discovery.
+ */
+int
+srv_getdom(res_state state, const char *svc_name, char **rrname)
+{
+ union {
+ HEADER hdr;
+ uchar_t buf[NS_MAXMSG];
+ } msg;
+ int len, qdcount, ancount;
+ uchar_t *ptr, *eom;
+ char namebuf[NS_MAXDNAME];
+
+ /* query necessary resource records */
+
+ *rrname = NULL;
+ if (DBG(DNS, 1)) {
+ logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'", svc_name);
+ }
+ len = res_nsearch(state, svc_name, C_IN, T_SRV,
+ msg.buf, sizeof (msg.buf));
+ if (len < 0) {
+ if (DBG(DNS, 0)) {
+ logger(LOG_DEBUG,
+ "DNS search for '%s' failed (%s)",
+ svc_name, hstrerror(state->res_h_errno));
+ }
+ return (-1);
+ }
+
+ if (len > sizeof (msg.buf)) {
+ logger(LOG_WARNING,
+ "DNS query %ib message doesn't fit into %ib buffer",
+ len, sizeof (msg.buf));
+ len = sizeof (msg.buf);
+ }
+
+ /* parse the reply header */
+
+ ptr = msg.buf + sizeof (msg.hdr);
+ eom = msg.buf + len;
+ qdcount = ntohs(msg.hdr.qdcount);
+ ancount = ntohs(msg.hdr.ancount);
+
+ /* skip the question section */
+
+ len = ns_skiprr(ptr, eom, ns_s_qd, qdcount);
+ if (len < 0) {
+ logger(LOG_ERR, "DNS query invalid message format");
+ return (-1);
+ }
+ ptr += len;
+
+ /* parse the answer section */
+ if (ancount < 1) {
+ logger(LOG_ERR, "DNS query - no answers");
+ return (-1);
+ }
+
+ len = dn_expand(msg.buf, eom, ptr, namebuf, sizeof (namebuf));
+ if (len < 0) {
+ logger(LOG_ERR, "DNS query invalid message format");
+ return (-1);
+ }
+ *rrname = strdup(namebuf);
+ if (*rrname == NULL) {
+ logger(LOG_ERR, "Out of memory");
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Compare SRC RRs; used with qsort(). Sort order:
+ * "Earliest" (lowest number) priority first,
+ * then weight highest to lowest.
+ */
+static int
+srvcmp(ad_disc_ds_t *s1, ad_disc_ds_t *s2)
+{
+ if (s1->priority < s2->priority)
+ return (-1);
+ else if (s1->priority > s2->priority)
+ return (1);
+
+ if (s1->weight < s2->weight)
+ return (1);
+ else if (s1->weight > s2->weight)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Query or search the SRV RRs for a given name.
+ *
+ * If dname == NULL then search (as in res_nsearch(3RESOLV), honoring any
+ * search list/option), else query (as in res_nquery(3RESOLV)).
+ *
+ * The output TTL will be the one of the SRV RR with the lowest TTL.
+ */
+ad_disc_cds_t *
+srv_query(res_state state, const char *svc_name, const char *dname,
+ ad_disc_ds_t *prefer)
+{
+ ad_disc_cds_t *cds_res = NULL;
+ uchar_t *msg = NULL;
+ int len, scnt, maxcnt;
+
+ msg = malloc(NS_MAXMSG);
+ if (msg == NULL) {
+ logger(LOG_ERR, "Out of memory");
+ return (NULL);
+ }
+
+ /* query necessary resource records */
+
+ /* Search, querydomain or query */
+ if (dname == NULL) {
+ dname = "*";
+ if (DBG(DNS, 1)) {
+ logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'",
+ svc_name);
+ }
+ len = res_nsearch(state, svc_name, C_IN, T_SRV,
+ msg, NS_MAXMSG);
+ if (len < 0) {
+ if (DBG(DNS, 0)) {
+ logger(LOG_DEBUG,
+ "DNS search for '%s' failed (%s)",
+ svc_name, hstrerror(state->res_h_errno));
+ }
+ goto errout;
+ }
+ } else { /* dname != NULL */
+ if (DBG(DNS, 1)) {
+ logger(LOG_DEBUG, "Looking for SRV RRs '%s.%s' ",
+ svc_name, dname);
+ }
+
+ len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV,
+ msg, NS_MAXMSG);
+
+ if (len < 0) {
+ if (DBG(DNS, 0)) {
+ logger(LOG_DEBUG, "DNS: %s.%s: %s",
+ svc_name, dname,
+ hstrerror(state->res_h_errno));
+ }
+ goto errout;
+ }
+ }
+
+ if (len > NS_MAXMSG) {
+ logger(LOG_WARNING,
+ "DNS query %ib message doesn't fit into %ib buffer",
+ len, NS_MAXMSG);
+ len = NS_MAXMSG;
+ }
+
+
+ /* parse the reply header */
+
+ cds_res = srv_parse(msg, len, &scnt, &maxcnt);
+ if (cds_res == NULL)
+ goto errout;
+
+ if (prefer != NULL)
+ add_preferred(cds_res, prefer, &scnt, maxcnt);
+
+ get_addresses(cds_res, scnt);
+
+ /* sort list of candidates */
+ if (scnt > 1)
+ qsort(cds_res, scnt, sizeof (*cds_res),
+ (int (*)(const void *, const void *))srvcmp);
+
+ free(msg);
+ return (cds_res);
+
+errout:
+ free(msg);
+ return (NULL);
+}
+
+static ad_disc_cds_t *
+srv_parse(uchar_t *msg, int len, int *scnt, int *maxcnt)
+{
+ ad_disc_cds_t *cds;
+ ad_disc_cds_t *cds_res = NULL;
+ HEADER *hdr;
+ int i, qdcount, ancount, nscount, arcount;
+ uchar_t *ptr, *eom;
+ uchar_t *end;
+ uint16_t type;
+ /* LINTED E_FUNC_SET_NOT_USED */
+ uint16_t class;
+ uint32_t rttl;
+ uint16_t size;
+ char namebuf[NS_MAXDNAME];
+
+ eom = msg + len;
+ hdr = (void *)msg;
+ ptr = msg + sizeof (HEADER);
+
+ qdcount = ntohs(hdr->qdcount);
+ ancount = ntohs(hdr->ancount);
+ nscount = ntohs(hdr->nscount);
+ arcount = ntohs(hdr->arcount);
+
+ /* skip the question section */
+
+ len = ns_skiprr(ptr, eom, ns_s_qd, qdcount);
+ if (len < 0) {
+ logger(LOG_ERR, "DNS query invalid message format");
+ return (NULL);
+ }
+ ptr += len;
+
+ /*
+ * Walk through the answer section, building the result array.
+ * The array size is +2 because we (possibly) add the preferred
+ * DC if it was not there, and an empty one (null termination).
+ */
+
+ *maxcnt = ancount + 2;
+ cds_res = calloc(*maxcnt, sizeof (*cds_res));
+ if (cds_res == NULL) {
+ logger(LOG_ERR, "Out of memory");
+ return (NULL);
+ }
+
+ cds = cds_res;
+ for (i = 0; i < ancount; i++) {
+
+ len = dn_expand(msg, eom, ptr, namebuf,
+ sizeof (namebuf));
+ if (len < 0) {
+ logger(LOG_ERR, "DNS query invalid message format");
+ goto err;
+ }
+ ptr += len;
+ NS_GET16(type, ptr);
+ NS_GET16(class, ptr);
+ NS_GET32(rttl, ptr);
+ NS_GET16(size, ptr);
+ if ((end = ptr + size) > eom) {
+ logger(LOG_ERR, "DNS query invalid message format");
+ goto err;
+ }
+
+ if (type != T_SRV) {
+ ptr = end;
+ continue;
+ }
+
+ NS_GET16(cds->cds_ds.priority, ptr);
+ NS_GET16(cds->cds_ds.weight, ptr);
+ NS_GET16(cds->cds_ds.port, ptr);
+ len = dn_expand(msg, eom, ptr, cds->cds_ds.host,
+ sizeof (cds->cds_ds.host));
+ if (len < 0) {
+ logger(LOG_ERR, "DNS query invalid SRV record");
+ goto err;
+ }
+
+ cds->cds_ds.ttl = rttl;
+
+ if (DBG(DNS, 2)) {
+ logger(LOG_DEBUG, " %s", namebuf);
+ logger(LOG_DEBUG,
+ " ttl=%d pri=%d weight=%d %s:%d",
+ rttl, cds->cds_ds.priority, cds->cds_ds.weight,
+ cds->cds_ds.host, cds->cds_ds.port);
+ }
+ cds++;
+
+ /* move ptr to the end of current record */
+ ptr = end;
+ }
+ *scnt = (cds - cds_res);
+
+ /* skip the nameservers section (if any) */
+
+ len = ns_skiprr(ptr, eom, ns_s_ns, nscount);
+ if (len < 0) {
+ logger(LOG_ERR, "DNS query invalid message format");
+ goto err;
+ }
+ ptr += len;
+
+ /* walk through the additional records */
+ for (i = 0; i < arcount; i++) {
+ sa_family_t af;
+
+ len = dn_expand(msg, eom, ptr, namebuf,
+ sizeof (namebuf));
+ if (len < 0) {
+ logger(LOG_ERR, "DNS query invalid message format");
+ goto err;
+ }
+ ptr += len;
+ NS_GET16(type, ptr);
+ NS_GET16(class, ptr);
+ NS_GET32(rttl, ptr);
+ NS_GET16(size, ptr);
+ if ((end = ptr + size) > eom) {
+ logger(LOG_ERR, "DNS query invalid message format");
+ goto err;
+ }
+ switch (type) {
+ case ns_t_a:
+ af = AF_INET;
+ break;
+ case ns_t_aaaa:
+ af = AF_INET6;
+ break;
+ default:
+ continue;
+ }
+
+ if (DBG(DNS, 2)) {
+ char abuf[INET6_ADDRSTRLEN];
+ const char *ap;
+
+ ap = inet_ntop(af, ptr, abuf, sizeof (abuf));
+ logger(LOG_DEBUG, " %s %s %s",
+ (af == AF_INET) ? "A " : "AAAA",
+ (ap) ? ap : "?", namebuf);
+ }
+
+ /* Find the server, add to its address list. */
+ for (cds = cds_res; cds->cds_ds.host[0] != '\0'; cds++)
+ if (0 == strcmp(namebuf, cds->cds_ds.host))
+ save_addr(cds, af, ptr, size);
+
+ /* move ptr to the end of current record */
+ ptr = end;
+ }
+
+ return (cds_res);
+
+err:
+ free(cds_res);
+ return (NULL);
+}
+
+/*
+ * Save this address on the server, if not already there.
+ */
+static void
+save_addr(ad_disc_cds_t *cds, sa_family_t af, uchar_t *addr, size_t alen)
+{
+ struct addrinfo *ai, *new_ai, *last_ai;
+
+ new_ai = make_addrinfo(af, addr, alen);
+ if (new_ai == NULL)
+ return;
+
+ last_ai = NULL;
+ for (ai = cds->cds_ai; ai != NULL; ai = ai->ai_next) {
+ last_ai = ai;
+
+ if (new_ai->ai_family == ai->ai_family &&
+ new_ai->ai_addrlen == ai->ai_addrlen &&
+ 0 == memcmp(new_ai->ai_addr, ai->ai_addr,
+ ai->ai_addrlen)) {
+ /* it's already there */
+ freeaddrinfo(new_ai);
+ return;
+ }
+ }
+
+ /* Not found. Append. */
+ if (last_ai != NULL) {
+ last_ai->ai_next = new_ai;
+ } else {
+ cds->cds_ai = new_ai;
+ }
+}
+
+static struct addrinfo *
+make_addrinfo(sa_family_t af, uchar_t *addr, size_t alen)
+{
+ struct addrinfo *ai;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int slen;
+
+ ai = calloc(1, sizeof (*ai));
+ sa = calloc(1, sizeof (struct sockaddr_in6));
+
+ if (ai == NULL || sa == NULL) {
+ logger(LOG_ERR, "Out of memory");
+ goto errout;
+ }
+
+ switch (af) {
+ case AF_INET:
+ sin = (void *)sa;
+ if (alen < sizeof (in_addr_t)) {
+ logger(LOG_ERR, "bad IPv4 addr len");
+ goto errout;
+ }
+ alen = sizeof (in_addr_t);
+ sin->sin_family = af;
+ (void) memcpy(&sin->sin_addr, addr, alen);
+ slen = sizeof (*sin);
+ break;
+
+ case AF_INET6:
+ sin6 = (void *)sa;
+ if (alen < sizeof (in6_addr_t)) {
+ logger(LOG_ERR, "bad IPv6 addr len");
+ goto errout;
+ }
+ alen = sizeof (in6_addr_t);
+ sin6->sin6_family = af;
+ (void) memcpy(&sin6->sin6_addr, addr, alen);
+ slen = sizeof (*sin6);
+ break;
+
+ default:
+ goto errout;
+ }
+
+ ai->ai_family = af;
+ ai->ai_addrlen = slen;
+ ai->ai_addr = sa;
+ sa->sa_family = af;
+ return (ai);
+
+errout:
+ free(ai);
+ free(sa);
+ return (NULL);
+}
+
+/*
+ * Set a preferred candidate, which may already be in the list,
+ * in which case we just bump its priority, or else add it.
+ */
+static void
+add_preferred(ad_disc_cds_t *cds, ad_disc_ds_t *prefer, int *nds, int maxds)
+{
+ ad_disc_ds_t *ds;
+ int i;
+
+ assert(*nds < maxds);
+ for (i = 0; i < *nds; i++) {
+ ds = &cds[i].cds_ds;
+
+ if (strcasecmp(ds->host, prefer->host) == 0) {
+ /* Force this element to be sorted first. */
+ ds->priority = 0;
+ ds->weight = 200;
+ return;
+ }
+ }
+
+ /*
+ * The preferred DC was not found in this DNS response,
+ * so add it. Again arrange for it to be sorted first.
+ * Address info. is added later.
+ */
+ ds = &cds[i].cds_ds;
+ (void) memcpy(ds, prefer, sizeof (*ds));
+ ds->priority = 0;
+ ds->weight = 200;
+ *nds = i + 1;
+}
+
+/*
+ * Do another pass over the array to check for missing addresses and
+ * try resolving the names. Normally, the DNS response from AD will
+ * have supplied additional address records for all the SRV records.
+ */
+static void
+get_addresses(ad_disc_cds_t *cds, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ if (cds[i].cds_ai == NULL) {
+ do_getaddrinfo(&cds[i]);
+ }
+ }
+}
+
+static void
+do_getaddrinfo(ad_disc_cds_t *cds)
+{
+ struct addrinfo hints;
+ struct addrinfo *ai;
+ ad_disc_ds_t *ds;
+ time_t t0, t1;
+ int err;
+
+ (void) memset(&hints, 0, sizeof (hints));
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ ds = &cds->cds_ds;
+
+ /*
+ * This getaddrinfo call may take a LONG time, i.e. if our
+ * DNS servers are misconfigured or not responding.
+ * We need something like getaddrinfo_a(), with a timeout.
+ * For now, just log when this happens so we'll know
+ * if these calls are taking a long time.
+ */
+ if (DBG(DNS, 2))
+ logger(LOG_DEBUG, "getaddrinfo %s ...", ds->host);
+ t0 = time(NULL);
+ err = getaddrinfo(cds->cds_ds.host, NULL, &hints, &ai);
+ t1 = time(NULL);
+ if (DBG(DNS, 2))
+ logger(LOG_DEBUG, "getaddrinfo %s rc=%d", ds->host, err);
+ if (t1 > (t0 + 5)) {
+ logger(LOG_WARNING, "Lookup host (%s) took %u sec. "
+ "(Check DNS settings)", ds->host, (int)(t1 - t0));
+ }
+ if (err != 0) {
+ logger(LOG_ERR, "No address for host: %s (%s)",
+ ds->host, gai_strerror(err));
+ /* Make this sort at the end. */
+ ds->priority = 1 << 16;
+ return;
+ }
+
+ cds->cds_ai = ai;
+}
+
+void
+srv_free(ad_disc_cds_t *cds_vec)
+{
+ ad_disc_cds_t *cds;
+
+ for (cds = cds_vec; cds->cds_ds.host[0] != '\0'; cds++) {
+ if (cds->cds_ai != NULL) {
+ freeaddrinfo(cds->cds_ai);
+ }
+ }
+ free(cds_vec);
+}
diff --git a/usr/src/lib/libkrb5/Makefile b/usr/src/lib/libkrb5/Makefile
index 68feb56d99..07a5fcc0e5 100644
--- a/usr/src/lib/libkrb5/Makefile
+++ b/usr/src/lib/libkrb5/Makefile
@@ -22,19 +22,23 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
include ../Makefile.lib
SUBDIRS = $(MACH) $(BUILD64) $(MACH64)
INSTALLED_HDRS = \
- com_err.h krb5.h
+ com_err.h krb5.h locate_plugin.h
COMERRH = $(SRC)/lib/gss_mechs/mech_krb5/include/com_err.h
+LOCPLUGH = $(SRC)/lib/gss_mechs/mech_krb5/include/locate_plugin.h
KRB5H = $(SRC)/uts/common/gssapi/mechs/krb5/include/krb5.h
KRB5INCDIR= $(ROOT)/usr/include/kerberosv5
COMERRHINST = $(KRB5INCDIR)/com_err.h
+LOCPLUGHINST = $(KRB5INCDIR)/locate_plugin.h
KRB5HINST = $(KRB5INCDIR)/krb5.h
KRB5HDRS = $(INSTALLED_HDRS:%=$(KRB5INCDIR)/%)
@@ -59,5 +63,7 @@ $(COMERRHINST): $(COMERRH)
install -s -m 644 -f $(KRB5INCDIR) $(COMERRH)
$(KRB5HINST): $(KRB5H)
install -s -m 644 -f $(KRB5INCDIR) $(KRB5H)
+$(LOCPLUGHINST): $(LOCPLUGH)
+ install -s -m 644 -f $(KRB5INCDIR) $(LOCPLUGH)
FRC:
diff --git a/usr/src/lib/libldap5/mapfile-vers b/usr/src/lib/libldap5/mapfile-vers
index fc89fa2e4c..0da199128f 100644
--- a/usr/src/lib/libldap5/mapfile-vers
+++ b/usr/src/lib/libldap5/mapfile-vers
@@ -20,7 +20,7 @@
#
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
#
@@ -179,6 +179,7 @@ SYMBOL_VERSION SUNW_5.1 {
ldap_parse_sort_control;
ldap_parse_virtuallist_control;
ldap_perror;
+ ldap_put_filter;
ldap_rename;
ldap_rename_s;
ldap_result;
diff --git a/usr/src/lib/libresolv2/common/resolv/res_send.c b/usr/src/lib/libresolv2/common/resolv/res_send.c
index 8e629a3d96..a043cf2624 100644
--- a/usr/src/lib/libresolv2/common/resolv/res_send.c
+++ b/usr/src/lib/libresolv2/common/resolv/res_send.c
@@ -1,6 +1,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
@@ -94,6 +96,7 @@ static const char rcsid[] = "$Id: res_send.c,v 1.22 2009/01/22 23:49:23 tbox Exp
#include <sys/uio.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
@@ -672,6 +675,29 @@ send_vc(res_state statp,
(void)setsockopt(statp->_vcsock, SOL_SOCKET, SO_NOSIGPIPE, &on,
sizeof(on));
#endif
+#ifdef TCP_CONN_ABORT_THRESHOLD
+ /*
+ * The default connection timeout is over two minutes.
+ * We need something more reasonable here. The default
+ * retrans value is 5 sec., then 10, 20, 40, on retries.
+ * TCP connect does its own retries, so we want just one
+ * reasonable timeout value. Using 2X retrans, which
+ * gives us a 10 sec. connect timeout. If we're waiting
+ * that long to connect, we probably want to give up and
+ * try the next DNS server in our list.
+ *
+ * It might be reasonable to do this for all callers,
+ * but for now do it only when we see MS_INTEROP in the
+ * environment (set in smbd and idmapd)
+ */
+ if (getenv("MS_INTEROP") != NULL) {
+ int conn_tmo;
+ conn_tmo = statp->retrans * 2000; /* mSec */
+ (void)setsockopt(statp->_vcsock, IPPROTO_TCP,
+ TCP_CONN_ABORT_THRESHOLD, &conn_tmo,
+ sizeof(conn_tmo));
+ }
+#endif
errno = 0;
if (connect(statp->_vcsock, nsap, nsaplen) < 0) {
*terrno = errno;
diff --git a/usr/src/lib/nsswitch/ad/common/ad_common.c b/usr/src/lib/nsswitch/ad/common/ad_common.c
index 4aab4e7320..aa1bf0424d 100644
--- a/usr/src/lib/nsswitch/ad/common/ad_common.c
+++ b/usr/src/lib/nsswitch/ad/common/ad_common.c
@@ -21,6 +21,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <malloc.h>
@@ -96,7 +98,7 @@ nssad_cfg_reload_ad(nssad_prop_t *props, adutils_ad_t **ad)
static
int
-update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new)
+update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new)
{
if (*value == *new)
return (0);
diff --git a/usr/src/lib/nsswitch/ad/common/ad_common.h b/usr/src/lib/nsswitch/ad/common/ad_common.h
index 7592d3613b..be90f2e2a9 100644
--- a/usr/src/lib/nsswitch/ad/common/ad_common.h
+++ b/usr/src/lib/nsswitch/ad/common/ad_common.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _AD_COMMON_H
@@ -88,7 +89,7 @@ struct ad_backend {
typedef struct nssad_prop {
char *domain_name;
- idmap_ad_disc_ds_t *domain_controller;
+ ad_disc_ds_t *domain_controller;
} nssad_prop_t;
typedef struct nssad_cfg {
diff --git a/usr/src/lib/smbsrv/libmlsvc/Makefile.com b/usr/src/lib/smbsrv/libmlsvc/Makefile.com
index 151a1e8337..50e596caa4 100644
--- a/usr/src/lib/smbsrv/libmlsvc/Makefile.com
+++ b/usr/src/lib/smbsrv/libmlsvc/Makefile.com
@@ -83,7 +83,7 @@ include ../../Makefile.lib
INCS += -I$(SRC)/common/smbsrv
LDLIBS += $(MACH_LDLIBS)
-LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lresolv -lnsl -lpkcs11 \
+LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lnsl -lpkcs11 \
-lscf -lcmdutils -lsec -lavl -lnvpair -luutil -luuid -lgen -lzfs -lc
CPPFLAGS += $(INCS) -D_REENTRANT
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/dfs.c b/usr/src/lib/smbsrv/libmlsvc/common/dfs.c
index 9acda4430e..7668d433b6 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/dfs.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/dfs.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <strings.h>
@@ -151,15 +152,13 @@ static uint32_t dfs_modinfo(uint32_t, dfs_info_t *, dfs_info_t *, uint32_t);
void
dfs_init(void)
{
- smb_domain_t di;
-
smb_cache_create(&dfs_nscache, 0, dfs_cache_cmp, free, bcopy,
sizeof (dfs_nscnode_t));
- if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
+ if (smb_getnetbiosname(dfs_nbname, sizeof (dfs_nbname)) != 0) {
+ syslog(LOG_ERR, "dfs: can't get machine name");
return;
-
- (void) strlcpy(dfs_nbname, di.di_nbname, NETBIOS_NAME_SZ);
+ }
bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops));
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c
index f067583f28..02b57b9328 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c
@@ -19,8 +19,8 @@
* CDDL HEADER END
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -47,7 +47,7 @@ dssetup_get_domain_info(ds_primary_domain_info_t *ds_info)
if (!smb_domain_getinfo(&di))
return (-1);
- if (ndr_rpc_bind(&handle, di.d_dc, di.d_primary.di_nbname,
+ if (ndr_rpc_bind(&handle, di.d_dci.dc_name, di.d_primary.di_nbname,
MLSVC_ANON_USER, "DSSETUP") != 0)
return (-1);
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
index 685a760e16..381a2ea98c 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
+++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _LIBMLSVC_H
@@ -59,7 +59,10 @@ uint32_t lsa_lookup_sid(smb_sid_t *, smb_account_t *);
* information.
*/
-extern boolean_t smb_locate_dc(char *, char *, smb_domainex_t *);
+extern boolean_t smb_locate_dc(char *, smb_domainex_t *);
+uint32_t smb_ddiscover_dns(char *, smb_domainex_t *);
+extern void smb_ddiscover_bad_dc(char *);
+extern void smb_ddiscover_refresh(void);
extern int smb_ddiscover_wait(void);
extern int dssetup_check_service(void);
@@ -68,7 +71,7 @@ extern void mlsvc_disconnect(const char *);
extern int mlsvc_init(void);
extern void mlsvc_fini(void);
extern DWORD mlsvc_netlogon(char *, char *);
-extern DWORD mlsvc_join(smb_domainex_t *, char *, char *);
+extern void mlsvc_join(smb_joininfo_t *, smb_joinres_t *);
extern void smb_logon_domain(smb_logon_t *, smb_token_t *);
extern uint32_t smb_decode_krb5_pac(smb_token_t *, char *, uint_t);
@@ -158,7 +161,7 @@ typedef struct mlsvc_handle {
void ndr_rpc_init(void);
void ndr_rpc_fini(void);
-int ndr_rpc_bind(mlsvc_handle_t *, char *, char *, char *, const char *);
+uint32_t ndr_rpc_bind(mlsvc_handle_t *, char *, char *, char *, const char *);
void ndr_rpc_unbind(mlsvc_handle_t *);
int ndr_rpc_call(mlsvc_handle_t *, int, void *);
void ndr_rpc_set_nonull(mlsvc_handle_t *);
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c
index 62732106c6..e995cf1c68 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -43,7 +44,8 @@ static uint32_t lsa_lookup_name_domain(char *, smb_account_t *);
static uint32_t lsa_lookup_sid_builtin(smb_sid_t *, smb_account_t *);
static uint32_t lsa_lookup_sid_domain(smb_sid_t *, smb_account_t *);
-static int lsa_list_accounts(mlsvc_handle_t *);
+static uint32_t lsa_list_accounts(mlsvc_handle_t *);
+static uint32_t lsa_map_status(uint32_t);
/*
* Lookup the given account and returns the account information
@@ -133,20 +135,21 @@ lsa_lookup_sid(smb_sid_t *sid, smb_account_t *info)
*
* The requested information will be returned via 'info' argument.
*
- * Returns NT status codes.
+ * Returns NT status codes. (Raw, not LSA-ized)
*/
DWORD
lsa_query_primary_domain_info(char *server, char *domain,
smb_domain_t *info)
{
mlsvc_handle_t domain_handle;
- DWORD status;
char user[SMB_USERNAME_MAXLEN];
+ DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
- if ((lsar_open(server, domain, user, &domain_handle)) != 0)
- return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ status = lsar_open(server, domain, user, &domain_handle);
+ if (status != 0)
+ return (status);
status = lsar_query_info_policy(&domain_handle,
MSLSA_POLICY_PRIMARY_DOMAIN_INFO, info);
@@ -161,20 +164,21 @@ lsa_query_primary_domain_info(char *server, char *domain,
*
* The requested information will be returned via 'info' argument.
*
- * Returns NT status codes.
+ * Returns NT status codes. (Raw, not LSA-ized)
*/
DWORD
lsa_query_account_domain_info(char *server, char *domain,
smb_domain_t *info)
{
mlsvc_handle_t domain_handle;
- DWORD status;
char user[SMB_USERNAME_MAXLEN];
+ DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
- if ((lsar_open(server, domain, user, &domain_handle)) != 0)
- return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ status = lsar_open(server, domain, user, &domain_handle);
+ if (status != 0)
+ return (status);
status = lsar_query_info_policy(&domain_handle,
MSLSA_POLICY_ACCOUNT_DOMAIN_INFO, info);
@@ -191,19 +195,20 @@ lsa_query_account_domain_info(char *server, char *domain,
*
* The requested information will be returned via 'info' argument.
*
- * Returns NT status codes.
+ * Returns NT status codes. (Raw, not LSA-ized)
*/
DWORD
lsa_query_dns_domain_info(char *server, char *domain, smb_domain_t *info)
{
mlsvc_handle_t domain_handle;
- DWORD status;
char user[SMB_USERNAME_MAXLEN];
+ DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
- if ((lsar_open(server, domain, user, &domain_handle)) != 0)
- return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ status = lsar_open(server, domain, user, &domain_handle);
+ if (status != 0)
+ return (status);
status = lsar_query_info_policy(&domain_handle,
MSLSA_POLICY_DNS_DOMAIN_INFO, info);
@@ -219,21 +224,22 @@ lsa_query_dns_domain_info(char *server, char *domain, smb_domain_t *info)
*
* The requested information will be returned via 'info' argument.
*
- * Returns NT status codes.
+ * Returns NT status codes. (Raw, not LSA-ized)
*/
DWORD
lsa_enum_trusted_domains(char *server, char *domain,
smb_trusted_domains_t *info)
{
mlsvc_handle_t domain_handle;
+ char user[SMB_USERNAME_MAXLEN];
DWORD enum_context;
DWORD status;
- char user[SMB_USERNAME_MAXLEN];
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
- if ((lsar_open(server, domain, user, &domain_handle)) != 0)
- return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ status = lsar_open(server, domain, user, &domain_handle);
+ if (status != 0)
+ return (status);
enum_context = 0;
@@ -258,21 +264,22 @@ lsa_enum_trusted_domains(char *server, char *domain,
*
* The requested information will be returned via 'info' argument.
*
- * Returns NT status codes.
+ * Returns NT status codes. (Raw, not LSA-ized)
*/
DWORD
lsa_enum_trusted_domains_ex(char *server, char *domain,
smb_trusted_domains_t *info)
{
mlsvc_handle_t domain_handle;
+ char user[SMB_USERNAME_MAXLEN];
DWORD enum_context;
DWORD status;
- char user[SMB_USERNAME_MAXLEN];
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
- if ((lsar_open(server, domain, user, &domain_handle)) != 0)
- return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ status = lsar_open(server, domain, user, &domain_handle);
+ if (status != 0)
+ return (status);
enum_context = 0;
@@ -333,28 +340,31 @@ lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info)
}
/*
- * Lookup the given account in domain.
+ * Lookup a domain account by its name.
*
* The information is returned in the user_info structure.
* The caller is responsible for allocating and releasing
* this structure.
+ *
+ * Returns NT status codes. (LSA-ized)
*/
static uint32_t
lsa_lookup_name_domain(char *account_name, smb_account_t *info)
{
mlsvc_handle_t domain_handle;
smb_domainex_t dinfo;
- uint32_t status;
char user[SMB_USERNAME_MAXLEN];
+ uint32_t status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
if (!smb_domain_getinfo(&dinfo))
return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
- if (lsar_open(dinfo.d_dc, dinfo.d_primary.di_nbname, user,
- &domain_handle) != 0)
- return (NT_STATUS_INVALID_PARAMETER);
+ status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
+ user, &domain_handle);
+ if (status != 0)
+ return (lsa_map_status(status));
status = lsar_lookup_names(&domain_handle, account_name, info);
@@ -373,29 +383,30 @@ lsa_lookup_name_domain(char *account_name, smb_account_t *info)
* 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.
+ * Returns NT status codes. (LSA-ized)
*/
/*ARGSUSED*/
-int
+DWORD
lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo)
{
mlsvc_handle_t domain_handle;
- int rc;
smb_domainex_t dinfo;
char user[SMB_USERNAME_MAXLEN];
+ DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
if (!smb_domain_getinfo(&dinfo))
- return (-1);
+ return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
- if ((lsar_open(dinfo.d_dc, dinfo.d_primary.di_nbname, user,
- &domain_handle)) != 0)
- return (-1);
+ status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
+ user, &domain_handle);
+ if (status != 0)
+ return (lsa_map_status(status));
- rc = lsa_list_accounts(&domain_handle);
+ status = lsa_list_accounts(&domain_handle);
(void) lsar_close(&domain_handle);
- return (rc);
+ return (status);
}
/*
@@ -404,7 +415,7 @@ lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo)
* List the privileges supported by the specified server.
* This function is only intended for diagnostics.
*
- * Returns NT status codes.
+ * Returns NT status codes. (LSA-ized)
*/
DWORD
lsa_list_privs(char *server, char *domain)
@@ -412,15 +423,16 @@ lsa_list_privs(char *server, char *domain)
static char name[128];
static struct ms_luid luid;
mlsvc_handle_t domain_handle;
+ char user[SMB_USERNAME_MAXLEN];
+ DWORD status;
int rc;
int i;
- char user[SMB_USERNAME_MAXLEN];
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
- rc = lsar_open(server, domain, user, &domain_handle);
- if (rc != 0)
- return (NT_STATUS_INVALID_PARAMETER);
+ status = lsar_open(server, domain, user, &domain_handle);
+ if (status != 0)
+ return (lsa_map_status(status));
for (i = 0; i < 30; ++i) {
luid.low_part = i;
@@ -443,9 +455,9 @@ lsa_list_privs(char *server, char *domain)
* 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.
+ * Returns NT status
*/
-static int
+static DWORD
lsa_list_accounts(mlsvc_handle_t *domain_handle)
{
mlsvc_handle_t account_handle;
@@ -453,16 +465,16 @@ lsa_list_accounts(mlsvc_handle_t *domain_handle)
struct mslsa_sid *sid;
smb_account_t ainfo;
DWORD enum_context = 0;
- int rc;
+ DWORD status;
int i;
bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf));
do {
- rc = lsar_enum_accounts(domain_handle, &enum_context,
+ status = lsar_enum_accounts(domain_handle, &enum_context,
&accounts);
- if (rc != 0)
- return (rc);
+ if (status != 0)
+ return (status);
for (i = 0; i < accounts.entries_read; ++i) {
sid = accounts.info[i].sid;
@@ -479,7 +491,7 @@ lsa_list_accounts(mlsvc_handle_t *domain_handle)
if (accounts.info)
free(accounts.info);
- } while (rc == 0 && accounts.entries_read != 0);
+ } while (status == 0 && accounts.entries_read != 0);
return (0);
}
@@ -522,25 +534,66 @@ lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo)
return (NT_STATUS_SUCCESS);
}
+/*
+ * Lookup a domain account by its SID.
+ *
+ * The information is returned in the user_info structure.
+ * The caller is responsible for allocating and releasing
+ * this structure.
+ *
+ * Returns NT status codes. (LSA-ized)
+ */
static uint32_t
lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo)
{
mlsvc_handle_t domain_handle;
- uint32_t status;
smb_domainex_t dinfo;
char user[SMB_USERNAME_MAXLEN];
+ uint32_t status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
if (!smb_domain_getinfo(&dinfo))
return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
- if (lsar_open(dinfo.d_dc, dinfo.d_primary.di_nbname, user,
- &domain_handle) != 0)
- return (NT_STATUS_INVALID_PARAMETER);
+ status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
+ user, &domain_handle);
+ if (status != 0)
+ return (lsa_map_status(status));
status = lsar_lookup_sids(&domain_handle, sid, ainfo);
(void) lsar_close(&domain_handle);
return (status);
}
+
+/*
+ * Most functions that call the local security authority expect
+ * only a limited set of status returns. This function maps the
+ * status we get from talking to our domain controller into one
+ * that LSA functions can return. Most common errors become:
+ * NT_STATUS_CANT_ACCESS_DOMAIN_INFO (when no DC etc.)
+ */
+static uint32_t
+lsa_map_status(uint32_t status)
+{
+ switch (status) {
+ case NT_STATUS_SUCCESS:
+ break;
+ case NT_STATUS_INVALID_PARAMETER: /* rpc bind */
+ break;
+ case NT_STATUS_NO_MEMORY:
+ break;
+ case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
+ case NT_STATUS_BAD_NETWORK_PATH: /* get server addr */
+ case NT_STATUS_NETWORK_ACCESS_DENIED: /* authentication */
+ case NT_STATUS_BAD_NETWORK_NAME: /* tree connect */
+ case NT_STATUS_ACCESS_DENIED: /* open pipe */
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ break;
+ default:
+ status = NT_STATUS_UNSUCCESSFUL;
+ break;
+ }
+ return (status);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.h b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.h
index f6f3f11fcd..c26eab4a13 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.h
+++ b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.h
@@ -21,6 +21,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _LSALIB_H
@@ -61,8 +63,8 @@ DWORD lsa_enum_trusted_domains_ex(char *, char *, smb_trusted_domains_t *);
/*
* lsar_open.c
*/
-int lsar_open(char *, char *, char *, mlsvc_handle_t *);
-int lsar_open_policy2(char *, char *, char *, mlsvc_handle_t *);
+DWORD lsar_open(char *, char *, char *, mlsvc_handle_t *);
+DWORD lsar_open_policy2(char *, char *, char *, mlsvc_handle_t *);
int lsar_open_account(mlsvc_handle_t *, struct mslsa_sid *, mlsvc_handle_t *);
int lsar_close(mlsvc_handle_t *);
@@ -74,7 +76,7 @@ DWORD lsar_query_info_policy(mlsvc_handle_t *, WORD, smb_domain_t *);
uint32_t lsar_lookup_names(mlsvc_handle_t *, char *, smb_account_t *);
uint32_t lsar_lookup_sids(mlsvc_handle_t *, smb_sid_t *, smb_account_t *);
-int lsar_enum_accounts(mlsvc_handle_t *, DWORD *,
+DWORD lsar_enum_accounts(mlsvc_handle_t *, DWORD *,
struct mslsa_EnumAccountBuf *);
DWORD lsar_enum_trusted_domains(mlsvc_handle_t *, DWORD *,
smb_trusted_domains_t *);
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c
index 043ee29549..eeab7745f2 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -87,19 +87,23 @@ static void lsar_set_trusted_domains(struct mslsa_EnumTrustedDomainBuf *,
* If username argument is NULL, an anonymous connection will be established.
* Otherwise, an authenticated connection will be established.
*
- * On success 0 is returned. Otherwise a -ve error code.
+ * Returns 0 or NT status (Raw, not LSA-ized)
*/
-int
+DWORD
lsar_open(char *server, char *domain, char *username,
mlsvc_handle_t *domain_handle)
{
+ DWORD status;
+
if (server == NULL || domain == NULL)
- return (-1);
+ return (NT_STATUS_INTERNAL_ERROR);
if (username == NULL)
username = MLSVC_ANON_USER;
- return (lsar_open_policy2(server, domain, username, domain_handle));
+ status = lsar_open_policy2(server, domain, username, domain_handle);
+
+ return (status);
}
/*
@@ -111,20 +115,20 @@ lsar_open(char *server, char *domain, char *username,
* function via lsar_open to ensure that the appropriate connection is
* in place.
*
- * Returns 0 on success. Otherwise non-zero to indicate a failure.
+ * Returns 0 or NT status (Raw, not LSA-ized)
*/
-int
-lsar_open_policy2(char *server, char *domain, char *username,
+DWORD
+lsar_open_policy2(char *server, char *domain, char *user,
mlsvc_handle_t *lsa_handle)
{
struct mslsa_OpenPolicy2 arg;
+ DWORD status;
int opnum;
int len;
- int rc;
- rc = ndr_rpc_bind(lsa_handle, server, domain, username, "LSARPC");
- if (rc != 0)
- return (-1);
+ status = ndr_rpc_bind(lsa_handle, server, domain, user, "LSARPC");
+ if (status != 0)
+ return (status);
opnum = LSARPC_OPNUM_OpenPolicy2;
bzero(&arg, sizeof (struct mslsa_OpenPolicy2));
@@ -132,34 +136,33 @@ lsar_open_policy2(char *server, char *domain, char *username,
len = strlen(server) + 4;
arg.servername = ndr_rpc_malloc(lsa_handle, len);
if (arg.servername == NULL) {
- ndr_rpc_unbind(lsa_handle);
- return (-1);
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
}
(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
arg.attributes.length = sizeof (struct mslsa_object_attributes);
arg.desiredAccess = MAXIMUM_ALLOWED;
- if ((rc = ndr_rpc_call(lsa_handle, opnum, &arg)) != 0) {
- ndr_rpc_unbind(lsa_handle);
- return (-1);
+ if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) {
+ status = RPC_NT_CALL_FAILED;
+ goto out;
}
-
- if (arg.status != 0) {
- rc = -1;
- } else {
+ status = arg.status;
+ if (status == NT_STATUS_SUCCESS) {
(void) memcpy(&lsa_handle->handle, &arg.domain_handle,
sizeof (ndr_hdid_t));
if (ndr_is_null_handle(lsa_handle))
- rc = -1;
+ status = NT_STATUS_INVALID_PARAMETER;
}
ndr_rpc_release(lsa_handle);
- if (rc != 0)
+out:
+ if (status != NT_STATUS_SUCCESS)
ndr_rpc_unbind(lsa_handle);
- return (rc);
+ return (status);
}
/*
@@ -946,7 +949,7 @@ lsar_lookup_sids3(mlsvc_handle_t *lsa_handle, lsa_sid_t *sid,
* This list is dynamically allocated using malloc, it should be freed
* by the caller when it is no longer required.
*/
-int
+DWORD
lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context,
struct mslsa_EnumAccountBuf *accounts)
{
@@ -954,12 +957,13 @@ lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context,
struct mslsa_AccountInfo *info;
int opnum;
int rc;
+ DWORD status;
DWORD n_entries;
DWORD i;
int nbytes;
if (lsa_handle == NULL || enum_context == NULL || accounts == NULL)
- return (-1);
+ return (NT_STATUS_INTERNAL_ERROR);
accounts->entries_read = 0;
accounts->info = 0;
@@ -973,12 +977,12 @@ lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context,
rc = ndr_rpc_call(lsa_handle, opnum, &arg);
if (rc == 0) {
+ status = arg.status;
if (arg.status != 0) {
if (arg.status == NT_STATUS_NO_MORE_ENTRIES) {
*enum_context = arg.enum_context;
} else {
ndr_rpc_status(lsa_handle, opnum, arg.status);
- rc = -1;
}
} else if (arg.enum_buf->entries_read != 0) {
n_entries = arg.enum_buf->entries_read;
@@ -986,7 +990,7 @@ lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context,
if ((info = malloc(nbytes)) == NULL) {
ndr_rpc_release(lsa_handle);
- return (-1);
+ return (NT_STATUS_NO_MEMORY);
}
for (i = 0; i < n_entries; ++i)
@@ -997,10 +1001,12 @@ lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context,
accounts->info = info;
*enum_context = arg.enum_context;
}
+ } else {
+ status = NT_STATUS_INVALID_PARAMETER;
}
ndr_rpc_release(lsa_handle);
- return (rc);
+ return (status);
}
/*
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
index eece54e174..4354bbc7d4 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
@@ -54,6 +54,8 @@ SYMBOL_VERSION SUNWprivate {
mlsvc_netlogon;
smb_autohome_add;
smb_autohome_remove;
+ smb_ddiscover_bad_dc;
+ smb_ddiscover_refresh;
smb_decode_krb5_pac;
smb_locate_dc;
smb_logon;
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h
index d693d8825c..dcf2c5f0e7 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SMBSRV_MLSVC_H
@@ -33,6 +33,8 @@
extern "C" {
#endif
+struct netr_info;
+
int smb_dclocator_init(void);
void smbrdr_initialize(void);
void dssetup_initialize(void);
@@ -54,12 +56,13 @@ void svcctl_finalize(void);
void spoolss_finalize(void);
void netdfs_finalize(void);
-int netr_open(char *, char *, mlsvc_handle_t *);
+/* netr_auth.c */
+DWORD netr_open(char *, char *, mlsvc_handle_t *);
int netr_close(mlsvc_handle_t *);
DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD);
-int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *,
+int netr_setup_authenticator(struct netr_info *, struct netr_authenticator *,
struct netr_authenticator *);
-DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *);
+DWORD netr_validate_chain(struct netr_info *, struct netr_authenticator *);
void ndr_srvsvc_timecheck(char *, char *);
@@ -81,9 +84,9 @@ void smb_quota_fini(void);
void smb_quota_add_fs(const char *);
void smb_quota_remove_fs(const char *);
+uint32_t smb_ddiscover_main(char *, smb_domainex_t *);
+
#ifdef __cplusplus
}
#endif
-
-
#endif /* _SMBSRV_MLSVC_H */
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
index 0a52a43e65..ef3ca34bcd 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -43,6 +43,7 @@
#include <netsmb/smbfs_api.h>
#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbns.h>
#include <smbsrv/libmlrpc.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/ndl/srvsvc.ndl>
@@ -69,8 +70,18 @@ static void ndr_xa_release(ndr_client_t *);
* The client points to this top-level handle so that we know when to
* unbind and teardown the connection. As each handle is initialized it
* will inherit a reference to the client context.
+ *
+ * Returns 0 or an NT_STATUS:
+ * NT_STATUS_BAD_NETWORK_PATH (get server addr)
+ * NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth)
+ * NT_STATUS_BAD_NETWORK_NAME (tcon, open)
+ * NT_STATUS_ACCESS_DENIED (open pipe)
+ * NT_STATUS_INVALID_PARAMETER (rpc bind)
+ *
+ * NT_STATUS_INTERNAL_ERROR (bad args etc)
+ * NT_STATUS_NO_MEMORY
*/
-int
+DWORD
ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
char *username, const char *service)
{
@@ -78,15 +89,17 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
ndr_client_t *clnt = NULL;
ndr_service_t *svc;
srvsvc_server_info_t svinfo;
+ DWORD status;
int fd = -1;
int rc;
- if (handle == NULL || server == NULL ||
+ if (handle == NULL || server == NULL || server[0] == '\0' ||
domain == NULL || username == NULL)
- return (-1);
+ return (NT_STATUS_INTERNAL_ERROR);
+ /* In case the service was not registered... */
if ((svc = ndr_svc_lookup_name(service)) == NULL)
- return (-1);
+ return (NT_STATUS_INTERNAL_ERROR);
/*
* Set the default based on the assumption that most
@@ -112,13 +125,19 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
* Setup smbfs library handle, authenticate, connect to
* the IPC$ share. This will reuse an existing connection
* if the driver already has one for this combination of
- * server, user, domain.
+ * server, user, domain. It may return any of:
+ * NT_STATUS_BAD_NETWORK_PATH (get server addr)
+ * NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth)
+ * NT_STATUS_BAD_NETWORK_NAME (tcon)
*/
- if ((rc = smbrdr_ctx_new(&ctx, server, domain, username)) != 0) {
+ status = smbrdr_ctx_new(&ctx, server, domain, username);
+ if (status != NT_STATUS_SUCCESS) {
syslog(LOG_ERR, "ndr_rpc_bind: smbrdr_ctx_new"
"(Srv=%s Dom=%s User=%s), %s (0x%x)",
server, domain, username,
- xlate_nt_status(rc), rc);
+ xlate_nt_status(status), status);
+ /* Tell the DC Locator this DC failed. */
+ smb_ddiscover_bad_dc(server);
goto errout;
}
@@ -127,16 +146,28 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
*/
fd = smb_fh_open(ctx, svc->endpoint, O_RDWR);
if (fd < 0) {
+ rc = errno;
syslog(LOG_DEBUG, "ndr_rpc_bind: "
- "smb_fh_open, err=%d", errno);
+ "smb_fh_open (%s) err=%d",
+ svc->endpoint, rc);
+ switch (rc) {
+ case EACCES:
+ status = NT_STATUS_ACCESS_DENIED;
+ break;
+ default:
+ status = NT_STATUS_BAD_NETWORK_NAME;
+ break;
+ }
goto errout;
}
/*
* Setup the RPC client handle.
*/
- if ((clnt = malloc(sizeof (ndr_client_t))) == NULL)
+ if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) {
+ status = NT_STATUS_NO_MEMORY;
goto errout;
+ }
bzero(clnt, sizeof (ndr_client_t));
clnt->handle = &handle->handle;
@@ -152,8 +183,10 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
ndr_svc_binding_pool_init(&clnt->binding_list,
clnt->binding_pool, NDR_N_BINDING_POOL);
- if ((clnt->heap = ndr_heap_create()) == NULL)
+ if ((clnt->heap = ndr_heap_create()) == NULL) {
+ status = NT_STATUS_NO_MEMORY;
goto errout;
+ }
/*
* Fill in the caller's handle.
@@ -166,14 +199,26 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
* Do the OtW RPC bind.
*/
rc = ndr_clnt_bind(clnt, service, &clnt->binding);
- if (NDR_DRC_IS_FAULT(rc)) {
- syslog(LOG_DEBUG, "ndr_rpc_bind: "
- "ndr_clnt_bind, rc=0x%x", rc);
- goto errout;
+ switch (rc) {
+ case NDR_DRC_FAULT_OUT_OF_MEMORY:
+ status = NT_STATUS_NO_MEMORY;
+ break;
+ case NDR_DRC_FAULT_API_SERVICE_INVALID: /* not registered */
+ status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ default:
+ if (NDR_DRC_IS_FAULT(rc)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+ /* FALLTHROUGH */
+ case NDR_DRC_OK:
+ return (NT_STATUS_SUCCESS);
}
- /* Success! */
- return (0);
+ syslog(LOG_DEBUG, "ndr_rpc_bind: "
+ "ndr_clnt_bind, %s (0x%x)",
+ xlate_nt_status(status), status);
errout:
handle->clnt = NULL;
@@ -187,7 +232,7 @@ errout:
smbrdr_ctx_free(ctx);
}
- return (-1);
+ return (status);
}
/*
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c
index 00040f5482..74da88b2d5 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <syslog.h>
@@ -44,6 +44,7 @@
#include <smbsrv/smbinfo.h>
#include <lsalib.h>
+#include <mlsvc.h>
/*
* DC Locator
@@ -52,9 +53,11 @@
#define SMB_IS_FQDN(domain) (strchr(domain, '.') != NULL)
typedef struct smb_dclocator {
+ smb_dcinfo_t sdl_dci; /* .dc_name .dc_addr */
char sdl_domain[SMB_PI_MAX_DOMAIN];
- char sdl_dc[MAXHOSTNAMELEN];
boolean_t sdl_locate;
+ boolean_t sdl_bad_dc;
+ boolean_t sdl_cfg_chg;
mutex_t sdl_mtx;
cond_t sdl_cv;
uint32_t sdl_status;
@@ -64,14 +67,11 @@ static smb_dclocator_t smb_dclocator;
static pthread_t smb_dclocator_thr;
static void *smb_ddiscover_service(void *);
-static void smb_ddiscover_main(char *, char *);
-static uint32_t smb_ddiscover_dns(char *, char *, smb_domainex_t *);
-static boolean_t smb_ddiscover_nbt(char *, char *, smb_domainex_t *);
-static boolean_t smb_ddiscover_domain_match(char *, char *, uint32_t);
static uint32_t smb_ddiscover_qinfo(char *, char *, smb_domainex_t *);
static void smb_ddiscover_enum_trusted(char *, char *, smb_domainex_t *);
static uint32_t smb_ddiscover_use_config(char *, smb_domainex_t *);
static void smb_domainex_free(smb_domainex_t *);
+static void smb_set_krb5_realm(char *);
/*
* ===================================================================
@@ -93,45 +93,54 @@ smb_dclocator_init(void)
(void) pthread_attr_init(&tattr);
(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
rc = pthread_create(&smb_dclocator_thr, &tattr,
- smb_ddiscover_service, 0);
+ smb_ddiscover_service, &smb_dclocator);
(void) pthread_attr_destroy(&tattr);
return (rc);
}
/*
* This is the entry point for discovering a domain controller for the
- * specified domain.
+ * specified domain. Called during join domain, and then periodically
+ * by smbd_dc_update (the "DC monitor" thread).
*
* The actual work of discovering a DC is handled by DC locator thread.
* All we do here is signal the request and wait for a DC or a timeout.
*
* Input parameters:
* domain - domain to be discovered (can either be NetBIOS or DNS domain)
- * dc - preferred DC. If the preferred DC is set to empty string, it
- * will attempt to discover any DC in the specified domain.
*
* Output parameter:
* dp - on success, dp will be filled with the discovered DC and domain
* information.
+ *
* Returns B_TRUE if the DC/domain info is available.
*/
boolean_t
-smb_locate_dc(char *domain, char *dc, smb_domainex_t *dp)
+smb_locate_dc(char *domain, smb_domainex_t *dp)
{
int rc;
+ boolean_t rv;
timestruc_t to;
smb_domainex_t domain_info;
- if (domain == NULL || *domain == '\0')
+ if (domain == NULL || *domain == '\0') {
+ syslog(LOG_DEBUG, "smb_locate_dc NULL dom");
+ smb_set_krb5_realm(NULL);
return (B_FALSE);
+ }
(void) mutex_lock(&smb_dclocator.sdl_mtx);
+ if (strcmp(smb_dclocator.sdl_domain, domain)) {
+ (void) strlcpy(smb_dclocator.sdl_domain, domain,
+ sizeof (smb_dclocator.sdl_domain));
+ smb_dclocator.sdl_cfg_chg = B_TRUE;
+ syslog(LOG_DEBUG, "smb_locate_dc new dom=%s", domain);
+ smb_set_krb5_realm(domain);
+ }
+
if (!smb_dclocator.sdl_locate) {
smb_dclocator.sdl_locate = B_TRUE;
- (void) strlcpy(smb_dclocator.sdl_domain, domain,
- SMB_PI_MAX_DOMAIN);
- (void) strlcpy(smb_dclocator.sdl_dc, dc, MAXHOSTNAMELEN);
(void) cond_broadcast(&smb_dclocator.sdl_cv);
}
@@ -141,17 +150,106 @@ smb_locate_dc(char *domain, char *dc, smb_domainex_t *dp)
rc = cond_reltimedwait(&smb_dclocator.sdl_cv,
&smb_dclocator.sdl_mtx, &to);
- if (rc == ETIME)
- break;
+ if (rc == ETIME) {
+ syslog(LOG_NOTICE, "smb_locate_dc timeout");
+ rv = B_FALSE;
+ goto out;
+ }
+ }
+ if (smb_dclocator.sdl_status != 0) {
+ syslog(LOG_NOTICE, "smb_locate_dc status 0x%x",
+ smb_dclocator.sdl_status);
+ rv = B_FALSE;
+ goto out;
}
if (dp == NULL)
dp = &domain_info;
- rc = smb_domain_getinfo(dp);
+ rv = smb_domain_getinfo(dp);
+out:
(void) mutex_unlock(&smb_dclocator.sdl_mtx);
- return (rc);
+ return (rv);
+}
+
+/*
+ * Tell the domain discovery service to run again now,
+ * and assume changed configuration (i.e. a new DC).
+ * Like the first part of smb_locate_dc().
+ *
+ * Note: This is called from the service refresh handler
+ * and the door handler to tell the ddiscover thread to
+ * request the new DC from idmap. Therefore, we must not
+ * trigger a new idmap discovery run from here, or that
+ * would start a ping-pong match.
+ */
+/* ARGSUSED */
+void
+smb_ddiscover_refresh()
+{
+
+ (void) mutex_lock(&smb_dclocator.sdl_mtx);
+
+ if (smb_dclocator.sdl_cfg_chg == B_FALSE) {
+ smb_dclocator.sdl_cfg_chg = B_TRUE;
+ syslog(LOG_DEBUG, "smb_ddiscover_refresh set cfg changed");
+ }
+ if (!smb_dclocator.sdl_locate) {
+ smb_dclocator.sdl_locate = B_TRUE;
+ (void) cond_broadcast(&smb_dclocator.sdl_cv);
+ }
+
+ (void) mutex_unlock(&smb_dclocator.sdl_mtx);
+}
+
+/*
+ * Called by our client-side threads after they fail to connect to
+ * the DC given to them by smb_locate_dc(). This is often called
+ * after some delay, because the connection timeout delays these
+ * threads for a while, so it's quite common that the DC locator
+ * service has already started looking for a new DC. These late
+ * notifications should not continually restart the DC locator.
+ */
+void
+smb_ddiscover_bad_dc(char *bad_dc)
+{
+
+ assert(bad_dc[0] != '\0');
+
+ (void) mutex_lock(&smb_dclocator.sdl_mtx);
+
+ syslog(LOG_DEBUG, "smb_ddiscover_bad_dc, cur=%s, bad=%s",
+ smb_dclocator.sdl_dci.dc_name, bad_dc);
+
+ if (strcmp(smb_dclocator.sdl_dci.dc_name, bad_dc)) {
+ /*
+ * The "bad" DC is no longer the current one.
+ * Probably a late "bad DC" report.
+ */
+ goto out;
+ }
+ if (smb_dclocator.sdl_bad_dc) {
+ /* Someone already marked the current DC as "bad". */
+ syslog(LOG_DEBUG, "smb_ddiscover_bad_dc repeat");
+ goto out;
+ }
+
+ /*
+ * Mark the current DC as "bad" and let the DC Locator
+ * run again if it's not already.
+ */
+ syslog(LOG_INFO, "smb_ddiscover, bad DC: %s", bad_dc);
+ smb_dclocator.sdl_bad_dc = B_TRUE;
+
+ /* In-line smb_ddiscover_kick */
+ if (!smb_dclocator.sdl_locate) {
+ smb_dclocator.sdl_locate = B_TRUE;
+ (void) cond_broadcast(&smb_dclocator.sdl_cv);
+ }
+
+out:
+ (void) mutex_unlock(&smb_dclocator.sdl_mtx);
}
/*
@@ -195,27 +293,94 @@ smb_ddiscover_wait(void)
static void *
smb_ddiscover_service(void *arg)
{
- char domain[SMB_PI_MAX_DOMAIN];
- char sought_dc[MAXHOSTNAMELEN];
+ smb_domainex_t dxi;
+ smb_dclocator_t *sdl = arg;
+ uint32_t status;
+ boolean_t bad_dc;
+ boolean_t cfg_chg;
for (;;) {
- (void) mutex_lock(&smb_dclocator.sdl_mtx);
-
- while (!smb_dclocator.sdl_locate)
- (void) cond_wait(&smb_dclocator.sdl_cv,
- &smb_dclocator.sdl_mtx);
-
- (void) strlcpy(domain, smb_dclocator.sdl_domain,
- SMB_PI_MAX_DOMAIN);
- (void) strlcpy(sought_dc, smb_dclocator.sdl_dc, MAXHOSTNAMELEN);
- (void) mutex_unlock(&smb_dclocator.sdl_mtx);
+ /*
+ * Wait to be signaled for work by one of:
+ * smb_locate_dc(), smb_ddiscover_refresh(),
+ * smb_ddiscover_bad_dc()
+ */
+ syslog(LOG_DEBUG, "smb_ddiscover_service waiting");
+
+ (void) mutex_lock(&sdl->sdl_mtx);
+ while (!sdl->sdl_locate)
+ (void) cond_wait(&sdl->sdl_cv,
+ &sdl->sdl_mtx);
+
+ if (!smb_config_getbool(SMB_CI_DOMAIN_MEMB)) {
+ sdl->sdl_status = NT_STATUS_INVALID_SERVER_STATE;
+ syslog(LOG_DEBUG, "smb_ddiscover_service: "
+ "not a domain member");
+ goto wait_again;
+ }
- smb_ddiscover_main(domain, sought_dc);
+ /*
+ * Want to know if these change below.
+ * Note: mutex held here
+ */
+ find_again:
+ bad_dc = sdl->sdl_bad_dc;
+ sdl->sdl_bad_dc = B_FALSE;
+ if (bad_dc) {
+ /*
+ * Need to clear the current DC name or
+ * ddiscover_bad_dc will keep setting bad_dc
+ */
+ sdl->sdl_dci.dc_name[0] = '\0';
+ }
+ cfg_chg = sdl->sdl_cfg_chg;
+ sdl->sdl_cfg_chg = B_FALSE;
+
+ (void) mutex_unlock(&sdl->sdl_mtx);
+
+ syslog(LOG_DEBUG, "smb_ddiscover_service running "
+ "cfg_chg=%d bad_dc=%d", (int)cfg_chg, (int)bad_dc);
+
+ /*
+ * Clear the cached DC now so that we'll ask idmap again.
+ * If our current DC gave us errors, force rediscovery.
+ */
+ smb_ads_refresh(bad_dc);
+
+ /*
+ * Search for the DC, save the result.
+ */
+ bzero(&dxi, sizeof (dxi));
+ status = smb_ddiscover_main(sdl->sdl_domain, &dxi);
+ if (status == 0)
+ smb_domain_save();
+ (void) mutex_lock(&sdl->sdl_mtx);
+ sdl->sdl_status = status;
+ if (status == 0)
+ sdl->sdl_dci = dxi.d_dci;
+
+ /*
+ * Run again if either of cfg_chg or bad_dc
+ * was turned on during smb_ddiscover_main().
+ * Note: mutex held here.
+ */
+ if (sdl->sdl_bad_dc) {
+ syslog(LOG_DEBUG, "smb_ddiscover_service "
+ "restart because bad_dc was set");
+ goto find_again;
+ }
+ if (sdl->sdl_cfg_chg) {
+ syslog(LOG_DEBUG, "smb_ddiscover_service "
+ "restart because cfg_chg was set");
+ goto find_again;
+ }
- (void) mutex_lock(&smb_dclocator.sdl_mtx);
- smb_dclocator.sdl_locate = B_FALSE;
- (void) cond_broadcast(&smb_dclocator.sdl_cv);
- (void) mutex_unlock(&smb_dclocator.sdl_mtx);
+ wait_again:
+ sdl->sdl_locate = B_FALSE;
+ sdl->sdl_bad_dc = B_FALSE;
+ sdl->sdl_cfg_chg = B_FALSE;
+ (void) cond_broadcast(&sdl->sdl_cv);
+ (void) mutex_unlock(&sdl->sdl_mtx);
}
/*NOTREACHED*/
@@ -223,186 +388,81 @@ smb_ddiscover_service(void *arg)
}
/*
- * Discovers a domain controller for the specified domain either via
- * DNS or NetBIOS. After the domain controller is discovered successfully
- * primary and trusted domain infromation will be queried using RPC queries.
- * If the RPC queries fail, the domain information stored in SMF might be used
- * if the the discovered domain is the same as the previously joined domain.
- * If everything is successful domain cache will be updated with all the
- * obtained information.
- */
-static void
-smb_ddiscover_main(char *domain, char *server)
-{
- smb_domainex_t dxi;
- uint32_t status;
-
- bzero(&dxi, sizeof (smb_domainex_t));
-
- if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS)
- return;
-
- status = smb_ddiscover_dns(domain, server, &dxi);
- if (status != 0 && !SMB_IS_FQDN(domain)) {
- if (smb_ddiscover_nbt(domain, server, &dxi))
- status = 0;
- }
-
- if (status == 0)
- smb_domain_update(&dxi);
-
- smb_domain_end_update();
-
- smb_domainex_free(&dxi);
-
- if (status == 0)
- smb_domain_save();
-}
-
-/*
- * Discovers a DC for the specified domain via DNS. If a DC is found
- * primary and trusted domains information will be queried.
- */
-static uint32_t
-smb_ddiscover_dns(char *domain, char *server, smb_domainex_t *dxi)
-{
- uint32_t status;
-
- if (!smb_ads_lookup_msdcs(domain, server, dxi->d_dc, MAXHOSTNAMELEN))
- return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
-
- status = smb_ddiscover_qinfo(domain, dxi->d_dc, dxi);
- return (status);
-}
-
-/*
- * Discovers a DC for the specified domain using NETLOGON protocol.
- * If a DC cannot be found using NETLOGON then it will
- * try to resolve it via DNS, i.e. find out if it is the first label
- * of a DNS domain name. If the corresponding DNS name is found, DC
- * discovery will be done via DNS query.
+ * Discovers a domain controller for the specified domain via DNS.
+ * After the domain controller is discovered successfully primary and
+ * trusted domain infromation will be queried using RPC queries.
*
- * If the fully-qualified domain name is derived from the DNS config
- * file, the NetBIOS domain name specified by the user will be compared
- * against the NetBIOS domain name obtained via LSA query. If there is
- * a mismatch, the DC discovery will fail since the discovered DC is
- * actually for another domain, whose first label of its FQDN somehow
- * matches with the NetBIOS name of the domain we're interested in.
+ * Caller should zero out *dxi before calling, and after a
+ * successful return should call: smb_domain_save()
*/
-static boolean_t
-smb_ddiscover_nbt(char *domain, char *server, smb_domainex_t *dxi)
+uint32_t
+smb_ddiscover_main(char *domain, smb_domainex_t *dxi)
{
- char dnsdomain[MAXHOSTNAMELEN];
uint32_t status;
- *dnsdomain = '\0';
-
- if (!smb_browser_netlogon(domain, dxi->d_dc, MAXHOSTNAMELEN)) {
- if (!smb_ddiscover_domain_match(domain, dnsdomain,
- MAXHOSTNAMELEN))
- return (B_FALSE);
-
- if (!smb_ads_lookup_msdcs(dnsdomain, server, dxi->d_dc,
- MAXHOSTNAMELEN))
- return (B_FALSE);
+ if (domain[0] == '\0') {
+ syslog(LOG_DEBUG, "smb_ddiscover_main NULL domain");
+ return (NT_STATUS_INTERNAL_ERROR);
}
- status = smb_ddiscover_qinfo(domain, dxi->d_dc, dxi);
- if (status != NT_STATUS_SUCCESS)
- return (B_FALSE);
-
- if ((*dnsdomain != '\0') &&
- smb_strcasecmp(domain, dxi->d_primary.di_nbname, 0))
- return (B_FALSE);
-
- /*
- * Now that we get the fully-qualified DNS name of the
- * domain via LSA query. Verifies ADS configuration
- * if we previously locate a DC via NetBIOS. On success,
- * ADS cache will be populated.
- */
- if (smb_ads_lookup_msdcs(dxi->d_primary.di_fqname, server,
- dxi->d_dc, MAXHOSTNAMELEN) == 0)
- return (B_FALSE);
-
- return (B_TRUE);
-}
-
-/*
- * Tries to find a matching DNS domain for the given NetBIOS domain
- * name by checking the first label of system's configured DNS domains.
- * If a match is found, it'll be returned in the passed buffer.
- */
-static boolean_t
-smb_ddiscover_domain_match(char *nb_domain, char *buf, uint32_t len)
-{
- struct __res_state res_state;
- int i;
- char *entry, *p;
- char first_label[MAXHOSTNAMELEN];
- boolean_t found;
-
- if (!nb_domain || !buf)
- return (B_FALSE);
+ if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS) {
+ syslog(LOG_DEBUG, "smb_ddiscover_main can't get lock");
+ return (NT_STATUS_INTERNAL_ERROR);
+ }
- *buf = '\0';
- bzero(&res_state, sizeof (struct __res_state));
- if (res_ninit(&res_state))
- return (B_FALSE);
+ status = smb_ads_lookup_msdcs(domain, &dxi->d_dci);
+ if (status != 0) {
+ syslog(LOG_DEBUG, "smb_ddiscover_main can't find DC (%s)",
+ xlate_nt_status(status));
+ goto out;
+ }
- found = B_FALSE;
- entry = res_state.defdname;
- for (i = 0; entry != NULL; i++) {
- (void) strlcpy(first_label, entry, MAXHOSTNAMELEN);
- if ((p = strchr(first_label, '.')) != NULL) {
- *p = '\0';
- if (strlen(first_label) > 15)
- first_label[15] = '\0';
- }
+ status = smb_ddiscover_qinfo(domain, dxi->d_dci.dc_name, dxi);
+ if (status != 0) {
+ syslog(LOG_DEBUG,
+ "smb_ddiscover_main can't get domain info (%s)",
+ xlate_nt_status(status));
+ goto out;
+ }
- if (smb_strcasecmp(nb_domain, first_label, 0) == 0) {
- found = B_TRUE;
- (void) strlcpy(buf, entry, len);
- break;
- }
+ smb_domain_update(dxi);
- entry = res_state.dnsrch[i];
- }
+out:
+ smb_domain_end_update();
+ /* Don't need the trusted domain list anymore. */
+ smb_domainex_free(dxi);
- res_ndestroy(&res_state);
- return (found);
+ return (status);
}
/*
* Obtain primary and trusted domain information using LSA queries.
*
- * Disconnect any existing connection with the domain controller.
- * This will ensure that no stale connection will be used, it will
- * also pickup any configuration changes in either side by trying
- * to establish a new connection.
- *
* domain - either NetBIOS or fully-qualified domain name
*/
static uint32_t
smb_ddiscover_qinfo(char *domain, char *server, smb_domainex_t *dxi)
{
- uint32_t status;
-
- mlsvc_disconnect(server);
-
- status = lsa_query_dns_domain_info(server, domain, &dxi->d_primary);
- if (status != NT_STATUS_SUCCESS) {
- status = smb_ddiscover_use_config(domain, dxi);
- if (status != NT_STATUS_SUCCESS)
- status = lsa_query_primary_domain_info(server, domain,
- &dxi->d_primary);
- }
-
- if (status == NT_STATUS_SUCCESS)
- smb_ddiscover_enum_trusted(domain, server, dxi);
-
- return (status);
+ uint32_t ret, tmp;
+
+ /* If we must return failure, use this first one. */
+ ret = lsa_query_dns_domain_info(server, domain, &dxi->d_primary);
+ if (ret == NT_STATUS_SUCCESS)
+ goto success;
+ tmp = smb_ddiscover_use_config(domain, dxi);
+ if (tmp == NT_STATUS_SUCCESS)
+ goto success;
+ tmp = lsa_query_primary_domain_info(server, domain, &dxi->d_primary);
+ if (tmp == NT_STATUS_SUCCESS)
+ goto success;
+
+ /* All of the above failed. */
+ return (ret);
+
+success:
+ smb_ddiscover_enum_trusted(domain, server, dxi);
+ return (NT_STATUS_SUCCESS);
}
/*
@@ -459,4 +519,22 @@ static void
smb_domainex_free(smb_domainex_t *dxi)
{
free(dxi->d_trusted.td_domains);
+ dxi->d_trusted.td_domains = NULL;
+}
+
+static void
+smb_set_krb5_realm(char *domain)
+{
+ static char realm[MAXHOSTNAMELEN];
+
+ if (domain == NULL || domain[0] == '\0') {
+ (void) unsetenv("KRB5_DEFAULT_REALM");
+ return;
+ }
+
+ /* In case krb5.conf is not configured, set the default realm. */
+ (void) strlcpy(realm, domain, sizeof (realm));
+ (void) smb_strupr(realm);
+
+ (void) setenv("KRB5_DEFAULT_REALM", realm, 1);
}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c
index 0fbe2a6890..794e6c9576 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/errno.h>
@@ -111,7 +111,8 @@ mlsvc_timecheck(void *arg)
if (!smb_domain_getinfo(&di))
continue;
- ndr_srvsvc_timecheck(di.d_dc, di.d_primary.di_nbname);
+ ndr_srvsvc_timecheck(di.d_dci.dc_name,
+ di.d_primary.di_nbname);
}
/*NOTREACHED*/
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
index 18d5bd18b0..ad3565699b 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -43,14 +43,11 @@
#include <smbsrv/libmlsvc.h>
#include <smbsrv/ntaccess.h>
#include <smbsrv/smbinfo.h>
+#include <smbsrv/netrauth.h>
#include <libsmbrdr.h>
#include <lsalib.h>
#include <samlib.h>
-#include <smbsrv/netrauth.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);
+#include <mlsvc.h>
static DWORD
mlsvc_join_rpc(smb_domainex_t *dxi,
@@ -67,18 +64,25 @@ mlsvc_netlogon(char *server, char *domain)
mlsvc_handle_t netr_handle;
DWORD status;
- if (netr_open(server, domain, &netr_handle) == 0) {
- if ((status = netlogon_auth(server, &netr_handle,
- NETR_FLG_INIT)) != NT_STATUS_SUCCESS)
- syslog(LOG_NOTICE, "Failed to establish NETLOGON "
- "credential chain");
- (void) netr_close(&netr_handle);
- } else {
+ status = netr_open(server, domain, &netr_handle);
+ if (status != 0) {
syslog(LOG_NOTICE, "Failed to connect to %s "
- "for domain %s", server, domain);
- status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ "for domain %s (%s)", server, domain,
+ xlate_nt_status(status));
+ return (status);
}
+ status = netlogon_auth(server, &netr_handle, NETR_FLG_INIT);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_NOTICE, "Failed to establish NETLOGON "
+ "credential chain with DC: %s (%s)", server,
+ xlate_nt_status(status));
+ syslog(LOG_NOTICE, "The machine account information on the "
+ "domain controller does not match the local storage.");
+ syslog(LOG_NOTICE, "To correct this, use 'smbadm join'");
+ }
+ (void) netr_close(&netr_handle);
+
return (status);
}
@@ -90,69 +94,133 @@ mlsvc_netlogon(char *server, char *domain)
* machine attempts to join, and we just change the password from the
* (weak) default password for a new machine account to a random one.
*
- * Note that the caller has already done "DC discovery" and passes the
- * domain info. in the first arg.
- *
* Returns NT status codes.
*/
-DWORD
-mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw)
+void
+mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res)
{
+ static unsigned char zero_hash[SMBAUTH_HASH_SZ];
char machine_name[SMB_SAMACCT_MAXLEN];
char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX];
unsigned char passwd_hash[SMBAUTH_HASH_SZ];
- smb_domain_t *di = &dxi->d_primary;
+ smb_domainex_t dxi;
+ smb_domain_t *di = &dxi.d_primary;
DWORD status;
int rc;
+ bzero(&dxi, sizeof (dxi));
+
/*
* Domain join support: AD (Kerberos+LDAP) or MS-RPC?
- * Leave the AD code path disabled until it can be
- * fixed up so that the SMB server is in complete
- * control of which AD server we talk to. See:
- * NX 12427 (Re-enable Kerberos+LDAP with...)
*/
boolean_t ads_enabled = smb_config_get_ads_enable();
- if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0)
- return (NT_STATUS_INTERNAL_ERROR);
+ if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) {
+ res->status = NT_STATUS_INVALID_COMPUTER_NAME;
+ return;
+ }
(void) smb_gen_random_passwd(machine_pw, sizeof (machine_pw));
/*
- * A non-null user means we do "secure join".
+ * Ensure that any previous membership of this domain has
+ * been cleared from the environment before we start. This
+ * will ensure that we don't attempt a NETLOGON_SAMLOGON
+ * when attempting to find the PDC.
*/
- if (admin_user != NULL && admin_user[0] != '\0') {
- /*
- * Doing "secure join", so authenticate as the
- * specified user (with admin. rights).
- */
- (void) smb_auth_ntlm_hash(admin_pw, passwd_hash);
- smb_ipc_set(admin_user, passwd_hash);
+ (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
+
+ if (info->domain_username[0] != '\0') {
+ (void) smb_auth_ntlm_hash(info->domain_passwd, passwd_hash);
+ smb_ipc_set(info->domain_username, passwd_hash);
+ } else {
+ smb_ipc_set(MLSVC_ANON_USER, zero_hash);
+ }
+
+ /*
+ * Tentatively set the idmap domain to the one we're joining,
+ * so that the DC locator in idmap knows what to look for.
+ * Ditto the SMB server domain.
+ */
+ if (smb_config_set_idmap_domain(info->domain_name) != 0)
+ syslog(LOG_NOTICE, "Failed to set idmap domain name");
+ if (smb_config_refresh_idmap() != 0)
+ syslog(LOG_NOTICE, "Failed to refresh idmap service");
+
+ /* Clear DNS local (ADS) lookup cache. */
+ smb_ads_refresh(B_FALSE);
+
+ /*
+ * Locate a DC for this domain. Intentionally bypass the
+ * ddiscover service here because we're still joining.
+ * This also allows better reporting of any failures.
+ */
+ status = smb_ads_lookup_msdcs(info->domain_name, &dxi.d_dci);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR,
+ "smbd: failed to locate AD server for domain %s (%s)",
+ info->domain_name, xlate_nt_status(status));
+ goto out;
+ }
+
+ /*
+ * Found a DC. Report what we found along with the return status
+ * so that admin will know which AD server we were talking to.
+ */
+ (void) strlcpy(res->dc_name, dxi.d_dci.dc_name, MAXHOSTNAMELEN);
+ syslog(LOG_INFO, "smbd: found AD server %s", dxi.d_dci.dc_name);
+ /*
+ * Domain discovery needs to authenticate with the AD server.
+ * Disconnect any existing connection with the domain controller
+ * to make sure we won't use any prior authentication context
+ * our redirector might have.
+ */
+ mlsvc_disconnect(dxi.d_dci.dc_name);
+
+ /*
+ * Get the domain policy info (domain SID etc).
+ * Here too, bypass the smb_ddiscover_service.
+ */
+ status = smb_ddiscover_main(info->domain_name, &dxi);
+ if (status != NT_STATUS_SUCCESS) {
+ syslog(LOG_ERR,
+ "smbd: failed getting domain info for %s (%s)",
+ info->domain_name, xlate_nt_status(status));
+ goto out;
+ }
+ /*
+ * After a successful smbd_ddiscover_main() call
+ * we should call smb_domain_save() to update the
+ * data shown by smbadm list. Do that at the end,
+ * only if all goes well with joining the domain.
+ */
+
+ /*
+ * Create or update our machine account on the DC.
+ * A non-null user means we do "secure join".
+ */
+ if (info->domain_username[0] != '\0') {
/*
* If enabled, try to join using AD Services.
- * The ADS code needs work. Not enabled yet.
*/
status = NT_STATUS_UNSUCCESSFUL;
if (ads_enabled) {
- smb_adjoin_status_t err;
- err = smb_ads_join(di->di_fqname,
- admin_user, admin_pw, machine_pw);
- if (err != SMB_ADJOIN_SUCCESS) {
- smb_ads_join_errmsg(err);
- } else {
+ res->join_err = smb_ads_join(di->di_fqname,
+ info->domain_username, info->domain_passwd,
+ machine_pw);
+ if (res->join_err == SMB_ADS_SUCCESS) {
status = NT_STATUS_SUCCESS;
}
- }
-
- /*
- * If ADS was disabled or gave an error,
- * fall-back and try to join using RPC.
- */
- if (status != NT_STATUS_SUCCESS) {
- status = mlsvc_join_rpc(dxi,
- admin_user, admin_pw,
+ } else {
+ syslog(LOG_DEBUG, "use_ads=false (do RPC join)");
+
+ /*
+ * If ADS was disabled, join using RPC.
+ */
+ status = mlsvc_join_rpc(&dxi,
+ info->domain_username,
+ info->domain_passwd,
machine_name, machine_pw);
}
@@ -160,10 +228,7 @@ mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw)
/*
* Doing "Unsecure join" (pre-created account)
*/
- bzero(passwd_hash, sizeof (passwd_hash));
- smb_ipc_set(MLSVC_ANON_USER, passwd_hash);
-
- status = mlsvc_join_noauth(dxi, machine_name, machine_pw);
+ status = mlsvc_join_noauth(&dxi, machine_name, machine_pw);
}
if (status != NT_STATUS_SUCCESS)
@@ -175,47 +240,63 @@ mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw)
*/
(void) smb_auth_ntlm_hash(machine_pw, passwd_hash);
smb_ipc_set(machine_name, passwd_hash);
- rc = smbrdr_logon(dxi->d_dc, di->di_nbname, machine_name);
+ rc = smbrdr_logon(dxi.d_dci.dc_name, di->di_nbname, machine_name);
if (rc != 0) {
syslog(LOG_NOTICE, "Authenticate with "
"new/updated machine account: %s",
strerror(rc));
+ res->join_err = SMB_ADJOIN_ERR_AUTH_NETLOGON;
status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
goto out;
}
/*
- * Store the new machine account password.
+ * Store the new machine account password, and
+ * SMB_CI_DOMAIN_MEMB etc.
*/
- rc = smb_setdomainprops(NULL, dxi->d_dc, machine_pw);
+ rc = smb_setdomainprops(NULL, dxi.d_dci.dc_name, machine_pw);
if (rc != 0) {
syslog(LOG_NOTICE,
"Failed to save machine account password");
+ res->join_err = SMB_ADJOIN_ERR_STORE_PROPS;
status = NT_STATUS_INTERNAL_DB_ERROR;
goto out;
}
/*
- * Update idmap config
+ * Update idmap config?
+ * Already set the domain_name above.
*/
- if (smb_config_set_idmap_domain(di->di_fqname) != 0)
- syslog(LOG_NOTICE, "Failed to set idmap domain name");
- if (smb_config_refresh_idmap() != 0)
- syslog(LOG_NOTICE, "Failed to refresh idmap service");
/*
- * Note: The caller (smbd) saves the "secmode" and
- * domain info (via smb_config_setdomaininfo) and
- * and does smb_ipc_commit (or rollback).
+ * Save the SMB server config. Sets: SMB_CI_DOMAIN_*
+ * Should unify SMB vs idmap configs.
*/
+ smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
+ di->di_sid,
+ di->di_u.di_dns.ddi_forest,
+ di->di_u.di_dns.ddi_guid);
+ smb_ipc_commit();
+ smb_domain_save();
+
status = 0;
out:
+
+ if (status != 0) {
+ /*
+ * Undo the tentative domain settings.
+ */
+ (void) smb_config_set_idmap_domain("");
+ (void) smb_config_refresh_idmap();
+ smb_ipc_rollback();
+ }
+
/* Avoid leaving cleartext passwords around. */
bzero(machine_pw, sizeof (machine_pw));
bzero(passwd_hash, sizeof (passwd_hash));
- return (status);
+ res->status = status;
}
static DWORD
@@ -227,7 +308,7 @@ mlsvc_join_rpc(smb_domainex_t *dxi,
mlsvc_handle_t domain_handle;
mlsvc_handle_t user_handle;
smb_account_t ainfo;
- char *server = dxi->d_dc;
+ char *server = dxi->d_dci.dc_name;
smb_domain_t *di = &dxi->d_primary;
DWORD account_flags;
DWORD rid;
@@ -264,7 +345,7 @@ mlsvc_join_rpc(smb_domainex_t *dxi,
}
if (status != NT_STATUS_SUCCESS) {
syslog(LOG_NOTICE,
- "Create or open machine account: %s",
+ "smbd: failed to open machine account (%s)",
xlate_nt_status(status));
goto out_domain_handle;
}
@@ -277,7 +358,7 @@ mlsvc_join_rpc(smb_domainex_t *dxi,
status = netr_set_user_password(&user_handle, machine_pw);
if (status != NT_STATUS_SUCCESS) {
syslog(LOG_NOTICE,
- "Set machine account password: %s",
+ "smbd: failed to set machine account password (%s)",
xlate_nt_status(status));
goto out_user_handle;
}
@@ -329,7 +410,7 @@ mlsvc_join_noauth(smb_domainex_t *dxi,
return (NT_STATUS_INTERNAL_ERROR);
old_pw[14] = '\0';
- status = netr_change_password(dxi->d_dc, machine_name,
+ status = netr_change_password(dxi->d_dci.dc_name, machine_name,
old_pw, machine_pw);
if (status != NT_STATUS_SUCCESS) {
syslog(LOG_NOTICE,
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
index 4b0224261b..ea31c935ed 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -94,6 +94,7 @@ netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags)
if (rc != 0)
return (NT_STATUS_UNSUCCESSFUL);
+ /* server is our DC. Note: normally an FQDN. */
(void) snprintf(netr_info->server, sizeof (netr_info->server),
"\\\\%s", server);
@@ -128,18 +129,20 @@ netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags)
*
* We store the remote server information, which is used to drive Windows
* version specific behavior.
+ *
+ * Returns 0 or NT status
*/
-int
+DWORD
netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle)
{
char user[SMB_USERNAME_MAXLEN];
+ DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
- if (ndr_rpc_bind(netr_handle, server, domain, user, "NETR") < 0)
- return (-1);
+ status = ndr_rpc_bind(netr_handle, server, domain, user, "NETR");
- return (0);
+ return (status);
}
/*
@@ -195,9 +198,10 @@ static int
netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
{
struct netr_ServerAuthenticate2 arg;
+ /* sizeof netr_info->hostname, + 1 for the '$' */
+ char account_name[(NETBIOS_NAME_SZ * 2) + 1];
int opnum;
int rc;
- char account_name[NETBIOS_NAME_SZ * 2];
bzero(&arg, sizeof (struct netr_ServerAuthenticate2));
opnum = NETR_OPNUM_ServerAuthenticate2;
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c
index a3fbb4d96f..ab99db75f8 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c
@@ -227,7 +227,7 @@ static uint32_t
netlogon_logon(smb_logon_t *user_info, smb_token_t *token)
{
char resource_domain[SMB_PI_MAX_DOMAIN];
- char server[NETBIOS_NAME_SZ * 2];
+ char server[MAXHOSTNAMELEN];
mlsvc_handle_t netr_handle;
smb_domainex_t di;
uint32_t status;
@@ -243,13 +243,14 @@ netlogon_logon(smb_logon_t *user_info, smb_token_t *token)
}
do {
- if (netr_open(di.d_dc, di.d_primary.di_nbname, &netr_handle)
- != 0)
+ if (netr_open(di.d_dci.dc_name, di.d_primary.di_nbname,
+ &netr_handle) != 0)
return (NT_STATUS_OPEN_FAILED);
- if (di.d_dc && (*netr_global_info.server != '\0')) {
+ if (di.d_dci.dc_name[0] != '\0' &&
+ (*netr_global_info.server != '\0')) {
(void) snprintf(server, sizeof (server),
- "\\\\%s", di.d_dc);
+ "\\\\%s", di.d_dci.dc_name);
if (strncasecmp(netr_global_info.server,
server, strlen(server)) != 0)
netr_invalidate_chain();
@@ -257,7 +258,7 @@ netlogon_logon(smb_logon_t *user_info, smb_token_t *token)
if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
!smb_match_netlogon_seqnum()) {
- status = netlogon_auth(di.d_dc, &netr_handle,
+ status = netlogon_auth(di.d_dci.dc_name, &netr_handle,
NETR_FLG_NULL);
if (status != 0) {
@@ -269,7 +270,7 @@ netlogon_logon(smb_logon_t *user_info, smb_token_t *token)
}
status = netr_server_samlogon(&netr_handle,
- &netr_global_info, di.d_dc, user_info, token);
+ &netr_global_info, di.d_dci.dc_name, user_info, token);
(void) netr_close(&netr_handle);
} while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3);
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samlib.c b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c
index 1a2233ce39..8b0c4e3fbd 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/samlib.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -293,14 +293,14 @@ netr_change_password(
uint8_t old_nt_hash[SAMR_PWHASH_LEN];
uint8_t new_nt_hash[SAMR_PWHASH_LEN];
mlsvc_handle_t handle;
- DWORD rc;
+ DWORD status;
/*
* Create an RPC handle to this server, bound to SAMR.
*/
- rc = ndr_rpc_bind(&handle, server, "", "", "SAMR");
- if (rc)
- return (RPC_NT_SERVER_UNAVAILABLE);
+ status = ndr_rpc_bind(&handle, server, "", "", "SAMR");
+ if (status != NT_STATUS_SUCCESS)
+ return (status);
/*
* Encrypt the new p/w (plus random filler) with the
@@ -319,7 +319,7 @@ netr_change_password(
/*
* Finally, ready to try the OtW call.
*/
- rc = samr_change_password(
+ status = samr_change_password(
&handle, server, account,
&epw, &old_hash);
@@ -328,7 +328,7 @@ netr_change_password(
(void) memset(new_nt_hash, 0, sizeof (new_nt_hash));
ndr_rpc_unbind(&handle);
- return (rc);
+ return (status);
}
/*
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c
index fc2956f226..289e8470cf 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -84,7 +84,7 @@ samr_open(char *server, char *domain, char *username, DWORD access_mask,
if (server == NULL || domain == NULL) {
if (!smb_domain_getinfo(&di))
return (NT_STATUS_INTERNAL_ERROR);
- server = di.d_dc;
+ server = di.d_dci.dc_name;
domain = di.d_primary.di_nbname;
}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c b/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c
index ab127b6150..724c509d8d 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -115,6 +115,9 @@ smbrdr_ctx_new(struct smb_ctx **ctx_p, char *server,
assert(domain != NULL);
assert(user != NULL);
+ if (server[0] == '\0')
+ return (NT_STATUS_INTERNAL_ERROR);
+
if ((err = smb_ctx_alloc(&ctx)) != 0)
return (NT_STATUS_NO_MEMORY);
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c
index 819f5f1aa7..def8ced54a 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -62,14 +63,14 @@ srvsvc_open(char *server, char *domain, char *username, mlsvc_handle_t *handle)
if (!smb_domain_getinfo(&di))
return (-1);
- server = di.d_dc;
+ server = di.d_dci.dc_name;
domain = di.d_primary.di_nbname;
}
if (username == NULL)
username = MLSVC_ANON_USER;
- if (ndr_rpc_bind(handle, server, domain, username, "SRVSVC") < 0)
+ if (ndr_rpc_bind(handle, server, domain, username, "SRVSVC") != 0)
return (-1);
return (0);
@@ -422,8 +423,8 @@ srvsvc_timesync(void)
if (!smb_domain_getinfo(&di))
return;
- if (srvsvc_net_remote_tod(di.d_dc, di.d_primary.di_nbname, &tv, &tm)
- != 0)
+ if (srvsvc_net_remote_tod(di.d_dci.dc_name, di.d_primary.di_nbname,
+ &tv, &tm) != 0)
return;
if (settimeofday(&tv, 0))
@@ -447,8 +448,8 @@ srvsvc_gettime(unsigned long *t)
if (!smb_domain_getinfo(&di))
return (-1);
- if (srvsvc_net_remote_tod(di.d_dc, di.d_primary.di_nbname, &tv, &tm)
- != 0)
+ if (srvsvc_net_remote_tod(di.d_dci.dc_name, di.d_primary.di_nbname,
+ &tv, &tm) != 0)
return (-1);
*t = tv.tv_sec;
@@ -553,7 +554,7 @@ srvsvc_net_test(char *server, char *domain, char *netname)
(void) smb_tracef("%s %s %s", server, domain, netname);
if (smb_domain_getinfo(&di)) {
- server = di.d_dc;
+ server = di.d_dci.dc_name;
domain = di.d_primary.di_nbname;
}
diff --git a/usr/src/lib/smbsrv/libsmb/common/libsmb.h b/usr/src/lib/smbsrv/libsmb/common/libsmb.h
index 82bbe995c3..6932e5d3a4 100644
--- a/usr/src/lib/smbsrv/libsmb/common/libsmb.h
+++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _LIBSMB_H
@@ -217,6 +217,7 @@ extern void smb_update_netlogon_seqnum(void);
#define SMB_PASSWD_MAXLEN 127
#define SMB_USERNAME_MAXLEN 40
+/* See also: smb_joininfo_xdr() */
typedef struct smb_joininfo {
char domain_name[MAXHOSTNAMELEN];
char domain_username[SMB_USERNAME_MAXLEN + 1];
@@ -224,10 +225,19 @@ typedef struct smb_joininfo {
uint32_t mode;
} smb_joininfo_t;
+/* See also: smb_joinres_xdr() */
+typedef struct smb_joinres {
+ uint32_t status;
+ int join_err;
+ char dc_name[MAXHOSTNAMELEN];
+} smb_joinres_t;
+
/* APIs to communicate with SMB daemon via door calls */
-uint32_t smb_join(smb_joininfo_t *info);
+int smb_join(smb_joininfo_t *, smb_joinres_t *info);
bool_t smb_joininfo_xdr(XDR *, smb_joininfo_t *);
+bool_t smb_joinres_xdr(XDR *, smb_joinres_t *);
boolean_t smb_find_ads_server(char *, char *, int);
+void smb_notify_dc_changed(void);
extern void smb_config_getdomaininfo(char *, char *, char *, char *, char *);
extern void smb_config_setdomaininfo(char *, char *, char *, char *, char *);
@@ -606,6 +616,11 @@ typedef struct smb_trusted_domains {
#define SMB_DOMAIN_NO_MEMORY 6
#define SMB_DOMAIN_NO_CACHE 7
+typedef struct smb_dcinfo {
+ char dc_name[MAXHOSTNAMELEN];
+ smb_inaddr_t dc_addr;
+} smb_dcinfo_t;
+
/*
* This structure could contain information about
* the primary domain the name of selected domain controller
@@ -615,7 +630,7 @@ typedef struct smb_trusted_domains {
* which only contains information about a single domain.
*/
typedef struct smb_domainex {
- char d_dc[MAXHOSTNAMELEN];
+ smb_dcinfo_t d_dci;
smb_domain_t d_primary;
smb_trusted_domains_t d_trusted;
} smb_domainex_t;
@@ -636,7 +651,7 @@ void smb_domain_set_dns_info(char *, char *, char *, char *, char *,
smb_domain_t *);
void smb_domain_set_trust_info(char *, char *, char *,
uint32_t, uint32_t, uint32_t, smb_domain_t *);
-void smb_domain_current_dc(char *buf, size_t len);
+void smb_domain_current_dc(smb_dcinfo_t *);
typedef struct smb_gsid {
smb_sid_t *gs_sid;
diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
index c4c3814864..5c88aa1d76 100644
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
@@ -196,6 +196,7 @@ SYMBOL_VERSION SUNWprivate {
smb_gmttoken_response_xdr;
smb_gmttoken_snapname_xdr;
smb_joininfo_xdr;
+ smb_joinres_xdr;
smb_idmap_batch_create;
smb_idmap_batch_destroy;
smb_idmap_batch_getid;
@@ -304,6 +305,7 @@ SYMBOL_VERSION SUNWprivate {
smb_nic_getnext;
smb_nic_getnum;
smb_nic_init;
+ smb_notify_dc_changed;
smb_priv_getbyname;
smb_priv_getbyvalue;
smb_priv_presentable_ids;
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
index 1110fd8792..83a7cae7bc 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -60,6 +60,7 @@ typedef struct smb_cfg_param {
#define MACHINE_SID "machine_sid"
#define MACHINE_UUID "machine_uuid"
#define IDMAP_DOMAIN "domain_name"
+#define IDMAP_PREF_DC "preferred_dc"
#define IDMAP_PG_NAME "config"
#define SMB_SECMODE_WORKGRP_STR "workgroup"
@@ -144,6 +145,8 @@ static smb_cfg_param_t *smb_config_getent(smb_cfg_id_t);
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_get_idmap_preferred_dc(char *, int);
+static int smb_config_set_idmap_preferred_dc(char *);
char *
smb_config_getname(smb_cfg_id_t id)
@@ -365,6 +368,9 @@ smb_config_getstr(smb_cfg_id_t id, char *cbuf, int bufsz)
cfg = smb_config_getent(id);
assert(cfg->sc_type == SCF_TYPE_ASTRING);
+ if (id == SMB_CI_DOMAIN_SRV)
+ return (smb_config_get_idmap_preferred_dc(cbuf, bufsz));
+
handle = smb_smf_scf_init(SMBD_FMRI_PREFIX);
if (handle == NULL)
return (SMBD_SMF_SYSTEM_ERR);
@@ -568,6 +574,9 @@ smb_config_setstr(smb_cfg_id_t id, char *value)
cfg = smb_config_getent(id);
assert(cfg->sc_type == SCF_TYPE_ASTRING);
+ if (id == SMB_CI_DOMAIN_SRV)
+ return (smb_config_set_idmap_preferred_dc(value));
+
protected = B_FALSE;
switch (cfg->sc_flags) {
@@ -792,7 +801,7 @@ smb_config_get_ads_enable(void)
rc = smb_smf_get_boolean_property(handle, "use_ads", &vbool);
smb_smf_scf_fini(handle);
- return ((rc == SMBD_SMF_OK) ? (vbool == 1) : B_FALSE);
+ return ((rc == SMBD_SMF_OK) ? (vbool == 1) : B_TRUE);
}
/*
@@ -835,6 +844,30 @@ smb_config_get_localuuid(uuid_t uu)
return (0);
}
+static int
+smb_config_get_idmap_preferred_dc(char *cbuf, int bufsz)
+{
+ char *s;
+ int len, rc = -1;
+
+ s = smb_config_getenv_generic(IDMAP_PREF_DC,
+ IDMAP_FMRI_PREFIX, IDMAP_PG_NAME);
+ if (s != NULL) {
+ len = strlcpy(cbuf, s, bufsz);
+ if (len < bufsz)
+ rc = 0;
+ free(s);
+ }
+ return (rc);
+}
+
+static int
+smb_config_set_idmap_preferred_dc(char *value)
+{
+ return (smb_config_setenv_generic(IDMAP_FMRI_PREFIX, IDMAP_PG_NAME,
+ IDMAP_PREF_DC, value));
+}
+
/*
* smb_config_set_idmap_domain
*
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_domain.c b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c
index e3974dba8d..3d673d2d4c 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_domain.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c
@@ -22,7 +22,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -74,7 +74,7 @@ typedef struct smb_domain_cache {
cond_t dc_cv;
uint32_t dc_state;
uint32_t dc_nops;
- char dc_server[MAXHOSTNAMELEN];
+ smb_dcinfo_t dc_dci;
} smb_domain_cache_t;
static smb_domain_cache_t smb_dcache;
@@ -90,8 +90,8 @@ static uint32_t smb_dcache_lock(int);
static void smb_dcache_unlock(void);
static void smb_dcache_remove(smb_domain_t *);
static uint32_t smb_dcache_add(smb_domain_t *);
-static void smb_dcache_getdc(char *, size_t);
-static void smb_dcache_setdc(char *);
+static boolean_t smb_dcache_getdc(smb_dcinfo_t *);
+static void smb_dcache_setdc(const smb_dcinfo_t *);
static boolean_t smb_dcache_wait(void);
static uint32_t smb_dcache_updating(void);
static void smb_dcache_ready(void);
@@ -293,13 +293,14 @@ smb_domain_lookup_type(smb_domain_type_t type, smb_domain_t *di)
boolean_t
smb_domain_getinfo(smb_domainex_t *dxi)
{
- boolean_t success;
+ boolean_t rv;
- success = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary);
- if (success)
- smb_dcache_getdc(dxi->d_dc, sizeof (dxi->d_dc));
+ /* Note: this waits for the dcache lock. */
+ rv = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary);
+ if (rv)
+ rv = smb_dcache_getdc(&dxi->d_dci);
- return (success);
+ return (rv);
}
/*
@@ -307,9 +308,9 @@ smb_domain_getinfo(smb_domainex_t *dxi)
* Does NOT block.
*/
void
-smb_domain_current_dc(char *buf, size_t len)
+smb_domain_current_dc(smb_dcinfo_t *dci)
{
- smb_dcache_getdc(buf, len);
+ (void) smb_dcache_getdc(dci);
}
/*
@@ -364,7 +365,7 @@ smb_domain_update(smb_domainex_t *dxi)
for (i = 0; i < dxi->d_trusted.td_num; i++)
(void) smb_dcache_add(&dxi->d_trusted.td_domains[i]);
- smb_dcache_setdc(dxi->d_dc);
+ smb_dcache_setdc(&dxi->d_dci);
}
smb_dcache_unlock();
@@ -594,7 +595,7 @@ smb_dcache_create(void)
offsetof(smb_domain_t, di_lnd));
smb_dcache.dc_nops = 0;
- *smb_dcache.dc_server = '\0';
+ bzero(&smb_dcache.dc_dci, sizeof (smb_dcache.dc_dci));
smb_dcache.dc_state = SMB_DCACHE_STATE_READY;
(void) mutex_unlock(&smb_dcache.dc_mtx);
}
@@ -727,19 +728,23 @@ smb_dcache_remove(smb_domain_t *di)
}
static void
-smb_dcache_setdc(char *dc)
+smb_dcache_setdc(const smb_dcinfo_t *dci)
{
(void) mutex_lock(&smb_dcache.dc_mtx);
- (void) strlcpy(smb_dcache.dc_server, dc, sizeof (smb_dcache.dc_server));
+ smb_dcache.dc_dci = *dci; /* struct assignment! */
(void) mutex_unlock(&smb_dcache.dc_mtx);
}
-static void
-smb_dcache_getdc(char *buf, size_t buflen)
+/*
+ * Return B_TRUE if we have DC information.
+ */
+static boolean_t
+smb_dcache_getdc(smb_dcinfo_t *dci)
{
(void) mutex_lock(&smb_dcache.dc_mtx);
- (void) strlcpy(buf, smb_dcache.dc_server, buflen);
+ *dci = smb_dcache.dc_dci; /* struct assignment! */
(void) mutex_unlock(&smb_dcache.dc_mtx);
+ return (dci->dc_name[0] != '\0');
}
/*
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c b/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c
index 97b4bef60e..e51f8bbca1 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <assert.h>
@@ -112,27 +112,24 @@ smb_lookup_name(const char *name, sid_type_t sidtype, lsa_account_t *acct)
return (rc);
}
-uint32_t
-smb_join(smb_joininfo_t *jdi)
+int
+smb_join(smb_joininfo_t *jdi, smb_joinres_t *jres)
{
- uint32_t status;
int rc;
- if (jdi == NULL)
- return (NT_STATUS_INVALID_PARAMETER);
-
rc = smb_door_call(SMB_DR_JOIN, jdi, smb_joininfo_xdr,
- &status, xdr_uint32_t);
+ jres, smb_joinres_xdr);
if (rc != 0) {
/*
* This usually means the SMB service is not running.
*/
syslog(LOG_DEBUG, "smb_join: %m");
- status = NT_STATUS_SERVER_DISABLED;
+ jres->status = NT_STATUS_SERVER_DISABLED;
+ return (rc);
}
- return (status);
+ return (0);
}
/*
@@ -198,6 +195,23 @@ smb_joininfo_xdr(XDR *xdrs, smb_joininfo_t *objp)
return (TRUE);
}
+bool_t
+smb_joinres_xdr(XDR *xdrs, smb_joinres_t *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->status))
+ return (FALSE);
+
+ if (!xdr_int(xdrs, &objp->join_err))
+ return (FALSE);
+
+ if (!xdr_vector(xdrs, (char *)objp->dc_name, MAXHOSTNAMELEN,
+ sizeof (char), (xdrproc_t)xdr_char))
+ return (FALSE);
+
+ return (TRUE);
+}
+
/*
* Parameters:
* fqdn (input) - fully-qualified domain name
@@ -249,6 +263,19 @@ smb_find_ads_server(char *fqdn, char *buf, int buflen)
return (found);
}
+void
+smb_notify_dc_changed(void)
+{
+ int rc;
+
+ rc = smb_door_call(SMB_DR_NOTIFY_DC_CHANGED,
+ NULL, NULL, NULL, NULL);
+
+ if (rc != 0)
+ syslog(LOG_DEBUG, "smb_notify_dc_changed: %m");
+}
+
+
/*
* After a successful door call the local door_arg->data_ptr is assigned
* to the caller's arg->rbuf so that arg has references to both input and
diff --git a/usr/src/lib/smbsrv/libsmbns/Makefile.com b/usr/src/lib/smbsrv/libsmbns/Makefile.com
index 31d30cb358..e0dd1beec7 100644
--- a/usr/src/lib/smbsrv/libsmbns/Makefile.com
+++ b/usr/src/lib/smbsrv/libsmbns/Makefile.com
@@ -49,7 +49,8 @@ SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
$(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c)
LDLIBS += $(MACH_LDLIBS)
-LDLIBS += -lsmb -lgss -lcmdutils -lldap -lresolv -lnsl -lsocket -lc
+LDLIBS += -lsmb -lads -lgss -lcmdutils -lldap \
+ -lsocket -lnsl -lc
CPPFLAGS += -D_REENTRANT
CPPFLAGS += -Dsyslog=smb_syslog
CERRWARN += -_gcc=-Wno-unused-function
diff --git a/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h b/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h
index 95d16b104e..11396695d2 100644
--- a/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h
+++ b/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _LIBSMBNS_H
@@ -54,11 +54,24 @@ typedef struct smb_ads_host_info {
} smb_ads_host_info_t;
/*
- * The possible return status of the adjoin routine.
+ * Return status codes for the ads functions.
*/
-typedef enum smb_adjoin_status {
- SMB_ADJOIN_SUCCESS = 0,
- SMB_ADJOIN_ERR_GET_HANDLE,
+typedef enum smb_ads_status {
+ SMB_ADS_SUCCESS = 0,
+ /* errno values... */
+ SMB_ADS_ERRNO_GAP = 200,
+ SMB_ADS_KRB5_INIT_CTX,
+ SMB_ADS_KRB5_CC_DEFAULT,
+ SMB_ADS_KRB5_PARSE_PRINCIPAL,
+ SMB_ADS_KRB5_GET_INIT_CREDS_PW,
+ SMB_ADS_KRB5_CC_INITIALIZE,
+ SMB_ADS_KRB5_CC_STORE_CRED,
+ SMB_ADS_CANT_LOCATE_DC,
+ SMB_ADS_LDAP_INIT,
+ SMB_ADS_LDAP_SETOPT,
+ SMB_ADS_LDAP_SET_DOM,
+ SMB_ADS_LDAP_SASL_BIND,
+
SMB_ADJOIN_ERR_GEN_PWD,
SMB_ADJOIN_ERR_GET_DCLEVEL,
SMB_ADJOIN_ERR_ADD_TRUST_ACCT,
@@ -72,13 +85,15 @@ typedef enum smb_adjoin_status {
SMB_ADJOIN_ERR_WRITE_KEYTAB,
SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN,
SMB_ADJOIN_ERR_IDMAP_REFRESH,
- SMB_ADJOIN_ERR_COMMIT_KEYTAB
-} smb_adjoin_status_t;
+ SMB_ADJOIN_ERR_COMMIT_KEYTAB,
+ SMB_ADJOIN_ERR_AUTH_NETLOGON,
+ SMB_ADJOIN_ERR_STORE_PROPS,
+} smb_ads_status_t;
/* ADS functions */
extern void smb_ads_init(void);
extern void smb_ads_fini(void);
-extern void smb_ads_refresh(void);
+extern void smb_ads_refresh(boolean_t);
extern smb_ads_handle_t *smb_ads_open(void);
extern void smb_ads_close(smb_ads_handle_t *);
extern int smb_ads_publish_share(smb_ads_handle_t *, const char *, const char *,
@@ -90,10 +105,11 @@ extern int smb_ads_lookup_share(smb_ads_handle_t *, const char *, const char *,
char *);
extern int smb_ads_add_share(smb_ads_handle_t *, const char *, const char *,
const char *);
-extern smb_adjoin_status_t smb_ads_join(char *, char *, char *, char *);
-extern void smb_ads_join_errmsg(smb_adjoin_status_t);
-extern boolean_t smb_ads_lookup_msdcs(char *, char *, char *, uint32_t);
-extern smb_ads_host_info_t *smb_ads_find_host(char *, char *);
+extern smb_ads_status_t smb_ads_join(char *, char *, char *, char *);
+extern void smb_ads_log_errmsg(smb_ads_status_t);
+extern const char *smb_ads_strerror(int);
+extern uint32_t smb_ads_lookup_msdcs(char *, smb_dcinfo_t *);
+extern smb_ads_host_info_t *smb_ads_find_host(char *);
/* DYNDNS functions */
extern void *dyndns_publisher(void *);
diff --git a/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers b/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers
index 48af7b02ea..9b9ae24e54 100644
--- a/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers
+++ b/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
#
@@ -53,13 +54,14 @@ SYMBOL_VERSION SUNWprivate {
smb_ads_fini;
smb_ads_init;
smb_ads_join;
- smb_ads_join_errmsg;
+ smb_ads_log_errmsg;
smb_ads_lookup_msdcs;
smb_ads_lookup_share;
smb_ads_open;
smb_ads_publish_share;
smb_ads_refresh;
smb_ads_remove_share;
+ smb_ads_strerror;
smb_browser_netlogon;
smb_browser_reconfig;
smb_ccache_init;
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c
index c88ab69cec..4c574786d5 100644
--- a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/param.h>
@@ -47,6 +47,7 @@
#include <note.h>
#include <errno.h>
#include <cryptoutil.h>
+#include <ads/dsgetdc.h>
#include <smbsrv/libsmbns.h>
#include <smbns_dyndns.h>
@@ -169,7 +170,6 @@ static mutex_t smb_ads_cached_host_mtx;
*/
typedef struct smb_ads_config {
char c_site[SMB_ADS_SITE_MAX];
- smb_inaddr_t c_pdc;
mutex_t c_mtx;
} smb_ads_config_t;
@@ -195,7 +195,7 @@ typedef struct smb_ads_host_list {
smb_ads_host_info_t *ah_list;
} smb_ads_host_list_t;
-static smb_ads_handle_t *smb_ads_open_main(char *, char *, char *);
+static int smb_ads_open_main(smb_ads_handle_t **, char *, char *, char *);
static int smb_ads_add_computer(smb_ads_handle_t *, int, char *);
static int smb_ads_modify_computer(smb_ads_handle_t *, int, char *);
static int smb_ads_computer_op(smb_ads_handle_t *, int, int, char *);
@@ -207,16 +207,12 @@ static void smb_ads_free_cached_host(void);
static int smb_ads_alloc_attr(LDAPMod **, int);
static void smb_ads_free_attr(LDAPMod **);
static int smb_ads_get_dc_level(smb_ads_handle_t *);
-static smb_ads_host_info_t *smb_ads_select_dc(smb_ads_host_list_t *);
static smb_ads_qstat_t smb_ads_find_computer(smb_ads_handle_t *, char *);
static smb_ads_qstat_t smb_ads_getattr(LDAP *, LDAPMessage *,
smb_ads_avpair_t *);
static smb_ads_qstat_t smb_ads_get_qstat(smb_ads_handle_t *, LDAPMessage *,
smb_ads_avpair_t *);
-static boolean_t smb_ads_match_pdc(smb_ads_host_info_t *);
-static boolean_t smb_ads_is_sought_host(smb_ads_host_info_t *, char *);
static boolean_t smb_ads_is_same_domain(char *, char *);
-static boolean_t smb_ads_is_pdc_configured(void);
static smb_ads_host_info_t *smb_ads_dup_host_info(smb_ads_host_info_t *);
static char *smb_ads_get_sharedn(const char *, const char *, const char *);
static krb5_enctype *smb_ads_get_enctypes(int, int *);
@@ -232,8 +228,10 @@ smb_ads_init(void)
(void) mutex_lock(&smb_ads_cfg.c_mtx);
(void) smb_config_getstr(SMB_CI_ADS_SITE,
smb_ads_cfg.c_site, SMB_ADS_SITE_MAX);
- (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &smb_ads_cfg.c_pdc);
(void) mutex_unlock(&smb_ads_cfg.c_mtx);
+
+ /* Force -lads to load, for dtrace. */
+ DsFreeDcInfo(NULL);
}
void
@@ -246,52 +244,29 @@ smb_ads_fini(void)
* smb_ads_refresh
*
* This function will be called when smb/server SMF service is refreshed.
+ * (See smbd_join.c)
+ *
* Clearing the smb_ads_cached_host_info would allow the next DC
* discovery process to pick up an AD based on the new AD configuration.
*/
void
-smb_ads_refresh(void)
+smb_ads_refresh(boolean_t force_rediscovery)
{
char new_site[SMB_ADS_SITE_MAX];
- smb_inaddr_t new_pdc;
- boolean_t purge = B_FALSE;
(void) smb_config_getstr(SMB_CI_ADS_SITE, new_site, SMB_ADS_SITE_MAX);
- (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &new_pdc);
(void) mutex_lock(&smb_ads_cfg.c_mtx);
- if (smb_strcasecmp(smb_ads_cfg.c_site, new_site, 0)) {
- (void) strlcpy(smb_ads_cfg.c_site, new_site, SMB_ADS_SITE_MAX);
- purge = B_TRUE;
- }
-
- smb_ads_cfg.c_pdc = new_pdc;
+ (void) strlcpy(smb_ads_cfg.c_site, new_site, SMB_ADS_SITE_MAX);
(void) mutex_unlock(&smb_ads_cfg.c_mtx);
- (void) mutex_lock(&smb_ads_cached_host_mtx);
- if (smb_ads_cached_host_info &&
- smb_ads_is_pdc_configured() &&
- !smb_ads_match_pdc(smb_ads_cached_host_info))
- purge = B_TRUE;
- (void) mutex_unlock(&smb_ads_cached_host_mtx);
+ smb_ads_free_cached_host();
- if (purge)
- smb_ads_free_cached_host();
+ if (force_rediscovery) {
+ (void) _DsForceRediscovery(NULL, 0);
+ }
}
-
-static boolean_t
-smb_ads_is_pdc_configured(void)
-{
- boolean_t configured;
-
- (void) mutex_lock(&smb_ads_cfg.c_mtx);
- configured = !smb_inet_iszero(&smb_ads_cfg.c_pdc);
- (void) mutex_unlock(&smb_ads_cfg.c_mtx);
-
- return (configured);
-}
-
/*
* smb_ads_build_unc_name
*
@@ -315,44 +290,6 @@ smb_ads_build_unc_name(char *unc_name, int maxlen,
}
/*
- * smb_ads_ldap_ping
- *
- * This is used to bind to an ADS server to see
- * if it is still alive.
- *
- * Returns:
- * -1: error
- * 0: successful
- */
-/*ARGSUSED*/
-static int
-smb_ads_ldap_ping(smb_ads_host_info_t *ads_host)
-{
- int ldversion = LDAP_VERSION3, status, timeoutms = 5 * 1000;
- LDAP *ld = NULL;
-
- ld = ldap_init(ads_host->name, ads_host->port);
- if (ld == NULL)
- return (-1);
-
- ldversion = LDAP_VERSION3;
- (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
- /* setup TCP/IP connect timeout */
- (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
-
- status = ldap_bind_s(ld, "", NULL, LDAP_AUTH_SIMPLE);
-
- if (status != LDAP_SUCCESS) {
- (void) ldap_unbind(ld);
- return (-1);
- }
-
- (void) ldap_unbind(ld);
-
- return (0);
-}
-
-/*
* The cached ADS host is no longer valid if one of the following criteria
* is satisfied:
*
@@ -366,7 +303,7 @@ smb_ads_ldap_ping(smb_ads_host_info_t *ads_host)
* Return B_TRUE if the cache host is still valid. Otherwise, return B_FALSE.
*/
static boolean_t
-smb_ads_validate_cache_host(char *domain, char *srv)
+smb_ads_validate_cache_host(char *domain)
{
if (!smb_ads_cached_host_info)
return (B_FALSE);
@@ -374,33 +311,6 @@ smb_ads_validate_cache_host(char *domain, char *srv)
if (!smb_ads_is_same_domain(smb_ads_cached_host_info->name, domain))
return (B_FALSE);
- if (smb_ads_ldap_ping(smb_ads_cached_host_info) == 0) {
- if (!srv)
- return (B_TRUE);
-
- if (smb_ads_is_sought_host(smb_ads_cached_host_info, srv))
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-/*
- * smb_ads_is_sought_host
- *
- * Returns true, if the sought host name matches the input host (host) name.
- * The sought host is expected to be in Fully Qualified Domain Name (FQDN)
- * format.
- */
-static boolean_t
-smb_ads_is_sought_host(smb_ads_host_info_t *host, char *sought_host_name)
-{
- if ((host == NULL) || (sought_host_name == NULL))
- return (B_FALSE);
-
- if (smb_strcasecmp(host->name, sought_host_name, 0))
- return (B_FALSE);
-
return (B_TRUE);
}
@@ -430,163 +340,6 @@ smb_ads_is_same_domain(char *cached_host_name, char *current_domain)
}
/*
- * smb_ads_skip_ques_sec
- * Skips the question section.
- */
-static int
-smb_ads_skip_ques_sec(int qcnt, uchar_t **ptr, uchar_t *eom)
-{
- int i, len;
-
- for (i = 0; i < qcnt; i++) {
- if ((len = dn_skipname(*ptr, eom)) < 0)
- return (-1);
-
- *ptr += len + QFIXEDSZ;
- }
-
- return (0);
-}
-
-/*
- * smb_ads_decode_host_ans_sec
- * Decodes ADS hosts, priority, weight and port number from the answer
- * section based on the current buffer pointer.
- */
-static int
-smb_ads_decode_host_ans_sec(int ans_cnt, uchar_t **ptr, uchar_t *eom,
- uchar_t *buf, smb_ads_host_info_t *ads_host_list)
-{
- int i, len;
- smb_ads_host_info_t *ads_host;
-
- for (i = 0; i < ans_cnt; i++) {
- ads_host = &ads_host_list[i];
-
- if ((len = dn_skipname(*ptr, eom)) < 0)
- return (-1);
-
-
- *ptr += len;
-
- /* skip type, class, ttl */
- *ptr += 8;
- /* data size */
- *ptr += 2;
-
- /* Get priority, weight */
- /* LINTED: E_CONSTANT_CONDITION */
- NS_GET16(ads_host->priority, *ptr);
- /* LINTED: E_CONSTANT_CONDITION */
- NS_GET16(ads_host->weight, *ptr);
-
- /* port */
- /* LINTED: E_CONSTANT_CONDITION */
- NS_GET16(ads_host->port, *ptr);
- /* domain name */
- len = dn_expand(buf, eom, *ptr, ads_host->name, MAXHOSTNAMELEN);
- if (len < 0)
- return (-1);
-
- *ptr += len;
- }
-
- return (0);
-}
-
-/*
- * smb_ads_skip_auth_sec
- * Skips the authority section.
- */
-static int
-smb_ads_skip_auth_sec(int ns_cnt, uchar_t **ptr, uchar_t *eom)
-{
- int i, len;
- uint16_t size;
-
- for (i = 0; i < ns_cnt; i++) {
- if ((len = dn_skipname(*ptr, eom)) < 0)
- return (-1);
-
- *ptr += len;
- /* skip type, class, ttl */
- *ptr += 8;
- /* get len of data */
- /* LINTED: E_CONSTANT_CONDITION */
- NS_GET16(size, *ptr);
- if ((*ptr + size) > eom)
- return (-1);
-
- *ptr += size;
- }
-
- return (0);
-}
-
-/*
- * smb_ads_decode_host_ip
- *
- * Decodes ADS hosts and IP Addresses from the additional section based
- * on the current buffer pointer.
- */
-static int
-smb_ads_decode_host_ip(int addit_cnt, int ans_cnt, uchar_t **ptr,
- uchar_t *eom, uchar_t *buf, smb_ads_host_info_t *ads_host_list)
-{
- int i, j, len;
- smb_inaddr_t ipaddr;
- char hostname[MAXHOSTNAMELEN];
- char *name;
- uint16_t size = 0;
-
- for (i = 0; i < addit_cnt; i++) {
-
- /* domain name */
- len = dn_expand(buf, eom, *ptr, hostname, MAXHOSTNAMELEN);
- if (len < 0)
- return (-1);
-
- *ptr += len;
-
- /* skip type, class, TTL, data len */
- *ptr += 8;
- /* LINTED: E_CONSTANT_CONDITION */
- NS_GET16(size, *ptr);
-
- if (size == NS_INADDRSZ) {
- /* LINTED: E_CONSTANT_CONDITION */
- NS_GET32(ipaddr.a_ipv4, *ptr);
- ipaddr.a_ipv4 = htonl(ipaddr.a_ipv4);
- ipaddr.a_family = AF_INET;
- } else if (size == NS_IN6ADDRSZ) {
-#ifdef BIG_ENDIAN
- bcopy(*ptr, &ipaddr.a_ipv6, NS_IN6ADDRSZ);
-#else
- for (i = 0; i < NS_IN6ADDRSZ; i++)
- (uint8_t *)(ipaddr.a_ipv6)
- [NS_IN6ADDRSZ-1-i] = *(*ptr+i);
-#endif
- ipaddr.a_family = AF_INET6;
- *ptr += size;
- }
-
- /*
- * find the host in the list of DC records from
- * the answer section, that matches the host in the
- * additional section, and set its IP address.
- */
- for (j = 0; j < ans_cnt; j++) {
- if ((name = ads_host_list[j].name) == NULL)
- continue;
- if (smb_strcasecmp(name, hostname, 0) == 0) {
- ads_host_list[j].ipaddr = ipaddr;
- }
- }
- }
- return (0);
-}
-
-/*
* smb_ads_dup_host_info
*
* Duplicates the passed smb_ads_host_info_t structure.
@@ -612,240 +365,6 @@ smb_ads_dup_host_info(smb_ads_host_info_t *ads_host)
}
/*
- * smb_ads_hlist_alloc
- */
-static smb_ads_host_list_t *
-smb_ads_hlist_alloc(int count)
-{
- int size;
- smb_ads_host_list_t *hlist;
-
- if (count == 0)
- return (NULL);
-
- size = sizeof (smb_ads_host_info_t) * count;
- hlist = (smb_ads_host_list_t *)malloc(sizeof (smb_ads_host_list_t));
- if (hlist == NULL)
- return (NULL);
-
- hlist->ah_cnt = count;
- hlist->ah_list = (smb_ads_host_info_t *)malloc(size);
- if (hlist->ah_list == NULL) {
- free(hlist);
- return (NULL);
- }
-
- bzero(hlist->ah_list, size);
- return (hlist);
-}
-
-/*
- * smb_ads_hlist_free
- */
-static void
-smb_ads_hlist_free(smb_ads_host_list_t *host_list)
-{
- if (host_list == NULL)
- return;
-
- free(host_list->ah_list);
- free(host_list);
-}
-
-/*
- * smb_ads_query_dns_server
- *
- * This routine sends a DNS service location (SRV) query message to the
- * DNS server via TCP to query it for a list of ADS server(s). Once a reply
- * is received, the reply message is parsed to get the hostname. If there are IP
- * addresses populated in the additional section then the additional section
- * is parsed to obtain the IP addresses.
- *
- * 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.
- *
- * The DNS reply message may be in compress formed. The compression is done
- * on repeating domain name label in the message. i.e hostname.
- *
- * Upon successful completion, host list of ADS server(s) is returned.
- */
-static smb_ads_host_list_t *
-smb_ads_query_dns_server(char *domain, char *msdcs_svc_name)
-{
- smb_ads_host_list_t *hlist = NULL;
- int len, qcnt, ans_cnt, ns_cnt, addit_cnt;
- uchar_t *ptr, *eom;
- struct __res_state res_state;
- union {
- HEADER hdr;
- uchar_t buf[NS_MAXMSG];
- } msg;
-
- bzero(&res_state, sizeof (struct __res_state));
- if (res_ninit(&res_state) < 0)
- return (NULL);
-
- /* use TCP */
- res_state.options |= RES_USEVC;
-
- len = res_nquerydomain(&res_state, msdcs_svc_name, domain,
- C_IN, T_SRV, msg.buf, sizeof (msg.buf));
-
- if (len < 0) {
- syslog(LOG_NOTICE, "DNS query for %s failed: %s",
- msdcs_svc_name, hstrerror(res_state.res_h_errno));
- res_ndestroy(&res_state);
- return (NULL);
- }
-
- if (len > sizeof (msg.buf)) {
- syslog(LOG_NOTICE,
- "DNS query for %s failed: too big", msdcs_svc_name);
- res_ndestroy(&res_state);
- return (NULL);
- }
-
- /* parse the reply, skip header and question sections */
- ptr = msg.buf + sizeof (msg.hdr);
- eom = msg.buf + len;
-
- /* check truncated message bit */
- if (msg.hdr.tc)
- syslog(LOG_NOTICE,
- "DNS query for %s failed: truncated", msdcs_svc_name);
-
- qcnt = ntohs(msg.hdr.qdcount);
- ans_cnt = ntohs(msg.hdr.ancount);
- ns_cnt = ntohs(msg.hdr.nscount);
- addit_cnt = ntohs(msg.hdr.arcount);
-
- if (smb_ads_skip_ques_sec(qcnt, &ptr, eom) != 0) {
- res_ndestroy(&res_state);
- return (NULL);
- }
-
- hlist = smb_ads_hlist_alloc(ans_cnt);
- if (hlist == NULL) {
- res_ndestroy(&res_state);
- return (NULL);
- }
-
- /* walk through the answer section */
- if (smb_ads_decode_host_ans_sec(ans_cnt, &ptr, eom, msg.buf,
- hlist->ah_list) != 0) {
- smb_ads_hlist_free(hlist);
- res_ndestroy(&res_state);
- return (NULL);
- }
-
- /* check authority section */
- if (ns_cnt > 0) {
- if (smb_ads_skip_auth_sec(ns_cnt, &ptr, eom) != 0) {
- smb_ads_hlist_free(hlist);
- res_ndestroy(&res_state);
- return (NULL);
- }
- }
-
- /*
- * Check additional section to get IP address of ADS host.
- */
- if (addit_cnt > 0) {
- if (smb_ads_decode_host_ip(addit_cnt, ans_cnt,
- &ptr, eom, msg.buf, hlist->ah_list) != 0) {
- smb_ads_hlist_free(hlist);
- res_ndestroy(&res_state);
- return (NULL);
- }
- }
-
- res_ndestroy(&res_state);
- return (hlist);
-}
-
-/*
- * smb_ads_get_site_service
- *
- * Gets the msdcs SRV RR for the specified site.
- */
-static void
-smb_ads_get_site_service(char *site_service, size_t len)
-{
- (void) mutex_lock(&smb_ads_cfg.c_mtx);
- if (*smb_ads_cfg.c_site == '\0')
- *site_service = '\0';
- else
- (void) snprintf(site_service, len,
- SMB_ADS_MSDCS_SRV_SITE_RR, smb_ads_cfg.c_site);
-
- (void) mutex_unlock(&smb_ads_cfg.c_mtx);
-}
-
-/*
- * smb_ads_getipnodebyname
- *
- * This method gets the IP address by doing a host name lookup.
- */
-static int
-smb_ads_getipnodebyname(smb_ads_host_info_t *hentry)
-{
- struct hostent *h;
- int error;
-
- switch (hentry->ipaddr.a_family) {
- case AF_INET6:
- h = getipnodebyname(hentry->name, hentry->ipaddr.a_family,
- AI_DEFAULT, &error);
- if (h == NULL || h->h_length != NS_IN6ADDRSZ)
- return (-1);
- break;
-
- case AF_INET:
- h = getipnodebyname(hentry->name, hentry->ipaddr.a_family,
- 0, &error);
- if (h == NULL || h->h_length != NS_INADDRSZ)
- return (-1);
- break;
-
- default:
- return (-1);
- }
- bcopy(*(h->h_addr_list), &hentry->ipaddr.a_ip, h->h_length);
- freehostent(h);
- return (0);
-}
-
-/*
- * Checks the IP address to see if it is zero. If so, then do a host
- * lookup by hostname to get the IP address based on the IP family.
- *
- * If the family is unknown then do a lookup by hostame based on the
- * setting of the SMB_CI_IPV6_ENABLE property.
- */
-static int
-smb_ads_set_ipaddr(smb_ads_host_info_t *hentry)
-{
- if (smb_inet_iszero(&hentry->ipaddr)) {
- if (smb_ads_getipnodebyname(hentry) < 0)
- return (-1);
- } else if (SMB_ADS_AF_UNKNOWN(hentry)) {
- hentry->ipaddr.a_family =
- smb_config_getbool(SMB_CI_IPV6_ENABLE) ? AF_INET6 : AF_INET;
-
- if (smb_ads_getipnodebyname(hentry) < 0) {
- hentry->ipaddr.a_family = 0;
- return (-1);
- }
- }
-
- return (0);
-}
-
-/*
* smb_ads_find_host
*
* Finds an ADS host in a given domain.
@@ -867,7 +386,6 @@ smb_ads_set_ipaddr(smb_ads_host_info_t *hentry)
*
* Parameters:
* domain: fully-qualified domain name.
- * kpasswd_srv: fully-quailifed hostname of the kpasswd server.
*
* Returns:
* A copy of the cached host info is returned. The caller is responsible
@@ -875,20 +393,17 @@ smb_ads_set_ipaddr(smb_ads_host_info_t *hentry)
*/
/*ARGSUSED*/
smb_ads_host_info_t *
-smb_ads_find_host(char *domain, char *kpasswd_srv)
+smb_ads_find_host(char *domain)
{
- int i;
- char site_service[MAXHOSTNAMELEN];
- smb_ads_host_list_t *hlist, *hlist2;
- smb_ads_host_info_t *hlistp = NULL, *host = NULL;
- smb_ads_host_info_t *found_kpasswd_srv = NULL;
- smb_ads_host_info_t *found_pdc = NULL;
-
- if ((kpasswd_srv) && (*kpasswd_srv == '\0'))
- kpasswd_srv = NULL;
+ smb_ads_host_info_t *host = NULL;
+ DOMAIN_CONTROLLER_INFO *dci = NULL;
+ struct sockaddr_storage *ss;
+ uint32_t flags = DS_DS_FLAG;
+ uint32_t status;
+ int tries;
(void) mutex_lock(&smb_ads_cached_host_mtx);
- if (smb_ads_validate_cache_host(domain, kpasswd_srv)) {
+ if (smb_ads_validate_cache_host(domain)) {
host = smb_ads_dup_host_info(smb_ads_cached_host_info);
(void) mutex_unlock(&smb_ads_cached_host_mtx);
return (host);
@@ -898,79 +413,77 @@ smb_ads_find_host(char *domain, char *kpasswd_srv)
smb_ads_free_cached_host();
/*
- * First look for ADS hosts in ADS site if configured. Then try
- * without ADS site info.
+ * The _real_ DC Locator is over in idmapd.
+ * Door call over there to get it.
*/
- hlist = NULL;
- smb_ads_get_site_service(site_service, MAXHOSTNAMELEN);
-
+ tries = 15;
+again:
+ status = _DsGetDcName(
+ NULL, /* ComputerName */
+ domain,
+ NULL, /* DomainGuid */
+ NULL, /* SiteName */
+ flags,
+ &dci);
+ switch (status) {
+ case 0:
+ break;
/*
- * If we're given an AD, the DNS SRV RR lookup should not be restricted
- * to the specified site since there is no guarantee that the specified
- * AD is in the specified site.
+ * We can see these errors when joining a domain, if we race
+ * asking idmap for the DC before it knows the new domain.
*/
- if (*site_service != '\0' && !kpasswd_srv &&
- !smb_ads_is_pdc_configured())
- hlist = smb_ads_query_dns_server(domain, site_service);
-
- if (!hlist)
- hlist = smb_ads_query_dns_server(domain,
- SMB_ADS_MSDCS_SRV_DC_RR);
-
- if ((hlist == NULL) || (hlist->ah_list == NULL) || (hlist->ah_cnt == 0))
+ case NT_STATUS_NO_SUCH_DOMAIN: /* Specified domain unknown */
+ case NT_STATUS_INVALID_SERVER_STATE: /* not in domain mode. */
+ if (--tries > 0) {
+ (void) sleep(1);
+ goto again;
+ }
+ /* FALLTHROUGH */
+ case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
+ case NT_STATUS_CANT_WAIT: /* timeout over in idmap */
+ default:
return (NULL);
-
- for (i = 0, hlistp = hlist->ah_list; i < hlist->ah_cnt; i++) {
- if (smb_ads_set_ipaddr(&hlistp[i]) < 0)
- continue;
-
- if (smb_ads_is_sought_host(&hlistp[i], kpasswd_srv))
- found_kpasswd_srv = &hlistp[i];
-
- if (smb_ads_match_pdc(&hlistp[i]))
- found_pdc = &hlistp[i];
}
- if (found_kpasswd_srv && smb_ads_ldap_ping(found_kpasswd_srv) == 0) {
- host = found_kpasswd_srv;
- goto update_cache;
- }
+ host = calloc(1, sizeof (*host));
+ if (host == NULL)
+ goto out;
- if (found_pdc && smb_ads_ldap_ping(found_pdc) == 0) {
- host = found_pdc;
- goto update_cache;
+ (void) strlcpy(host->name, dci->DomainControllerName, MAXHOSTNAMELEN);
+ ss = (void *)dci->_sockaddr;
+ switch (ss->ss_family) {
+ case AF_INET: {
+ struct sockaddr_in *sin = (void *)ss;
+ host->port = ntohs(sin->sin_port);
+ host->ipaddr.a_family = AF_INET;
+ (void) memcpy(&host->ipaddr.a_ipv4, &sin->sin_addr,
+ sizeof (in_addr_t));
+ break;
}
-
- /*
- * If the specified DC (kpasswd_srv or pdc) is not found, fallback
- * to find a DC in the specified AD site.
- */
- if (*site_service != '\0' &&
- (kpasswd_srv || smb_ads_is_pdc_configured())) {
- hlist2 = smb_ads_query_dns_server(domain, site_service);
- if (hlist2 && hlist2->ah_list && hlist2->ah_cnt != 0) {
- smb_ads_hlist_free(hlist);
- hlist = hlist2;
- hlistp = hlist->ah_list;
-
- for (i = 0; i < hlist->ah_cnt; i++)
- (void) smb_ads_set_ipaddr(&hlistp[i]);
- }
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (void *)ss;
+ host->port = ntohs(sin6->sin6_port);
+ host->ipaddr.a_family = AF_INET6;
+ (void) memcpy(&host->ipaddr.a_ipv6, &sin6->sin6_addr,
+ sizeof (in6_addr_t));
+ break;
}
-
- /* Select DC from DC list */
- host = smb_ads_select_dc(hlist);
-
-update_cache:
- if (host) {
- (void) mutex_lock(&smb_ads_cached_host_mtx);
- if (!smb_ads_cached_host_info)
- smb_ads_cached_host_info = smb_ads_dup_host_info(host);
- host = smb_ads_dup_host_info(smb_ads_cached_host_info);
- (void) mutex_unlock(&smb_ads_cached_host_mtx);
+ default:
+ syslog(LOG_ERR, "no addr for DC %s",
+ dci->DomainControllerName);
+ free(host);
+ host = NULL;
+ goto out;
}
- smb_ads_hlist_free(hlist);
+ (void) mutex_lock(&smb_ads_cached_host_mtx);
+ if (!smb_ads_cached_host_info)
+ smb_ads_cached_host_info = smb_ads_dup_host_info(host);
+ host = smb_ads_dup_host_info(smb_ads_cached_host_info);
+ (void) mutex_unlock(&smb_ads_cached_host_mtx);
+
+out:
+ DsFreeDcInfo(dci);
return (host);
}
@@ -1063,6 +576,8 @@ smb_ads_handle_t *
smb_ads_open(void)
{
char domain[MAXHOSTNAMELEN];
+ smb_ads_handle_t *h;
+ smb_ads_status_t err;
if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
return (NULL);
@@ -1070,7 +585,13 @@ smb_ads_open(void)
if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0)
return (NULL);
- return (smb_ads_open_main(domain, NULL, NULL));
+ err = smb_ads_open_main(&h, domain, NULL, NULL);
+ if (err != 0) {
+ smb_ads_log_errmsg(err);
+ return (NULL);
+ }
+
+ return (h);
}
static int
@@ -1116,39 +637,44 @@ smb_ads_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
* NULL : can't connect to ADS server or other errors
* smb_ads_handle_t* : handle to ADS server
*/
-static smb_ads_handle_t *
-smb_ads_open_main(char *domain, char *user, char *password)
+static int
+smb_ads_open_main(smb_ads_handle_t **hp, char *domain, char *user,
+ char *password)
{
smb_ads_handle_t *ah;
LDAP *ld;
int version = 3;
smb_ads_host_info_t *ads_host = NULL;
- int rc;
+ int err, rc;
+
+ *hp = NULL;
if (user != NULL) {
- if (smb_kinit(user, password) == 0)
- return (NULL);
+ err = smb_kinit(domain, user, password);
+ if (err != 0)
+ return (err);
user = NULL;
password = NULL;
}
- ads_host = smb_ads_find_host(domain, NULL);
+ ads_host = smb_ads_find_host(domain);
if (ads_host == NULL)
- return (NULL);
+ return (SMB_ADS_CANT_LOCATE_DC);
ah = (smb_ads_handle_t *)malloc(sizeof (smb_ads_handle_t));
if (ah == NULL) {
free(ads_host);
- return (NULL);
+ return (ENOMEM);
}
(void) memset(ah, 0, sizeof (smb_ads_handle_t));
if ((ld = ldap_init(ads_host->name, ads_host->port)) == NULL) {
+ syslog(LOG_ERR, "smbns: ldap_init failed");
smb_ads_free_cached_host();
free(ah);
free(ads_host);
- return (NULL);
+ return (SMB_ADS_LDAP_INIT);
}
if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version)
@@ -1157,7 +683,7 @@ smb_ads_open_main(char *domain, char *user, char *password)
free(ah);
free(ads_host);
(void) ldap_unbind(ld);
- return (NULL);
+ return (SMB_ADS_LDAP_SETOPT);
}
(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
@@ -1167,7 +693,7 @@ smb_ads_open_main(char *domain, char *user, char *password)
if (ah->domain == NULL) {
smb_ads_close(ah);
free(ads_host);
- return (NULL);
+ return (SMB_ADS_LDAP_SETOPT);
}
/*
@@ -1179,14 +705,14 @@ smb_ads_open_main(char *domain, char *user, char *password)
if (ah->domain_dn == NULL) {
smb_ads_close(ah);
free(ads_host);
- return (NULL);
+ return (SMB_ADS_LDAP_SET_DOM);
}
ah->hostname = strdup(ads_host->name);
if (ah->hostname == NULL) {
smb_ads_close(ah);
free(ads_host);
- return (NULL);
+ return (ENOMEM);
}
(void) mutex_lock(&smb_ads_cfg.c_mtx);
if (*smb_ads_cfg.c_site != '\0') {
@@ -1194,7 +720,7 @@ smb_ads_open_main(char *domain, char *user, char *password)
smb_ads_close(ah);
(void) mutex_unlock(&smb_ads_cfg.c_mtx);
free(ads_host);
- return (NULL);
+ return (ENOMEM);
}
} else {
ah->site = NULL;
@@ -1204,15 +730,17 @@ smb_ads_open_main(char *domain, char *user, char *password)
rc = ldap_sasl_interactive_bind_s(ah->ld, "", "GSSAPI", NULL, NULL,
LDAP_SASL_INTERACTIVE, &smb_ads_saslcallback, NULL);
if (rc != LDAP_SUCCESS) {
- syslog(LOG_ERR, "ldal_sasl_interactive_bind_s failed (%s)",
+ syslog(LOG_ERR, "smbns: ldap_sasl_..._bind_s failed (%s)",
ldap_err2string(rc));
smb_ads_close(ah);
free(ads_host);
- return (NULL);
+ return (SMB_ADS_LDAP_SASL_BIND);
}
free(ads_host);
- return (ah);
+ *hp = ah;
+
+ return (SMB_ADS_SUCCESS);
}
/*
@@ -2163,28 +1691,28 @@ smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn)
* That would be needed while acquiring Kerberos TGT ticket for the host
* principal after the domain join operation.
*/
-smb_adjoin_status_t
+smb_ads_status_t
smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd)
{
smb_ads_handle_t *ah = NULL;
krb5_context ctx = NULL;
krb5_principal *krb5princs = NULL;
krb5_kvno kvno;
- boolean_t des_only, delete = B_TRUE;
- smb_adjoin_status_t rc = SMB_ADJOIN_SUCCESS;
+ boolean_t delete = B_TRUE;
+ smb_ads_status_t rc;
boolean_t new_acct;
int dclevel, num, usrctl_flags = 0;
smb_ads_qstat_t qstat;
char dn[SMB_ADS_DN_MAX];
char tmpfile[] = SMBNS_KRB5_KEYTAB_TMP;
- int cnt;
+ int cnt, x;
smb_krb5_pn_set_t spns;
-
krb5_enctype *encptr;
- if ((ah = smb_ads_open_main(domain, user, usr_passwd)) == NULL) {
+ rc = smb_ads_open_main(&ah, domain, user, usr_passwd);
+ if (rc != 0) {
smb_ccache_remove(SMB_CCACHE_PATH);
- return (SMB_ADJOIN_ERR_GET_HANDLE);
+ return (rc);
}
if ((dclevel = smb_ads_get_dc_level(ah)) == -1) {
@@ -2224,8 +1752,6 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd)
return (rc);
}
- des_only = B_FALSE;
-
if (smb_krb5_ctx_init(&ctx) != 0) {
rc = SMB_ADJOIN_ERR_INIT_KRB_CTX;
goto adjoin_cleanup;
@@ -2257,30 +1783,26 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd)
/*
* Only members of Domain Admins and Enterprise Admins can set
* the TRUSTED_FOR_DELEGATION userAccountControl flag.
+ * Try to set this, but don't fail the join if we can't.
+ * Look into just removing this...
*/
- if (smb_ads_update_computer_cntrl_attr(ah,
+ usrctl_flags = (
SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
- SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION, dn)
- == LDAP_INSUFFICIENT_ACCESS) {
- usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
- SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD);
-
+ SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION |
+ SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD);
+set_ctl_again:
+ x = smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn);
+ if (x != LDAP_SUCCESS && (usrctl_flags &
+ SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION) != 0) {
syslog(LOG_NOTICE, "Unable to set the "
- "TRUSTED_FOR_DELEGATION userAccountControl flag on "
- "the machine account in Active Directory. Please refer "
- "to the Troubleshooting guide for more information.");
-
- } else {
- usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
- SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION |
- SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD);
- }
-
- if (des_only)
- usrctl_flags |= SMB_ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY;
-
- if (smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn)
- != 0) {
+"TRUSTED_FOR_DELEGATION userAccountControl flag on the "
+"machine account in Active Directory. It may be necessary "
+"to set that via Active Directory administration.");
+ usrctl_flags &=
+ ~SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION;
+ goto set_ctl_again;
+ }
+ if (x != LDAP_SUCCESS) {
rc = SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR;
goto adjoin_cleanup;
}
@@ -2298,6 +1820,8 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd)
}
delete = B_FALSE;
+ rc = SMB_ADS_SUCCESS;
+
adjoin_cleanup:
if (new_acct && delete)
smb_ads_del_computer(ah, dn);
@@ -2309,7 +1833,7 @@ adjoin_cleanup:
}
/* commit keytab file */
- if (rc == SMB_ADJOIN_SUCCESS) {
+ if (rc == SMB_ADS_SUCCESS) {
if (rename(tmpfile, SMBNS_KRB5_KEYTAB) != 0) {
(void) unlink(tmpfile);
rc = SMB_ADJOIN_ERR_COMMIT_KEYTAB;
@@ -2323,221 +1847,103 @@ adjoin_cleanup:
return (rc);
}
-/*
- * smb_ads_join_errmsg
- *
- * Display error message for the specific adjoin error code.
- */
-void
-smb_ads_join_errmsg(smb_adjoin_status_t status)
-{
- int i;
- struct xlate_table {
- smb_adjoin_status_t status;
- char *msg;
- } adjoin_table[] = {
- { SMB_ADJOIN_ERR_GET_HANDLE, "Failed to connect to an "
- "Active Directory server." },
- { SMB_ADJOIN_ERR_GEN_PWD, "Failed to generate machine "
- "password." },
- { SMB_ADJOIN_ERR_GET_DCLEVEL, "Unknown functional level of "
- "the domain controller. The rootDSE attribute named "
- "\"domainControllerFunctionality\" is missing from the "
- "Active Directory." },
- { SMB_ADJOIN_ERR_ADD_TRUST_ACCT, "Failed to create the "
- "workstation trust account." },
- { SMB_ADJOIN_ERR_MOD_TRUST_ACCT, "Failed to modify the "
- "workstation trust account." },
- { SMB_ADJOIN_ERR_DUP_TRUST_ACCT, "Failed to create the "
- "workstation trust account because its name is already "
- "in use." },
- { SMB_ADJOIN_ERR_TRUST_ACCT, "Error in querying the "
- "workstation trust account" },
- { SMB_ADJOIN_ERR_INIT_KRB_CTX, "Failed to initialize Kerberos "
- "context." },
- { SMB_ADJOIN_ERR_GET_SPNS, "Failed to get Kerberos "
- "principals." },
- { SMB_ADJOIN_ERR_KSETPWD, "Failed to set machine password." },
- { SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR, "Failed to modify "
- "userAccountControl attribute of the workstation trust "
- "account." },
- { SMB_ADJOIN_ERR_WRITE_KEYTAB, "Error in writing to local "
- "keytab file (i.e /etc/krb5/krb5.keytab)." },
- { SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, "Failed to update idmap "
- "configuration." },
- { SMB_ADJOIN_ERR_IDMAP_REFRESH, "Failed to refresh idmap "
- "service." },
- { SMB_ADJOIN_ERR_COMMIT_KEYTAB, "Failed to commit changes to "
- "local keytab file (i.e. /etc/krb5/krb5.keytab)." }
- };
-
- for (i = 0; i < sizeof (adjoin_table) / sizeof (adjoin_table[0]); i++) {
- if (adjoin_table[i].status == status)
- syslog(LOG_NOTICE, "%s", adjoin_table[i].msg);
- }
-}
-
-/*
- * smb_ads_match_pdc
- *
- * Returns B_TRUE if the given host's IP address matches the preferred DC's
- * IP address. Otherwise, returns B_FALSE.
- */
-static boolean_t
-smb_ads_match_pdc(smb_ads_host_info_t *host)
-{
- boolean_t match = B_FALSE;
-
- if (!host)
- return (match);
-
- (void) mutex_lock(&smb_ads_cfg.c_mtx);
- if (smb_inet_equal(&host->ipaddr, &smb_ads_cfg.c_pdc))
- match = B_TRUE;
- (void) mutex_unlock(&smb_ads_cfg.c_mtx);
+struct xlate_table {
+ int err;
+ const char const *msg;
+};
- return (match);
-}
+static const struct xlate_table
+adjoin_table[] = {
+ { SMB_ADS_SUCCESS, "Success" },
+ { SMB_ADS_KRB5_INIT_CTX,
+ "Failed creating a Kerberos context." },
+ { SMB_ADS_KRB5_CC_DEFAULT,
+ "Failed to resolve default credential cache." },
+ { SMB_ADS_KRB5_PARSE_PRINCIPAL,
+ "Failed parsing the user principal name." },
+ { SMB_ADS_KRB5_GET_INIT_CREDS_PW,
+ "Failed getting initial credentials. (Wrong password?)" },
+ { SMB_ADS_KRB5_CC_INITIALIZE,
+ "Failed initializing the credential cache." },
+ { SMB_ADS_KRB5_CC_STORE_CRED,
+ "Failed to update the credential cache." },
+ { SMB_ADS_CANT_LOCATE_DC,
+ "Failed to locate a domain controller." },
+ { SMB_ADS_LDAP_INIT,
+ "Failed to create an LDAP handle." },
+ { SMB_ADS_LDAP_SETOPT,
+ "Failed to set an LDAP option." },
+ { SMB_ADS_LDAP_SET_DOM,
+ "Failed to set the LDAP handle DN." },
+ { SMB_ADS_LDAP_SASL_BIND,
+ "Failed to bind the LDAP handle. "
+ "Usually indicates an authentication problem." },
+
+ { SMB_ADJOIN_ERR_GEN_PWD,
+ "Failed to generate machine password." },
+ { SMB_ADJOIN_ERR_GET_DCLEVEL, "Unknown functional level of "
+ "the domain controller. The rootDSE attribute named "
+ "\"domainControllerFunctionality\" is missing from the "
+ "Active Directory." },
+ { SMB_ADJOIN_ERR_ADD_TRUST_ACCT, "Failed to create the "
+ "workstation trust account." },
+ { SMB_ADJOIN_ERR_MOD_TRUST_ACCT, "Failed to modify the "
+ "workstation trust account." },
+ { SMB_ADJOIN_ERR_DUP_TRUST_ACCT, "Failed to create the "
+ "workstation trust account because its name is already "
+ "in use." },
+ { SMB_ADJOIN_ERR_TRUST_ACCT, "Error in querying the "
+ "workstation trust account" },
+ { SMB_ADJOIN_ERR_INIT_KRB_CTX, "Failed to initialize Kerberos "
+ "context." },
+ { SMB_ADJOIN_ERR_GET_SPNS, "Failed to get Kerberos "
+ "principals." },
+ { SMB_ADJOIN_ERR_KSETPWD, "Failed to set machine password." },
+ { SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR, "Failed to modify "
+ "userAccountControl attribute of the workstation trust "
+ "account." },
+ { SMB_ADJOIN_ERR_WRITE_KEYTAB, "Error in writing to local "
+ "keytab file (i.e /etc/krb5/krb5.keytab)." },
+ { SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, "Failed to update idmap "
+ "configuration." },
+ { SMB_ADJOIN_ERR_IDMAP_REFRESH, "Failed to refresh idmap "
+ "service." },
+ { SMB_ADJOIN_ERR_COMMIT_KEYTAB, "Failed to commit changes to "
+ "local keytab file (i.e. /etc/krb5/krb5.keytab)." },
+ { SMB_ADJOIN_ERR_AUTH_NETLOGON,
+ "Failed to authenticate using the new computer account." },
+ { SMB_ADJOIN_ERR_STORE_PROPS,
+ "Failed to store computer account information locally." },
+ { 0, NULL }
+};
/*
- * smb_ads_select_dcfromsubnet
- *
- * This method walks the list of DCs and returns the first DC record that
- * responds to ldap ping and is in the same subnet as the host.
+ * smb_ads_strerror
*
- * Returns a pointer to the found DC record.
- * Returns NULL, on error or if no DC record is found.
+ * Lookup an error message for the specific adjoin error code.
*/
-static smb_ads_host_info_t *
-smb_ads_select_dcfromsubnet(smb_ads_host_list_t *hlist)
+const char *
+smb_ads_strerror(int err)
{
- smb_ads_host_info_t *hentry;
- smb_nic_t *lnic;
- smb_niciter_t ni;
- size_t cnt;
- int i;
-
- if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
- return (NULL);
- do {
- lnic = &ni.ni_nic;
- cnt = hlist->ah_cnt;
-
- for (i = 0; i < cnt; i++) {
- hentry = &hlist->ah_list[i];
- if ((hentry->ipaddr.a_family == AF_INET) &&
- (lnic->nic_ip.a_family == AF_INET)) {
- if ((hentry->ipaddr.a_ipv4 &
- lnic->nic_mask) ==
- (lnic->nic_ip.a_ipv4 &
- lnic->nic_mask))
- if (smb_ads_ldap_ping(hentry) == 0)
- return (hentry);
- }
- }
- } while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
-
- return (NULL);
-}
+ const struct xlate_table *xt;
-/*
- * smb_ads_select_dcfromlist
- *
- * This method walks the list of DCs and returns the first DC that
- * responds to ldap ping.
- *
- * Returns a pointer to the found DC record.
- * Returns NULL if no DC record is found.
- */
-static smb_ads_host_info_t *
-smb_ads_select_dcfromlist(smb_ads_host_list_t *hlist)
-{
- smb_ads_host_info_t *hentry;
- size_t cnt;
- int i;
+ if (err > 0 && err < SMB_ADS_ERRNO_GAP)
+ return (strerror(err));
- cnt = hlist->ah_cnt;
- for (i = 0; i < cnt; i++) {
- hentry = &hlist->ah_list[i];
- if (smb_ads_ldap_ping(hentry) == 0)
- return (hentry);
- }
+ for (xt = adjoin_table; xt->msg; xt++)
+ if (xt->err == err)
+ return (xt->msg);
- return (NULL);
+ return ("Unknown error code.");
}
-/*
- * smb_ads_dc_compare
- *
- * Comparision function for sorting host entries (SRV records of DC) via qsort.
- * RFC 2052/2782 are taken as reference, while implementing this algorithm.
- *
- * Domain Controllers(DCs) with lowest priority in their SRV DNS records
- * are selected first. If they have equal priorities, then DC with highest
- * weight in its SRV DNS record is selected. If the priority and weight are
- * both equal, then the DC at the top of the list is selected.
- */
-static int
-smb_ads_dc_compare(const void *p, const void *q)
+void
+smb_ads_log_errmsg(smb_ads_status_t err)
{
- smb_ads_host_info_t *h1 = (smb_ads_host_info_t *)p;
- smb_ads_host_info_t *h2 = (smb_ads_host_info_t *)q;
-
- if (h1->priority < h2->priority)
- return (-1);
- if (h1->priority > h2->priority)
- return (1);
-
- /* Priorities are equal */
- if (h1->weight < h2->weight)
- return (1);
- if (h1->weight > h2->weight)
- return (-1);
-
- return (0);
+ const char *s = smb_ads_strerror(err);
+ syslog(LOG_NOTICE, "%s", s);
}
-/*
- * smb_ads_select_dc
- *
- * The list of ADS hosts returned by ADS lookup, is sorted by lowest priority
- * and highest weight. On this sorted list, following additional rules are
- * applied, to select a DC.
- *
- * - If there is a DC in the same subnet, then return the DC,
- * if it responds to ldap ping.
- * - Else, return first DC that responds to ldap ping.
- *
- * A reference to the host entry from input host list is returned.
- *
- * Returns NULL on error.
- */
-static smb_ads_host_info_t *
-smb_ads_select_dc(smb_ads_host_list_t *hlist)
-{
- smb_ads_host_info_t *hentry = NULL;
-
- if (hlist->ah_cnt == 0)
- return (NULL);
-
- if (hlist->ah_cnt == 1) {
- hentry = hlist->ah_list;
- if (smb_ads_ldap_ping(hentry) == 0)
- return (hentry);
- }
-
- /* Sort the list by priority and weight */
- qsort(hlist->ah_list, hlist->ah_cnt,
- sizeof (smb_ads_host_info_t), smb_ads_dc_compare);
-
- if ((hentry = smb_ads_select_dcfromsubnet(hlist)) != NULL)
- return (hentry);
-
- if ((hentry = smb_ads_select_dcfromlist(hlist)) != NULL)
- return (hentry);
-
- return (NULL);
-}
/*
* smb_ads_lookup_msdcs
@@ -2547,39 +1953,30 @@ smb_ads_select_dc(smb_ads_host_list_t *hlist)
* Returns the discovered DC via buf.
*
* fqdn - fully-qualified domain name
- * server - fully-qualifed hostname of a DC
- * buf - the hostname of the discovered DC
+ * dci - the name and address of the found DC
*/
-boolean_t
-smb_ads_lookup_msdcs(char *fqdn, char *server, char *buf, uint32_t buflen)
+uint32_t
+smb_ads_lookup_msdcs(char *fqdn, smb_dcinfo_t *dci)
{
smb_ads_host_info_t *hinfo = NULL;
- char *p;
- char *sought_host;
char ipstr[INET6_ADDRSTRLEN];
- if (!fqdn || !buf)
- return (B_FALSE);
+ if (!fqdn || !dci)
+ return (NT_STATUS_INTERNAL_ERROR);
ipstr[0] = '\0';
- *buf = '\0';
- sought_host = (*server == 0 ? NULL : server);
- if ((hinfo = smb_ads_find_host(fqdn, sought_host)) == NULL)
- return (B_FALSE);
+ if ((hinfo = smb_ads_find_host(fqdn)) == NULL)
+ return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
(void) smb_inet_ntop(&hinfo->ipaddr, ipstr,
SMB_IPSTRLEN(hinfo->ipaddr.a_family));
smb_tracef("msdcsLookupADS: %s [%s]", hinfo->name, ipstr);
- (void) strlcpy(buf, hinfo->name, buflen);
- /*
- * Remove the domain extension
- */
- if ((p = strchr(buf, '.')) != 0)
- *p = '\0';
+ (void) strlcpy(dci->dc_name, hinfo->name, sizeof (dci->dc_name));
+ dci->dc_addr = hinfo->ipaddr;
free(hinfo);
- return (B_TRUE);
+ return (NT_STATUS_SUCCESS);
}
static krb5_enctype *
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c
index 4bb8ef49d6..aebc6f8c06 100644
--- a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Copyright 1990 by the Massachusetts Institute of Technology.
@@ -59,8 +60,9 @@
#include <smbns_krb.h>
int
-smb_kinit(char *principal_name, char *principal_passwd)
+smb_kinit(char *domain_name, char *principal_name, char *principal_passwd)
{
+ char default_realm[MAXHOSTNAMELEN];
krb5_context ctx = NULL;
krb5_ccache cc = NULL;
krb5_principal me = NULL;
@@ -68,6 +70,7 @@ smb_kinit(char *principal_name, char *principal_passwd)
krb5_error_code code;
const char *errmsg = NULL;
const char *doing = NULL;
+ smb_ads_status_t err;
assert(principal_name != NULL);
assert(principal_passwd != NULL);
@@ -81,12 +84,21 @@ smb_kinit(char *principal_name, char *principal_passwd)
code = krb5_init_context(&ctx);
if (code) {
+ err = SMB_ADS_KRB5_INIT_CTX;
doing = "smbns_krb: initializing context";
goto cleanup;
}
+ /*
+ * In case krb5.conf is not configured, set the default realm.
+ */
+ (void) strlcpy(default_realm, domain_name, sizeof (default_realm));
+ (void) smb_strupr(default_realm);
+ (void) krb5_set_default_realm(ctx, default_realm);
+
code = krb5_cc_default(ctx, &cc);
if (code != 0) {
+ err = SMB_ADS_KRB5_CC_DEFAULT;
doing = "smbns_krb: resolve default credentials cache";
goto cleanup;
}
@@ -94,6 +106,7 @@ smb_kinit(char *principal_name, char *principal_passwd)
/* Use specified name */
code = krb5_parse_name(ctx, principal_name, &me);
if (code != 0) {
+ err = SMB_ADS_KRB5_PARSE_PRINCIPAL;
doing = "smbns_krb: parsing principal name";
goto cleanup;
}
@@ -102,6 +115,7 @@ smb_kinit(char *principal_name, char *principal_passwd)
principal_passwd, NULL, 0, (krb5_deltat)0,
NULL, NULL);
if (code != 0) {
+ err = SMB_ADS_KRB5_GET_INIT_CREDS_PW;
doing = "smbns_krb: getting initial credentials";
if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
@@ -113,17 +127,20 @@ smb_kinit(char *principal_name, char *principal_passwd)
code = krb5_cc_initialize(ctx, cc, me);
if (code != 0) {
+ err = SMB_ADS_KRB5_CC_INITIALIZE;
doing = "smbns_krb: initializing cache";
goto cleanup;
}
code = krb5_cc_store_cred(ctx, cc, &my_creds);
if (code != 0) {
+ err = SMB_ADS_KRB5_CC_STORE_CRED;
doing = "smbns_krb: storing credentials";
goto cleanup;
}
/* SUCCESS! */
+ err = SMB_ADS_SUCCESS;
cleanup:
if (code != 0) {
@@ -145,7 +162,7 @@ cleanup:
if (ctx)
krb5_free_context(ctx);
- return (code == 0);
+ return (err);
}
/*
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h
index d60bca8785..50c3985564 100644
--- a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h
@@ -84,7 +84,7 @@ typedef struct smb_krb5_pn_set {
char **s_pns;
} smb_krb5_pn_set_t;
-int smb_kinit(char *, char *);
+int smb_kinit(char *, char *, char *);
int smb_krb5_ctx_init(krb5_context *);
void smb_krb5_ctx_fini(krb5_context);
int smb_krb5_get_kprincs(krb5_context, char **, size_t, krb5_principal **);
diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c
index 7a6a3491a1..9478b77291 100644
--- a/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c
+++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c
@@ -255,15 +255,20 @@ smb_krb5_setpwd(krb5_context ctx, const char *fqdn, char *passwd)
code = krb5_set_password_using_ccache(ctx, cc, passwd, princ,
&result_code, &result_code_string, &result_string);
+ (void) krb5_cc_close(ctx, cc);
+
if (code != 0)
smb_krb5_log_errmsg(ctx, "smbns_ksetpwd: KPASSWD protocol "
"exchange failed", code);
- (void) krb5_cc_close(ctx, cc);
-
- if (result_code != 0)
- syslog(LOG_ERR, "smbns_ksetpwd: KPASSWD failed: %s",
+ if (result_code != 0) {
+ syslog(LOG_ERR, "smbns_ksetpwd: KPASSWD failed: rc=%d %.*s",
+ result_code,
+ result_code_string.length,
result_code_string.data);
+ if (code == 0)
+ code = EACCES;
+ }
krb5_free_principal(ctx, princ);
free(result_code_string.data);