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