summaryrefslogtreecommitdiff
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
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>
-rw-r--r--exception_lists/cstyle1
-rw-r--r--exception_lists/packaging7
-rw-r--r--usr/src/Targetdirs3
-rw-r--r--usr/src/cmd/idmap/Makefile7
-rw-r--r--usr/src/cmd/idmap/idmap/namemaps.c3
-rw-r--r--usr/src/cmd/idmap/idmapd/Makefile42
-rw-r--r--usr/src/cmd/idmap/idmapd/adspriv_impl.c210
-rw-r--r--usr/src/cmd/idmap/idmapd/directory_provider_ad.c2
-rw-r--r--usr/src/cmd/idmap/idmapd/idmap.xml17
-rw-r--r--usr/src/cmd/idmap/idmapd/idmap_config.c585
-rw-r--r--usr/src/cmd/idmap/idmapd/idmap_config.h26
-rw-r--r--usr/src/cmd/idmap/idmapd/idmap_lsa.c11
-rw-r--r--usr/src/cmd/idmap/idmapd/idmapd.c28
-rw-r--r--usr/src/cmd/idmap/idmapd/idmapd.h9
-rw-r--r--usr/src/cmd/idmap/idmapd/init.c8
-rw-r--r--usr/src/cmd/idmap/idmapd/krb5_lookup.c156
-rw-r--r--usr/src/cmd/idmap/idmapd/rpc_svc.c334
-rw-r--r--usr/src/cmd/idmap/nltest/Makefile67
-rwxr-xr-xusr/src/cmd/idmap/nltest/Run.sh31
-rw-r--r--usr/src/cmd/idmap/nltest/nltest.c331
-rw-r--r--usr/src/cmd/idmap/test-getdc/.dbxrc23
-rw-r--r--usr/src/cmd/idmap/test-getdc/Makefile70
-rw-r--r--usr/src/cmd/idmap/test-getdc/getdc_main.c154
-rw-r--r--usr/src/cmd/smbsrv/fksmbd/Makefile1
-rw-r--r--usr/src/cmd/smbsrv/smbadm/Makefile4
-rw-r--r--usr/src/cmd/smbsrv/smbadm/smbadm.c130
-rw-r--r--usr/src/cmd/smbsrv/smbd/Makefile3
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd.h2
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c25
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_join.c249
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_krb5lookup.c158
-rw-r--r--usr/src/common/smbsrv/smb_xdr.c3
-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
-rw-r--r--usr/src/man/man1m/smbadm.1m21
-rw-r--r--usr/src/pkg/manifests/SUNWcs.mf3
-rw-r--r--usr/src/pkg/manifests/developer-library-lint.mf5
-rw-r--r--usr/src/pkg/manifests/service-file-system-smb.mf3
-rw-r--r--usr/src/pkg/manifests/system-header.mf7
-rw-r--r--usr/src/pkg/manifests/system-library.mf6
-rwxr-xr-xusr/src/tools/quick/make-idmap4
-rwxr-xr-xusr/src/tools/quick/make-smbsrv5
-rw-r--r--usr/src/uts/common/smbsrv/netrauth.h5
-rw-r--r--usr/src/uts/common/smbsrv/smb_door.h3
-rw-r--r--usr/src/uts/common/smbsrv/smb_privilege.h2
-rw-r--r--usr/src/uts/common/smbsrv/smb_token.h9
103 files changed, 6253 insertions, 2667 deletions
diff --git a/exception_lists/cstyle b/exception_lists/cstyle
index 4b1a68b5ce..2845dce902 100644
--- a/exception_lists/cstyle
+++ b/exception_lists/cstyle
@@ -569,6 +569,7 @@ usr/src/lib/krb5/ss/ss.h
usr/src/lib/krb5/ss/std_rqs.c
usr/src/lib/krb5/ss/utils.c
usr/src/lib/libgss/g_glue.c
+usr/src/lib/libresolv2/common
usr/src/lib/librstp/common/base.h
usr/src/lib/librstp/common/choose.h
usr/src/lib/librstp/common/edge.c
diff --git a/exception_lists/packaging b/exception_lists/packaging
index 5ff213104e..5c2a02fa69 100644
--- a/exception_lists/packaging
+++ b/exception_lists/packaging
@@ -22,8 +22,8 @@
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved.
-# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
#
@@ -990,3 +990,8 @@ usr/lib/sparcv9/llib-lpcidb.ln sparc
usr/lib/libpcidb.so
usr/lib/llib-lpcidb
usr/lib/llib-lpcidb.ln
+
+#
+# debugging program for libadutils
+#
+usr/bin/test-getdc
diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs
index d0b253305b..aba9f44c68 100644
--- a/usr/src/Targetdirs
+++ b/usr/src/Targetdirs
@@ -25,8 +25,8 @@
# Copyright (c) 2012, Igor Kozhukhov <ikozhukhov@gmail.com>
# Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright (c) 2013 RackTop Systems.
-# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
#
@@ -226,6 +226,7 @@ DIRS= \
/usr/has/lib \
/usr/has/man \
/usr/include \
+ /usr/include/ads \
/usr/include/ast \
/usr/include/fm \
/usr/include/gssapi \
diff --git a/usr/src/cmd/idmap/Makefile b/usr/src/cmd/idmap/Makefile
index 3242cdfa22..f66c25fe8a 100644
--- a/usr/src/cmd/idmap/Makefile
+++ b/usr/src/cmd/idmap/Makefile
@@ -21,15 +21,12 @@
#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
# Makefile for native identity mapping service
#
-SUBDIR_CMD= idmap
-SUBDIR_DAEMON= idmapd
-SUBDIRS= $(SUBDIR_CMD) $(SUBDIR_DAEMON)
+SUBDIRS= idmap idmapd nltest test-getdc
all := TARGET = all
install := TARGET = install
diff --git a/usr/src/cmd/idmap/idmap/namemaps.c b/usr/src/cmd/idmap/idmap/namemaps.c
index c906bfeaa3..1671350a49 100644
--- a/usr/src/cmd/idmap/idmap/namemaps.c
+++ b/usr/src/cmd/idmap/idmap/namemaps.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
@@ -244,7 +245,7 @@ idmap_stat
idmap_init_ad(idmap_nm_handle_t *p)
{
idmap_stat rc = IDMAP_SUCCESS;
- idmap_ad_disc_ds_t *dc = NULL;
+ ad_disc_ds_t *dc = NULL;
ad_disc_t ad_ctx;
ad_ctx = ad_disc_init();
diff --git a/usr/src/cmd/idmap/idmapd/Makefile b/usr/src/cmd/idmap/idmapd/Makefile
index b99b8677be..a4ab35da95 100644
--- a/usr/src/cmd/idmap/idmapd/Makefile
+++ b/usr/src/cmd/idmap/idmapd/Makefile
@@ -20,11 +20,13 @@
#
#
# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
PROG = idmapd
MANIFEST = idmap.xml
SERVEROBJS = \
+ adspriv_impl.o \
directory_provider_builtin.o \
directory_provider_nsswitch.o \
directory_provider_ad.o \
@@ -35,13 +37,18 @@ SERVEROBJS = \
idmapd.o \
init.o \
idmap_lsa.o \
+ krb5_lookup.o \
nldaputils.o \
- rpc_svc.o \
server.o \
wksids.o
+GENOBJS = \
+ adspriv_srv.o \
+ rpc_svc.o
+
SERVERSRCS = $(SERVEROBJS:%.o=%.c)
-OBJS = $(SERVEROBJS)
+GENSRCS = $(GENOBJS:%.o=%.c)
+OBJS = $(SERVEROBJS) $(GENOBJS)
SRCS = $(SERVERSRCS)
POFILES = $(OBJS:%.o=%.po)
@@ -69,15 +76,24 @@ RPC_MSGOUT_OPT = -DRPC_MSGOUT=idmap_rpc_msgout
ROOTMANIFESTDIR = $(ROOTSVCSYSTEM)
$(ROOTMANIFEST) := FILEMODE= 444
+RPCSVC= ../../../uts/common/rpcsvc
+ADS_CMN=../../../lib/libads/common
-INCS += -I. -I../../../lib/libidmap/common\
- -I../../../lib/libsldap/common\
+INCS += -I. -I../../../lib/libidmap/common \
+ -I../../../lib/libsldap/common \
-I../../../lib/libadutils/common \
+ -I $(ADS_CMN) \
-I../../../lib/smbsrv/libsmb/common
+# Should not have to do this, but the Kerberos includes are a mess.
+INCS += -I $(ROOT)/usr/include/kerberosv5
+
$(OBJS) := CPPFLAGS += $(INCS) -D_REENTRANT
$(POFILE) := CPPFLAGS += $(INCS)
+LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2
+LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2
+
CFLAGS += $(CCVERBOSE)
LDLIBS += \
-lsqlite-sys \
@@ -90,6 +106,7 @@ LDLIBS += \
-lldap \
-luuid \
-ladutils \
+ -lads \
-lumem \
-lnvpair \
-luutil \
@@ -126,15 +143,22 @@ install: all $(ROOTLIBPROG) $(ROOTMANIFEST)
check: $(CHKMANIFEST)
clean:
- $(RM) $(OBJS)
-
-clobber:
+ $(RM) $(OBJS) $(GENSRCS)
lint: lint_SRCS
lint_SRCS:
-include ../../Makefile.targ
+RPCGENFLAGS = -CMN
-FRC:
+adspriv_srv.o : adspriv_srv.c
+adspriv_srv.c:
+ $(RPCGEN) $(RPCGENFLAGS) -m $(ADS_CMN)/ads_priv.x > $@
+
+rpc_svc.o : rpc_svc.c
+
+rpc_svc.c : $(RPCSVC)/idmap_prot.x
+ $(RPCGEN) $(RPCGENFLAGS) -m $(RPCSVC)/idmap_prot.x > $@
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/idmap/idmapd/adspriv_impl.c b/usr/src/cmd/idmap/idmapd/adspriv_impl.c
new file mode 100644
index 0000000000..b1dcbe3163
--- /dev/null
+++ b/usr/src/cmd/idmap/idmapd/adspriv_impl.c
@@ -0,0 +1,210 @@
+/*
+ * 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 <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+#include <sys/uuid.h>
+#include <smb/ntstatus.h>
+#include <synch.h>
+#include <thread.h>
+#include <arpa/inet.h>
+#include <uuid/uuid.h>
+
+#include "idmapd.h"
+#include "libadutils.h"
+#include "dsgetdc.h"
+#include "ads_priv.h"
+
+void adspriv_program_1(struct svc_req *, register SVCXPRT *);
+
+SVCXPRT *dcl_xprt = NULL;
+
+void
+init_dc_locator(void)
+{
+ int connmaxrec = 32 * 1024;
+
+ dcl_xprt = svc_door_create(adspriv_program_1,
+ ADSPRIV_PROGRAM, ADSPRIV_V1, connmaxrec);
+ if (dcl_xprt == NULL) {
+ syslog(LOG_ERR, "unable to create door RPC service");
+ return;
+ }
+
+ if (!svc_control(dcl_xprt, SVCSET_CONNMAXREC, &connmaxrec)) {
+ syslog(LOG_ERR, "unable to limit RPC request size");
+ }
+}
+
+void
+fini_dc_locator(void)
+{
+ if (dcl_xprt != NULL)
+ svc_destroy(dcl_xprt);
+}
+
+/*
+ * Functions called by the (generated) adspriv_srv.c
+ */
+
+/* ARGSUSED */
+bool_t
+adspriv_null_1_svc(void *result, struct svc_req *rqstp)
+{
+ return (TRUE);
+}
+
+/* ARGSUSED */
+bool_t
+adspriv_forcerediscovery_1_svc(
+ DsForceRediscoveryArgs args,
+ int *res,
+ struct svc_req *sreq)
+{
+ /* Ignoring args for now. */
+
+ idmap_cfg_force_rediscovery();
+ *res = 0;
+
+ return (TRUE);
+}
+
+
+/* ARGSUSED */
+bool_t
+adspriv_getdcname_1_svc(
+ DsGetDcNameArgs args,
+ DsGetDcNameRes *res,
+ struct svc_req *sreq)
+{
+ uuid_t uuid;
+ adspriv_dcinfo *dci;
+ idmap_pg_config_t *pgcfg;
+ ad_disc_ds_t *ds;
+ char *s;
+
+ /* Init */
+ (void) memset(res, 0, sizeof (*res));
+ res->status = 0;
+ dci = &res->DsGetDcNameRes_u.res0;
+
+ if (args.Flags & DS_FORCE_REDISCOVERY)
+ idmap_cfg_force_rediscovery();
+
+ /*
+ * We normally should wait if discovery is running.
+ * Sort of mis-using the background flag as a way to
+ * skip the wait, until we really do background disc.
+ */
+ if ((args.Flags & DS_BACKGROUND_ONLY) == 0) {
+ timespec_t tv = { 15, 0 };
+ int rc = 0;
+ int waited = 0;
+
+ (void) mutex_lock(&_idmapdstate.addisc_lk);
+
+ if (_idmapdstate.addisc_st != 0)
+ idmapdlog(LOG_DEBUG, "getdcname wait begin");
+
+ while (_idmapdstate.addisc_st != 0) {
+ waited++;
+ rc = cond_reltimedwait(&_idmapdstate.addisc_cv,
+ &_idmapdstate.addisc_lk, &tv);
+ if (rc == ETIME)
+ break;
+ }
+ (void) mutex_unlock(&_idmapdstate.addisc_lk);
+
+ if (rc == ETIME) {
+ /* Caller will replace this with DC not found. */
+ idmapdlog(LOG_ERR, "getdcname timeout");
+ res->status = NT_STATUS_CANT_WAIT;
+ return (TRUE);
+ }
+ if (waited) {
+ idmapdlog(LOG_DEBUG, "getdcname wait done");
+ }
+ }
+
+ RDLOCK_CONFIG();
+ pgcfg = &_idmapdstate.cfg->pgcfg;
+
+ if (pgcfg->domain_name == NULL) {
+ res->status = NT_STATUS_INVALID_SERVER_STATE;
+ goto out;
+ }
+
+ if (args.DomainName != NULL && args.DomainName[0] != '\0' &&
+ 0 != strcasecmp(args.DomainName, pgcfg->domain_name)) {
+ /*
+ * They asked for a specific domain not our primary,
+ * which is not supported (and not needed).
+ */
+ res->status = NT_STATUS_NO_SUCH_DOMAIN;
+ goto out;
+ }
+
+ if ((ds = pgcfg->domain_controller) == NULL) {
+ res->status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ goto out;
+ }
+
+ dci->dci_DcName = strdup(ds->host);
+
+ dci->dci_DcAddr = calloc(1, INET6_ADDRSTRLEN);
+ if (dci->dci_DcAddr != NULL &&
+ ad_disc_getnameinfo(dci->dci_DcAddr, INET6_ADDRSTRLEN,
+ &ds->addr) == 0)
+ dci->dci_AddrType = DS_INET_ADDRESS;
+
+ if ((s = pgcfg->domain_guid) != NULL &&
+ 0 == uuid_parse(s, uuid)) {
+ (void) memcpy(dci->dci_guid, uuid, sizeof (uuid));
+ }
+
+ if ((s = pgcfg->domain_name) != NULL)
+ dci->dci_DomainName = strdup(s);
+
+ if ((s = pgcfg->forest_name) != NULL)
+ dci->dci_DnsForestName = strdup(s);
+
+ dci->dci_Flags = ds->flags;
+ dci->dci_DcSiteName = strdup(ds->site);
+
+ if ((s = pgcfg->site_name) != NULL)
+ dci->dci_ClientSiteName = strdup(s);
+
+ /* Address in binary form too. */
+ (void) memcpy(&dci->dci_sockaddr,
+ &ds->addr, ADSPRIV_SOCKADDR_LEN);
+
+out:
+ UNLOCK_CONFIG();
+
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+adspriv_program_1_freeresult(SVCXPRT *xprt, xdrproc_t fun, caddr_t res)
+{
+ (void) xdr_free(fun, res);
+ return (TRUE);
+}
diff --git a/usr/src/cmd/idmap/idmapd/directory_provider_ad.c b/usr/src/cmd/idmap/idmapd/directory_provider_ad.c
index 13233f6345..6bae13e963 100644
--- a/usr/src/cmd/idmap/idmapd/directory_provider_ad.c
+++ b/usr/src/cmd/idmap/idmapd/directory_provider_ad.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -144,6 +145,7 @@ directory_provider_ad_get(
/*
* If we don't have any AD servers handy, we can't find anything.
+ * XXX: this should be using our DC, not the GC.
*/
if (_idmapdstate.num_gcs < 1) {
return (NULL);
diff --git a/usr/src/cmd/idmap/idmapd/idmap.xml b/usr/src/cmd/idmap/idmapd/idmap.xml
index 1f051a2936..c1461616bc 100644
--- a/usr/src/cmd/idmap/idmapd/idmap.xml
+++ b/usr/src/cmd/idmap/idmapd/idmap.xml
@@ -2,6 +2,7 @@
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!--
Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright 2014 Nexenta Systems, Inc. All rights reserved.
CDDL HEADER START
@@ -104,6 +105,22 @@
name='name_cache_timeout'
type='count'
value='604800' />
+ <propval
+ name='preferred_dc'
+ type='astring'
+ value='' />
+ <propval
+ name='rediscovery_interval'
+ type='count'
+ value='3600' />
+ <propval
+ name='use_ads'
+ type='boolean'
+ value='true' />
+ <propval
+ name='use_lsa'
+ type='boolean'
+ value='true' />
</property_group>
<property_group name='debug' type='application' >
diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.c b/usr/src/cmd/idmap/idmapd/idmap_config.c
index a95441d4b9..ad3c70b932 100644
--- a/usr/src/cmd/idmap/idmapd/idmap_config.c
+++ b/usr/src/cmd/idmap/idmapd/idmap_config.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.
*/
@@ -33,15 +33,19 @@
#include <libintl.h>
#include <ctype.h>
#include <errno.h>
-#include "idmapd.h"
#include <stdio.h>
#include <stdarg.h>
#include <uuid/uuid.h>
#include <pthread.h>
#include <port.h>
+#include <sys/socket.h>
#include <net/route.h>
#include <sys/u8_textprep.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
#include <note.h>
+#include "idmapd.h"
#include "addisc.h"
#define MACHINE_SID_LEN (9 + 3 * 11)
@@ -50,6 +54,7 @@
#define DEBUG_PG "debug"
#define RECONFIGURE 1
#define POKE_AUTO_DISCOVERY 2
+#define KICK_AUTO_DISCOVERY 3
/*
* Default cache timeouts. Can override via svccfg
@@ -59,15 +64,30 @@
#define ID_CACHE_TMO_DEFAULT 86400
#define NAME_CACHE_TMO_DEFAULT 604800
+/*
+ * Default maximum time between rediscovery runs.
+ * config/rediscovery_interval = count: seconds
+ */
+#define REDISCOVERY_INTERVAL_DEFAULT 3600
+
+/*
+ * Mininum time between rediscovery runs, in case adutils gives us a
+ * really short TTL (which it never should, but be defensive)
+ * (not configurable) seconds.
+ */
+#define MIN_REDISCOVERY_INTERVAL 60
+
enum event_type {
EVENT_NOTHING, /* Woke up for no good reason */
EVENT_TIMEOUT, /* Timeout expired */
EVENT_ROUTING, /* An interesting routing event happened */
- EVENT_DEGRADE, /* An error occurred in the mainline */
+ EVENT_POKED, /* Requested from degrade_svc() */
+ EVENT_KICKED, /* Force rediscovery, i.e. DC failed. */
EVENT_REFRESH, /* SMF refresh */
};
+static void idmapd_set_krb5_realm(char *);
static pthread_t update_thread_handle = 0;
@@ -350,14 +370,17 @@ scf_value2string(const char *name, scf_value_t *value)
static int
get_val_ds(idmap_cfg_handles_t *handles, const char *name, int defport,
- idmap_ad_disc_ds_t **val)
+ ad_disc_ds_t **val)
{
- idmap_ad_disc_ds_t *servers = NULL;
+ char port_str[8];
+ struct addrinfo hints;
+ struct addrinfo *ai;
+ ad_disc_ds_t *servers = NULL;
scf_property_t *scf_prop;
scf_value_t *value;
scf_iter_t *iter;
char *host, *portstr;
- int len, i;
+ int err, len, i;
int count = 0;
int rc = -1;
@@ -423,14 +446,22 @@ restart:
goto destruction;
}
+ (void) memset(&hints, 0, sizeof (hints));
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ host = NULL;
+
i = 0;
while (i < count && scf_iter_next_value(iter, value) > 0) {
+ if (host) {
+ free(host);
+ host = NULL;
+ }
servers[i].priority = 0;
servers[i].weight = 100;
servers[i].port = defport;
- if ((host = scf_value2string(name, value)) == NULL) {
- goto destruction;
- }
+ if ((host = scf_value2string(name, value)) == NULL)
+ continue;
if ((portstr = strchr(host, ':')) != NULL) {
*portstr++ = '\0';
servers[i].port = strtol(portstr,
@@ -438,16 +469,56 @@ restart:
if (servers[i].port == 0)
servers[i].port = defport;
}
- len = strlcpy(servers[i].host, host,
- sizeof (servers->host));
- free(host);
+ /*
+ * Ignore this server if the hostname is too long
+ * or empty (continue without i++)
+ */
+ len = strlen(host);
+ if (len == 0) {
+ if (DBG(CONFIG, 1)) {
+ idmapdlog(LOG_INFO, "%s host=\"\"", name);
+ }
+ continue;
+ }
+ if (len >= sizeof (servers->host)) {
+ idmapdlog(LOG_ERR, "Host name too long: %s", host);
+ idmapdlog(LOG_ERR, "ignoring %s value", name);
+ continue;
+ }
+
+ /*
+ * Get the host address too. If we can't, then
+ * log an error and skip this host.
+ */
+ (void) snprintf(port_str, sizeof (port_str),
+ "%d", servers[i].port);
+ ai = NULL;
+ err = getaddrinfo(host, port_str, &hints, &ai);
+ if (err != 0) {
+ idmapdlog(LOG_ERR, "No address for host: %s (%s)",
+ host, gai_strerror(err));
+ idmapdlog(LOG_ERR, "ignoring %s value", name);
+ continue;
+ }
+
+ (void) strlcpy(servers[i].host, host,
+ sizeof (servers->host));
+ (void) memcpy(&servers[i].addr, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
- /* Ignore this server if the hostname is too long */
- if (len < sizeof (servers->host))
- i++;
+ /* Added a DS to the array. */
+ i++;
}
+ free(host);
+ if (i == 0) {
+ if (DBG(CONFIG, 1)) {
+ idmapdlog(LOG_INFO, "%s is empty", name);
+ }
+ free(servers);
+ servers = NULL;
+ }
*val = servers;
rc = 0;
@@ -466,7 +537,6 @@ destruction:
return (rc);
}
-
static int
get_val_astring(idmap_cfg_handles_t *handles, const char *name, char **val)
{
@@ -842,9 +912,8 @@ update_enum(int *value, int *new, char *name, struct enum_lookup_map *map)
* If nothing has changed it returns 0 else 1
*/
static int
-update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new, char *name)
+update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new, char *name)
{
- int i;
if (*value == *new)
/* Nothing to do */
@@ -872,9 +941,13 @@ update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new, char *name)
if (DBG(CONFIG, 1)) {
/* List all the new DSs */
- for (i = 0; (*value)[i].host[0] != '\0'; i++) {
- idmapdlog(LOG_INFO, "change %s=%s port=%d", name,
- (*value)[i].host, (*value)[i].port);
+ char buf[64];
+ ad_disc_ds_t *ds;
+ for (ds = *value; ds->host[0] != '\0'; ds++) {
+ if (ad_disc_getnameinfo(buf, sizeof (buf), &ds->addr))
+ (void) strlcpy(buf, "?", sizeof (buf));
+ idmapdlog(LOG_INFO, "change %s=%s addr=%s port=%d",
+ name, ds->host, buf, ds->port);
}
}
return (1);
@@ -1199,11 +1272,15 @@ wait_for_event(struct timespec *timeoutp)
return (EVENT_TIMEOUT);
case PORT_SOURCE_USER:
- if (pe.portev_events == POKE_AUTO_DISCOVERY)
- return (EVENT_DEGRADE);
- if (pe.portev_events == RECONFIGURE)
+ switch (pe.portev_events) {
+ case RECONFIGURE:
return (EVENT_REFRESH);
- break;
+ case POKE_AUTO_DISCOVERY:
+ return (EVENT_POKED);
+ case KICK_AUTO_DISCOVERY:
+ return (EVENT_KICKED);
+ }
+ return (EVENT_NOTHING);
case PORT_SOURCE_FD:
if (pe.portev_object == rt_sock) {
@@ -1249,18 +1326,19 @@ void *
idmap_cfg_update_thread(void *arg)
{
NOTE(ARGUNUSED(arg))
-
+ idmap_pg_config_t *pgcfg = &_idmapdstate.cfg->pgcfg;
const ad_disc_t ad_ctx = _idmapdstate.cfg->handles.ad_ctx;
+ int flags = CFG_DISCOVER;
for (;;) {
struct timespec timeout;
struct timespec *timeoutp;
int rc;
- int ttl;
+ int ttl, max_ttl;
(void) ad_disc_SubnetChanged(ad_ctx);
- rc = idmap_cfg_load(_idmapdstate.cfg, CFG_DISCOVER);
+ rc = idmap_cfg_load(_idmapdstate.cfg, flags);
if (rc < -1) {
idmapdlog(LOG_ERR, "Fatal errors while reading "
"SMF properties");
@@ -1275,17 +1353,34 @@ idmap_cfg_update_thread(void *arg)
* Wait for an interesting event. Note that we might get
* boring events between interesting events. If so, we loop.
*/
+ flags = CFG_DISCOVER;
for (;;) {
- ttl = ad_disc_get_TTL(ad_ctx);
-
+ /*
+ * If we don't know our domain name, don't bother
+ * with rediscovery until the next config change.
+ * Avoids hourly noise in workgroup mode.
+ */
+ if (pgcfg->domain_name == NULL)
+ ttl = -1;
+ else
+ ttl = ad_disc_get_TTL(ad_ctx);
if (ttl < 0) {
timeoutp = NULL;
} else {
- timeoutp = &timeout;
+ max_ttl = (int)pgcfg->rediscovery_interval;
+ if (ttl > max_ttl)
+ ttl = max_ttl;
+ if (ttl < MIN_REDISCOVERY_INTERVAL)
+ ttl = MIN_REDISCOVERY_INTERVAL;
timeout.tv_sec = ttl;
timeout.tv_nsec = 0;
+ timeoutp = &timeout;
}
+ if (DBG(CONFIG, 1))
+ idmapdlog(LOG_DEBUG,
+ "_cfg_update_thread waiting");
+
switch (wait_for_event(timeoutp)) {
case EVENT_NOTHING:
if (DBG(CONFIG, 2))
@@ -1295,16 +1390,24 @@ idmap_cfg_update_thread(void *arg)
if (DBG(CONFIG, 1))
idmapdlog(LOG_INFO, "SMF refresh");
/*
+ * Forget any DC we had previously.
+ */
+ flags |= CFG_FORGET_DC;
+
+ /*
* Blow away the ccache, we might have
* re-joined the domain or joined a new one
*/
(void) unlink(IDMAP_CACHEDIR "/ccache");
break;
- case EVENT_DEGRADE:
- if (DBG(CONFIG, 1)) {
- idmapdlog(LOG_DEBUG,
- "Service degraded");
- }
+ case EVENT_POKED:
+ if (DBG(CONFIG, 1))
+ idmapdlog(LOG_DEBUG, "poked");
+ break;
+ case EVENT_KICKED:
+ if (DBG(CONFIG, 1))
+ idmapdlog(LOG_DEBUG, "kicked");
+ flags |= CFG_FORGET_DC;
break;
case EVENT_TIMEOUT:
if (DBG(CONFIG, 1))
@@ -1432,8 +1535,7 @@ check_smf_debug_mode(idmap_cfg_handles_t *handles)
* -3 -> hard smf config failures
* reading from SMF.
*/
-static
-int
+static int
idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg,
int * const errors)
{
@@ -1513,6 +1615,13 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg,
if (pgcfg->name_cache_timeout == 0)
pgcfg->name_cache_timeout = NAME_CACHE_TMO_DEFAULT;
+ rc = get_val_int(handles, "rediscovery_interval",
+ &pgcfg->rediscovery_interval, SCF_TYPE_COUNT);
+ if (rc != 0)
+ (*errors)++;
+ if (pgcfg->rediscovery_interval == 0)
+ pgcfg->rediscovery_interval = REDISCOVERY_INTERVAL_DEFAULT;
+
rc = get_val_astring(handles, "domain_name",
&pgcfg->domain_name);
if (rc != 0)
@@ -1543,6 +1652,27 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg,
pgcfg->default_domain = strdup(pgcfg->domain_name);
}
+ rc = get_val_astring(handles, "domain_guid", &s);
+ if (rc != 0) {
+ (*errors)++;
+ } else if (s == NULL || s[0] == '\0') {
+ /* OK, not set. */
+ free(s);
+ } else {
+ uuid_t u;
+
+ if (uuid_parse(s, u) != 0) {
+ idmapdlog(LOG_ERR,
+ "config/domain_guid: invalid value \"%s\" ignored", s);
+ free(s);
+ (*errors)++;
+ } else {
+ pgcfg->domain_guid = s;
+ pgcfg->domain_guid_auto_disc = B_FALSE;
+ (void) ad_disc_set_DomainGUID(handles->ad_ctx, u);
+ }
+ }
+
rc = get_val_astring(handles, "machine_uuid", &pgcfg->machine_uuid);
if (rc != 0)
(*errors)++;
@@ -1583,6 +1713,16 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg,
pgcfg->domain_controller_auto_disc = B_FALSE;
}
+ rc = get_val_ds(handles, "preferred_dc", 389,
+ &pgcfg->preferred_dc);
+ if (rc != 0)
+ (*errors)++;
+ else {
+ (void) ad_disc_set_PreferredDC(handles->ad_ctx,
+ pgcfg->preferred_dc);
+ pgcfg->preferred_dc_auto_disc = B_FALSE;
+ }
+
rc = get_val_astring(handles, "forest_name", &pgcfg->forest_name);
if (rc != 0)
(*errors)++;
@@ -1656,7 +1796,6 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg,
}
return (rc);
-
}
static
@@ -1678,7 +1817,7 @@ discover_trusted_domains(idmap_pg_config_t *pgcfg, ad_disc_t ad_ctx)
int num_trusteddomains;
boolean_t new_forest;
char *trusteddomain;
- idmap_ad_disc_ds_t *globalcatalog;
+ ad_disc_ds_t *globalcatalog;
idmap_trustedforest_t *trustedforests;
ad_disc_domainsinforest_t *domainsinforest;
@@ -1773,8 +1912,7 @@ discover_trusted_domains(idmap_pg_config_t *pgcfg, ad_disc_t ad_ctx)
continue;
}
domainsinforest =
- ad_disc_get_DomainsInForest(trusted_ctx,
- NULL);
+ ad_disc_get_DomainsInForest(trusted_ctx, NULL);
if (domainsinforest == NULL) {
if (DBG(CONFIG, 1)) {
idmapdlog(LOG_DEBUG,
@@ -1819,72 +1957,154 @@ discover_trusted_domains(idmap_pg_config_t *pgcfg, ad_disc_t ad_ctx)
* idmap_cfg_discover() is called *after* idmap_cfg_load_smf(), so it
* needs to be careful not to overwrite any properties set in SMF.
*/
-static
-void
-idmap_cfg_discover(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
+static void
+idmap_cfg_discover1(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
{
ad_disc_t ad_ctx = handles->ad_ctx;
+ FILE *status_fp = NULL;
+ time_t t0, t1;
- if (pgcfg->use_ads == B_FALSE) {
- if (DBG(CONFIG, 1))
- idmapdlog(LOG_DEBUG, "ADS disabled.");
- return;
- }
-
+ t0 = time(NULL);
if (DBG(CONFIG, 1))
- idmapdlog(LOG_DEBUG, "Running discovery.");
+ idmapdlog(LOG_DEBUG, "Running domain discovery.");
- ad_disc_refresh(ad_ctx);
+ (void) unlink(IDMAP_CACHEDIR "/discovery.log");
+ status_fp = fopen(IDMAP_CACHEDIR "/discovery.log", "w");
+ if (status_fp) {
+ (void) fchmod(fileno(status_fp), 0644);
+ ad_disc_set_StatusFP(ad_ctx, status_fp);
+ }
if (pgcfg->domain_name == NULL) {
idmapdlog(LOG_DEBUG, "No domain name specified.");
- } else {
- if (pgcfg->domain_controller == NULL)
- pgcfg->domain_controller =
- ad_disc_get_DomainController(ad_ctx,
- AD_DISC_PREFER_SITE,
- &pgcfg->domain_controller_auto_disc);
-
- if (pgcfg->forest_name == NULL)
- pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx,
- &pgcfg->forest_name_auto_disc);
-
- if (pgcfg->site_name == NULL)
- pgcfg->site_name = ad_disc_get_SiteName(ad_ctx,
- &pgcfg->site_name_auto_disc);
-
- if (pgcfg->global_catalog == NULL)
- pgcfg->global_catalog =
- ad_disc_get_GlobalCatalog(ad_ctx,
- AD_DISC_PREFER_SITE,
- &pgcfg->global_catalog_auto_disc);
+ if (status_fp)
+ (void) fprintf(status_fp, "(no domain name)\n");
+ goto out;
+ }
+ if (pgcfg->domain_controller == NULL)
+ pgcfg->domain_controller =
+ ad_disc_get_DomainController(ad_ctx,
+ AD_DISC_PREFER_SITE,
+ &pgcfg->domain_controller_auto_disc);
+
+ if (pgcfg->domain_guid == NULL) {
+ char buf[UUID_PRINTABLE_STRING_LENGTH];
+ uchar_t *u = ad_disc_get_DomainGUID(ad_ctx,
+ &pgcfg->domain_guid_auto_disc);
+ (void) memset(buf, 0, sizeof (buf));
+ if (u != NULL) {
+ uuid_unparse(u, buf);
+ pgcfg->domain_guid = strdup(buf);
+ }
+ }
+
+ if (pgcfg->forest_name == NULL)
+ pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx,
+ &pgcfg->forest_name_auto_disc);
+
+ if (pgcfg->site_name == NULL)
+ pgcfg->site_name = ad_disc_get_SiteName(ad_ctx,
+ &pgcfg->site_name_auto_disc);
+
+ if (DBG(CONFIG, 1)) {
+ log_if_unable(pgcfg->domain_name, "Domain Name");
+ log_if_unable(pgcfg->domain_controller,
+ "Domain Controller");
+ log_if_unable(pgcfg->domain_guid, "Domain GUID");
+ log_if_unable(pgcfg->forest_name, "Forest Name");
+ log_if_unable(pgcfg->site_name, "Site Name");
+ }
+
+out:
+ if (status_fp) {
+ ad_disc_set_StatusFP(ad_ctx, NULL);
+ (void) fclose(status_fp);
+ status_fp = NULL;
+ }
+
+ if (DBG(CONFIG, 1))
+ idmapdlog(LOG_DEBUG, "Domain discovery done.");
+
+ /*
+ * Log when this took more than 15 sec.
+ */
+ t1 = time(NULL);
+ if (t1 > (t0 + 15)) {
+ idmapdlog(LOG_NOTICE, "Domain discovery took %d sec.",
+ (int)(t1 - t0));
+ idmapdlog(LOG_NOTICE, "Check the DNS configuration.");
+ }
+}
+
+/*
+ * This is the second part of discovery, which can take a while.
+ * We don't want to hold up parties who just want to know what
+ * domain controller we're using (like smbd), so this part runs
+ * after we've updated that info in the "live" config and told
+ * such consumers to go ahead.
+ *
+ * This is a lot like idmap_cfg_discover(), but used LDAP queries
+ * get the forest information from the global catalog servers.
+ *
+ * Note: the previous update_* calls have usually nuked any
+ * useful information from pgcfg before we get here, so we
+ * can only use it store discovery results, not to read.
+ */
+static void
+idmap_cfg_discover2(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
+{
+ ad_disc_t ad_ctx = handles->ad_ctx;
+ FILE *status_fp = NULL;
+ time_t t0, t1;
+
+ t0 = time(NULL);
+ if (DBG(CONFIG, 1))
+ idmapdlog(LOG_DEBUG, "Running forest discovery.");
+
+ status_fp = fopen(IDMAP_CACHEDIR "/discovery.log", "a");
+ if (status_fp)
+ ad_disc_set_StatusFP(ad_ctx, status_fp);
+
+ if (pgcfg->global_catalog == NULL)
+ pgcfg->global_catalog =
+ ad_disc_get_GlobalCatalog(ad_ctx,
+ AD_DISC_PREFER_SITE,
+ &pgcfg->global_catalog_auto_disc);
+
+ if (pgcfg->global_catalog != NULL) {
pgcfg->domains_in_forest =
ad_disc_get_DomainsInForest(ad_ctx, NULL);
if (!pgcfg->disable_cross_forest_trusts)
discover_trusted_domains(pgcfg, ad_ctx);
+ }
- if (DBG(CONFIG, 1)) {
- log_if_unable(pgcfg->domain_name, "Domain Name");
- log_if_unable(pgcfg->domain_controller,
- "Domain Controller");
- log_if_unable(pgcfg->forest_name, "Forest Name");
- log_if_unable(pgcfg->site_name, "Site Name");
- log_if_unable(pgcfg->global_catalog, "Global Catalog");
- log_if_unable(pgcfg->domains_in_forest,
- "Domains in the Forest");
- if (!pgcfg->disable_cross_forest_trusts) {
- log_if_unable(pgcfg->trusted_domains,
- "Trusted Domains");
- }
- }
+ if (DBG(CONFIG, 1)) {
+ log_if_unable(pgcfg->global_catalog, "Global Catalog");
+ log_if_unable(pgcfg->domains_in_forest,
+ "Domains in the Forest");
+ /* Empty trusted domains list is OK. */
}
- ad_disc_done(ad_ctx);
+ if (status_fp) {
+ ad_disc_set_StatusFP(ad_ctx, NULL);
+ (void) fclose(status_fp);
+ status_fp = NULL;
+ }
if (DBG(CONFIG, 1))
- idmapdlog(LOG_DEBUG, "Discovery done.");
+ idmapdlog(LOG_DEBUG, "Forest discovery done.");
+
+ /*
+ * Log when this took more than 30 sec.
+ */
+ t1 = time(NULL);
+ if (t1 > (t0 + 30)) {
+ idmapdlog(LOG_NOTICE, "Forest discovery took %d sec.",
+ (int)(t1 - t0));
+ idmapdlog(LOG_NOTICE, "Check AD join status.");
+ }
}
@@ -1912,9 +2132,11 @@ idmap_cfg_discover(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
int
idmap_cfg_load(idmap_cfg_t *cfg, int flags)
{
+ const ad_disc_t ad_ctx = cfg->handles.ad_ctx;
int rc = 0;
int errors;
int changed = 0;
+ int dc_changed = 0;
int ad_reload_required = 0;
idmap_pg_config_t new_pgcfg, *live_pgcfg;
@@ -1929,10 +2151,48 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags)
if ((rc = idmap_cfg_load_smf(&cfg->handles, &new_pgcfg, &errors)) < -1)
goto err;
- if (flags & CFG_DISCOVER)
- idmap_cfg_discover(&cfg->handles, &new_pgcfg);
+ if (flags & CFG_DISCOVER) {
+
+ ad_disc_refresh(ad_ctx);
+
+ /*
+ * Unless we've been asked to forget the current DC,
+ * give preference (in order) to the preferred DC if
+ * configured, or the current DC. These preferences
+ * reduce undesirable DC changes.
+ */
+ if (flags & CFG_FORGET_DC) {
+ (void) ad_disc_set_PreferredDC(ad_ctx, NULL);
+ } else if (new_pgcfg.preferred_dc != NULL) {
+ (void) ad_disc_set_PreferredDC(ad_ctx,
+ new_pgcfg.preferred_dc);
+ } else if (live_pgcfg->domain_controller != NULL) {
+ (void) ad_disc_set_PreferredDC(ad_ctx,
+ live_pgcfg->domain_controller);
+ } else {
+ (void) ad_disc_set_PreferredDC(ad_ctx, NULL);
+ }
+
+ /*
+ * We want a way to tell adspriv_getdcname_1_svc()
+ * (and others) that discovery is running and therefore
+ * they may want to wait a bit or return an error...
+ */
+ (void) mutex_lock(&_idmapdstate.addisc_lk);
+ _idmapdstate.addisc_st |= ADDISC_ST_RUNNING;
+ (void) mutex_unlock(&_idmapdstate.addisc_lk);
+
+ idmap_cfg_discover1(&cfg->handles, &new_pgcfg);
+
+ WRLOCK_CONFIG();
+ (void) mutex_lock(&_idmapdstate.addisc_lk);
+ _idmapdstate.addisc_st = 0;
+ (void) cond_broadcast(&_idmapdstate.addisc_cv);
+ (void) mutex_unlock(&_idmapdstate.addisc_lk);
+ } else {
+ WRLOCK_CONFIG();
+ }
- WRLOCK_CONFIG();
/* Non-discoverable props updated here */
changed += update_uint64(&live_pgcfg->list_size_limit,
@@ -1944,6 +2204,9 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags)
changed += update_uint64(&live_pgcfg->name_cache_timeout,
&new_pgcfg.name_cache_timeout, "name_cache_timeout");
+ changed += update_uint64(&live_pgcfg->rediscovery_interval,
+ &new_pgcfg.rediscovery_interval, "rediscovery_interval");
+
changed += update_string(&live_pgcfg->machine_sid,
&new_pgcfg.machine_sid, "machine_sid");
@@ -1973,16 +2236,29 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags)
changed += update_string(&live_pgcfg->nldap_winname_attr,
&new_pgcfg.nldap_winname_attr, "nldap_winname_attr");
- /* Props that can be discovered and set in SMF updated here */
changed += update_string(&live_pgcfg->default_domain,
&new_pgcfg.default_domain, "default_domain");
- changed += update_string(&live_pgcfg->domain_name,
- &new_pgcfg.domain_name, "domain_name");
+ changed += update_dirs(&live_pgcfg->preferred_dc,
+ &new_pgcfg.preferred_dc, "preferred_dc");
+
+ /* Props that can be discovered or set in SMF updated here */
+
+ if (update_string(&live_pgcfg->domain_name,
+ &new_pgcfg.domain_name, "domain_name")) {
+ changed++;
+ ad_reload_required = TRUE;
+ idmapd_set_krb5_realm(live_pgcfg->domain_name);
+ }
live_pgcfg->domain_name_auto_disc = new_pgcfg.domain_name_auto_disc;
- changed += update_dirs(&live_pgcfg->domain_controller,
+ changed += update_string(&live_pgcfg->domain_guid,
+ &new_pgcfg.domain_guid, "domain_guid");
+ live_pgcfg->domain_guid_auto_disc = new_pgcfg.domain_guid_auto_disc;
+
+ dc_changed = update_dirs(&live_pgcfg->domain_controller,
&new_pgcfg.domain_controller, "domain_controller");
+ changed += dc_changed;
live_pgcfg->domain_controller_auto_disc =
new_pgcfg.domain_controller_auto_disc;
@@ -1994,16 +2270,40 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags)
&new_pgcfg.site_name, "site_name");
live_pgcfg->site_name_auto_disc = new_pgcfg.site_name_auto_disc;
- if (update_dirs(&live_pgcfg->global_catalog,
- &new_pgcfg.global_catalog, "global_catalog")) {
- changed++;
- if (live_pgcfg->global_catalog != NULL &&
- live_pgcfg->global_catalog[0].host[0] != '\0')
- ad_reload_required = TRUE;
+ if (DBG(CONFIG, 1)) {
+ if (changed)
+ idmapdlog(LOG_NOTICE, "Configuration changed");
+ else
+ idmapdlog(LOG_NOTICE, "Configuration unchanged");
+ }
+
+ UNLOCK_CONFIG();
+
+ if (dc_changed != 0) {
+ notify_dc_changed();
}
+
+ /*
+ * Discovery2 can take a while.
+ */
+ if (flags & CFG_DISCOVER) {
+ if (live_pgcfg->domain_name != NULL &&
+ live_pgcfg->forest_name != NULL)
+ idmap_cfg_discover2(&cfg->handles, &new_pgcfg);
+ ad_disc_done(ad_ctx);
+ }
+
+ WRLOCK_CONFIG();
+
+ /* More props that can be discovered or set in SMF */
+
+ changed += update_dirs(&live_pgcfg->global_catalog,
+ &new_pgcfg.global_catalog, "global_catalog");
live_pgcfg->global_catalog_auto_disc =
new_pgcfg.global_catalog_auto_disc;
+ /* Props that are only discovered (never in SMF) */
+
if (update_domains_in_forest(&live_pgcfg->domains_in_forest,
&new_pgcfg.domains_in_forest, "domains_in_forest")) {
changed++;
@@ -2026,11 +2326,6 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags)
ad_reload_required = TRUE;
}
- if (ad_reload_required)
- reload_ad();
-
- idmap_cfg_unload(&new_pgcfg);
-
if (DBG(CONFIG, 1)) {
if (changed)
idmapdlog(LOG_NOTICE, "Configuration changed");
@@ -2040,6 +2335,11 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags)
UNLOCK_CONFIG();
+ if (ad_reload_required)
+ reload_ad();
+
+ idmap_cfg_unload(&new_pgcfg);
+
err:
(void) pthread_mutex_unlock(&cfg->handles.mutex);
@@ -2134,6 +2434,10 @@ idmap_cfg_unload(idmap_pg_config_t *pgcfg)
free(pgcfg->domain_name);
pgcfg->domain_name = NULL;
}
+ if (pgcfg->domain_guid) {
+ free(pgcfg->domain_guid);
+ pgcfg->domain_guid = NULL;
+ }
if (pgcfg->machine_sid) {
free(pgcfg->machine_sid);
pgcfg->machine_sid = NULL;
@@ -2199,8 +2503,45 @@ idmap_cfg_fini(idmap_cfg_t *cfg)
void
idmap_cfg_poke_updates(void)
{
- if (idmapd_ev_port != -1)
+ int prev_st;
+
+ if (DBG(CONFIG, 1)) {
+ idmapdlog(LOG_INFO, "idmap_cfg_poke_updates");
+ }
+
+ (void) mutex_lock(&_idmapdstate.addisc_lk);
+ prev_st = _idmapdstate.addisc_st;
+ _idmapdstate.addisc_st |= ADDISC_ST_REQUESTED;
+ (void) mutex_unlock(&_idmapdstate.addisc_lk);
+
+ if (prev_st & ADDISC_ST_REQUESTED) {
+ idmapdlog(LOG_DEBUG, "already poked");
+ } else {
+ idmapdlog(LOG_DEBUG, "port send poke");
(void) port_send(idmapd_ev_port, POKE_AUTO_DISCOVERY, NULL);
+ }
+}
+
+void
+idmap_cfg_force_rediscovery(void)
+{
+ int prev_st;
+
+ if (DBG(CONFIG, 1)) {
+ idmapdlog(LOG_INFO, "idmap_cfg_force_rediscovery");
+ }
+
+ (void) mutex_lock(&_idmapdstate.addisc_lk);
+ prev_st = _idmapdstate.addisc_st;
+ _idmapdstate.addisc_st |= ADDISC_ST_REQUESTED;
+ (void) mutex_unlock(&_idmapdstate.addisc_lk);
+
+ if (prev_st & ADDISC_ST_REQUESTED) {
+ idmapdlog(LOG_DEBUG, "already kicked");
+ } else {
+ idmapdlog(LOG_DEBUG, "port send kick");
+ (void) port_send(idmapd_ev_port, KICK_AUTO_DISCOVERY, NULL);
+ }
}
/*ARGSUSED*/
@@ -2362,3 +2703,31 @@ idmap_cfg_upgrade(idmap_cfg_t *cfg)
return (0);
}
+
+/*
+ * The LDAP code passes principal names lacking any
+ * realm information, which causes mech_krb5 to do
+ * awful things trying to figure out the realm.
+ * Avoid that by making sure it has a default,
+ * even when krb5.conf is not configured.
+ */
+static void
+idmapd_set_krb5_realm(char *domain)
+{
+ static char realm[MAXHOSTNAMELEN];
+ size_t ilen, olen;
+ int err;
+
+ if (domain == NULL) {
+ (void) unsetenv("KRB5_DEFAULT_REALM");
+ return;
+ }
+
+ /* Convert to upper case, in place. */
+ (void) strlcpy(realm, domain, sizeof (realm));
+ olen = ilen = strlen(realm);
+ (void) u8_textprep_str(realm, &ilen, realm, &olen,
+ U8_TEXTPREP_TOUPPER, U8_UNICODE_LATEST, &err);
+
+ (void) setenv("KRB5_DEFAULT_REALM", realm, 1);
+}
diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.h b/usr/src/cmd/idmap/idmapd/idmap_config.h
index f2ed7caa7c..6792d77774 100644
--- a/usr/src/cmd/idmap/idmapd/idmap_config.h
+++ b/usr/src/cmd/idmap/idmapd/idmap_config.h
@@ -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.
*/
#ifndef _IDMAP_CONFIG_H
@@ -31,6 +31,7 @@
#include "addisc.h"
#include <libscf.h>
#include <synch.h>
+#include <sys/uuid.h>
#ifdef __cplusplus
extern "C" {
@@ -66,8 +67,7 @@ typedef struct idmap_cfg_handles {
*/
typedef struct idmap_trustedforest {
char *forest_name;
- idmap_ad_disc_ds_t
- *global_catalog; /* global catalog hosts */
+ ad_disc_ds_t *global_catalog; /* global catalog hosts */
ad_disc_domainsinforest_t
*domains_in_forest;
} idmap_trustedforest_t;
@@ -77,20 +77,21 @@ typedef struct idmap_pg_config {
uint64_t list_size_limit;
uint64_t id_cache_timeout;
uint64_t name_cache_timeout;
+ uint64_t rediscovery_interval;
char *machine_uuid; /* machine uuid */
char *machine_sid; /* machine sid */
char *default_domain; /* default domain name */
char *domain_name; /* AD domain name */
- boolean_t domain_name_auto_disc;
- idmap_ad_disc_ds_t
- *domain_controller; /* domain controller hosts */
+ boolean_t domain_name_auto_disc;
+ char *domain_guid; /* GUID (string) */
+ boolean_t domain_guid_auto_disc;
+ ad_disc_ds_t *domain_controller; /* domain controller hosts */
boolean_t domain_controller_auto_disc;
char *forest_name; /* forest name */
boolean_t forest_name_auto_disc;
char *site_name; /* site name */
boolean_t site_name_auto_disc;
- idmap_ad_disc_ds_t
- *global_catalog; /* global catalog hosts */
+ ad_disc_ds_t *global_catalog; /* global catalog hosts */
boolean_t global_catalog_auto_disc;
ad_disc_domainsinforest_t
*domains_in_forest;
@@ -100,6 +101,9 @@ typedef struct idmap_pg_config {
idmap_trustedforest_t
*trusted_forests; /* Array of trusted forests */
+ ad_disc_ds_t *preferred_dc;
+ boolean_t preferred_dc_auto_disc;
+
/*
* Following properties are associated with directory-based
* name-mappings.
@@ -128,10 +132,12 @@ extern int idmap_cfg_fini(idmap_cfg_t *);
extern int idmap_cfg_upgrade(idmap_cfg_t *);
extern int idmap_cfg_start_updates(void);
extern void idmap_cfg_poke_updates(void);
+extern void idmap_cfg_force_rediscovery(void);
extern void idmap_cfg_hup_handler(int);
-#define CFG_DISCOVER 0x1
-#define CFG_LOG 0x2
+#define CFG_DISCOVER 0x1 /* Run discovery */
+#define CFG_FORGET_DC 0x2 /* Forget current DC. */
+#define CFG_LOG 0x4
#ifdef __cplusplus
}
diff --git a/usr/src/cmd/idmap/idmapd/idmap_lsa.c b/usr/src/cmd/idmap/idmapd/idmap_lsa.c
index 256e8494d8..28c6c2755e 100644
--- a/usr/src/cmd/idmap/idmapd/idmap_lsa.c
+++ b/usr/src/cmd/idmap/idmapd/idmap_lsa.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -235,3 +236,13 @@ out:
}
return (ret);
}
+
+/*
+ * This exists just so we can avoid exposing all of idmapd to libsmb.h.
+ * Like the above functions, it's a door call over to smbd.
+ */
+void
+notify_dc_changed(void)
+{
+ smb_notify_dc_changed();
+}
diff --git a/usr/src/cmd/idmap/idmapd/idmapd.c b/usr/src/cmd/idmap/idmapd/idmapd.c
index 828bfc6545..3698270ab6 100644
--- a/usr/src/cmd/idmap/idmapd/idmapd.c
+++ b/usr/src/cmd/idmap/idmapd/idmapd.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
@@ -57,10 +58,16 @@
#include <assert.h>
#include <note.h>
+#define CBUFSIZ 26 /* ctime(3c) */
+
static void term_handler(int);
static void init_idmapd();
static void fini_idmapd();
+/* The DC Locator lives inside idmap (for now). */
+extern void init_dc_locator(void);
+extern void fini_dc_locator(void);
+
idmapd_state_t _idmapdstate;
SVCXPRT *xprt = NULL;
@@ -170,6 +177,7 @@ static void
term_handler(int sig)
{
idmapdlog(LOG_INFO, "Terminating.");
+ fini_dc_locator();
fini_idmapd();
_exit(0);
}
@@ -250,6 +258,13 @@ main(int argc, char **argv)
int c;
struct rlimit rl;
+ if (rwlock_init(&_idmapdstate.rwlk_cfg, USYNC_THREAD, NULL) != 0)
+ return (-1);
+ if (mutex_init(&_idmapdstate.addisc_lk, USYNC_THREAD, NULL) != 0)
+ return (-1);
+ if (cond_init(&_idmapdstate.addisc_cv, USYNC_THREAD, NULL) != 0)
+ return (-1);
+
_idmapdstate.daemon_mode = TRUE;
while ((c = getopt(argc, argv, "d")) != -1) {
switch (c) {
@@ -303,6 +318,7 @@ main(int argc, char **argv)
idmap_init_tsd_key();
init_idmapd();
+ init_dc_locator();
/* signal handlers that should run only after we're initialized */
(void) sigset(SIGTERM, term_handler);
@@ -348,6 +364,7 @@ init_idmapd()
*/
(void) unlink(IDMAP_CACHEDIR "/ccache");
(void) putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache");
+ (void) putenv("MS_INTEROP=1");
if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname,
sizeof (_idmapdstate.hostname)) == -1) {
@@ -505,7 +522,18 @@ restore_svc(void)
/* printflike */
void
idmapdlog(int pri, const char *format, ...) {
+ static time_t prev_ts;
va_list args;
+ char cbuf[CBUFSIZ];
+ time_t ts;
+
+ ts = time(NULL);
+ if (prev_ts != ts) {
+ prev_ts = ts;
+ /* NB: cbuf has \n */
+ (void) fprintf(stderr, "@ %s",
+ ctime_r(&ts, cbuf, sizeof (cbuf)));
+ }
va_start(args, format);
(void) vfprintf(stderr, format, args);
diff --git a/usr/src/cmd/idmap/idmapd/idmapd.h b/usr/src/cmd/idmap/idmapd/idmapd.h
index 7ed2740a34..a364cd4d9b 100644
--- a/usr/src/cmd/idmap/idmapd/idmapd.h
+++ b/usr/src/cmd/idmap/idmapd/idmapd.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 2014 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _IDMAPD_H
@@ -104,10 +104,16 @@ typedef struct idmapd_state {
adutils_ad_t **gcs;
int num_dcs;
adutils_ad_t **dcs;
+ mutex_t addisc_lk;
+ cond_t addisc_cv;
+ int addisc_st;
int debug[IDMAPD_DEBUG_MAX+1];
} idmapd_state_t;
extern idmapd_state_t _idmapdstate;
+#define ADDISC_ST_REQUESTED 1
+#define ADDISC_ST_RUNNING 2
+
#define RDLOCK_CONFIG() \
(void) rw_rdlock(&_idmapdstate.rwlk_cfg);
#define WRLOCK_CONFIG() \
@@ -308,6 +314,7 @@ extern void reload_ad();
extern void idmap_init_tsd_key(void);
extern void degrade_svc(int, const char *);
extern void restore_svc(void);
+extern void notify_dc_changed(void);
extern int init_dbs();
diff --git a/usr/src/cmd/idmap/idmapd/init.c b/usr/src/cmd/idmap/idmapd/init.c
index 3e5dde7ad8..8516e07f4f 100644
--- a/usr/src/cmd/idmap/idmapd/init.c
+++ b/usr/src/cmd/idmap/idmapd/init.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.
*/
/*
@@ -44,8 +44,6 @@ init_mapping_system()
{
int rc = 0;
- if (rwlock_init(&_idmapdstate.rwlk_cfg, USYNC_THREAD, NULL) != 0)
- return (-1);
if ((rc = load_config()) < 0)
return (rc);
@@ -139,7 +137,7 @@ reload_gcs()
* If that stops working we'll go into degraded mode anyways
* when it does.
*/
- degrade_svc(0,
+ idmapdlog(LOG_INFO,
"Global Catalog servers not configured/discoverable");
return;
}
@@ -272,7 +270,7 @@ reload_dcs(void)
* If that stops working we'll go into degraded mode anyways
* when it does.
*/
- degrade_svc(0,
+ idmapdlog(LOG_INFO,
"Domain controller servers not configured/discoverable");
return;
}
diff --git a/usr/src/cmd/idmap/idmapd/krb5_lookup.c b/usr/src/cmd/idmap/idmapd/krb5_lookup.c
new file mode 100644
index 0000000000..a45fc5d8f3
--- /dev/null
+++ b/usr/src/cmd/idmap/idmapd/krb5_lookup.c
@@ -0,0 +1,156 @@
+/*
+ * 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 <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/note.h>
+#include <synch.h>
+#include <thread.h>
+
+#include "idmapd.h"
+#include "libadutils.h"
+#include "locate_plugin.h"
+
+/* osconf.h - sigh */
+#define KRB5_DEFAULT_PORT 88
+#define DEFAULT_KADM5_PORT 749
+#define DEFAULT_KPASSWD_PORT 464
+
+/*
+ * This is an "override plugin" used by libkrb5. See:
+ * lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c
+ *
+ * The interface is based on:
+ * http://web.mit.edu/~kerberos/krb5-1.12/doc/plugindev/locate.html
+ */
+
+/*
+ * Called by krb5int_locate_server / override_locate_server
+ */
+
+krb5_error_code
+_krb5_override_service_locator(
+ void *arg0,
+ enum locate_service_type svc,
+ const char *realm,
+ int socktype,
+ int family,
+ int (*cbfunc)(void *, int, struct sockaddr *),
+ void *cbdata)
+{
+ _NOTE(ARGUNUSED(arg0))
+ idmap_pg_config_t *pgcfg;
+ ad_disc_ds_t *ds;
+ int rc = KRB5_PLUGIN_NO_HANDLE;
+ short port;
+
+ /*
+ * Is this a service we want to override?
+ */
+ switch (svc) {
+ case locate_service_kdc:
+ case locate_service_master_kdc:
+ port = htons(KRB5_DEFAULT_PORT);
+ break;
+ case locate_service_kadmin:
+ port = htons(DEFAULT_KADM5_PORT);
+ break;
+ case locate_service_kpasswd:
+ port = htons(DEFAULT_KPASSWD_PORT);
+ break;
+ case locate_service_krb524:
+ default:
+ return (rc);
+ }
+
+ RDLOCK_CONFIG();
+ pgcfg = &_idmapdstate.cfg->pgcfg;
+
+ /*
+ * Is this a realm we want to override?
+ */
+ if (pgcfg->domain_name == NULL)
+ goto out;
+ if (0 != strcasecmp(realm, pgcfg->domain_name))
+ goto out;
+
+ /*
+ * Yes, this is our domain. Have a DC?
+ */
+ if ((ds = pgcfg->domain_controller) == NULL) {
+ rc = KRB5_REALM_CANT_RESOLVE;
+ goto out;
+ }
+
+ switch (family) {
+ case AF_UNSPEC:
+ break; /* OK */
+ case AF_INET:
+ case AF_INET6:
+ if (family == ds->addr.ss_family)
+ break; /* OK */
+ /* else fallthrough */
+ default:
+ rc = KRB5_ERR_NO_SERVICE;
+ goto out;
+ }
+
+ /*
+ * Provide the service address we have.
+ */
+ switch (ds->addr.ss_family) {
+ case AF_INET: {
+ struct sockaddr_in sin;
+ struct sockaddr_in *dsa = (void *)&ds->addr;
+ (void) memset(&sin, 0, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = port;
+ (void) memcpy(&sin.sin_addr, &dsa->sin_addr,
+ sizeof (sin.sin_addr));
+ rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin);
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in6 *dsa6 = (void *)&ds->addr;
+ (void) memset(&sin6, 0, sizeof (sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = port;
+ (void) memcpy(&sin6.sin6_addr, &dsa6->sin6_addr,
+ sizeof (sin6.sin6_addr));
+ rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin6);
+ break;
+ }
+ default:
+ rc = KRB5_ERR_NO_SERVICE;
+ goto out;
+ }
+ /* rc from cbfunc is special. */
+ if (rc)
+ rc = ENOMEM;
+
+out:
+ UNLOCK_CONFIG();
+
+ return (rc);
+}
diff --git a/usr/src/cmd/idmap/idmapd/rpc_svc.c b/usr/src/cmd/idmap/idmapd/rpc_svc.c
deleted file mode 100644
index f959d7b918..0000000000
--- a/usr/src/cmd/idmap/idmapd/rpc_svc.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * Please do not edit this file.
- * It was generated using rpcgen.
- * Edit idmap_prot.x and rebuild this file with
- * rpcgen -CMNm -o idmap_prot_svc.c.new ../../../uts/common/rpcsvc/idmap_prot.x
- * then merge as required. A recent version of rpcgen is needed to
- * produce this exact file; when the revised rpcgen is available in the
- * build environment this file can be built automatically.
- */
-
-#include "../../../uts/common/rpcsvc/idmap_prot.h"
-#include <stdio.h>
-#include <stdlib.h> /* getenv, exit */
-#include <signal.h>
-#include <rpc/pmap_clnt.h> /* for pmap_unset */
-#include <string.h> /* strcmp */
-#include <unistd.h> /* setsid */
-#include <sys/types.h>
-#include <memory.h>
-#include <stropts.h>
-#include <sys/resource.h> /* rlimit */
-#include <syslog.h>
-
-#ifndef SIG_PF
-#define SIG_PF void(*)(int)
-#endif
-
-#ifdef DEBUG
-#define RPC_SVC_FG
-#endif
-
-#define _RPCSVC_CLOSEDOWN 120
-extern int _rpcpmstart; /* Started by a port monitor ? */
-
-/* States a server can be in wrt request */
-
-#define _IDLE 0
-#define _SERVED 1
-
-/* LINTED static unused if no main */
-static int _rpcsvcstate = _IDLE; /* Set when a request is serviced */
-static int _rpcsvccount = 0; /* Number of requests being serviced */
-mutex_t _svcstate_lock; /* lock for _rpcsvcstate, _rpcsvccount */
-
-#if defined(RPC_MSGOUT)
-extern void RPC_MSGOUT(const char *, ...);
-#else /* defined(RPC_MSGOUT) */
-static void
-RPC_MSGOUT(const char *fmt, char *msg)
-{
-#ifdef RPC_SVC_FG
- if (_rpcpmstart)
- syslog(LOG_ERR, fmt, msg);
- else {
- (void) fprintf(stderr, fmt, msg);
- (void) putc('\n', stderr);
- }
-#else
- syslog(LOG_ERR, fmt, msg);
-#endif
-}
-#endif /* defined(RPC_MSGOUT) */
-
-/* ARGSUSED */
-int
-_idmap_null_1(
- void *argp,
- void *result,
- struct svc_req *rqstp)
-{
- return (idmap_null_1_svc(result, rqstp));
-}
-
-int
-_idmap_get_mapped_ids_1(
- idmap_mapping_batch *argp,
- idmap_ids_res *result,
- struct svc_req *rqstp)
-{
- return (idmap_get_mapped_ids_1_svc(*argp, result, rqstp));
-}
-
-int
-_idmap_list_mappings_1(
- idmap_list_mappings_1_argument *argp,
- idmap_mappings_res *result,
- struct svc_req *rqstp)
-{
- return (idmap_list_mappings_1_svc(
- argp->lastrowid,
- argp->limit,
- argp->flag,
- result, rqstp));
-}
-
-int
-_idmap_list_namerules_1(
- idmap_list_namerules_1_argument *argp,
- idmap_namerules_res *result,
- struct svc_req *rqstp)
-{
- return (idmap_list_namerules_1_svc(
- argp->rule,
- argp->lastrowid,
- argp->limit,
- result, rqstp));
-}
-
-int
-_idmap_update_1(
- idmap_update_batch *argp,
- idmap_update_res *result,
- struct svc_req *rqstp)
-{
- return (idmap_update_1_svc(*argp, result, rqstp));
-}
-
-int
-_idmap_get_mapped_id_by_name_1(
- idmap_mapping *argp,
- idmap_mappings_res *result,
- struct svc_req *rqstp)
-{
- return (idmap_get_mapped_id_by_name_1_svc(*argp, result, rqstp));
-}
-
-int
-_idmap_get_prop_1(
- idmap_prop_type *argp,
- idmap_prop_res *result,
- struct svc_req *rqstp)
-{
- return (idmap_get_prop_1_svc(*argp, result, rqstp));
-}
-
-int
-_directory_get_common_1(
- directory_get_common_1_argument *argp,
- directory_results_rpc *result,
- struct svc_req *rqstp)
-{
- return (directory_get_common_1_svc(
- argp->ids,
- argp->types,
- argp->attrs,
- result, rqstp));
-}
-
-int
-_idmap_flush_1(
- idmap_flush_op *argp,
- idmap_retcode *result,
- struct svc_req *rqstp)
-{
- return (idmap_flush_1_svc(*argp, result, rqstp));
-}
-
-void
-idmap_prog_1(struct svc_req *rqstp, register SVCXPRT *transp)
-{
- union {
- idmap_mapping_batch idmap_get_mapped_ids_1_arg;
- idmap_list_mappings_1_argument idmap_list_mappings_1_arg;
- idmap_list_namerules_1_argument idmap_list_namerules_1_arg;
- idmap_update_batch idmap_update_1_arg;
- idmap_mapping idmap_get_mapped_id_by_name_1_arg;
- idmap_prop_type idmap_get_prop_1_arg;
- directory_get_common_1_argument directory_get_common_1_arg;
- idmap_flush_op idmap_flush_1_arg;
- } argument;
- union {
- idmap_ids_res idmap_get_mapped_ids_1_res;
- idmap_mappings_res idmap_list_mappings_1_res;
- idmap_namerules_res idmap_list_namerules_1_res;
- idmap_update_res idmap_update_1_res;
- idmap_mappings_res idmap_get_mapped_id_by_name_1_res;
- idmap_prop_res idmap_get_prop_1_res;
- directory_results_rpc directory_get_common_1_res;
- idmap_retcode idmap_flush_1_res;
- } result;
- bool_t retval;
- xdrproc_t _xdr_argument, _xdr_result;
- bool_t (*local)(char *, void *, struct svc_req *);
-
- (void) mutex_lock(&_svcstate_lock);
- _rpcsvccount++;
- (void) mutex_unlock(&_svcstate_lock);
- switch (rqstp->rq_proc) {
- case IDMAP_NULL:
- _xdr_argument = (xdrproc_t)
- xdr_void;
- _xdr_result = (xdrproc_t)
- xdr_void;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _idmap_null_1;
- break;
-
- case IDMAP_GET_MAPPED_IDS:
- _xdr_argument = (xdrproc_t)
- xdr_idmap_mapping_batch;
- _xdr_result = (xdrproc_t)
- xdr_idmap_ids_res;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _idmap_get_mapped_ids_1;
- break;
-
- case IDMAP_LIST_MAPPINGS:
- _xdr_argument = (xdrproc_t)
- xdr_idmap_list_mappings_1_argument;
- _xdr_result = (xdrproc_t)
- xdr_idmap_mappings_res;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _idmap_list_mappings_1;
- break;
-
- case IDMAP_LIST_NAMERULES:
- _xdr_argument = (xdrproc_t)
- xdr_idmap_list_namerules_1_argument;
- _xdr_result = (xdrproc_t)
- xdr_idmap_namerules_res;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _idmap_list_namerules_1;
- break;
-
- case IDMAP_UPDATE:
- _xdr_argument = (xdrproc_t)
- xdr_idmap_update_batch;
- _xdr_result = (xdrproc_t)
- xdr_idmap_update_res;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _idmap_update_1;
- break;
-
- case IDMAP_GET_MAPPED_ID_BY_NAME:
- _xdr_argument = (xdrproc_t)
- xdr_idmap_mapping;
- _xdr_result = (xdrproc_t)
- xdr_idmap_mappings_res;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _idmap_get_mapped_id_by_name_1;
- break;
-
- case IDMAP_GET_PROP:
- _xdr_argument = (xdrproc_t)
- xdr_idmap_prop_type;
- _xdr_result = (xdrproc_t)
- xdr_idmap_prop_res;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _idmap_get_prop_1;
- break;
-
- case DIRECTORY_GET_COMMON:
- _xdr_argument = (xdrproc_t)
- xdr_directory_get_common_1_argument;
- _xdr_result = (xdrproc_t)
- xdr_directory_results_rpc;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _directory_get_common_1;
- break;
-
- case IDMAP_FLUSH:
- _xdr_argument = (xdrproc_t)
- xdr_idmap_flush_op;
- _xdr_result = (xdrproc_t)
- xdr_idmap_retcode;
- local = (bool_t (*) (char *, void *, struct svc_req *))
- _idmap_flush_1;
- break;
-
- default:
- svcerr_noproc(transp);
- (void) mutex_lock(&_svcstate_lock);
- _rpcsvccount--;
- _rpcsvcstate = _SERVED;
- (void) mutex_unlock(&_svcstate_lock);
- return; /* CSTYLED */
- }
- (void) memset((char *)&argument, 0, sizeof (argument));
- if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) {
- svcerr_decode(transp);
- (void) mutex_lock(&_svcstate_lock);
- _rpcsvccount--;
- _rpcsvcstate = _SERVED;
- (void) mutex_unlock(&_svcstate_lock);
- return; /* CSTYLED */
- }
- retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp);
- if (_xdr_result && retval > 0 &&
- !svc_sendreply(transp, _xdr_result, (char *)&result)) {
- svcerr_systemerr(transp);
- }
- if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) {
- RPC_MSGOUT("%s",
- "unable to free arguments");
- exit(1);
- }
- if (_xdr_result != NULL) {
- if (!idmap_prog_1_freeresult(transp, _xdr_result,
- (caddr_t)&result))
- RPC_MSGOUT("%s",
- "unable to free results");
-
- }
- (void) mutex_lock(&_svcstate_lock);
- _rpcsvccount--;
- _rpcsvcstate = _SERVED;
- (void) mutex_unlock(&_svcstate_lock);
- return; /* CSTYLED */
-}
diff --git a/usr/src/cmd/idmap/nltest/Makefile b/usr/src/cmd/idmap/nltest/Makefile
new file mode 100644
index 0000000000..1e6cb034fd
--- /dev/null
+++ b/usr/src/cmd/idmap/nltest/Makefile
@@ -0,0 +1,67 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL 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.
+#
+
+PROG = nltest
+CLIENTOBJS = nltest.o
+
+CLIENTSRCS = $(CLIENTOBJS:%.o=%.c)
+POFILES = $(CLIENTOBJS:.o=.po)
+OBJS = $(CLIENTOBJS)
+SRCS = $(CLIENTSRCS)
+
+include ../../Makefile.cmd
+
+POFILE = $(PROG)_all.po
+LDLIBS += -lads -luuid
+FILEMODE = 0555
+
+INCS += -I.
+
+CFLAGS += $(CCVERBOSE)
+$(OBJS) := CPPFLAGS += $(INCS) -D_REENTRANT
+$(POFILE) := CPPFLAGS += $(INCS)
+
+lint_SRCS := CPPFLAGS += $(INCS)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -g -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/idmap/nltest/Run.sh b/usr/src/cmd/idmap/nltest/Run.sh
new file mode 100755
index 0000000000..f77af1e7d9
--- /dev/null
+++ b/usr/src/cmd/idmap/nltest/Run.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Helper program to run nltest
+
+
+[ -n "$CODEMGR_WS" ] || {
+ echo "Need a buildenv to set CODEMGR_WS=..."
+ exit 1;
+}
+
+
+ROOT=${CODEMGR_WS}/proto/root_i386
+LD_LIBRARY_PATH=$ROOT/usr/lib:$ROOT/lib
+export LD_LIBRARY_PATH
+
+$ROOT/usr/sbin/nltest "$@"
diff --git a/usr/src/cmd/idmap/nltest/nltest.c b/usr/src/cmd/idmap/nltest/nltest.c
new file mode 100644
index 0000000000..002fe0f157
--- /dev/null
+++ b/usr/src/cmd/idmap/nltest/nltest.c
@@ -0,0 +1,331 @@
+/*
+ * 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 <libintl.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <err.h>
+#include <ads/dsgetdc.h>
+#include <smb/nterror.h>
+#include <uuid/uuid.h>
+
+
+static void dclist_usage(void);
+static int cmd_dclist(char *);
+static void dcname_usage(void);
+static int cmd_dcname(char *);
+static void dsgetdc_usage(void);
+static int cmd_dsgetdc(char *);
+static void dsgetdcname_usage(void);
+static int cmd_dsgetdcname(char *);
+static void kick_usage(void);
+static int cmd_kick(char *);
+static void help(void);
+
+typedef int cmd_fn_t (char *);
+typedef void cmd_usage_t (void);
+
+
+static struct commands {
+ const char *name; /* name of subcommand */
+ cmd_fn_t *fn; /* pointer to subcommand handler function */
+ cmd_usage_t *usage; /* pointer to subcommand help function */
+ int optreq; /* does this have a required optval */
+} commands[] = {
+ {"dclist", cmd_dclist, dclist_usage, 0},
+ {"dcname", cmd_dcname, dcname_usage, 0},
+ {"dsgetdc", cmd_dsgetdc, dsgetdc_usage, 0},
+ {"dsgetdcname", cmd_dsgetdcname, dsgetdcname_usage, 0},
+ {"kick", cmd_kick, kick_usage, 0},
+ {NULL, NULL, NULL, 0}
+};
+
+
+/*
+ * lookupcmd
+ */
+static struct commands *
+lookupcmd(const char *name)
+{
+ struct commands *cmd;
+
+ for (cmd = commands; cmd->name; cmd++) {
+ if (strcasecmp(cmd->name, name) == 0)
+ return (cmd);
+ }
+ return (NULL);
+}
+
+/*
+ * dclist
+ */
+static void
+dclist_usage(void)
+{
+ (void) printf(gettext("usage: nltest dclist... \n"));
+ exit(1);
+}
+
+/* ARGSUSED */
+static int
+cmd_dclist(char *optval)
+{
+ (void) printf("cmd_dclist() \n");
+ return (0);
+}
+
+/*
+ * dcname
+ */
+static void
+dcname_usage(void)
+{
+ (void) printf(gettext("usage: nltest dcname... \n"));
+ exit(1);
+}
+
+/* ARGSUSED */
+static int
+cmd_dcname(char *optval)
+{
+ (void) printf("cmd_dcname() \n");
+ return (0);
+}
+
+/*
+ * dsgetdc
+ */
+static void
+dsgetdc_usage(void)
+{
+ (void) printf(gettext("usage: nltest dsgetdc... \n"));
+ exit(1);
+}
+
+/* ARGSUSED */
+static int
+cmd_dsgetdc(char *optval)
+{
+ (void) printf("cmd_dsgetdc() \n");
+ return (0);
+}
+
+/*
+ * dsgetdcname
+ */
+static void
+dsgetdcname_usage(void)
+{
+ (void) printf(gettext("usage: nltest dsgetdcname domainname \n"));
+ exit(1);
+}
+
+static int
+cmd_dsgetdcname(char *domname)
+{
+ char uuid_buf[UUID_PRINTABLE_STRING_LENGTH];
+ int err = 0;
+ char *atype;
+ DOMAIN_CONTROLLER_INFO *dcinfo;
+
+ if (domname != NULL)
+ (void) printf(" Domain name supplied: %s \n", domname);
+
+ err = DsGetDcName(NULL, domname, NULL, NULL, 0, &dcinfo);
+
+ switch (err) {
+ case 0:
+ break;
+ case ERROR_NO_SUCH_DOMAIN:
+ (void) printf("Domain controller not found.\n");
+ (void) printf("See: /var/run/idmap/discovery.log\n");
+ exit(1);
+ default:
+ (void) printf("Unexpected error %d\n", err);
+ exit(1);
+ }
+
+ switch (dcinfo->DomainControllerAddressType) {
+ case DS_INET_ADDRESS:
+ atype = "inet";
+ break;
+ case DS_NETBIOS_ADDRESS:
+ atype = "netbios";
+ break;
+ default:
+ atype = "?";
+ break;
+ }
+
+ uuid_unparse(dcinfo->DomainGuid, uuid_buf);
+
+ (void) printf("Data Returned from DsGetDcName() call: \n");
+ (void) printf(" DC Name: %s \n", dcinfo->DomainControllerName);
+ (void) printf(" DC Addr: %s \n", dcinfo->DomainControllerAddress);
+ (void) printf(" DC Addr Type: %s \n", atype);
+ (void) printf(" Domain Name: %s \n", dcinfo->DomainName);
+ (void) printf(" Domain GUID: %s \n", uuid_buf);
+ (void) printf(" DNS Forest Name: %s \n", dcinfo->DnsForestName);
+ (void) printf(" Flags: 0x%x \n", dcinfo->Flags);
+ (void) printf(" DC Site Name: %s \n", dcinfo->DcSiteName);
+ (void) printf(" Client Site Name: %s \n", dcinfo->ClientSiteName);
+
+ return (0);
+}
+
+/*
+ * kick
+ */
+static void
+kick_usage(void)
+{
+ (void) printf(gettext("usage: nltest /KICK \n"));
+ exit(1);
+}
+
+
+static int
+cmd_kick(char *domname)
+{
+ int flags = 0;
+ int result;
+
+ result = _DsForceRediscovery(domname, flags);
+
+ return (result);
+}
+
+/*
+ * help functions
+ */
+
+static void
+help(void) {
+ (void) printf("\n");
+ /*
+ * TODO: We may want to revise this help text. It's basically
+ * a copy-paste from:
+ * http://technet.microsoft.com/en-us/library/cc731935.aspx
+ */
+ (void) printf(gettext("usage: %s /subcommand\n"),
+ (char *)getexecname());
+ (void) printf(gettext("where subcommands are:\n"
+#if 0 /* not yet */
+ " dclist Lists all domain controllers in the domain.\n"
+ " dcname Lists the PDC or PDC emulator.\n"
+ " dsgetdc Queries DNS server for list of DCs and"
+ " their IP addresses and contacts each DC to check"
+ " for connectivity.\n"
+#endif
+ " dsgetdcname returns the name of a domain controller in a"
+ " specified domain\n"
+ " help display help on specified subcommand\n"
+ " kick trigger domain controller re-discovery\n"
+ "\n"));
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct commands *cmd;
+ int err = 0;
+ char *option_cmd = NULL;
+ char *arg;
+ char *p;
+ char *optname;
+ char *optval = NULL;
+ int i;
+ int optind = 1;
+
+ /*
+ * Parse options.
+ */
+ while (optind < argc) {
+ arg = argv[optind];
+ optname = NULL;
+ optval = NULL;
+
+ /* Is this an option? */
+ if (arg[0] == '/') {
+ optname = arg + 1;
+ optind++;
+
+ /*
+ * May have /optname:value
+ */
+ if ((p = strchr(optname, ':')) != NULL) {
+ *p++ = '\0';
+ optval = p;
+ }
+ } else if (arg[0] == '-' && arg[1] == '-') {
+ optname = arg + 2;
+ optind++;
+
+ /*
+ * May have --optname=value
+ */
+ if ((p = strchr(optname, '=')) != NULL) {
+ *p++ = '\0';
+ optval = p;
+ }
+ } else {
+ /* Not an option. Stop parsing. */
+ break;
+ }
+
+ /*
+ * Handle each optname (and maybe its optval)
+ * Might put this logic in a table of options.
+ * (including a flag for "optval required",
+ * so that check could be factored out)
+ */
+ for (cmd = commands; cmd->name; cmd++) {
+ if (!strcasecmp(optname, cmd->name)) {
+ /* cmd->name requires an optval */
+ if (optval == NULL && optind < argc)
+ optval = argv[optind++];
+
+ if (optval == NULL && cmd->optreq > 0) {
+ (void) fprintf(stderr,
+ "%s: option %s requires a value\n",
+ argv[0], optname);
+ return (1);
+ }
+ option_cmd = optname;
+ }
+ }
+ }
+
+ /*
+ * Handle remaining non-option arguments
+ */
+ for (i = optind; i < argc; i++) {
+ (void) printf("arg: %s\n", argv[i]);
+ }
+
+ if (option_cmd == NULL)
+ help();
+
+ cmd = lookupcmd(option_cmd);
+ if (cmd == NULL)
+ err = 1;
+ else
+ err = cmd->fn(optval);
+
+ return (err);
+}
diff --git a/usr/src/cmd/idmap/test-getdc/.dbxrc b/usr/src/cmd/idmap/test-getdc/.dbxrc
new file mode 100644
index 0000000000..29a559ed45
--- /dev/null
+++ b/usr/src/cmd/idmap/test-getdc/.dbxrc
@@ -0,0 +1,23 @@
+
+#
+# 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.
+#
+
+set -o emacs
+
+setenv ROOT ${CODEMGR_WS}/proto/root_i386
+setenv LD_LIBRARY_PATH ${ROOT}/usr/lib:${ROOT}/lib
+
+loadobject -load ${ROOT}/usr/lib/libadutils.so.1
+debug ./test-getdc
diff --git a/usr/src/cmd/idmap/test-getdc/Makefile b/usr/src/cmd/idmap/test-getdc/Makefile
new file mode 100644
index 0000000000..10238ebb01
--- /dev/null
+++ b/usr/src/cmd/idmap/test-getdc/Makefile
@@ -0,0 +1,70 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL 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.
+#
+
+PROG = test-getdc
+OBJS = getdc_main.o
+
+SRCS = $(OBJS:%.o=%.c)
+POFILES = $(OBJS:.o=.po)
+
+include ../../Makefile.cmd
+
+POFILE = $(PROG)_all.po
+
+LDLIBS += -ladutils -lnsl -lumem
+LDFLAGS += -R'$$ORIGIN/../lib'
+
+INCS += -I. -I../../../lib/libadutils/common
+
+LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2
+
+CFLAGS += $(CCVERBOSE)
+$(OBJS) := CPPFLAGS += $(INCS) -D_REENTRANT
+$(POFILE) := CPPFLAGS += $(INCS)
+
+lint_SRCS := CPPFLAGS += $(INCS)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS) FRC
+ $(LINK.c) -g -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../Makefile.targ
+
+FRC:
diff --git a/usr/src/cmd/idmap/test-getdc/getdc_main.c b/usr/src/cmd/idmap/test-getdc/getdc_main.c
new file mode 100644
index 0000000000..a0465bcc07
--- /dev/null
+++ b/usr/src/cmd/idmap/test-getdc/getdc_main.c
@@ -0,0 +1,154 @@
+/*
+ * 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 <sys/note.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <addisc.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+int debug;
+char *domainname = NULL;
+
+void print_ds(ad_disc_ds_t *);
+void mylogger(int pri, const char *format, ...);
+
+int
+main(int argc, char *argv[])
+{
+ ad_disc_t ad_ctx = NULL;
+ boolean_t autodisc;
+ ad_disc_ds_t *dc, *gc;
+ char *s;
+ int c;
+
+ while ((c = getopt(argc, argv, "d")) != -1) {
+ switch (c) {
+ case '?':
+ (void) fprintf(stderr, "bad option: -%c\n", optopt);
+ return (1);
+ case 'd':
+ debug++;
+ break;
+ }
+ }
+
+ if (optind < argc)
+ domainname = argv[optind];
+
+ adutils_set_logger(mylogger);
+ adutils_set_debug(AD_DEBUG_ALL, debug);
+
+ ad_ctx = ad_disc_init();
+ ad_disc_set_StatusFP(ad_ctx, stdout);
+
+ if (domainname)
+ (void) ad_disc_set_DomainName(ad_ctx, domainname);
+
+ ad_disc_refresh(ad_ctx);
+
+ dc = ad_disc_get_DomainController(ad_ctx,
+ AD_DISC_PREFER_SITE, &autodisc);
+ if (dc == NULL) {
+ (void) printf("getdc failed\n");
+ return (1);
+ }
+ (void) printf("Found a DC:\n");
+ print_ds(dc);
+ free(dc);
+
+ s = ad_disc_get_ForestName(ad_ctx, NULL);
+ (void) printf("Forest: %s\n", s);
+ free(s);
+
+ s = ad_disc_get_SiteName(ad_ctx, NULL);
+ (void) printf("Site: %s\n", s);
+ free(s);
+
+ gc = ad_disc_get_GlobalCatalog(ad_ctx,
+ AD_DISC_PREFER_SITE, &autodisc);
+ if (gc != NULL) {
+ (void) printf("Found a GC:\n");
+ print_ds(gc);
+ free(gc);
+ }
+
+ ad_disc_done(ad_ctx);
+ ad_disc_fini(ad_ctx);
+ ad_ctx = NULL;
+
+ return (0);
+}
+
+void
+print_ds(ad_disc_ds_t *ds)
+{
+ char buf[64];
+
+ for (; ds->host[0] != '\0'; ds++) {
+ const char *p;
+
+ (void) printf("Name: %s\n", ds->host);
+ (void) printf(" flags: 0x%X\n", ds->flags);
+ if (ds->addr.ss_family == AF_INET) {
+ struct sockaddr_in *sin;
+ sin = (struct sockaddr_in *)&ds->addr;
+ p = inet_ntop(AF_INET, &sin->sin_addr,
+ buf, sizeof (buf));
+ if (p == NULL)
+ p = "?";
+ (void) printf(" A %s %d\n", p, ds->port);
+ }
+ if (ds->addr.ss_family == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+ sin6 = (struct sockaddr_in6 *)&ds->addr;
+ p = inet_ntop(AF_INET6, &sin6->sin6_addr,
+ buf, sizeof (buf));
+ if (p == NULL)
+ p = "?";
+ (void) printf(" AAAA %s %d\n", p, ds->port);
+ }
+ }
+}
+
+/* printflike */
+void
+mylogger(int pri, const char *format, ...)
+{
+ _NOTE(ARGUNUSED(pri))
+ va_list args;
+
+ va_start(args, format);
+ (void) vfprintf(stderr, format, args);
+ (void) fprintf(stderr, "\n");
+ va_end(args);
+}
+
+/*
+ * This is a unit-test program. Always enable libumem debugging.
+ */
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose"); /* $UMEM_DEBUG setting */
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents"); /* $UMEM_LOGGING setting */
+}
diff --git a/usr/src/cmd/smbsrv/fksmbd/Makefile b/usr/src/cmd/smbsrv/fksmbd/Makefile
index a245c2422e..ca3a8b5fb9 100644
--- a/usr/src/cmd/smbsrv/fksmbd/Makefile
+++ b/usr/src/cmd/smbsrv/fksmbd/Makefile
@@ -29,6 +29,7 @@ OBJS_SMBD= \
smbd_authsvc.o \
smbd_doorsvc.o \
smbd_join.o \
+ smbd_krb5lookup.o \
smbd_krb5ssp.o \
smbd_logon.o \
smbd_main.o \
diff --git a/usr/src/cmd/smbsrv/smbadm/Makefile b/usr/src/cmd/smbsrv/smbadm/Makefile
index 3cdc52d13f..a31acdef20 100644
--- a/usr/src/cmd/smbsrv/smbadm/Makefile
+++ b/usr/src/cmd/smbsrv/smbadm/Makefile
@@ -22,6 +22,8 @@
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+#
PROG= smbadm
SRCS= smbadm.c
@@ -29,7 +31,7 @@ SRCS= smbadm.c
include ../../Makefile.cmd
include ../Makefile.smbsrv.defs
-LDLIBS += -L$(ROOT)/usr/lib/smbsrv -lsmb -lsecdb -lumem
+LDLIBS += -L$(ROOT)/usr/lib/smbsrv -lsmb -lsmbns -lsecdb -lumem
LDFLAGS += -R/usr/lib/smbsrv
.KEEP_STATE:
diff --git a/usr/src/cmd/smbsrv/smbadm/smbadm.c b/usr/src/cmd/smbsrv/smbadm/smbadm.c
index 2ab0e6e06d..f181d45556 100644
--- a/usr/src/cmd/smbsrv/smbadm/smbadm.c
+++ b/usr/src/cmd/smbsrv/smbadm/smbadm.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.
*/
/*
@@ -46,6 +46,7 @@
#include <auth_attr.h>
#include <locale.h>
#include <smbsrv/libsmb.h>
+#include <smbsrv/libsmbns.h>
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
@@ -97,8 +98,8 @@ static char *progname;
static boolean_t smbadm_checkauth(const char *);
static void smbadm_usage(boolean_t);
-static int smbadm_join_workgroup(const char *);
-static int smbadm_join_domain(const char *, const char *);
+static int smbadm_join_workgroup(const char *, boolean_t);
+static int smbadm_join_domain(const char *, const char *, boolean_t);
static void smbadm_extract_domain(char *, char **, char **);
static int smbadm_join(int, char **);
@@ -230,12 +231,12 @@ smbadm_cmdusage(FILE *fp, smbadm_cmdinfo_t *cmd)
case HELP_JOIN:
#if 0 /* Don't document "-p" yet, still needs work (NX 11960) */
- (void) fprintf(fp, gettext("\t%s -p domain\n"
- "\t%s -u username domain\n\t%s -w workgroup\n"),
+ (void) fprintf(fp, gettext("\t%s [-y] -p domain\n"
+ "\t%s [-y] -u username domain\n\t%s [-y] -w workgroup\n"),
cmd->name, cmd->name, cmd->name);
#else
- (void) fprintf(fp, gettext("\t%s -u username domain\n"
- "\t%s -w workgroup\n"), cmd->name, cmd->name);
+ (void) fprintf(fp, gettext("\t%s [-y] -u username domain\n"
+ "\t%s [-y] -w workgroup\n"), cmd->name, cmd->name);
#endif
return;
@@ -461,9 +462,10 @@ smbadm_join(int argc, char **argv)
char *domain = NULL;
char *username = NULL;
uint32_t mode = 0;
+ boolean_t do_prompt = B_TRUE;
char option;
- while ((option = getopt(argc, argv, "pu:w")) != -1) {
+ while ((option = getopt(argc, argv, "pu:wy")) != -1) {
if (mode != 0) {
(void) fprintf(stderr, gettext(
"join options are mutually exclusive\n"));
@@ -484,6 +486,10 @@ smbadm_join(int argc, char **argv)
mode = SMB_SECMODE_WORKGRP;
break;
+ case 'y':
+ do_prompt = B_FALSE;
+ break;
+
default:
smbadm_usage(B_FALSE);
break;
@@ -509,9 +515,9 @@ smbadm_join(int argc, char **argv)
}
if (mode == SMB_SECMODE_WORKGRP) {
- return (smbadm_join_workgroup(domain));
+ return (smbadm_join_workgroup(domain, do_prompt));
}
- return (smbadm_join_domain(domain, username));
+ return (smbadm_join_domain(domain, username, do_prompt));
}
/*
@@ -520,11 +526,13 @@ smbadm_join(int argc, char **argv)
* with no formal membership mechanism.
*/
static int
-smbadm_join_workgroup(const char *workgroup)
+smbadm_join_workgroup(const char *workgroup, boolean_t prompt)
{
smb_joininfo_t jdi;
+ smb_joinres_t jdres;
uint32_t status;
+ bzero(&jdres, sizeof (jdres));
bzero(&jdi, sizeof (jdi));
jdi.mode = SMB_SECMODE_WORKGRP;
(void) strlcpy(jdi.domain_name, workgroup, sizeof (jdi.domain_name));
@@ -535,10 +543,10 @@ smbadm_join_workgroup(const char *workgroup)
smbadm_usage(B_FALSE);
}
- if (!smbadm_join_prompt(jdi.domain_name))
+ if (prompt && !smbadm_join_prompt(jdi.domain_name))
return (0);
- if ((status = smb_join(&jdi)) != NT_STATUS_SUCCESS) {
+ if ((status = smb_join(&jdi, &jdres)) != NT_STATUS_SUCCESS) {
(void) fprintf(stderr, gettext("failed to join %s: %s\n"),
jdi.domain_name, xlate_nt_status(status));
return (1);
@@ -559,14 +567,15 @@ smbadm_join_workgroup(const char *workgroup)
* to be appended to the username using '+' as a scripting convenience.
*/
static int
-smbadm_join_domain(const char *domain, const char *username)
+smbadm_join_domain(const char *domain, const char *username, boolean_t prompt)
{
smb_joininfo_t jdi;
- uint32_t status;
- char *prompt;
+ smb_joinres_t jdres;
+ char *passwd_prompt;
char *p;
- int len;
+ int len, rc;
+ bzero(&jdres, sizeof (jdres));
bzero(&jdi, sizeof (jdi));
jdi.mode = SMB_SECMODE_DOMAIN;
(void) strlcpy(jdi.domain_name, domain, sizeof (jdi.domain_name));
@@ -577,7 +586,7 @@ smbadm_join_domain(const char *domain, const char *username)
smbadm_usage(B_FALSE);
}
- if (!smbadm_join_prompt(jdi.domain_name))
+ if (prompt && !smbadm_join_prompt(jdi.domain_name))
return (0);
/*
@@ -609,9 +618,9 @@ smbadm_join_domain(const char *domain, const char *username)
}
if (*jdi.domain_passwd == '\0') {
- prompt = gettext("Enter domain password: ");
+ passwd_prompt = gettext("Enter domain password: ");
- if ((p = getpassphrase(prompt)) == NULL) {
+ if ((p = getpassphrase(passwd_prompt)) == NULL) {
(void) fprintf(stderr, gettext(
"missing password\n"));
smbadm_usage(B_FALSE);
@@ -625,41 +634,77 @@ smbadm_join_domain(const char *domain, const char *username)
(void) printf(gettext("Joining %s ... this may take a minute ...\n"),
jdi.domain_name);
- status = smb_join(&jdi);
+ rc = smb_join(&jdi, &jdres);
+ if (rc != 0) {
+ (void) printf(gettext("Cannot call the SMB service. "
+ " (error %d: %s) "
+ "Please check the service status "
+ "(svcs -vx network/smb/server)\n"),
+ rc, strerror(rc));
+ bzero(&jdi, sizeof (jdi));
+ return (1);
+ }
- switch (status) {
+ switch (jdres.status) {
case NT_STATUS_SUCCESS:
- (void) printf(gettext("Successfully joined %s\n"),
- jdi.domain_name);
+ (void) printf(gettext(
+ "Successfully joined domain %s using AD server %s\n"),
+ jdi.domain_name, jdres.dc_name);
bzero(&jdi, sizeof (jdi));
smbadm_restart_service();
return (0);
case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
- (void) fprintf(stderr,
- gettext("failed to find any domain controllers for %s\n"),
+ /* See: smb_ads_lookup_msdcs */
+ (void) fprintf(stderr, gettext(
+ "failed to find any AD servers for domain: %s\n"),
jdi.domain_name);
- bzero(&jdi, sizeof (jdi));
- return (1);
+ goto common;
case NT_STATUS_BAD_NETWORK_PATH:
- (void) fprintf(stderr,
- gettext("failed to resolve domain controller name\n"));
- bzero(&jdi, sizeof (jdi));
- return (1);
+ /* See: smbrdr_ctx_new / smb_ctx_resolve */
+ (void) fprintf(stderr, gettext(
+ "failed to resolve address of AD server: %s\n"),
+ jdres.dc_name);
+ goto common;
case NT_STATUS_NETWORK_ACCESS_DENIED:
+ /* See: smbrdr_ctx_new / smb_ctx_get_ssn */
+ (void) fprintf(stderr, gettext(
+ "failed to authenticate with AD server: %s\n"),
+ jdres.dc_name);
+ goto common;
+
case NT_STATUS_BAD_NETWORK_NAME:
- (void) fprintf(stderr,
- gettext("failed connecting to domain controller\n"));
- bzero(&jdi, sizeof (jdi));
- return (1);
+ /*
+ * See: smbrdr_ctx_new / smb_ctx_get_tree
+ * and: ndr_rpc_bind / smb_fh_open
+ */
+ (void) fprintf(stderr, gettext(
+ "failed connecting to services on AD server: %s\n"),
+ jdres.dc_name);
+ goto common;
default:
- (void) fprintf(stderr, gettext("failed to join %s: %s\n"),
- jdi.domain_name, xlate_nt_status(status));
- (void) fprintf(stderr, gettext("Please refer to the system log"
- " for more information.\n"));
+ (void) fprintf(stderr, gettext(
+ "failed to join domain %s\n"),
+ jdi.domain_name);
+ if (jdres.dc_name[0] != '\0') {
+ (void) fprintf(stderr, gettext(
+ "using AD server: %s\n"),
+ jdres.dc_name);
+ }
+ /* FALLTHROUGH */
+ common:
+ if (jdres.join_err != 0) {
+ (void) fprintf(stderr, "%s\n",
+ smb_ads_strerror(jdres.join_err));
+ } else if (jdres.status != 0) {
+ (void) fprintf(stderr, "(%s)\n",
+ xlate_nt_status(jdres.status));
+ }
+ (void) fprintf(stderr, gettext("Please refer to the "
+ "service log for more information.\n"));
bzero(&jdi, sizeof (jdi));
return (1);
}
@@ -749,10 +794,11 @@ smbadm_list(int argc, char **argv)
(!smb_inet_iszero(&srvipaddr))) {
(void) smb_inet_ntop(&srvipaddr, ipstr,
SMB_IPSTRLEN(srvipaddr.a_family));
- (void) printf(gettext("\t[+%s.%s] [%s]\n"),
- srvname, fqdn, ipstr);
+ (void) printf(gettext("\t[+%s] [%s]\n"),
+ srvname, ipstr);
}
+ /* Print the local and domain SID. */
smb_domain_show();
return (0);
}
diff --git a/usr/src/cmd/smbsrv/smbd/Makefile b/usr/src/cmd/smbsrv/smbd/Makefile
index 134a317fd8..eaa86dc5ae 100644
--- a/usr/src/cmd/smbsrv/smbd/Makefile
+++ b/usr/src/cmd/smbsrv/smbd/Makefile
@@ -29,6 +29,7 @@ OBJS= \
smbd_authsvc.o \
smbd_doorsvc.o \
smbd_join.o \
+ smbd_krb5lookup.o \
smbd_krb5ssp.o \
smbd_logon.o \
smbd_main.o \
@@ -110,3 +111,5 @@ include ../../Makefile.targ
install: all .WAIT $(ROOTETCDEFAULTFILES) $(ROOTMANIFEST) \
$(ROOTSMBDFILE) ${ROOTSVCMETHOD} $(ROOTVARSMBDLL)
+
+.KEEP_STATE:
diff --git a/usr/src/cmd/smbsrv/smbd/smbd.h b/usr/src/cmd/smbsrv/smbd/smbd.h
index f3c1351851..436120a745 100644
--- a/usr/src/cmd/smbsrv/smbd/smbd.h
+++ b/usr/src/cmd/smbsrv/smbd/smbd.h
@@ -52,7 +52,7 @@ void smbd_dc_monitor_refresh(void);
smb_token_t *smbd_user_auth_logon(smb_logon_t *);
void smbd_user_nonauth_logon(uint32_t);
void smbd_user_auth_logoff(uint32_t);
-uint32_t smbd_join(smb_joininfo_t *);
+void smbd_join(smb_joininfo_t *, smb_joinres_t *);
void smbd_set_secmode(int);
boolean_t smbd_online(void);
void smbd_online_wait(const char *);
diff --git a/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c
index b8705b1d91..6de84cadbe 100644
--- a/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c
+++ b/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c
@@ -75,6 +75,7 @@ static int smbd_dop_quota_set(smbd_arg_t *);
static int smbd_dop_dfs_get_referrals(smbd_arg_t *);
static int smbd_dop_shr_hostaccess(smbd_arg_t *);
static int smbd_dop_shr_exec(smbd_arg_t *);
+static int smbd_dop_notify_dc_changed(smbd_arg_t *);
typedef int (*smbd_dop_t)(smbd_arg_t *);
@@ -101,7 +102,8 @@ smbd_doorop_t smbd_doorops[] = {
{ SMB_DR_QUOTA_SET, smbd_dop_quota_set },
{ SMB_DR_DFS_GET_REFERRALS, smbd_dop_dfs_get_referrals },
{ SMB_DR_SHR_HOSTACCESS, smbd_dop_shr_hostaccess },
- { SMB_DR_SHR_EXEC, smbd_dop_shr_exec }
+ { SMB_DR_SHR_EXEC, smbd_dop_shr_exec },
+ { SMB_DR_NOTIFY_DC_CHANGED, smbd_dop_notify_dc_changed }
};
static int smbd_ndoorop = (sizeof (smbd_doorops) / sizeof (smbd_doorops[0]));
@@ -668,17 +670,18 @@ static int
smbd_dop_join(smbd_arg_t *arg)
{
smb_joininfo_t jdi;
- uint32_t status;
+ smb_joinres_t jdres;
bzero(&jdi, sizeof (smb_joininfo_t));
+ bzero(&jdres, sizeof (smb_joinres_t));
if (smb_common_decode(arg->data, arg->datalen,
smb_joininfo_xdr, &jdi) != 0)
return (SMB_DOP_DECODE_ERROR);
- status = smbd_join(&jdi);
+ smbd_join(&jdi, &jdres);
- arg->rbuf = smb_common_encode(&status, xdr_uint32_t, &arg->rsize);
+ arg->rbuf = smb_common_encode(&jdres, smb_joinres_xdr, &arg->rsize);
if (arg->rbuf == NULL)
return (SMB_DOP_ENCODE_ERROR);
@@ -693,7 +696,7 @@ smbd_dop_get_dcinfo(smbd_arg_t *arg)
if (!smb_domain_getinfo(&dxi))
return (SMB_DOP_EMPTYBUF);
- arg->rbuf = smb_string_encode(dxi.d_dc, &arg->rsize);
+ arg->rbuf = smb_string_encode(dxi.d_dci.dc_name, &arg->rsize);
if (arg->rbuf == NULL)
return (SMB_DOP_ENCODE_ERROR);
@@ -828,7 +831,7 @@ smbd_dop_ads_find_host(smbd_arg_t *arg)
if (smb_string_decode(&fqdn, arg->data, arg->datalen) != 0)
return (SMB_DOP_DECODE_ERROR);
- if ((hinfo = smb_ads_find_host(fqdn.buf, NULL)) != NULL)
+ if ((hinfo = smb_ads_find_host(fqdn.buf)) != NULL)
hostname = hinfo->name;
xdr_free(smb_string_xdr, (char *)&fqdn);
@@ -981,3 +984,13 @@ smbd_dop_shr_exec(smbd_arg_t *arg)
return (SMB_DOP_ENCODE_ERROR);
return (SMB_DOP_SUCCESS);
}
+
+/* ARGSUSED */
+static int
+smbd_dop_notify_dc_changed(smbd_arg_t *arg)
+{
+
+ smbd_dc_monitor_refresh();
+
+ return (SMB_DOP_SUCCESS);
+}
diff --git a/usr/src/cmd/smbsrv/smbd/smbd_join.c b/usr/src/cmd/smbsrv/smbd/smbd_join.c
index bca6a1c9e9..cfff2b11f6 100644
--- a/usr/src/cmd/smbsrv/smbd/smbd_join.c
+++ b/usr/src/cmd/smbsrv/smbd/smbd_join.c
@@ -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.
*/
#include <syslog.h>
@@ -30,7 +30,9 @@
#include <unistd.h>
#include <string.h>
#include <strings.h>
-#include <sys/errno.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <smbsrv/libsmb.h>
#include <smbsrv/libsmbns.h>
@@ -49,9 +51,10 @@ static cond_t smbd_dc_cv;
static void *smbd_dc_monitor(void *);
static void smbd_dc_update(void);
+static int smbd_dc_check(smb_domainex_t *);
/* Todo: static boolean_t smbd_set_netlogon_cred(void); */
-static uint32_t smbd_join_workgroup(smb_joininfo_t *);
-static uint32_t smbd_join_domain(smb_joininfo_t *);
+static void smbd_join_workgroup(smb_joininfo_t *, smb_joinres_t *);
+static void smbd_join_domain(smb_joininfo_t *, smb_joinres_t *);
/*
* Launch the DC discovery and monitor thread.
@@ -78,26 +81,23 @@ smbd_dc_monitor_init(void)
return (rc);
}
+/*
+ * Refresh the DC monitor. Called from SMF refresh and when idmap
+ * finds a different DC from what we were using previously.
+ * Update our domain (and current DC) information.
+ */
void
smbd_dc_monitor_refresh(void)
{
- char site[MAXHOSTNAMELEN];
- smb_inaddr_t pdc;
- site[0] = '\0';
- bzero(&pdc, sizeof (smb_inaddr_t));
- (void) smb_config_getstr(SMB_CI_ADS_SITE, site, MAXHOSTNAMELEN);
- (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &pdc);
+ syslog(LOG_INFO, "smbd_dc_monitor_refresh");
+
+ smb_ddiscover_refresh();
(void) mutex_lock(&smbd_dc_mutex);
- if ((bcmp(&smbd.s_pdc, &pdc, sizeof (smb_inaddr_t)) != 0) ||
- (smb_strcasecmp(smbd.s_site, site, 0) != 0)) {
- bcopy(&pdc, &smbd.s_pdc, sizeof (smb_inaddr_t));
- (void) strlcpy(smbd.s_site, site, MAXHOSTNAMELEN);
- smbd.s_pdc_changed = B_TRUE;
- (void) cond_signal(&smbd_dc_cv);
- }
+ smbd.s_pdc_changed = B_TRUE;
+ (void) cond_signal(&smbd_dc_cv);
(void) mutex_unlock(&smbd_dc_mutex);
}
@@ -106,15 +106,19 @@ smbd_dc_monitor_refresh(void)
static void *
smbd_dc_monitor(void *arg)
{
- boolean_t ds_not_responding = B_FALSE;
- boolean_t ds_cfg_changed = B_FALSE;
+ smb_domainex_t di;
+ boolean_t ds_not_responding;
+ boolean_t ds_cfg_changed;
timestruc_t delay;
int i;
- smbd_dc_update();
+ /* Wait for smb_dclocator_init() to complete. */
smbd_online_wait("smbd_dc_monitor");
+ smbd_dc_update();
while (smbd_online()) {
+ ds_not_responding = B_FALSE;
+ ds_cfg_changed = B_FALSE;
delay.tv_sec = SMBD_DC_MONITOR_INTERVAL;
delay.tv_nsec = 0;
@@ -124,12 +128,28 @@ smbd_dc_monitor(void *arg)
if (smbd.s_pdc_changed) {
smbd.s_pdc_changed = B_FALSE;
ds_cfg_changed = B_TRUE;
+ /* NB: smb_ddiscover_refresh was called. */
}
(void) mutex_unlock(&smbd_dc_mutex);
+ if (ds_cfg_changed) {
+ syslog(LOG_DEBUG, "smbd_dc_monitor: config changed");
+ goto rediscover;
+ }
+
+ if (!smb_domain_getinfo(&di)) {
+ syslog(LOG_DEBUG, "smbd_dc_monitor: no domain info");
+ goto rediscover;
+ }
+
+ if (di.d_dci.dc_name[0] == '\0') {
+ syslog(LOG_DEBUG, "smbd_dc_monitor: no DC name");
+ goto rediscover;
+ }
+
for (i = 0; i < SMBD_DC_MONITOR_ATTEMPTS; ++i) {
- if (dssetup_check_service() == 0) {
+ if (smbd_dc_check(&di) == 0) {
ds_not_responding = B_FALSE;
break;
}
@@ -138,13 +158,21 @@ smbd_dc_monitor(void *arg)
(void) sleep(SMBD_DC_MONITOR_RETRY_INTERVAL);
}
- if (ds_not_responding)
+ if (ds_not_responding) {
syslog(LOG_NOTICE,
- "smbd_dc_monitor: domain service not responding");
+ "smbd_dc_monitor: DC not responding: %s",
+ di.d_dci.dc_name);
+ smb_ddiscover_bad_dc(di.d_dci.dc_name);
+ }
if (ds_not_responding || ds_cfg_changed) {
- ds_cfg_changed = B_FALSE;
- smb_ads_refresh();
+ rediscover:
+ /*
+ * An smb_ads_refresh will be done by the
+ * smb_ddiscover_service when necessary.
+ * Note: smbd_dc_monitor_refresh was already
+ * called if appropriate.
+ */
smbd_dc_update();
}
}
@@ -154,6 +182,56 @@ smbd_dc_monitor(void *arg)
}
/*
+ * Simply attempt a connection to the DC.
+ */
+static int
+smbd_dc_check(smb_domainex_t *di)
+{
+ struct sockaddr sa;
+ int salen = 0;
+ int sock = -1;
+ int tmo = 5 * 1000; /* 5 sec. */
+ int rc;
+
+ bzero(&sa, sizeof (sa));
+ switch (di->d_dci.dc_addr.a_family) {
+ case AF_INET: {
+ struct sockaddr_in *sin = (void *)&sa;
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(IPPORT_SMB);
+ sin->sin_addr.s_addr = di->d_dci.dc_addr.a_ipv4;
+ salen = sizeof (*sin);
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (void *)&sa;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(IPPORT_SMB);
+ (void) memcpy(&sin6->sin6_addr,
+ &di->d_dci.dc_addr.a_ipv6,
+ sizeof (in6_addr_t));
+ salen = sizeof (*sin6);
+ break;
+ }
+ default:
+ return (-1);
+ }
+
+ sock = socket(di->d_dci.dc_addr.a_family, SOCK_STREAM, 0);
+ if (sock < 0)
+ return (errno);
+ (void) setsockopt(sock, IPPROTO_TCP,
+ TCP_CONN_ABORT_THRESHOLD, &tmo, sizeof (tmo));
+
+ rc = connect(sock, &sa, salen);
+ if (rc < 0)
+ rc = errno;
+
+ (void) close(sock);
+ return (rc);
+}
+
+/*
* Locate a domain controller in the current resource domain and Update
* the Netlogon credential chain.
*
@@ -167,12 +245,22 @@ smbd_dc_update(void)
smb_domain_t *di;
DWORD status;
- if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) {
- (void) smb_getdomainname(domain, MAXHOSTNAMELEN);
- (void) smb_strupr(domain);
+ /*
+ * Don't want this active until we're a domain member.
+ */
+ if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
+ return;
+
+ if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0)
+ return;
+
+ if (domain[0] == '\0') {
+ syslog(LOG_NOTICE,
+ "smbd_dc_update: no domain name set");
+ return;
}
- if (!smb_locate_dc(domain, "", &info)) {
+ if (!smb_locate_dc(domain, &info)) {
syslog(LOG_NOTICE,
"smbd_dc_update: %s: locate failed", domain);
return;
@@ -180,24 +268,15 @@ smbd_dc_update(void)
di = &info.d_primary;
syslog(LOG_INFO,
- "smbd_dc_update: %s: located %s", domain, info.d_dc);
+ "smbd_dc_update: %s: located %s", domain, info.d_dci.dc_name);
- status = mlsvc_netlogon(info.d_dc, di->di_nbname);
+ status = mlsvc_netlogon(info.d_dci.dc_name, di->di_nbname);
if (status != NT_STATUS_SUCCESS) {
syslog(LOG_NOTICE,
"failed to establish NETLOGON credential chain");
-
- /*
- * Restart required because the domain changed
- * or the credential chain setup failed.
- */
- syslog(LOG_NOTICE,
- "smbd_dc_update: smb/server restart required");
-
- if (smb_smf_restart_service() != 0)
- syslog(LOG_ERR,
- "restart failed: run 'svcs -xv smb/server'"
- " for more information");
+ syslog(LOG_NOTICE, " with server %s for domain %s (%s)",
+ info.d_dci.dc_name, domain,
+ xlate_nt_status(status));
}
}
@@ -209,95 +288,49 @@ smbd_dc_update(void)
* If the security mode or domain name is being changed,
* the caller must restart the service.
*/
-uint32_t
-smbd_join(smb_joininfo_t *info)
+void
+smbd_join(smb_joininfo_t *info, smb_joinres_t *res)
{
- uint32_t status;
-
dssetup_clear_domain_info();
if (info->mode == SMB_SECMODE_WORKGRP)
- status = smbd_join_workgroup(info);
+ smbd_join_workgroup(info, res);
else
- status = smbd_join_domain(info);
-
- return (status);
+ smbd_join_domain(info, res);
}
-static uint32_t
-smbd_join_workgroup(smb_joininfo_t *info)
+static void
+smbd_join_workgroup(smb_joininfo_t *info, smb_joinres_t *res)
{
char nb_domain[SMB_PI_MAX_DOMAIN];
+ syslog(LOG_DEBUG, "smbd: join workgroup: %s", info->domain_name);
+
(void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain,
sizeof (nb_domain));
smbd_set_secmode(SMB_SECMODE_WORKGRP);
smb_config_setdomaininfo(info->domain_name, "", "", "", "");
+ (void) smb_config_set_idmap_domain("");
+ (void) smb_config_refresh_idmap();
if (strcasecmp(nb_domain, info->domain_name))
smb_browser_reconfig();
- return (NT_STATUS_SUCCESS);
+ res->status = NT_STATUS_SUCCESS;
}
-static uint32_t
-smbd_join_domain(smb_joininfo_t *info)
+static void
+smbd_join_domain(smb_joininfo_t *info, smb_joinres_t *res)
{
- static unsigned char zero_hash[SMBAUTH_HASH_SZ];
- smb_domainex_t dxi;
- smb_domain_t *di;
- uint32_t status;
- /*
- * 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.
- */
- (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
-
- /* Clear DNS local (ADS) lookup cache too. */
- smb_ads_refresh();
-
- /*
- * Use a NULL session while searching for a DC, and
- * while getting information about the domain.
- */
- smb_ipc_set(MLSVC_ANON_USER, zero_hash);
-
- if (!smb_locate_dc(info->domain_name, "", &dxi)) {
- syslog(LOG_ERR, "smbd: failed locating "
- "domain controller for %s",
- info->domain_name);
- status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
- goto errout;
- }
+ syslog(LOG_DEBUG, "smbd: join domain: %s", info->domain_name);
/* info->domain_name could either be NetBIOS domain name or FQDN */
- status = mlsvc_join(&dxi, info->domain_username, info->domain_passwd);
- if (status != NT_STATUS_SUCCESS) {
+ mlsvc_join(info, res);
+ if (res->status == 0) {
+ smbd_set_secmode(SMB_SECMODE_DOMAIN);
+ } else {
syslog(LOG_ERR, "smbd: failed joining %s (%s)",
- info->domain_name, xlate_nt_status(status));
- goto errout;
+ info->domain_name, xlate_nt_status(res->status));
}
-
- /*
- * Success!
- *
- * Strange, mlsvc_join does some of the work to
- * save the config, then the rest happens here.
- * Todo: Do the config update all in one place.
- */
- di = &dxi.d_primary;
- smbd_set_secmode(SMB_SECMODE_DOMAIN);
- 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();
- return (status);
-
-errout:
- smb_ipc_rollback();
- return (status);
}
diff --git a/usr/src/cmd/smbsrv/smbd/smbd_krb5lookup.c b/usr/src/cmd/smbsrv/smbd/smbd_krb5lookup.c
new file mode 100644
index 0000000000..af98b15b1b
--- /dev/null
+++ b/usr/src/cmd/smbsrv/smbd/smbd_krb5lookup.c
@@ -0,0 +1,158 @@
+/*
+ * 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 <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/note.h>
+
+#include <smbsrv/libsmbns.h>
+
+#include "smbd.h"
+#include "locate_plugin.h"
+
+/* osconf.h - sigh */
+#define KRB5_DEFAULT_PORT 88
+#define DEFAULT_KADM5_PORT 749
+#define DEFAULT_KPASSWD_PORT 464
+
+/*
+ * This is an "override plugin" used by libkrb5. See:
+ * lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c
+ *
+ * The interface is based on:
+ * http://web.mit.edu/~kerberos/krb5-1.12/doc/plugindev/locate.html
+ */
+
+/*
+ * Called by krb5int_locate_server / override_locate_server
+ */
+
+krb5_error_code
+_krb5_override_service_locator(
+ void *arg0,
+ enum locate_service_type svc,
+ const char *realm,
+ int socktype,
+ int family,
+ int (*cbfunc)(void *, int, struct sockaddr *),
+ void *cbdata)
+{
+ _NOTE(ARGUNUSED(arg0))
+ smb_domainex_t dxi;
+ int rc = KRB5_PLUGIN_NO_HANDLE;
+ short port;
+
+ /*
+ * Is this a service we want to override?
+ */
+ switch (svc) {
+ case locate_service_kdc:
+ case locate_service_master_kdc:
+ port = htons(KRB5_DEFAULT_PORT);
+ break;
+ case locate_service_kadmin:
+ port = htons(DEFAULT_KADM5_PORT);
+ break;
+ case locate_service_kpasswd:
+ port = htons(DEFAULT_KPASSWD_PORT);
+ break;
+ case locate_service_krb524:
+ default:
+ return (rc);
+ }
+
+ /*
+ * What's my domain? Note: have to get this in a way
+ * that works while join domain is underway.
+ */
+ if (!smb_domain_getinfo(&dxi)) {
+ smbd_report("_krb5_override_service_locator "
+ "failed getting domain info");
+ return (KRB5_ERR_HOST_REALM_UNKNOWN);
+ }
+
+ /*
+ * Is this a realm we want to override?
+ */
+ if (0 != strcasecmp(realm, dxi.d_primary.di_fqname)) {
+ syslog(LOG_DEBUG, "_krb5_override_service_locator, "
+ "realm=%s, fqdn=%s", realm, dxi.d_primary.di_fqname);
+ return (rc);
+ }
+
+ /*
+ * Yes, this is our domain. Have a DC?
+ */
+ if (dxi.d_dci.dc_name[0] == '\0' ||
+ dxi.d_dci.dc_addr.a_family == 0)
+ return (KRB5_REALM_CANT_RESOLVE);
+
+ switch (family) {
+ case AF_UNSPEC:
+ break; /* OK */
+ case AF_INET:
+ case AF_INET6:
+ if (family == dxi.d_dci.dc_addr.a_family)
+ break; /* OK */
+ /* else fallthrough */
+ default:
+ return (KRB5_ERR_NO_SERVICE);
+ }
+
+ /*
+ * Provide the service address we have.
+ */
+ switch (dxi.d_dci.dc_addr.a_family) {
+ case AF_INET: {
+ struct sockaddr_in sin;
+ (void) memset(&sin, 0, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = port;
+ (void) memcpy(&sin.sin_addr, &dxi.d_dci.dc_addr.a_ipv4,
+ sizeof (sin.sin_addr));
+ rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin);
+ /* rc from cbfunc is special. */
+ if (rc)
+ rc = ENOMEM;
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 sin6;
+ (void) memset(&sin6, 0, sizeof (sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = port;
+ (void) memcpy(&sin6.sin6_addr, &dxi.d_dci.dc_addr.a_ipv6,
+ sizeof (sin6.sin6_addr));
+ rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin6);
+ /* rc from cbfunc is special. */
+ if (rc)
+ rc = ENOMEM;
+ break;
+ }
+ default:
+ rc = KRB5_ERR_NO_SERVICE;
+ break;
+ }
+
+ return (rc);
+}
diff --git a/usr/src/common/smbsrv/smb_xdr.c b/usr/src/common/smbsrv/smb_xdr.c
index 54e0cc8222..e7640554aa 100644
--- a/usr/src/common/smbsrv/smb_xdr.c
+++ b/usr/src/common/smbsrv/smb_xdr.c
@@ -97,7 +97,8 @@ smb_doorhdr_opname(uint32_t op)
{ SMB_DR_QUOTA_SET, "quota_set" },
{ SMB_DR_DFS_GET_REFERRALS, "dfs_get_referrals" },
{ SMB_DR_SHR_HOSTACCESS, "share_hostaccess" },
- { SMB_DR_SHR_EXEC, "share_exec" }
+ { SMB_DR_SHR_EXEC, "share_exec" },
+ { SMB_DR_NOTIFY_DC_CHANGED, "notify_dc_changed" }
};
int i;
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);
diff --git a/usr/src/man/man1m/smbadm.1m b/usr/src/man/man1m/smbadm.1m
index b1e6e2de98..3ce3ecf923 100644
--- a/usr/src/man/man1m/smbadm.1m
+++ b/usr/src/man/man1m/smbadm.1m
@@ -1,10 +1,10 @@
'\" te
-.\" Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+.\" Copyright 2014 Nexenta Systems, Inc. All rights reserved.
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH SMBADM 1M "Jan 8, 2009"
+.TH SMBADM 1M "Feb 19, 2014"
.SH NAME
smbadm \- configure and manage CIFS local groups and users, and manage domain
membership
@@ -41,12 +41,12 @@ membership
.LP
.nf
-\fBsmbadm join\fR -u \fIusername\fR \fIdomain\fR
+\fBsmbadm join\fR [-y] -u \fIusername\fR \fIdomain\fR
.fi
.LP
.nf
-\fBsmbadm join\fR -w \fIworkgroup\fR
+\fBsmbadm join\fR [-y] -w \fIworkgroup\fR
.fi
.LP
@@ -80,7 +80,6 @@ membership
.fi
.SH DESCRIPTION
-.sp
.LP
The \fBsmbadm\fR command is used to configure \fBCIFS\fR local groups and to
manage domain membership. You can also use the \fBsmbadm\fR command to enable
@@ -161,7 +160,6 @@ then reset the user's password by using the \fBpasswd\fR command. The
\fBpam_smb_passwd.so.1\fR module must be added to the system's PAM
configuration to generate an SMB password.
.SS "Escaping Backslash Character"
-.sp
.LP
For the \fBadd-member\fR, \fBremove-member\fR, and \fBjoin\fR (with \fB-u\fR)
subcommands, the backslash character (\fB\e\fR) is a valid separator between
@@ -171,7 +169,6 @@ backslash character with another backslash character:
\fIdomain\fR\fB\e\e\fR\fIusername\fR. For more information about handling shell
special characters, see the man page for your shell.
.SH OPERANDS
-.sp
.LP
The \fBsmbadm\fR command uses the following operands:
.sp
@@ -205,7 +202,6 @@ Specifies the name of a Solaris local user.
.RE
.SH SUB-COMMANDS
-.sp
.LP
The \fBsmbadm\fR command includes these subcommands:
.sp
@@ -301,7 +297,7 @@ all property values are shown.
.sp
.ne 2
.na
-\fB\fBjoin\fR \fB-u\fR \fIusername\fR \fIdomain\fR\fR
+\fB\fBjoin\fR \fB[-y] -u\fR \fIusername\fR \fIdomain\fR\fR
.ad
.sp .6
.RS 4n
@@ -335,12 +331,13 @@ controller, any authenticated user account can be used when joining the domain.
However, if the machine trust account does \fBnot\fR already exist, an account
that has administrative privileges on the domain is required to join the
domain.
+Specifying \fB-y\fR will bypass the smb service restart prompt.
.RE
.sp
.ne 2
.na
-\fB\fBjoin\fR \fB-w\fR \fIworkgroup\fR\fR
+\fB\fBjoin\fR \fB[-y] -w\fR \fIworkgroup\fR\fR
.ad
.sp .6
.RS 4n
@@ -348,6 +345,7 @@ Joins a Windows domain or a workgroup.
.sp
The \fB-w\fR \fIworkgroup\fR option specifies the name of the workgroup to join
when using the \fBjoin\fR subcommand.
+Specifying \fB-y\fR will bypass the smb service restart prompt.
.RE
.sp
@@ -528,7 +526,6 @@ specified, the group privileges are also shown.
.RE
.SH EXIT STATUS
-.sp
.LP
The following exit values are returned:
.sp
@@ -550,7 +547,6 @@ An error occurred.
.RE
.SH ATTRIBUTES
-.sp
.LP
See the \fBattributes\fR(5) man page for descriptions of the following
attributes:
@@ -571,7 +567,6 @@ _
.TE
.SH SEE ALSO
-.sp
.LP
\fBpasswd\fR(1), \fBgroupadd\fR(1M), \fBidmap\fR(1M), \fBidmapd\fR(1M),
\fBkclient\fR(1M), \fBshare\fR(1M), \fBsharectl\fR(1M), \fBsharemgr\fR(1M),
diff --git a/usr/src/pkg/manifests/SUNWcs.mf b/usr/src/pkg/manifests/SUNWcs.mf
index 47923f1f78..5568756574 100644
--- a/usr/src/pkg/manifests/SUNWcs.mf
+++ b/usr/src/pkg/manifests/SUNWcs.mf
@@ -21,9 +21,9 @@
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
# Copyright (c) 2013 Gary Mills
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
#
<include SUNWcs.man1.inc>
@@ -1354,6 +1354,7 @@ file path=usr/sbin/mvdir mode=0555
file path=usr/sbin/ndd mode=0555
file path=usr/sbin/ndp mode=0555
file path=usr/sbin/nlsadmin group=adm mode=0755
+file path=usr/sbin/nltest mode=0555
file path=usr/sbin/nscd mode=0555
file path=usr/sbin/nwamadm mode=0555
file path=usr/sbin/nwamcfg mode=0555
diff --git a/usr/src/pkg/manifests/developer-library-lint.mf b/usr/src/pkg/manifests/developer-library-lint.mf
index f9397a1c45..b513373f6e 100644
--- a/usr/src/pkg/manifests/developer-library-lint.mf
+++ b/usr/src/pkg/manifests/developer-library-lint.mf
@@ -20,9 +20,9 @@
#
#
-# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved.
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
set name=pkg.fmri value=pkg:/developer/library/lint@$(PKGVERS)
@@ -198,6 +198,7 @@ $(i386_ONLY)file path=usr/lib/$(ARCH64)/crt1.o
file path=usr/lib/$(ARCH64)/crti.o
file path=usr/lib/$(ARCH64)/crtn.o
$(i386_ONLY)file path=usr/lib/$(ARCH64)/gcrt1.o
+file path=usr/lib/$(ARCH64)/llib-lads.ln
file path=usr/lib/$(ARCH64)/llib-ladutils.ln
file path=usr/lib/$(ARCH64)/llib-lbsdmalloc.ln
file path=usr/lib/$(ARCH64)/llib-lcfgadm.ln
@@ -246,6 +247,8 @@ $(i386_ONLY)file path=usr/lib/crt1.o
file path=usr/lib/crti.o
file path=usr/lib/crtn.o
$(i386_ONLY)file path=usr/lib/gcrt1.o
+file path=usr/lib/llib-lads
+file path=usr/lib/llib-lads.ln
file path=usr/lib/llib-ladutils
file path=usr/lib/llib-ladutils.ln
file path=usr/lib/llib-lbsdmalloc
diff --git a/usr/src/pkg/manifests/service-file-system-smb.mf b/usr/src/pkg/manifests/service-file-system-smb.mf
index 3bcf91d291..2cc02917e3 100644
--- a/usr/src/pkg/manifests/service-file-system-smb.mf
+++ b/usr/src/pkg/manifests/service-file-system-smb.mf
@@ -20,8 +20,8 @@
#
#
-# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
set name=pkg.fmri value=pkg:/service/file-system/smb@$(PKGVERS)
@@ -79,6 +79,7 @@ file path=usr/lib/mdb/kvm/$(ARCH64)/smbsrv.so group=sys mode=0555
$(i386_ONLY)file path=usr/lib/mdb/kvm/smbsrv.so group=sys mode=0555
file path=usr/lib/reparse/libreparse_smb.so.1
file path=usr/lib/security/pam_smb_passwd.so.1
+file path=usr/lib/smbsrv/dtrace/smbd-all.d mode=0555
file path=usr/lib/smbsrv/dtrace/smbd-authsvc.d mode=0555
file path=usr/lib/smbsrv/dtrace/smbd-doorsvc.d mode=0555
file path=usr/lib/smbsrv/dtrace/smbd-pipesvc.d mode=0555
diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf
index 08f0b19416..4551ca095c 100644
--- a/usr/src/pkg/manifests/system-header.mf
+++ b/usr/src/pkg/manifests/system-header.mf
@@ -22,9 +22,9 @@
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2012 by Delphix. All rights reserved.
-# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
-# Copyright 2014 Garrett D'Amore <garrett@damore.org>
# Copyright 2013 Saso Kiselkov. All rights reserved.
+# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
#
set name=pkg.fmri value=pkg:/system/header@$(PKGVERS)
@@ -37,6 +37,7 @@ dir path=usr group=sys
dir path=usr/include
$(i386_ONLY)dir path=usr/include/$(ARCH64)
$(i386_ONLY)dir path=usr/include/$(ARCH64)/sys
+dir path=usr/include/ads
dir path=usr/include/arpa
dir path=usr/include/asm
dir path=usr/include/ast
@@ -192,6 +193,7 @@ dir path=usr/xpg4/include
$(i386_ONLY)file path=usr/include/$(ARCH64)/sys/kdi_regs.h
$(i386_ONLY)file path=usr/include/$(ARCH64)/sys/privmregs.h
$(i386_ONLY)file path=usr/include/$(ARCH64)/sys/privregs.h
+file path=usr/include/ads/dsgetdc.h
file path=usr/include/aio.h
file path=usr/include/alloca.h
file path=usr/include/apptrace.h
@@ -461,6 +463,7 @@ file path=usr/include/iso/wctype_iso.h
file path=usr/include/iso646.h
file path=usr/include/kerberosv5/com_err.h
file path=usr/include/kerberosv5/krb5.h
+file path=usr/include/kerberosv5/locate_plugin.h
file path=usr/include/kerberosv5/mit-sipb-copyright.h
file path=usr/include/kerberosv5/mit_copyright.h
file path=usr/include/klpd.h
diff --git a/usr/src/pkg/manifests/system-library.mf b/usr/src/pkg/manifests/system-library.mf
index e50eb582df..e468a18091 100644
--- a/usr/src/pkg/manifests/system-library.mf
+++ b/usr/src/pkg/manifests/system-library.mf
@@ -20,10 +20,10 @@
#
#
-# Copyright 2014 Nexenta Systems, Inc.
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright (c) 2013 Gary Mills
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
#
<include system-library.man3.inc>
@@ -321,6 +321,7 @@ file path=lib/nss_nis.so.1
file path=lib/nss_user.so.1
file path=usr/lib/$(ARCH64)/0@0.so.1
file path=usr/lib/$(ARCH64)/getloginx.so.1
+file path=usr/lib/$(ARCH64)/libads.so.1
file path=usr/lib/$(ARCH64)/libadutils.so.1
file path=usr/lib/$(ARCH64)/libast.so.1
file path=usr/lib/$(ARCH64)/libbsdmalloc.so.1
@@ -392,6 +393,7 @@ file path=usr/lib/cfgadm/usb.so.1
file path=usr/lib/extendedFILE.so.1
file path=usr/lib/getloginx.so.1
file path=usr/lib/lib.b mode=0444
+file path=usr/lib/libads.so.1
file path=usr/lib/libadutils.so.1
file path=usr/lib/libast.so.1
file path=usr/lib/libbsdmalloc.so.1
@@ -757,6 +759,7 @@ link path=usr/lib/$(ARCH64)/libadm.so \
target=../../../lib/$(ARCH64)/libadm.so.1
link path=usr/lib/$(ARCH64)/libadm.so.1 \
target=../../../lib/$(ARCH64)/libadm.so.1
+link path=usr/lib/$(ARCH64)/libads.so target=./libads.so.1
link path=usr/lib/$(ARCH64)/libadutils.so target=./libadutils.so.1
link path=usr/lib/$(ARCH64)/libaio.so \
target=../../../lib/$(ARCH64)/libaio.so.1
@@ -1039,6 +1042,7 @@ link path=usr/lib/cfgadm/shp.so target=./shp.so.1
link path=usr/lib/cfgadm/usb.so target=./usb.so.1
link path=usr/lib/libadm.so target=../../lib/libadm.so.1
link path=usr/lib/libadm.so.1 target=../../lib/libadm.so.1
+link path=usr/lib/libads.so target=./libads.so.1
link path=usr/lib/libadutils.so target=./libadutils.so.1
link path=usr/lib/libaio.so target=../../lib/libaio.so.1
link path=usr/lib/libaio.so.1 target=../../lib/libaio.so.1
diff --git a/usr/src/tools/quick/make-idmap b/usr/src/tools/quick/make-idmap
index b587d2c5bc..a69ab3a1a2 100755
--- a/usr/src/tools/quick/make-idmap
+++ b/usr/src/tools/quick/make-idmap
@@ -97,6 +97,7 @@ fi
# Need some library headers too...
for lib in \
+ libads \
libbsm \
libcmdutils \
libcryptoutil \
@@ -135,6 +136,7 @@ for lib in \
libadutils \
libuutil \
libidmap \
+ libads \
libsmbfs \
libsqlite \
nsswitch/ad
@@ -186,6 +188,8 @@ do_tar() {
files="
lib/svc/manifest/system/idmap.xml
usr/lib/idmapd
+usr/lib/libads.so.1
+usr/lib/$arch64/libads.so.1
usr/lib/libadutils.so.1
usr/lib/$arch64/libadutils.so.1
usr/lib/libidmap.so.1
diff --git a/usr/src/tools/quick/make-smbsrv b/usr/src/tools/quick/make-smbsrv
index a865e44258..31c76379ca 100755
--- a/usr/src/tools/quick/make-smbsrv
+++ b/usr/src/tools/quick/make-smbsrv
@@ -20,7 +20,8 @@ make=${MAKE:-dmake}
CLOSED_IS_PRESENT=no
export CLOSED_IS_PRESENT
-export SOURCEDEBUG=yes
+# Do this if you want to use dbx or gdb
+# export SOURCEDEBUG=yes
[ -n "$SRC" ] || {
echo "SRC not set. Run 'ws' or 'bldenv' first."
@@ -97,6 +98,7 @@ fi
# Need some library headers too...
for lib in \
+ libads \
libbsm \
libcmdutils \
libcryptoutil \
@@ -159,6 +161,7 @@ do_lib2() {
for lib in \
libfakekernel \
+ libads \
libsmbfs
do
(cd $SRC/lib/$lib && $make $1)
diff --git a/usr/src/uts/common/smbsrv/netrauth.h b/usr/src/uts/common/smbsrv/netrauth.h
index 54ea798a0d..bc11d81e8c 100644
--- a/usr/src/uts/common/smbsrv/netrauth.h
+++ b/usr/src/uts/common/smbsrv/netrauth.h
@@ -36,6 +36,7 @@
#include <smbsrv/wintypes.h>
#include <smbsrv/netbios.h>
#include <smbsrv/smbinfo.h>
+#include <netdb.h>
#ifdef __cplusplus
extern "C" {
@@ -120,8 +121,8 @@ typedef struct netr_session_key {
typedef struct netr_info {
DWORD flags;
- char server[NETBIOS_NAME_SZ * 2];
- char hostname[NETBIOS_NAME_SZ * 2];
+ char server[MAXHOSTNAMELEN]; /* Current DC, FQDN */
+ char hostname[NETBIOS_NAME_SZ * 2]; /* local "flat" name */
netr_cred_t client_challenge;
netr_cred_t server_challenge;
netr_cred_t client_credential;
diff --git a/usr/src/uts/common/smbsrv/smb_door.h b/usr/src/uts/common/smbsrv/smb_door.h
index 909f0778bf..9e284f5d9d 100644
--- a/usr/src/uts/common/smbsrv/smb_door.h
+++ b/usr/src/uts/common/smbsrv/smb_door.h
@@ -67,7 +67,8 @@ typedef enum smb_dopcode {
SMB_DR_QUOTA_SET,
SMB_DR_DFS_GET_REFERRALS,
SMB_DR_SHR_HOSTACCESS,
- SMB_DR_SHR_EXEC
+ SMB_DR_SHR_EXEC,
+ SMB_DR_NOTIFY_DC_CHANGED
} smb_dopcode_t;
struct smb_event;
diff --git a/usr/src/uts/common/smbsrv/smb_privilege.h b/usr/src/uts/common/smbsrv/smb_privilege.h
index 8a6504a6f1..0bfd6cbdbf 100644
--- a/usr/src/uts/common/smbsrv/smb_privilege.h
+++ b/usr/src/uts/common/smbsrv/smb_privilege.h
@@ -26,6 +26,8 @@
#ifndef _SMB_PRIVILEGE_H
#define _SMB_PRIVILEGE_H
+#include <smbsrv/wintypes.h>
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/usr/src/uts/common/smbsrv/smb_token.h b/usr/src/uts/common/smbsrv/smb_token.h
index 5b7a4eba37..dd20d90c9e 100644
--- a/usr/src/uts/common/smbsrv/smb_token.h
+++ b/usr/src/uts/common/smbsrv/smb_token.h
@@ -29,10 +29,17 @@
#ifndef _SMB_TOKEN_H
#define _SMB_TOKEN_H
-#include <smbsrv/netrauth.h>
+#include <smbsrv/smb_inet.h>
#include <smbsrv/smb_privilege.h>
#include <smbsrv/smb_sid.h>
+/*
+ * Don't want <smbsrv/netrauth.h> in here, but
+ * uts/common/fs/smbsrv/smb_authenticate.c
+ * wants this. Todo: cleanup
+ */
+#define NETR_NETWORK_LOGON 0x02
+
#ifdef __cplusplus
extern "C" {
#endif