[PATCH] Rework idmap_ad

Volker Lendecke Volker.Lendecke at SerNet.DE
Thu Mar 31 10:18:55 UTC 2016


Hi!

The attached patchset is supposed to fix a longstanding bug in
winbind's idmap_ad backend. Assume a configuration where you have sfu
attributes in a trusted domain. Start winbind and immediately do a
idmapping call (sid2xid or vice versa). This will fail if winbind did
not have the chance yet to list the trusted domains from the DC it is
joined to, the AD_STRUCT based code even in child processes depends on
the winbindd_domain list to be correctly filled.

This patchset solves the issue just for the sfu idmap backend,
hopefully the rest of the winbind code can follow later.

Comments appreciated!

Thanks, Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de, mailto:kontakt at sernet.de
-------------- next part --------------
From 25c088dad1b7d964e966846bcef84f6fadd30e13 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 29 Mar 2016 16:03:04 +0200
Subject: [PATCH 1/7] idmap_ad: Separate out the nss functions

The nss functions technically right now are part of the idmap modules. However,
there is no intrinsic reason for this mixture of concerns. I would like to
heavily modify the idmap_ad idmapping functions without modifying the nss
functions (yet!!). So as a first step this patch moves the nss functions
textually out of the way.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/idmap_ad.c     |  425 +------------------------------
 source3/winbindd/idmap_ad_nss.c |  531 +++++++++++++++++++++++++++++++++++++++
 source3/winbindd/idmap_proto.h  |    2 +
 source3/winbindd/wscript_build  |    2 +-
 4 files changed, 539 insertions(+), 421 deletions(-)
 create mode 100644 source3/winbindd/idmap_ad_nss.c

diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
index e5dea20..8f15ecb 100644
--- a/source3/winbindd/idmap_ad.c
+++ b/source3/winbindd/idmap_ad.c
@@ -561,377 +561,6 @@ done:
 	return ret;
 }
 
-/*
- * nss_info_{sfu,sfu20,rfc2307}
- */
-
-/************************************************************************
- Initialize the {sfu,sfu20,rfc2307} state
- ***********************************************************************/
-
-static const char *wb_posix_map_unknown_string = "WB_POSIX_MAP_UNKNOWN";
-static const char *wb_posix_map_template_string = "WB_POSIX_MAP_TEMPLATE";
-static const char *wb_posix_map_sfu_string = "WB_POSIX_MAP_SFU";
-static const char *wb_posix_map_sfu20_string = "WB_POSIX_MAP_SFU20";
-static const char *wb_posix_map_rfc2307_string = "WB_POSIX_MAP_RFC2307";
-static const char *wb_posix_map_unixinfo_string = "WB_POSIX_MAP_UNIXINFO";
-
-static const char *ad_map_type_string(enum wb_posix_mapping map_type)
-{
-	switch (map_type) {
-		case WB_POSIX_MAP_TEMPLATE:
-			return wb_posix_map_template_string;
-		case WB_POSIX_MAP_SFU:
-			return wb_posix_map_sfu_string;
-		case WB_POSIX_MAP_SFU20:
-			return wb_posix_map_sfu20_string;
-		case WB_POSIX_MAP_RFC2307:
-			return wb_posix_map_rfc2307_string;
-		case WB_POSIX_MAP_UNIXINFO:
-			return wb_posix_map_unixinfo_string;
-		default:
-			return wb_posix_map_unknown_string;
-	}
-}
-
-static NTSTATUS nss_ad_generic_init(struct nss_domain_entry *e,
-				    enum wb_posix_mapping new_ad_map_type)
-{
-	struct idmap_domain *dom;
-	struct idmap_ad_context *ctx;
-
-	if (e->state != NULL) {
-		dom = talloc_get_type(e->state, struct idmap_domain);
-	} else {
-		dom = talloc_zero(e, struct idmap_domain);
-		if (dom == NULL) {
-			DEBUG(0, ("Out of memory!\n"));
-			return NT_STATUS_NO_MEMORY;
-		}
-		e->state = dom;
-	}
-
-	if (e->domain != NULL) {
-		dom->name = talloc_strdup(dom, e->domain);
-		if (dom->name == NULL) {
-			DEBUG(0, ("Out of memory!\n"));
-			return NT_STATUS_NO_MEMORY;
-		}
-	}
-
-	if (dom->private_data != NULL) {
-		ctx = talloc_get_type(dom->private_data,
-				      struct idmap_ad_context);
-	} else {
-		ctx = talloc_zero(dom, struct idmap_ad_context);
-		if (ctx == NULL) {
-			DEBUG(0, ("Out of memory!\n"));
-			return NT_STATUS_NO_MEMORY;
-		}
-		ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
-		dom->private_data = ctx;
-	}
-
-	if ((ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
-	    (ctx->ad_map_type != new_ad_map_type))
-	{
-		DEBUG(2, ("nss_ad_generic_init: "
-			  "Warning: overriding previously set posix map type "
-			  "%s for domain %s with map type %s.\n",
-			  ad_map_type_string(ctx->ad_map_type),
-			  dom->name,
-			  ad_map_type_string(new_ad_map_type)));
-	}
-
-	ctx->ad_map_type = new_ad_map_type;
-
-	return NT_STATUS_OK;
-}
-
-static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
-{
-	return nss_ad_generic_init(e, WB_POSIX_MAP_SFU);
-}
-
-static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
-{
-	return nss_ad_generic_init(e, WB_POSIX_MAP_SFU20);
-}
-
-static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
-{
-	return nss_ad_generic_init(e, WB_POSIX_MAP_RFC2307);
-}
-
-
-/************************************************************************
- ***********************************************************************/
-
-static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, 
-				  const struct dom_sid *sid,
-				  TALLOC_CTX *mem_ctx,
-				  const char **homedir,
-				  const char **shell,
-				  const char **gecos,
-				  gid_t *p_gid )
-{
-	const char *attrs[] = {NULL, /* attr_homedir */
-			       NULL, /* attr_shell */
-			       NULL, /* attr_gecos */
-			       NULL, /* attr_gidnumber */
-			       NULL };
-	char *filter = NULL;
-	LDAPMessage *msg_internal = NULL;
-	ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
-	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
-	char *sidstr = NULL;
-	struct idmap_domain *dom;
-	struct idmap_ad_context *ctx;
-
-	DEBUG(10, ("nss_ad_get_info called for sid [%s] in domain '%s'\n",
-		   sid_string_dbg(sid), e->domain?e->domain:"NULL"));
-
-	/* Only do query if we are online */
-	if (idmap_is_offline())	{
-		return NT_STATUS_FILE_IS_OFFLINE;
-	}
-
-	dom = talloc_get_type(e->state, struct idmap_domain);
-	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
-
-	ads_status = ad_idmap_cached_connection(dom);
-	if (!ADS_ERR_OK(ads_status)) {
-		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-	}
-
-	if (!ctx->ad_schema) {
-		DEBUG(10, ("nss_ad_get_info: no ad_schema configured!\n"));
-		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-	}
-
-	if (!sid || !homedir || !shell || !gecos) {
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	/* Have to do our own query */
-
-	DEBUG(10, ("nss_ad_get_info: no ads connection given, doing our "
-		   "own query\n"));
-
-	attrs[0] = ctx->ad_schema->posix_homedir_attr;
-	attrs[1] = ctx->ad_schema->posix_shell_attr;
-	attrs[2] = ctx->ad_schema->posix_gecos_attr;
-	attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
-
-	sidstr = ldap_encode_ndr_dom_sid(mem_ctx, sid);
-	filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr);
-	TALLOC_FREE(sidstr);
-
-	if (!filter) {
-		nt_status = NT_STATUS_NO_MEMORY;
-		goto done;
-	}
-
-	ads_status = ads_search_retry(ctx->ads, &msg_internal, filter, attrs);
-	if (!ADS_ERR_OK(ads_status)) {
-		nt_status = ads_ntstatus(ads_status);
-		goto done;
-	}
-
-	*homedir = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_homedir_attr);
-	*shell   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_shell_attr);
-	*gecos   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_gecos_attr);
-
-	if (p_gid != NULL) {
-		uint32_t gid = UINT32_MAX;
-		bool ok;
-
-		ok = ads_pull_uint32(ctx->ads, msg_internal,
-				     ctx->ad_schema->posix_gidnumber_attr,
-				     &gid);
-		if (ok) {
-			*p_gid = gid;
-		} else {
-			*p_gid = (gid_t)-1;
-		}
-	}
-
-	nt_status = NT_STATUS_OK;
-
-done:
-	if (msg_internal) {
-		ads_msgfree(ctx->ads, msg_internal);
-	}
-
-	return nt_status;
-}
-
-/**********************************************************************
- *********************************************************************/
-
-static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
-				    struct nss_domain_entry *e,
-				    const char *name,
-				    char **alias)
-{
-	const char *attrs[] = {NULL, /* attr_uid */
-			       NULL };
-	char *filter = NULL;
-	LDAPMessage *msg = NULL;
-	ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
-	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
-	struct idmap_domain *dom;
-	struct idmap_ad_context *ctx = NULL;
-
-	/* Check incoming parameters */
-
-	if ( !e || !e->domain || !name || !*alias) {
-		nt_status = NT_STATUS_INVALID_PARAMETER;
-		goto done;
-	}
-
-	/* Only do query if we are online */
-
-	if (idmap_is_offline())	{
-		nt_status = NT_STATUS_FILE_IS_OFFLINE;
-		goto done;
-	}
-
-	dom = talloc_get_type(e->state, struct idmap_domain);
-	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
-
-	ads_status = ad_idmap_cached_connection(dom);
-	if (!ADS_ERR_OK(ads_status)) {
-		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-	}
-
-	if (!ctx->ad_schema) {
-		nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-		goto done;
-	}
-
-	attrs[0] = ctx->ad_schema->posix_uid_attr;
-
-	filter = talloc_asprintf(mem_ctx,
-				 "(sAMAccountName=%s)",
-				 name);
-	if (!filter) {
-		nt_status = NT_STATUS_NO_MEMORY;
-		goto done;
-	}
-
-	ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
-	if (!ADS_ERR_OK(ads_status)) {
-		nt_status = ads_ntstatus(ads_status);
-		goto done;
-	}
-
-	*alias = ads_pull_string(ctx->ads, mem_ctx, msg, ctx->ad_schema->posix_uid_attr);
-
-	if (!*alias) {
-		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-	}
-
-	nt_status = NT_STATUS_OK;
-
-done:
-	if (filter) {
-		talloc_destroy(filter);
-	}
-	if (msg) {
-		ads_msgfree(ctx->ads, msg);
-	}
-
-	return nt_status;
-}
-
-/**********************************************************************
- *********************************************************************/
-
-static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
-					     struct nss_domain_entry *e,
-					     const char *alias,
-					     char **name )
-{
-	const char *attrs[] = {"sAMAccountName",
-			       NULL };
-	char *filter = NULL;
-	LDAPMessage *msg = NULL;
-	ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
-	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
-	char *username;
-	struct idmap_domain *dom;
-	struct idmap_ad_context *ctx = NULL;
-
-	/* Check incoming parameters */
-
-	if ( !alias || !name) {
-		nt_status = NT_STATUS_INVALID_PARAMETER;
-		goto done;
-	}
-
-	/* Only do query if we are online */
-
-	if (idmap_is_offline())	{
-		nt_status = NT_STATUS_FILE_IS_OFFLINE;
-		goto done;
-	}
-
-	dom = talloc_get_type(e->state, struct idmap_domain);
-	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
-
-	ads_status = ad_idmap_cached_connection(dom);
-	if (!ADS_ERR_OK(ads_status)) {
-		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-	}
-
-	if (!ctx->ad_schema) {
-		nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-		goto done;
-	}
-
-	filter = talloc_asprintf(mem_ctx,
-				 "(%s=%s)",
-				 ctx->ad_schema->posix_uid_attr,
-				 alias);
-	if (!filter) {
-		nt_status = NT_STATUS_NO_MEMORY;
-		goto done;
-	}
-
-	ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
-	if (!ADS_ERR_OK(ads_status)) {
-		nt_status = ads_ntstatus(ads_status);
-		goto done;
-	}
-
-	username = ads_pull_string(ctx->ads, mem_ctx, msg,
-				   "sAMAccountName");
-	if (!username) {
-		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-	}
-
-	*name = talloc_asprintf(mem_ctx, "%s\\%s",
-				lp_workgroup(),
-				username);
-	if (!*name) {
-		nt_status = NT_STATUS_NO_MEMORY;
-		goto done;
-	}
-
-	nt_status = NT_STATUS_OK;
-
-done:
-	if (filter) {
-		talloc_destroy(filter);
-	}
-	if (msg) {
-		ads_msgfree(ctx->ads, msg);
-	}
-
-	return nt_status;
-}
-
 /************************************************************************
  Function dispatch tables for the idmap and nss plugins
  ***********************************************************************/
@@ -942,32 +571,6 @@ static struct idmap_methods ad_methods = {
 	.sids_to_unixids = idmap_ad_sids_to_unixids,
 };
 
-/* The SFU and RFC2307 NSS plugins share everything but the init
-   function which sets the intended schema model to use */
-
-static struct nss_info_methods nss_rfc2307_methods = {
-	.init           = nss_rfc2307_init,
-	.get_nss_info   = nss_ad_get_info,
-	.map_to_alias   = nss_ad_map_to_alias,
-	.map_from_alias = nss_ad_map_from_alias,
-};
-
-static struct nss_info_methods nss_sfu_methods = {
-	.init           = nss_sfu_init,
-	.get_nss_info   = nss_ad_get_info,
-	.map_to_alias   = nss_ad_map_to_alias,
-	.map_from_alias = nss_ad_map_from_alias,
-};
-
-static struct nss_info_methods nss_sfu20_methods = {
-	.init           = nss_sfu20_init,
-	.get_nss_info   = nss_ad_get_info,
-	.map_to_alias   = nss_ad_map_to_alias,
-	.map_from_alias = nss_ad_map_from_alias,
-};
-
-
-
 /************************************************************************
  Initialize the plugins
  ***********************************************************************/
@@ -976,9 +579,7 @@ static_decl_idmap;
 NTSTATUS idmap_ad_init(void)
 {
 	static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL;
-	static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL;
-	static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL;
-	static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL;
+	static NTSTATUS status_ad_nss = NT_STATUS_UNSUCCESSFUL;
 
 	/* Always register the AD method first in order to get the
 	   idmap_domain interface called */
@@ -990,27 +591,11 @@ NTSTATUS idmap_ad_init(void)
 			return status_idmap_ad;		
 	}
 
-	if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) {
-		status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
-							    "rfc2307",  &nss_rfc2307_methods );		
-		if ( !NT_STATUS_IS_OK(status_nss_rfc2307) )
-			return status_nss_rfc2307;
-	}
-
-	if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) {
-		status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
-							"sfu",  &nss_sfu_methods );		
-		if ( !NT_STATUS_IS_OK(status_nss_sfu) )
-			return status_nss_sfu;		
-	}
-
-	if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) {
-		status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
-							"sfu20",  &nss_sfu20_methods );		
-		if ( !NT_STATUS_IS_OK(status_nss_sfu20) )
-			return status_nss_sfu20;		
+	if ( !NT_STATUS_IS_OK( status_ad_nss ) ) {
+		status_ad_nss = idmap_ad_nss_init();
+		if ( !NT_STATUS_IS_OK(status_ad_nss) )
+			return status_ad_nss;
 	}
 
 	return NT_STATUS_OK;	
 }
-
diff --git a/source3/winbindd/idmap_ad_nss.c b/source3/winbindd/idmap_ad_nss.c
new file mode 100644
index 0000000..8c5a13d
--- /dev/null
+++ b/source3/winbindd/idmap_ad_nss.c
@@ -0,0 +1,531 @@
+/*
+ *  idmap_ad: map between Active Directory and RFC 2307 or "Services for Unix" (SFU) Accounts
+ *
+ * Unix SMB/CIFS implementation.
+ *
+ * Winbind ADS backend functions
+ *
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett <abartlet at samba.org> 2003
+ * Copyright (C) Gerald (Jerry) Carter 2004-2007
+ * Copyright (C) Luke Howard 2001-2004
+ * Copyright (C) Michael Adam 2008,2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libds/common/flags.h"
+#include "ads.h"
+#include "libads/ldap_schema.h"
+#include "nss_info.h"
+#include "idmap.h"
+#include "../libcli/ldap/ldap_ndr.h"
+#include "../libcli/security/security.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+#define CHECK_ALLOC_DONE(mem) do { \
+     if (!mem) { \
+           DEBUG(0, ("Out of memory!\n")); \
+           ret = NT_STATUS_NO_MEMORY; \
+           goto done; \
+      } \
+} while (0)
+
+struct idmap_ad_context {
+	ADS_STRUCT *ads;
+	struct posix_schema *ad_schema;
+	enum wb_posix_mapping ad_map_type; /* WB_POSIX_MAP_UNKNOWN */
+};
+
+/************************************************************************
+ ***********************************************************************/
+
+static ADS_STATUS ad_idmap_cached_connection(struct idmap_domain *dom)
+{
+	ADS_STATUS status;
+	struct idmap_ad_context * ctx;
+
+	DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
+		   dom->name));
+
+	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+
+	status = ads_idmap_cached_connection(&ctx->ads, dom->name);
+	if (!ADS_ERR_OK(status)) {
+		return status;
+	}
+
+	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+
+	/* if we have a valid ADS_STRUCT and the schema model is
+	   defined, then we can return here. */
+
+	if ( ctx->ad_schema ) {
+		return ADS_SUCCESS;
+	}
+
+	/* Otherwise, set the schema model */
+
+	if ( (ctx->ad_map_type ==  WB_POSIX_MAP_SFU) ||
+	     (ctx->ad_map_type ==  WB_POSIX_MAP_SFU20) ||
+	     (ctx->ad_map_type ==  WB_POSIX_MAP_RFC2307) )
+	{
+		status = ads_check_posix_schema_mapping(
+			ctx, ctx->ads, ctx->ad_map_type, &ctx->ad_schema);
+		if ( !ADS_ERR_OK(status) ) {
+			DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n"));
+		}
+	}
+
+	return status;
+}
+
+/*
+ * nss_info_{sfu,sfu20,rfc2307}
+ */
+
+/************************************************************************
+ Initialize the {sfu,sfu20,rfc2307} state
+ ***********************************************************************/
+
+static const char *wb_posix_map_unknown_string = "WB_POSIX_MAP_UNKNOWN";
+static const char *wb_posix_map_template_string = "WB_POSIX_MAP_TEMPLATE";
+static const char *wb_posix_map_sfu_string = "WB_POSIX_MAP_SFU";
+static const char *wb_posix_map_sfu20_string = "WB_POSIX_MAP_SFU20";
+static const char *wb_posix_map_rfc2307_string = "WB_POSIX_MAP_RFC2307";
+static const char *wb_posix_map_unixinfo_string = "WB_POSIX_MAP_UNIXINFO";
+
+static const char *ad_map_type_string(enum wb_posix_mapping map_type)
+{
+	switch (map_type) {
+		case WB_POSIX_MAP_TEMPLATE:
+			return wb_posix_map_template_string;
+		case WB_POSIX_MAP_SFU:
+			return wb_posix_map_sfu_string;
+		case WB_POSIX_MAP_SFU20:
+			return wb_posix_map_sfu20_string;
+		case WB_POSIX_MAP_RFC2307:
+			return wb_posix_map_rfc2307_string;
+		case WB_POSIX_MAP_UNIXINFO:
+			return wb_posix_map_unixinfo_string;
+		default:
+			return wb_posix_map_unknown_string;
+	}
+}
+
+static NTSTATUS nss_ad_generic_init(struct nss_domain_entry *e,
+				    enum wb_posix_mapping new_ad_map_type)
+{
+	struct idmap_domain *dom;
+	struct idmap_ad_context *ctx;
+
+	if (e->state != NULL) {
+		dom = talloc_get_type(e->state, struct idmap_domain);
+	} else {
+		dom = talloc_zero(e, struct idmap_domain);
+		if (dom == NULL) {
+			DEBUG(0, ("Out of memory!\n"));
+			return NT_STATUS_NO_MEMORY;
+		}
+		e->state = dom;
+	}
+
+	if (e->domain != NULL) {
+		dom->name = talloc_strdup(dom, e->domain);
+		if (dom->name == NULL) {
+			DEBUG(0, ("Out of memory!\n"));
+			return NT_STATUS_NO_MEMORY;
+		}
+	}
+
+	if (dom->private_data != NULL) {
+		ctx = talloc_get_type(dom->private_data,
+				      struct idmap_ad_context);
+	} else {
+		ctx = talloc_zero(dom, struct idmap_ad_context);
+		if (ctx == NULL) {
+			DEBUG(0, ("Out of memory!\n"));
+			return NT_STATUS_NO_MEMORY;
+		}
+		ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
+		dom->private_data = ctx;
+	}
+
+	if ((ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
+	    (ctx->ad_map_type != new_ad_map_type))
+	{
+		DEBUG(2, ("nss_ad_generic_init: "
+			  "Warning: overriding previously set posix map type "
+			  "%s for domain %s with map type %s.\n",
+			  ad_map_type_string(ctx->ad_map_type),
+			  dom->name,
+			  ad_map_type_string(new_ad_map_type)));
+	}
+
+	ctx->ad_map_type = new_ad_map_type;
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
+{
+	return nss_ad_generic_init(e, WB_POSIX_MAP_SFU);
+}
+
+static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
+{
+	return nss_ad_generic_init(e, WB_POSIX_MAP_SFU20);
+}
+
+static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
+{
+	return nss_ad_generic_init(e, WB_POSIX_MAP_RFC2307);
+}
+
+
+/************************************************************************
+ ***********************************************************************/
+
+static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e,
+				  const struct dom_sid *sid,
+				  TALLOC_CTX *mem_ctx,
+				  const char **homedir,
+				  const char **shell,
+				  const char **gecos,
+				  gid_t *p_gid )
+{
+	const char *attrs[] = {NULL, /* attr_homedir */
+			       NULL, /* attr_shell */
+			       NULL, /* attr_gecos */
+			       NULL, /* attr_gidnumber */
+			       NULL };
+	char *filter = NULL;
+	LDAPMessage *msg_internal = NULL;
+	ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+	char *sidstr = NULL;
+	struct idmap_domain *dom;
+	struct idmap_ad_context *ctx;
+
+	DEBUG(10, ("nss_ad_get_info called for sid [%s] in domain '%s'\n",
+		   sid_string_dbg(sid), e->domain?e->domain:"NULL"));
+
+	/* Only do query if we are online */
+	if (idmap_is_offline())	{
+		return NT_STATUS_FILE_IS_OFFLINE;
+	}
+
+	dom = talloc_get_type(e->state, struct idmap_domain);
+	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+
+	ads_status = ad_idmap_cached_connection(dom);
+	if (!ADS_ERR_OK(ads_status)) {
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (!ctx->ad_schema) {
+		DEBUG(10, ("nss_ad_get_info: no ad_schema configured!\n"));
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (!sid || !homedir || !shell || !gecos) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	/* Have to do our own query */
+
+	DEBUG(10, ("nss_ad_get_info: no ads connection given, doing our "
+		   "own query\n"));
+
+	attrs[0] = ctx->ad_schema->posix_homedir_attr;
+	attrs[1] = ctx->ad_schema->posix_shell_attr;
+	attrs[2] = ctx->ad_schema->posix_gecos_attr;
+	attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
+
+	sidstr = ldap_encode_ndr_dom_sid(mem_ctx, sid);
+	filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr);
+	TALLOC_FREE(sidstr);
+
+	if (!filter) {
+		nt_status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	ads_status = ads_search_retry(ctx->ads, &msg_internal, filter, attrs);
+	if (!ADS_ERR_OK(ads_status)) {
+		nt_status = ads_ntstatus(ads_status);
+		goto done;
+	}
+
+	*homedir = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_homedir_attr);
+	*shell   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_shell_attr);
+	*gecos   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_gecos_attr);
+
+	if (p_gid != NULL) {
+		uint32_t gid = UINT32_MAX;
+		bool ok;
+
+		ok = ads_pull_uint32(ctx->ads, msg_internal,
+				     ctx->ad_schema->posix_gidnumber_attr,
+				     &gid);
+		if (ok) {
+			*p_gid = gid;
+		} else {
+			*p_gid = (gid_t)-1;
+		}
+	}
+
+	nt_status = NT_STATUS_OK;
+
+done:
+	if (msg_internal) {
+		ads_msgfree(ctx->ads, msg_internal);
+	}
+
+	return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
+				    struct nss_domain_entry *e,
+				    const char *name,
+				    char **alias)
+{
+	const char *attrs[] = {NULL, /* attr_uid */
+			       NULL };
+	char *filter = NULL;
+	LDAPMessage *msg = NULL;
+	ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+	struct idmap_domain *dom;
+	struct idmap_ad_context *ctx = NULL;
+
+	/* Check incoming parameters */
+
+	if ( !e || !e->domain || !name || !*alias) {
+		nt_status = NT_STATUS_INVALID_PARAMETER;
+		goto done;
+	}
+
+	/* Only do query if we are online */
+
+	if (idmap_is_offline())	{
+		nt_status = NT_STATUS_FILE_IS_OFFLINE;
+		goto done;
+	}
+
+	dom = talloc_get_type(e->state, struct idmap_domain);
+	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+
+	ads_status = ad_idmap_cached_connection(dom);
+	if (!ADS_ERR_OK(ads_status)) {
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (!ctx->ad_schema) {
+		nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+		goto done;
+	}
+
+	attrs[0] = ctx->ad_schema->posix_uid_attr;
+
+	filter = talloc_asprintf(mem_ctx,
+				 "(sAMAccountName=%s)",
+				 name);
+	if (!filter) {
+		nt_status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
+	if (!ADS_ERR_OK(ads_status)) {
+		nt_status = ads_ntstatus(ads_status);
+		goto done;
+	}
+
+	*alias = ads_pull_string(ctx->ads, mem_ctx, msg, ctx->ad_schema->posix_uid_attr);
+
+	if (!*alias) {
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	nt_status = NT_STATUS_OK;
+
+done:
+	if (filter) {
+		talloc_destroy(filter);
+	}
+	if (msg) {
+		ads_msgfree(ctx->ads, msg);
+	}
+
+	return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
+					     struct nss_domain_entry *e,
+					     const char *alias,
+					     char **name )
+{
+	const char *attrs[] = {"sAMAccountName",
+			       NULL };
+	char *filter = NULL;
+	LDAPMessage *msg = NULL;
+	ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+	char *username;
+	struct idmap_domain *dom;
+	struct idmap_ad_context *ctx = NULL;
+
+	/* Check incoming parameters */
+
+	if ( !alias || !name) {
+		nt_status = NT_STATUS_INVALID_PARAMETER;
+		goto done;
+	}
+
+	/* Only do query if we are online */
+
+	if (idmap_is_offline())	{
+		nt_status = NT_STATUS_FILE_IS_OFFLINE;
+		goto done;
+	}
+
+	dom = talloc_get_type(e->state, struct idmap_domain);
+	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+
+	ads_status = ad_idmap_cached_connection(dom);
+	if (!ADS_ERR_OK(ads_status)) {
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (!ctx->ad_schema) {
+		nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+		goto done;
+	}
+
+	filter = talloc_asprintf(mem_ctx,
+				 "(%s=%s)",
+				 ctx->ad_schema->posix_uid_attr,
+				 alias);
+	if (!filter) {
+		nt_status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
+	if (!ADS_ERR_OK(ads_status)) {
+		nt_status = ads_ntstatus(ads_status);
+		goto done;
+	}
+
+	username = ads_pull_string(ctx->ads, mem_ctx, msg,
+				   "sAMAccountName");
+	if (!username) {
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	*name = talloc_asprintf(mem_ctx, "%s\\%s",
+				lp_workgroup(),
+				username);
+	if (!*name) {
+		nt_status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	nt_status = NT_STATUS_OK;
+
+done:
+	if (filter) {
+		talloc_destroy(filter);
+	}
+	if (msg) {
+		ads_msgfree(ctx->ads, msg);
+	}
+
+	return nt_status;
+}
+
+/************************************************************************
+ Function dispatch tables for the idmap and nss plugins
+ ***********************************************************************/
+
+/* The SFU and RFC2307 NSS plugins share everything but the init
+   function which sets the intended schema model to use */
+
+static struct nss_info_methods nss_rfc2307_methods = {
+	.init           = nss_rfc2307_init,
+	.get_nss_info   = nss_ad_get_info,
+	.map_to_alias   = nss_ad_map_to_alias,
+	.map_from_alias = nss_ad_map_from_alias,
+};
+
+static struct nss_info_methods nss_sfu_methods = {
+	.init           = nss_sfu_init,
+	.get_nss_info   = nss_ad_get_info,
+	.map_to_alias   = nss_ad_map_to_alias,
+	.map_from_alias = nss_ad_map_from_alias,
+};
+
+static struct nss_info_methods nss_sfu20_methods = {
+	.init           = nss_sfu20_init,
+	.get_nss_info   = nss_ad_get_info,
+	.map_to_alias   = nss_ad_map_to_alias,
+	.map_from_alias = nss_ad_map_from_alias,
+};
+
+
+
+/************************************************************************
+ Initialize the plugins
+ ***********************************************************************/
+
+NTSTATUS idmap_ad_nss_init(void)
+{
+	static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL;
+	static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL;
+	static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL;
+
+	if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) {
+		status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+							    "rfc2307",  &nss_rfc2307_methods );
+		if ( !NT_STATUS_IS_OK(status_nss_rfc2307) )
+			return status_nss_rfc2307;
+	}
+
+	if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) {
+		status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+							"sfu",  &nss_sfu_methods );
+		if ( !NT_STATUS_IS_OK(status_nss_sfu) )
+			return status_nss_sfu;
+	}
+
+	if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) {
+		status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+							"sfu20",  &nss_sfu20_methods );
+		if ( !NT_STATUS_IS_OK(status_nss_sfu20) )
+			return status_nss_sfu20;
+	}
+
+	return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/idmap_proto.h b/source3/winbindd/idmap_proto.h
index ea96d72..84cc2f0 100644
--- a/source3/winbindd/idmap_proto.h
+++ b/source3/winbindd/idmap_proto.h
@@ -63,4 +63,6 @@ struct id_map **id_map_ptrs_init(TALLOC_CTX *mem_ctx, size_t num_ids);
 /* max number of ids requested per LDAP batch query */
 #define IDMAP_LDAP_MAX_IDS 30
 
+NTSTATUS idmap_ad_nss_init(void);
+
 #endif /* _WINBINDD_IDMAP_PROTO_H_ */
diff --git a/source3/winbindd/wscript_build b/source3/winbindd/wscript_build
index 3ee280c..eab788f 100644
--- a/source3/winbindd/wscript_build
+++ b/source3/winbindd/wscript_build
@@ -20,7 +20,7 @@ bld.SAMBA3_SUBSYSTEM('IDMAP_HASH',
                     enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_hash'))
 
 bld.SAMBA3_SUBSYSTEM('IDMAP_AD',
-                    source='idmap_ad.c',
+                    source='idmap_ad.c idmap_ad_nss.c',
                     deps='ads nss_info',
                     enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad'))
 
-- 
1.7.9.5


From 1dd6a49049f938c512ecadb397040963c2d097a7 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 27 Dec 2015 12:37:25 +0100
Subject: [PATCH 2/7] tldap: Add tldap_get/set_stream

This will be used to replace a nonencrypted socket with a sasl sealed one.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/include/tldap.h |    4 ++++
 source3/lib/tldap.c     |   11 +++++++++++
 2 files changed, 15 insertions(+)

diff --git a/source3/include/tldap.h b/source3/include/tldap.h
index 0d7e55d..74279a4 100644
--- a/source3/include/tldap.h
+++ b/source3/include/tldap.h
@@ -117,6 +117,10 @@ bool tevent_req_ldap_error(struct tevent_req *req, TLDAPRC rc);
 bool tevent_req_is_ldap_error(struct tevent_req *req, TLDAPRC *perr);
 
 struct tldap_context *tldap_context_create(TALLOC_CTX *mem_ctx, int fd);
+struct tstream_context *tldap_get_tstream(struct tldap_context *ld);
+void tldap_set_tstream(struct tldap_context *ld,
+		       struct tstream_context *stream);
+
 bool tldap_connection_ok(struct tldap_context *ld);
 bool tldap_context_setattr(struct tldap_context *ld,
 			   const char *name, const void *pptr);
diff --git a/source3/lib/tldap.c b/source3/lib/tldap.c
index 6f42e61..5fcb43c 100644
--- a/source3/lib/tldap.c
+++ b/source3/lib/tldap.c
@@ -196,6 +196,17 @@ static size_t tldap_pending_reqs(struct tldap_context *ld)
 	return talloc_array_length(ld->pending);
 }
 
+struct tstream_context *tldap_get_tstream(struct tldap_context *ld)
+{
+	return ld->conn;
+}
+
+void tldap_set_tstream(struct tldap_context *ld,
+		       struct tstream_context *stream)
+{
+	ld->conn = stream;
+}
+
 static struct tldap_ctx_attribute *tldap_context_findattr(
 	struct tldap_context *ld, const char *name)
 {
-- 
1.7.9.5


From 853c2265f66dd9828804ed729fb27bd764659a24 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 18 Dec 2015 16:41:41 +0100
Subject: [PATCH 3/7] tldap: Add tldap_gensec_bind

This enables sasl sign/sealed connections via tldap

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/tldap_gensec_bind.c |  366 +++++++++++++++++++++++++++++++++++++++
 source3/lib/tldap_gensec_bind.h |   40 +++++
 source3/wscript_build           |    1 +
 3 files changed, 407 insertions(+)
 create mode 100644 source3/lib/tldap_gensec_bind.c
 create mode 100644 source3/lib/tldap_gensec_bind.h

diff --git a/source3/lib/tldap_gensec_bind.c b/source3/lib/tldap_gensec_bind.c
new file mode 100644
index 0000000..043a9fa
--- /dev/null
+++ b/source3/lib/tldap_gensec_bind.c
@@ -0,0 +1,366 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Gensec based tldap auth
+ * Copyright (C) Volker Lendecke 2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tldap_gensec_bind.h"
+#include "tldap_util.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/util/samba_util.h"
+#include "lib/util/debug.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_internal.h" /* TODO: remove this */
+#include "lib/param/param.h"
+#include "source4/auth/gensec/gensec_tstream.h"
+
+struct tldap_gensec_bind_state {
+	struct tevent_context *ev;
+	struct tldap_context *ctx;
+	struct cli_credentials *creds;
+	const char *target_service;
+	const char *target_hostname;
+	const char *target_principal;
+ 	struct loadparm_context *lp_ctx;
+	uint32_t gensec_features;
+
+	bool first;
+	struct gensec_security *gensec;
+	NTSTATUS gensec_status;
+	DATA_BLOB gensec_output;
+};
+
+static void tldap_gensec_bind_got_mechs(struct tevent_req *subreq);
+static void tldap_gensec_update_done(struct tevent_req *subreq);
+static void tldap_gensec_bind_done(struct tevent_req *subreq);
+
+struct tevent_req *tldap_gensec_bind_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct tldap_context *ctx, struct cli_credentials *creds,
+	const char *target_service, const char *target_hostname,
+	const char *target_principal, struct loadparm_context *lp_ctx,
+	uint32_t gensec_features)
+{
+	struct tevent_req *req, *subreq;
+	struct tldap_gensec_bind_state *state;
+
+	const char *attrs[] = { "supportedSASLMechanisms" };
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct tldap_gensec_bind_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->ctx = ctx;
+	state->creds = creds;
+	state->target_service = target_service;
+	state->target_hostname = target_hostname;
+	state->target_principal = target_principal;
+	state->lp_ctx = lp_ctx;
+	state->gensec_features = gensec_features;
+	state->first = true;
+
+	subreq = tldap_search_all_send(
+		state, state->ev, state->ctx, "", TLDAP_SCOPE_BASE,
+		"(objectclass=*)", attrs, ARRAY_SIZE(attrs),
+		false, NULL, 0, NULL, 0, 0, 1 /* sizelimit */, 0);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, tldap_gensec_bind_got_mechs, req);
+	return req;
+}
+
+static void tldap_gensec_bind_got_mechs(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct tldap_gensec_bind_state *state = tevent_req_data(
+		req, struct tldap_gensec_bind_state);
+	struct tldap_message **msgs, *msg, *result;
+	struct tldap_attribute *attribs, *attrib;
+	int num_attribs;
+	size_t num_msgs;
+	TLDAPRC rc;
+	int i;
+	bool ok;
+	const char **sasl_mechs;
+	NTSTATUS status;
+
+	rc = tldap_search_all_recv(subreq, state, &msgs, &result);
+	TALLOC_FREE(subreq);
+	if (tevent_req_ldap_error(req, rc)) {
+		return;
+	}
+
+	/*
+	 * TODO: Inspect "Result"
+	 */
+
+	num_msgs = talloc_array_length(msgs);
+	if (num_msgs != 1) {
+		DBG_DEBUG("num_msgs = %zu\n", num_msgs);
+		tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+		return;
+	}
+	msg = msgs[0];
+
+	ok = tldap_entry_attributes(msg, &attribs, &num_attribs);
+	if (!ok) {
+		DBG_DEBUG("tldap_entry_attributes failed\n");
+		tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+		return;
+	}
+
+	if (num_attribs != 1) {
+		DBG_DEBUG("num_attribs = %d\n", num_attribs);
+		tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+		return;
+	}
+	attrib = &attribs[0];
+
+	sasl_mechs = talloc_array(state, const char *, attrib->num_values+1);
+	if (tevent_req_nomem(sasl_mechs, req)) {
+		return;
+	}
+
+	for (i=0; i<attrib->num_values; i++) {
+		DATA_BLOB *v = &attrib->values[i];
+		size_t len;
+
+		ok = convert_string_talloc(sasl_mechs, CH_UTF8, CH_UNIX,
+					   v->data, v->length,
+					   &sasl_mechs[i], &len);
+		if (!ok) {
+			DBG_DEBUG("convert_string_talloc failed\n");
+			tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+			return;
+		}
+	}
+	sasl_mechs[attrib->num_values] = NULL;
+
+	gensec_init();
+
+	status = gensec_client_start(
+		state, &state->gensec,
+		lpcfg_gensec_settings(state, state->lp_ctx));
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("gensec_client_start failed: %s\n",
+			  nt_errstr(status));
+		tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+		return;
+	}
+
+	status = gensec_set_credentials(state->gensec, state->creds);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("gensec_set_credentials failed: %s\n",
+			  nt_errstr(status));
+		tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+		return;
+	}
+
+	status = gensec_set_target_service(state->gensec,
+					   state->target_service);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("gensec_set_target_service failed: %s\n",
+			  nt_errstr(status));
+		tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+		return;
+	}
+
+	if (state->target_hostname != NULL) {
+		status = gensec_set_target_hostname(state->gensec,
+						    state->target_hostname);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_DEBUG("gensec_set_target_hostname failed: %s\n",
+				  nt_errstr(status));
+			tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+			return;
+		}
+	}
+
+	if (state->target_principal != NULL) {
+		status = gensec_set_target_principal(state->gensec,
+						     state->target_principal);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_DEBUG("gensec_set_target_principal failed: %s\n",
+				  nt_errstr(status));
+			tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+			return;
+		}
+	}
+
+	gensec_want_feature(state->gensec, state->gensec_features);
+
+	status = gensec_start_mech_by_sasl_list(state->gensec, sasl_mechs);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("gensec_start_mech_by_sasl_list failed: %s\n",
+			  nt_errstr(status));
+		tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
+		return;
+	}
+
+	subreq = gensec_update_send(state, state->ev, state->gensec,
+				    data_blob_null);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, tldap_gensec_update_done, req);
+}
+
+static void tldap_gensec_update_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct tldap_gensec_bind_state *state = tevent_req_data(
+		req, struct tldap_gensec_bind_state);
+
+	state->gensec_status = gensec_update_recv(
+		subreq, state, &state->gensec_output);
+
+	TALLOC_FREE(subreq);
+
+	if (!NT_STATUS_IS_OK(state->gensec_status) &&
+	    !NT_STATUS_EQUAL(state->gensec_status,
+			     NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		DBG_DEBUG("gensec_update failed: %s\n",
+			  nt_errstr(state->gensec_status));
+		tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS);
+		return;
+	}
+
+	if (NT_STATUS_IS_OK(state->gensec_status) &&
+	    (state->gensec_output.length == 0)) {
+
+		if (state->first) {
+			tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS);
+		} else {
+			tevent_req_done(req);
+		}
+		return;
+	}
+
+	state->first = false;
+
+	subreq = tldap_sasl_bind_send(
+		state, state->ev, state->ctx, "",
+		state->gensec->ops->sasl_name, &state->gensec_output,
+		NULL, 0, NULL, 0);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, tldap_gensec_bind_done, req);
+}
+
+static void tldap_gensec_bind_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct tldap_gensec_bind_state *state = tevent_req_data(
+		req, struct tldap_gensec_bind_state);
+	DATA_BLOB input;
+	TLDAPRC rc;
+
+	rc = tldap_sasl_bind_recv(subreq, state, &input);
+	TALLOC_FREE(subreq);
+	if (!TLDAP_RC_IS_SUCCESS(rc) &&
+	    !TLDAP_RC_EQUAL(rc, TLDAP_SASL_BIND_IN_PROGRESS)) {
+		tevent_req_ldap_error(req, rc);
+		return;
+	}
+
+	if (TLDAP_RC_IS_SUCCESS(rc) && NT_STATUS_IS_OK(state->gensec_status)) {
+		tevent_req_done(req);
+		return;
+	}
+
+	subreq = gensec_update_send(state, state->ev, state->gensec, input);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, tldap_gensec_update_done, req);
+}
+
+TLDAPRC tldap_gensec_bind_recv(struct tevent_req *req)
+{
+	struct tldap_gensec_bind_state *state = tevent_req_data(
+		req, struct tldap_gensec_bind_state);
+	struct tstream_context *plain, *sec;
+	NTSTATUS status;
+	TLDAPRC rc;
+
+	if (tevent_req_is_ldap_error(req, &rc)) {
+		return rc;
+	}
+
+	if (!gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN) &&
+	    !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) {
+		return TLDAP_SUCCESS;
+	}
+
+	/*
+	 * The gensec ctx needs to survive as long as the ldap context
+	 * lives
+	 */
+	talloc_steal(state->ctx, state->gensec);
+
+	plain = tldap_get_tstream(state->ctx);
+
+	status = gensec_create_tstream(state->ctx, state->gensec,
+				       plain, &sec);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("gensec_create_tstream failed: %s\n",
+			  nt_errstr(status));
+		return TLDAP_OPERATIONS_ERROR;
+	}
+
+	tldap_set_tstream(state->ctx, sec);
+
+	return TLDAP_SUCCESS;
+}
+
+TLDAPRC tldap_gensec_bind(
+	struct tldap_context *ctx, struct cli_credentials *creds,
+	const char *target_service, const char *target_hostname,
+	const char *target_principal, struct loadparm_context *lp_ctx,
+	uint32_t gensec_features)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct tevent_context *ev;
+	struct tevent_req *req;
+	TLDAPRC rc = TLDAP_NO_MEMORY;
+
+	ev = samba_tevent_context_init(frame);
+	if (ev == NULL) {
+		goto fail;
+	}
+	req = tldap_gensec_bind_send(frame, ev, ctx, creds, target_service,
+				     target_hostname, target_principal, lp_ctx,
+				     gensec_features);
+	if (req == NULL) {
+		goto fail;
+	}
+	if (!tevent_req_poll(req, ev)) {
+		rc = TLDAP_OPERATIONS_ERROR;
+		goto fail;
+	}
+	rc = tldap_gensec_bind_recv(req);
+ fail:
+	TALLOC_FREE(frame);
+	return rc;
+}
diff --git a/source3/lib/tldap_gensec_bind.h b/source3/lib/tldap_gensec_bind.h
new file mode 100644
index 0000000..deddc23
--- /dev/null
+++ b/source3/lib/tldap_gensec_bind.h
@@ -0,0 +1,40 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Gensec based tldap bind
+ * Copyright (C) Volker Lendecke 2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TLDAP_GENSEC_BIND_H__
+#define __TLDAP_GENSEC_BIND_H__
+
+#include "replace.h"
+#include "tldap.h"
+#include "auth/credentials/credentials.h"
+
+struct tevent_req *tldap_gensec_bind_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct tldap_context *ctx, struct cli_credentials *creds,
+	const char *target_service, const char *target_hostname,
+	const char *target_principal, struct loadparm_context *lp_ctx,
+	uint32_t gensec_features);
+TLDAPRC tldap_gensec_bind_recv(struct tevent_req *req);
+TLDAPRC tldap_gensec_bind(
+	struct tldap_context *ctx, struct cli_credentials *creds,
+	const char *target_service, const char *target_hostname,
+	const char *target_principal, struct loadparm_context *lp_ctx,
+	uint32_t gensec_features);
+
+#endif
diff --git a/source3/wscript_build b/source3/wscript_build
index 436b112..ed2424d 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -96,6 +96,7 @@ bld.SAMBA3_SUBSYSTEM('GROUPDB',
 bld.SAMBA3_SUBSYSTEM('TLDAP',
                     source='''lib/tldap.c
                     lib/tldap_util.c
+                    lib/tldap_gensec_bind.c
                     ''',
                     deps='asn1util LIBTSOCKET samba3util')
 
-- 
1.7.9.5


From e16d3c9ab0c641d5a06b9d916e49ea9d0fc8077f Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 17 Mar 2016 14:27:32 +0100
Subject: [PATCH 4/7] winbind: Add wb_dsgetdcname_gencache_[gs]et

This is a sneaky way to pass the DC info from the parent winbind to children
and other users.

Not sure where exactly to put these routines. For now, put them into the parent
code to find the dcinfo from "our" dc.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_dsgetdcname.c |  107 +++++++++++++++++++++++++++++++++++++
 source3/winbindd/winbindd_proto.h |    5 ++
 2 files changed, 112 insertions(+)

diff --git a/source3/winbindd/wb_dsgetdcname.c b/source3/winbindd/wb_dsgetdcname.c
index db6cde9..5aea7de 100644
--- a/source3/winbindd/wb_dsgetdcname.c
+++ b/source3/winbindd/wb_dsgetdcname.c
@@ -20,6 +20,7 @@
 #include "includes.h"
 #include "winbindd.h"
 #include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
 
 struct wb_dsgetdcname_state {
 	struct netr_DsRGetDCNameInfo *dcinfo;
@@ -123,3 +124,109 @@ NTSTATUS wb_dsgetdcname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 	*pdcinfo = talloc_move(mem_ctx, &state->dcinfo);
 	return NT_STATUS_OK;
 }
+
+NTSTATUS wb_dsgetdcname_gencache_set(const char *domname,
+				     struct netr_DsRGetDCNameInfo *dcinfo)
+{
+	DATA_BLOB blob;
+	enum ndr_err_code ndr_err;
+	char *key;
+	bool ok;
+
+	key = talloc_asprintf_strupper_m(talloc_tos(), "DCINFO/%s", domname);
+	if (key == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (DEBUGLEVEL >= 10) {
+		NDR_PRINT_DEBUG(netr_DsRGetDCNameInfo, dcinfo);
+	}
+
+	ndr_err = ndr_push_struct_blob(
+		&blob, key, dcinfo,
+		(ndr_push_flags_fn_t)ndr_push_netr_DsRGetDCNameInfo);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+		DBG_WARNING("ndr_push_struct_blob failed: %s\n",
+			    ndr_errstr(ndr_err));
+		TALLOC_FREE(key);
+		return status;
+	}
+
+	ok = gencache_set_data_blob(key, &blob, time(NULL)+3600);
+
+	if (!ok) {
+		DBG_WARNING("gencache_set_data_blob for key %s failed\n", key);
+		TALLOC_FREE(key);
+		return NT_STATUS_UNSUCCESSFUL;
+	}
+
+	TALLOC_FREE(key);
+	return NT_STATUS_OK;
+}
+
+struct dcinfo_parser_state {
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx;
+	struct netr_DsRGetDCNameInfo *dcinfo;
+};
+
+static void dcinfo_parser(time_t timeout, DATA_BLOB blob, void *private_data)
+{
+	struct dcinfo_parser_state *state = private_data;
+	enum ndr_err_code ndr_err;
+
+	state->dcinfo = talloc(state->mem_ctx, struct netr_DsRGetDCNameInfo);
+	if (state->dcinfo == NULL) {
+		state->status = NT_STATUS_NO_MEMORY;
+		return;
+	}
+
+	ndr_err = ndr_pull_struct_blob_all(
+		&blob, state->dcinfo, state->dcinfo,
+		(ndr_pull_flags_fn_t)ndr_pull_netr_DsRGetDCNameInfo);
+
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DBG_ERR("ndr_pull_struct_blob failed\n");
+		state->status = ndr_map_error2ntstatus(ndr_err);
+		return;
+	}
+
+	state->status = NT_STATUS_OK;
+}
+
+NTSTATUS wb_dsgetdcname_gencache_get(TALLOC_CTX *mem_ctx,
+				     const char *domname,
+				     struct netr_DsRGetDCNameInfo **dcinfo)
+{
+	struct dcinfo_parser_state state;
+	char *key;
+	bool ok;
+
+	key = talloc_asprintf_strupper_m(mem_ctx, "DCINFO/%s", domname);
+	if (key == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state = (struct dcinfo_parser_state) {
+		.status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND,
+		.mem_ctx = mem_ctx,
+	};
+
+	ok = gencache_parse(key, dcinfo_parser, &state);
+	TALLOC_FREE(key);
+	if (!ok) {
+		return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+	}
+
+	if (!NT_STATUS_IS_OK(state.status)) {
+		return state.status;
+	}
+
+	if (DEBUGLEVEL >= 10) {
+		NDR_PRINT_DEBUG(netr_DsRGetDCNameInfo, state.dcinfo);
+	}
+
+	*dcinfo = state.dcinfo;
+	return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 7c9a924..49ae475 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -771,6 +771,11 @@ struct tevent_req *wb_dsgetdcname_send(TALLOC_CTX *mem_ctx,
 				       uint32_t flags);
 NTSTATUS wb_dsgetdcname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 			     struct netr_DsRGetDCNameInfo **pdcinfo);
+NTSTATUS wb_dsgetdcname_gencache_set(const char *domname,
+				     struct netr_DsRGetDCNameInfo *dcinfo);
+NTSTATUS wb_dsgetdcname_gencache_get(TALLOC_CTX *mem_ctx,
+				     const char *domname,
+				     struct netr_DsRGetDCNameInfo **dcinfo);
 
 struct tevent_req *winbindd_getdcname_send(TALLOC_CTX *mem_ctx,
 					   struct tevent_context *ev,
-- 
1.7.9.5


From 2369e7f58687df9072ff8bfe150314d4f03106a0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 12 Jan 2016 21:21:17 +0100
Subject: [PATCH 5/7] winbind: handle DC_NOT_FOUND in wb_sids2xids

The idmap_ad child is designed to connect to domain controllers on
its own.  Finding a DC is a nontrivial task that the child should not
do on its own, in particular it should not have to connect to "our"
DC's NETLOGON pipe separately. So when idmap_ad finds that it needs to
connect to a DC, it returns NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND. The
parent then asynchronously does the lookup and stores the DC info in
gencache. After that the parent re-does the idmap child call, during
which the child will find the DC-info in gencache.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_sids2xids.c |   64 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)

diff --git a/source3/winbindd/wb_sids2xids.c b/source3/winbindd/wb_sids2xids.c
index 7cb7ec4..e16917f 100644
--- a/source3/winbindd/wb_sids2xids.c
+++ b/source3/winbindd/wb_sids2xids.c
@@ -23,6 +23,7 @@
 #include "../libcli/security/security.h"
 #include "idmap_cache.h"
 #include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
 #include "lsa.h"
 
 struct wb_sids2xids_state {
@@ -52,6 +53,7 @@ struct wb_sids2xids_state {
 	uint32_t dom_index;
 	struct wbint_TransIDArray *dom_ids;
 	struct lsa_RefDomainList idmap_dom;
+	bool tried_dclookup;
 
 	struct wbint_TransIDArray ids;
 };
@@ -60,6 +62,7 @@ struct wb_sids2xids_state {
 static bool wb_sids2xids_in_cache(struct dom_sid *sid, struct id_map *map);
 static void wb_sids2xids_lookupsids_done(struct tevent_req *subreq);
 static void wb_sids2xids_done(struct tevent_req *subreq);
+static void wb_sids2xids_gotdc(struct tevent_req *subreq);
 
 struct tevent_req *wb_sids2xids_send(TALLOC_CTX *mem_ctx,
 				     struct tevent_context *ev,
@@ -254,7 +257,6 @@ static enum id_type lsa_SidType_to_id_type(const enum lsa_SidType sid_type)
 	return type;
 }
 
-
 static void wb_sids2xids_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
@@ -270,6 +272,27 @@ static void wb_sids2xids_done(struct tevent_req *subreq)
 	status = dcerpc_wbint_Sids2UnixIDs_recv(subreq, state, &result);
 	TALLOC_FREE(subreq);
 
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
+	    !state->tried_dclookup) {
+
+		struct lsa_DomainInfo *d;
+
+		d = &state->idmap_doms.domains[state->dom_index];
+
+		subreq = wb_dsgetdcname_send(
+			state, state->ev, d->name.string, NULL, NULL,
+			DS_RETURN_DNS_NAME);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, wb_sids2xids_gotdc, req);
+		return;
+	}
+
 	src = state->dom_ids;
 	src_idx = 0;
 	dst = &state->ids;
@@ -297,6 +320,7 @@ static void wb_sids2xids_done(struct tevent_req *subreq)
 	TALLOC_FREE(state->dom_ids);
 
 	state->dom_index += 1;
+	state->tried_dclookup = false;
 
 	if (state->dom_index == state->idmap_doms.count) {
 		tevent_req_done(req);
@@ -326,6 +350,44 @@ static void wb_sids2xids_done(struct tevent_req *subreq)
 	tevent_req_set_callback(subreq, wb_sids2xids_done, req);
 }
 
+static void wb_sids2xids_gotdc(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_sids2xids_state *state = tevent_req_data(
+		req, struct wb_sids2xids_state);
+	struct winbindd_child *child = idmap_child();
+	struct netr_DsRGetDCNameInfo *dcinfo;
+	NTSTATUS status;
+
+	status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	state->tried_dclookup = true;
+
+	{
+		struct lsa_DomainInfo *d =
+			&state->idmap_doms.domains[state->dom_index];
+		const char *dom_name = d->name.string;
+
+		status = wb_dsgetdcname_gencache_set(dom_name, dcinfo);
+		if (tevent_req_nterror(req, status)) {
+			return;
+		}
+	}
+
+	subreq = dcerpc_wbint_Sids2UnixIDs_send(
+		state, state->ev, child->binding_handle, &state->idmap_dom,
+		state->dom_ids);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, wb_sids2xids_done, req);
+}
+
 NTSTATUS wb_sids2xids_recv(struct tevent_req *req,
 			   struct unixid xids[], uint32_t num_xids)
 {
-- 
1.7.9.5


From 4b19d2398523d633c356502861c782f65bb0e45c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 12 Jan 2016 21:21:17 +0100
Subject: [PATCH 6/7] winbind: handle DC_NOT_FOUND in wb_xids2sids

The idmap_ad child is designed to connect to domain controllers on
its own.  Finding a DC is a nontrivial task that the child should not
do on its own, in particular it should not have to connect to "our"
DC's NETLOGON pipe separately. So when idmap_ad finds that it needs to
connect to a DC, it returns NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND. The
parent then asynchronously does the lookup and stores the DC info in
gencache. After that the parent re-does the idmap child call, during
which the child will find the DC-info in gencache.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_xids2sids.c |   52 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/source3/winbindd/wb_xids2sids.c b/source3/winbindd/wb_xids2sids.c
index 77372f7..7fc8a72 100644
--- a/source3/winbindd/wb_xids2sids.c
+++ b/source3/winbindd/wb_xids2sids.c
@@ -22,6 +22,7 @@
 #include "../libcli/security/security.h"
 #include "idmap_cache.h"
 #include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
 
 struct wb_xids2sids_dom_map {
 	unsigned low_id;
@@ -136,10 +137,12 @@ static void wb_xids2sids_init_dom_maps(void)
 }
 
 struct wb_xids2sids_dom_state {
+	struct tevent_context *ev;
 	struct unixid *all_xids;
 	size_t num_all_xids;
 	struct dom_sid *all_sids;
 	struct wb_xids2sids_dom_map *dom_map;
+	bool tried_dclookup;
 
 	size_t num_dom_xids;
 	struct unixid *dom_xids;
@@ -147,6 +150,7 @@ struct wb_xids2sids_dom_state {
 };
 
 static void wb_xids2sids_dom_done(struct tevent_req *subreq);
+static void wb_xids2sids_dom_gotdc(struct tevent_req *subreq);
 
 static struct tevent_req *wb_xids2sids_dom_send(
 	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
@@ -163,6 +167,7 @@ static struct tevent_req *wb_xids2sids_dom_send(
 	if (req == NULL) {
 		return NULL;
 	}
+	state->ev = ev;
 	state->all_xids = xids;
 	state->num_all_xids = num_xids;
 	state->all_sids = sids;
@@ -224,6 +229,20 @@ static void wb_xids2sids_dom_done(struct tevent_req *subreq)
 	if (tevent_req_nterror(req, status)) {
 		return;
 	}
+
+	if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
+	    !state->tried_dclookup) {
+
+		subreq = wb_dsgetdcname_send(
+			state, state->ev, state->dom_map->name, NULL, NULL,
+			DS_RETURN_DNS_NAME);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, wb_xids2sids_dom_gotdc, req);
+		return;
+	}
+
 	if (!NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED) &&
 	    tevent_req_nterror(req, result)) {
 		return;
@@ -249,6 +268,39 @@ static void wb_xids2sids_dom_done(struct tevent_req *subreq)
 	tevent_req_done(req);
 }
 
+static void wb_xids2sids_dom_gotdc(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_xids2sids_dom_state *state = tevent_req_data(
+		req, struct wb_xids2sids_dom_state);
+	struct winbindd_child *child = idmap_child();
+	struct netr_DsRGetDCNameInfo *dcinfo;
+	NTSTATUS status;
+
+	status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	state->tried_dclookup = true;
+
+	status = wb_dsgetdcname_gencache_set(state->dom_map->name, dcinfo);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	child = idmap_child();
+	subreq = dcerpc_wbint_UnixIDs2Sids_send(
+		state, state->ev, child->binding_handle, state->dom_map->name,
+		state->num_dom_xids, state->dom_xids, state->dom_sids);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, wb_xids2sids_dom_done, req);
+}
+
 static NTSTATUS wb_xids2sids_dom_recv(struct tevent_req *req)
 {
 	return tevent_req_simple_recv_ntstatus(req);
-- 
1.7.9.5


From 0a8d2d8a3c7c708c0bf9da444cbb7f78967335fe Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 27 Dec 2015 16:22:22 +0100
Subject: [PATCH 7/7] winbind: Base idmap_ad on tldap

The main reason for this is to do proper connection management. I tried hard,
but I failed trying to slowly migrate the ads_struct based code to something
saner. So I polished tldap, which thanks to metze does proper sasl.

This patch is pretty much a complete rewrite, so looking at it in diff -u
format does not really make sense. Look at the final output.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/idmap_ad.c    | 1059 +++++++++++++++++++++++++---------------
 source3/winbindd/wscript_build |    2 +-
 2 files changed, 655 insertions(+), 406 deletions(-)

diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
index 8f15ecb..242b788 100644
--- a/source3/winbindd/idmap_ad.c
+++ b/source3/winbindd/idmap_ad.c
@@ -1,15 +1,7 @@
 /*
- *  idmap_ad: map between Active Directory and RFC 2307 or "Services for Unix" (SFU) Accounts
+ * idmap_ad: map between Active Directory and RFC 2307 accounts
  *
- * Unix SMB/CIFS implementation.
- *
- * Winbind ADS backend functions
- *
- * Copyright (C) Andrew Tridgell 2001
- * Copyright (C) Andrew Bartlett <abartlet at samba.org> 2003
- * Copyright (C) Gerald (Jerry) Carter 2004-2007
- * Copyright (C) Luke Howard 2001-2004
- * Copyright (C) Michael Adam 2008,2010
+ * Copyright (C) Volker Lendecke 2015
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,480 +19,739 @@
 
 #include "includes.h"
 #include "winbindd.h"
-#include "../libds/common/flags.h"
-#include "ads.h"
-#include "libads/ldap_schema.h"
-#include "nss_info.h"
 #include "idmap.h"
-#include "../libcli/ldap/ldap_ndr.h"
-#include "../libcli/security/security.h"
-
-#undef DBGC_CLASS
-#define DBGC_CLASS DBGC_IDMAP
+#include "tldap_gensec_bind.h"
+#include "tldap_util.h"
+#include "lib/param/param.h"
+#include "utils/net.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "libads/ldap_schema_oids.h"
+#include "../libds/common/flags.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/security/dom_sid.h"
 
-#define CHECK_ALLOC_DONE(mem) do { \
-     if (!mem) { \
-           DEBUG(0, ("Out of memory!\n")); \
-           ret = NT_STATUS_NO_MEMORY; \
-           goto done; \
-      } \
-} while (0)
+struct idmap_ad_schema_names;
 
 struct idmap_ad_context {
-	ADS_STRUCT *ads;
-	struct posix_schema *ad_schema;
-	enum wb_posix_mapping ad_map_type; /* WB_POSIX_MAP_UNKNOWN */
+	struct idmap_domain *dom;
+	struct tldap_context *ld;
+	struct idmap_ad_schema_names *schema;
+	const char *default_nc;
 };
 
-/************************************************************************
- ***********************************************************************/
+static char *get_schema_path(TALLOC_CTX *mem_ctx, struct tldap_context *ld)
+{
+	struct tldap_message *rootdse;
 
-static ADS_STATUS ad_idmap_cached_connection(struct idmap_domain *dom)
+	rootdse = tldap_rootdse(ld);
+	if (rootdse == NULL) {
+		return NULL;
+	}
+
+	return tldap_talloc_single_attribute(rootdse, "schemaNamingContext",
+					     mem_ctx);
+}
+
+static char *get_default_nc(TALLOC_CTX *mem_ctx, struct tldap_context *ld)
 {
-	ADS_STATUS status;
-	struct idmap_ad_context * ctx;
+	struct tldap_message *rootdse;
 
-	DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
-		   dom->name));
+	rootdse = tldap_rootdse(ld);
+	if (rootdse == NULL) {
+		return NULL;
+	}
 
-	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+	return tldap_talloc_single_attribute(rootdse, "defaultNamingContext",
+					     mem_ctx);
+}
 
-	status = ads_idmap_cached_connection(&ctx->ads, dom->name);
-	if (!ADS_ERR_OK(status)) {
-		return status;
+struct idmap_ad_schema_names {
+	char *name;
+	char *uid;
+	char *gid;
+	char *gecos;
+	char *dir;
+	char *shell;
+};
+
+static TLDAPRC get_attrnames_by_oids(struct tldap_context *ld,
+				     TALLOC_CTX *mem_ctx,
+				     const char *schema_path,
+				     size_t num_oids,
+				     const char **oids,
+				     char **names)
+{
+	char *filter;
+	const char *attrs[] = { "lDAPDisplayName", "attributeId" };
+	size_t i;
+	TLDAPRC rc;
+	struct tldap_message **msgs;
+	size_t num_msgs;
+
+	filter = talloc_strdup(mem_ctx, "(|");
+	if (filter == NULL) {
+		return TLDAP_NO_MEMORY;
+	}
+
+	for (i=0; i<num_oids; i++) {
+		filter = talloc_asprintf_append_buffer(
+			filter, "(attributeId=%s)", oids[i]);
+		if (filter == NULL) {
+			return TLDAP_NO_MEMORY;
+		}
 	}
 
-	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+	filter = talloc_asprintf_append_buffer(filter, ")");
+	if (filter == NULL) {
+		return TLDAP_NO_MEMORY;
+	}
 
-	/* if we have a valid ADS_STRUCT and the schema model is
-	   defined, then we can return here. */
+	rc = tldap_search(ld, schema_path, TLDAP_SCOPE_SUB, filter,
+			  attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0,
+			  0, 0, 0, mem_ctx, &msgs);;
+	TALLOC_FREE(filter);
+	if (!TLDAP_RC_IS_SUCCESS(rc)) {
+		return rc;
+	}
 
-	if ( ctx->ad_schema ) {
-		return ADS_SUCCESS;
+	for (i=0; i<num_oids; i++) {
+		names[i] = NULL;
 	}
 
-	/* Otherwise, set the schema model */
+	num_msgs = talloc_array_length(msgs);
 
-	if ( (ctx->ad_map_type ==  WB_POSIX_MAP_SFU) ||
-	     (ctx->ad_map_type ==  WB_POSIX_MAP_SFU20) ||
-	     (ctx->ad_map_type ==  WB_POSIX_MAP_RFC2307) )
-	{
-		status = ads_check_posix_schema_mapping(
-			ctx, ctx->ads, ctx->ad_map_type, &ctx->ad_schema);
-		if ( !ADS_ERR_OK(status) ) {
-			DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n"));
+	for (i=0; i<num_msgs; i++) {
+		struct tldap_message *msg = msgs[i];
+		char *oid;
+		size_t j;
+
+		if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+			/* Could be a TLDAP_RES_SEARCH_REFERENCE */
+			continue;
+		}
+
+		oid = tldap_talloc_single_attribute(
+			msg, "attributeId", msg);
+		if (oid == NULL) {
+			continue;
 		}
+
+		for (j=0; j<num_oids; j++) {
+			if (strequal(oid, oids[j])) {
+				break;
+			}
+		}
+		TALLOC_FREE(oid);
+
+		if (j == num_oids) {
+			/* not found */
+			continue;
+		}
+
+		names[j] = tldap_talloc_single_attribute(
+			msg, "lDAPDisplayName", mem_ctx);
 	}
 
-	return status;
+	TALLOC_FREE(msgs);
+
+	return TLDAP_SUCCESS;
+}
+
+static TLDAPRC get_posix_schema_names(struct tldap_context *ld,
+				      const char *schema_mode,
+				      TALLOC_CTX *mem_ctx,
+				      struct idmap_ad_schema_names **pschema)
+{
+	char *schema_path;
+	struct idmap_ad_schema_names *schema;
+	char *names[6];
+	const char *oids_sfu[] = {
+		ADS_ATTR_SFU_UIDNUMBER_OID,
+		ADS_ATTR_SFU_GIDNUMBER_OID,
+		ADS_ATTR_SFU_HOMEDIR_OID,
+		ADS_ATTR_SFU_SHELL_OID,
+		ADS_ATTR_SFU_GECOS_OID,
+		ADS_ATTR_SFU_UID_OID
+	};
+	const char *oids_sfu20[] = {
+		ADS_ATTR_SFU20_UIDNUMBER_OID,
+		ADS_ATTR_SFU20_GIDNUMBER_OID,
+		ADS_ATTR_SFU20_HOMEDIR_OID,
+		ADS_ATTR_SFU20_SHELL_OID,
+		ADS_ATTR_SFU20_GECOS_OID,
+		ADS_ATTR_SFU20_UID_OID
+	};
+	const char *oids_rfc2307[] = {
+		ADS_ATTR_RFC2307_UIDNUMBER_OID,
+		ADS_ATTR_RFC2307_GIDNUMBER_OID,
+		ADS_ATTR_RFC2307_HOMEDIR_OID,
+		ADS_ATTR_RFC2307_SHELL_OID,
+		ADS_ATTR_RFC2307_GECOS_OID,
+		ADS_ATTR_RFC2307_UID_OID
+	};
+	const char **oids;
+
+	TLDAPRC rc;
+
+	schema = talloc(mem_ctx, struct idmap_ad_schema_names);
+	if (schema == NULL) {
+		return TLDAP_NO_MEMORY;
+	}
+
+	schema_path = get_schema_path(schema, ld);
+	if (schema_path == NULL) {
+		TALLOC_FREE(schema);
+		return TLDAP_NO_MEMORY;
+	}
+
+	oids = oids_rfc2307;
+
+	if ((schema_mode != NULL) && (schema_mode[0] != '\0')) {
+		if (strequal(schema_mode, "sfu")) {
+			oids = oids_sfu;
+		} else if (strequal(schema_mode, "sfu20")) {
+			oids = oids_sfu20;
+		} else if (strequal(schema_mode, "rfc2307" )) {
+			oids = oids_rfc2307;
+		} else {
+			DBG_WARNING("Unknown schema mode %s\n", schema_mode);
+		}
+	}
+
+	rc = get_attrnames_by_oids(ld, schema, schema_path, 6, oids, names);
+	TALLOC_FREE(schema_path);
+	if (!TLDAP_RC_IS_SUCCESS(rc)) {
+		TALLOC_FREE(schema);
+		return rc;
+	}
+
+	schema->uid = names[0];
+	schema->gid = names[1];
+	schema->dir = names[2];
+	schema->shell = names[3];
+	schema->gecos = names[4];
+	schema->name = names[5];
+
+	*pschema = schema;
+
+	return TLDAP_SUCCESS;
+}
+
+static NTSTATUS idmap_ad_get_tldap_ctx(TALLOC_CTX *mem_ctx,
+				       const char *domname,
+				       struct tldap_context **pld)
+{
+	struct netr_DsRGetDCNameInfo *dcinfo;
+	struct sockaddr_storage dcaddr;
+	struct cli_credentials *creds;
+	struct loadparm_context *lp_ctx;
+	struct tldap_context *ld;
+	int fd;
+	NTSTATUS status;
+	bool ok;
+	TLDAPRC rc;
+
+	status = wb_dsgetdcname_gencache_get(mem_ctx, domname, &dcinfo);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("Could not get dcinfo for %s: %s\n", domname,
+			  nt_errstr(status));
+		return status;
+	}
+
+	if (dcinfo->dc_unc == NULL) {
+		TALLOC_FREE(dcinfo);
+		return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+	}
+	if (dcinfo->dc_unc[0] == '\\') {
+		dcinfo->dc_unc += 1;
+	}
+	if (dcinfo->dc_unc[0] == '\\') {
+		dcinfo->dc_unc += 1;
+	}
+
+	ok = resolve_name(dcinfo->dc_unc, &dcaddr, 0x20, true);
+	if (!ok) {
+		DBG_DEBUG("Could not resolve name %s\n", dcinfo->dc_unc);
+		TALLOC_FREE(dcinfo);
+		return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+	}
+
+	status = open_socket_out(&dcaddr, 389, 10000, &fd);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("open_socket_out failed: %s\n", nt_errstr(status));
+		TALLOC_FREE(dcinfo);
+		return status;
+	}
+
+	ld = tldap_context_create(dcinfo, fd);
+	if (ld == NULL) {
+		DBG_DEBUG("tldap_context_create failed\n");
+		close(fd);
+		TALLOC_FREE(dcinfo);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	creds = cli_credentials_init(dcinfo);
+	if (creds == NULL) {
+		DBG_DEBUG("cli_credentials_init failed\n");
+		TALLOC_FREE(dcinfo);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	lp_ctx = loadparm_init_s3(dcinfo, loadparm_s3_helpers());
+	if (lp_ctx == NULL) {
+		DBG_DEBUG("loadparm_init_s3 failed\n");
+		TALLOC_FREE(dcinfo);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	cli_credentials_set_conf(creds, lp_ctx);
+
+	status = cli_credentials_set_machine_account(creds, lp_ctx);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("cli_credentials_set_machine_account "
+			  "failed: %s\n", nt_errstr(status));
+		TALLOC_FREE(dcinfo);
+		return status;
+	}
+
+	rc = tldap_gensec_bind(ld, creds, "ldap", dcinfo->dc_unc, NULL, lp_ctx,
+			       GENSEC_FEATURE_SIGN | GENSEC_FEATURE_SEAL);
+	if (!TLDAP_RC_IS_SUCCESS(rc)) {
+		DBG_DEBUG("tldap_gensec_bind failed: %s\n",
+			  tldap_errstr(dcinfo, ld, rc));
+		TALLOC_FREE(dcinfo);
+		return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+	}
+
+	rc = tldap_fetch_rootdse(ld);
+	if (!TLDAP_RC_IS_SUCCESS(rc)) {
+		DBG_DEBUG("tldap_fetch_rootdse failed: %s\n",
+			  tldap_errstr(dcinfo, ld, rc));
+		TALLOC_FREE(dcinfo);
+		return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+	}
+
+	*pld = talloc_move(mem_ctx, &ld);
+	TALLOC_FREE(dcinfo);
+	return NT_STATUS_OK;
 }
 
 static int idmap_ad_context_destructor(struct idmap_ad_context *ctx)
 {
-	if (ctx->ads != NULL) {
-		/* we own this ADS_STRUCT so make sure it goes away */
-		ctx->ads->is_mine = True;
-		ads_destroy( &ctx->ads );
-		ctx->ads = NULL;
+	if ((ctx->dom != NULL) && (ctx->dom->private_data == ctx)) {
+		ctx->dom->private_data = NULL;
 	}
 	return 0;
 }
 
-/************************************************************************
- ***********************************************************************/
-
-static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom)
+static NTSTATUS idmap_ad_context_create(TALLOC_CTX *mem_ctx,
+					struct idmap_domain *dom,
+					const char *domname,
+					struct idmap_ad_context **pctx)
 {
 	struct idmap_ad_context *ctx;
-	char *config_option;
-	const char *schema_mode = NULL;	
+	char *schema_config_option;
+	const char *schema_mode;
+	NTSTATUS status;
+	TLDAPRC rc;
 
-	ctx = talloc_zero(dom, struct idmap_ad_context);
+	ctx = talloc(mem_ctx, struct idmap_ad_context);
 	if (ctx == NULL) {
-		DEBUG(0, ("Out of memory!\n"));
 		return NT_STATUS_NO_MEMORY;
 	}
+	ctx->dom = dom;
+
 	talloc_set_destructor(ctx, idmap_ad_context_destructor);
 
-	config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
-	if (config_option == NULL) {
-		DEBUG(0, ("Out of memory!\n"));
-		talloc_free(ctx);
-		return NT_STATUS_NO_MEMORY;
+	status = idmap_ad_get_tldap_ctx(ctx, domname, &ctx->ld);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("idmap_ad_get_tldap_ctx failed: %s\n",
+			  nt_errstr(status));
+		TALLOC_FREE(ctx);
+		return status;
 	}
 
-	/* default map type */
-	ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
+	ctx->default_nc = get_default_nc(ctx, ctx->ld);
+	if (ctx->default_nc == NULL) {
+		DBG_DEBUG("No default nc\n");
+		TALLOC_FREE(ctx);
+		return status;
+	}
 
-	/* schema mode */
-	schema_mode = lp_parm_const_string(-1, config_option, "schema_mode", NULL);
-	if ( schema_mode && schema_mode[0] ) {
-		if ( strequal(schema_mode, "sfu") )
-			ctx->ad_map_type = WB_POSIX_MAP_SFU;
-		else if ( strequal(schema_mode, "sfu20" ) )
-			ctx->ad_map_type = WB_POSIX_MAP_SFU20;
-		else if ( strequal(schema_mode, "rfc2307" ) )
-			ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
-		else
-			DEBUG(0,("idmap_ad_initialize: Unknown schema_mode (%s)\n",
-				 schema_mode));
+	schema_config_option = talloc_asprintf(
+		ctx, "idmap config %s", domname);
+	if (schema_config_option == NULL) {
+		TALLOC_FREE(ctx);
+		return NT_STATUS_NO_MEMORY;
 	}
 
-	dom->private_data = ctx;
+	schema_mode = lp_parm_const_string(
+		-1, schema_config_option, "schema_mode", "rfc2307");
+	TALLOC_FREE(schema_config_option);
 
-	talloc_free(config_option);
+	rc = get_posix_schema_names(ctx->ld, schema_mode, ctx, &ctx->schema);
+	if (!TLDAP_RC_IS_SUCCESS(rc)) {
+		DBG_DEBUG("get_posix_schema_names failed: %s\n",
+			  tldap_errstr(ctx, ctx->ld, rc));
+		TALLOC_FREE(ctx);
+		return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+	}
 
+	*pctx = ctx;
 	return NT_STATUS_OK;
 }
 
-/************************************************************************
- ***********************************************************************/
+static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom)
+{
+	dom->private_data = NULL;
+	return NT_STATUS_OK;
+}
 
-static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
+static NTSTATUS idmap_ad_get_context(struct idmap_domain *dom,
+				     struct idmap_ad_context **pctx)
 {
-	NTSTATUS ret;
-	TALLOC_CTX *memctx;
-	struct idmap_ad_context *ctx;
-	ADS_STATUS rc;
-	const char *attrs[] = { "sAMAccountType", 
-				"objectSid",
-				NULL, /* uidnumber */
-				NULL, /* gidnumber */
-				NULL };
-	LDAPMessage *res = NULL;
-	LDAPMessage *entry = NULL;
-	char *filter = NULL;
-	int idx = 0;
-	int bidx = 0;
-	int count;
-	int i;
-	char *u_filter = NULL;
-	char *g_filter = NULL;
-
-	/* initialize the status to avoid suprise */
-	for (i = 0; ids[i]; i++) {
-		ids[i]->status = ID_UNKNOWN;
+	struct idmap_ad_context *ctx = NULL;
+	NTSTATUS status;
+
+	if (dom->private_data != NULL) {
+		*pctx = talloc_get_type_abort(dom->private_data,
+					      struct idmap_ad_context);
+		return NT_STATUS_OK;
 	}
 
-	/* Only do query if we are online */
-	if (idmap_is_offline())	{
-		return NT_STATUS_FILE_IS_OFFLINE;
+	status = idmap_ad_context_create(dom, dom, dom->name, &ctx);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("idmap_ad_context_create failed: %s\n",
+			  nt_errstr(status));
+		return status;
 	}
 
-	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+	dom->private_data = ctx;
+	*pctx = ctx;
+	return NT_STATUS_OK;
+}
 
-	if ( (memctx = talloc_new(ctx)) == NULL ) {
-		DEBUG(0, ("Out of memory!\n"));
-		return NT_STATUS_NO_MEMORY;
+static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom,
+					 struct id_map **ids)
+{
+	struct idmap_ad_context *ctx;
+	TLDAPRC rc;
+	NTSTATUS status;
+	struct tldap_message **msgs;
+
+	size_t i, num_msgs;
+	char *u_filter, *g_filter, *filter;
+
+	const char *attrs[] = {
+		"sAMAccountType",
+		"objectSid",
+		NULL, /* attr_uidnumber */
+		NULL, /* attr_gidnumber */
+	};
+
+	status = idmap_ad_get_context(dom, &ctx);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
 
-	rc = ad_idmap_cached_connection(dom);
-	if (!ADS_ERR_OK(rc)) {
-		DEBUG(1, ("ADS uninitialized: %s\n", ads_errstr(rc)));
-		ret = NT_STATUS_UNSUCCESSFUL;
-		/* ret = ads_ntstatus(rc); */
-		goto done;
-	}
-
-	attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
-	attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
-
-again:
-	bidx = idx;
-	for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) {
-		switch (ids[idx]->xid.type) {
-		case ID_TYPE_UID:     
-			if ( ! u_filter) {
-				u_filter = talloc_asprintf(memctx, "(&(|"
-							   "(sAMAccountType=%d)"
-							   "(sAMAccountType=%d)"
-							   "(sAMAccountType=%d))(|",
-							   ATYPE_NORMAL_ACCOUNT,
-							   ATYPE_WORKSTATION_TRUST,
-							   ATYPE_INTERDOMAIN_TRUST);
-			}
-			u_filter = talloc_asprintf_append_buffer(u_filter, "(%s=%lu)",
-							  ctx->ad_schema->posix_uidnumber_attr,
-							  (unsigned long)ids[idx]->xid.id);
-			CHECK_ALLOC_DONE(u_filter);
-			break;
+	attrs[2] = ctx->schema->uid;
+	attrs[3] = ctx->schema->gid;
 
-		case ID_TYPE_GID:
-			if ( ! g_filter) {
-				g_filter = talloc_asprintf(memctx, "(&(|"
-							   "(sAMAccountType=%d)"
-							   "(sAMAccountType=%d))(|",
-							   ATYPE_SECURITY_GLOBAL_GROUP,
-							   ATYPE_SECURITY_LOCAL_GROUP);
-			}
-			g_filter = talloc_asprintf_append_buffer(g_filter, "(%s=%lu)",
-							  ctx->ad_schema->posix_gidnumber_attr,
-							  (unsigned long)ids[idx]->xid.id);
-			CHECK_ALLOC_DONE(g_filter);
-			break;
+	u_filter = talloc_strdup(talloc_tos(), "");
+	if (u_filter == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
 
-		default:
-			DEBUG(3, ("Error: mapping requested but Unknown ID type\n"));
-			ids[idx]->status = ID_UNKNOWN;
-			continue;
+	g_filter = talloc_strdup(talloc_tos(), "");
+	if (g_filter == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	for (i=0; ids[i] != NULL; i++) {
+		struct id_map *id = ids[i];
+
+		id->status = ID_UNKNOWN;
+
+		switch (id->xid.type) {
+		    case ID_TYPE_UID: {
+			    u_filter = talloc_asprintf_append_buffer(
+				    u_filter, "(%s=%ju)", ctx->schema->uid,
+				    (uintmax_t)id->xid.id);
+			    if (u_filter == NULL) {
+				    return NT_STATUS_NO_MEMORY;
+			    }
+			    break;
+		    }
+
+		    case ID_TYPE_GID: {
+			    g_filter = talloc_asprintf_append_buffer(
+				    g_filter, "(%s=%ju)", ctx->schema->gid,
+				    (uintmax_t)id->xid.id);
+			    if (g_filter == NULL) {
+				    return NT_STATUS_NO_MEMORY;
+			    }
+			    break;
+		    }
+
+		    default:
+			    DBG_WARNING("Unknown id type: %u\n",
+					(unsigned)id->xid.type);
+			    break;
 		}
 	}
-	filter = talloc_asprintf(memctx, "(|");
-	CHECK_ALLOC_DONE(filter);
-	if ( u_filter) {
-		filter = talloc_asprintf_append_buffer(filter, "%s))", u_filter);
-		CHECK_ALLOC_DONE(filter);
-			TALLOC_FREE(u_filter);
+
+	filter = talloc_strdup(talloc_tos(), "(|");
+	if (filter == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (*u_filter != '\0') {
+		filter = talloc_asprintf_append_buffer(
+			filter,
+			"(&(|(sAMAccountType=%d)(sAMAccountType=%d)"
+			"(sAMAccountType=%d))(|%s))",
+			ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST,
+			ATYPE_INTERDOMAIN_TRUST, u_filter);
+		if (filter == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
 	}
-	if ( g_filter) {
-		filter = talloc_asprintf_append_buffer(filter, "%s))", g_filter);
-		CHECK_ALLOC_DONE(filter);
-		TALLOC_FREE(g_filter);
+	TALLOC_FREE(u_filter);
+
+	if (*g_filter != '\0') {
+		filter = talloc_asprintf_append_buffer(
+			filter,
+			"(&(|(sAMAccountType=%d)(sAMAccountType=%d))(|%s))",
+			ATYPE_SECURITY_GLOBAL_GROUP,
+			ATYPE_SECURITY_LOCAL_GROUP,
+			g_filter);
+		if (filter == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
 	}
-	filter = talloc_asprintf_append_buffer(filter, ")");
-	CHECK_ALLOC_DONE(filter);
+	TALLOC_FREE(g_filter);
 
-	rc = ads_search_retry(ctx->ads, &res, filter, attrs);
-	if (!ADS_ERR_OK(rc)) {
-		DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
-		ret = NT_STATUS_UNSUCCESSFUL;
-		goto done;
+	filter = talloc_asprintf_append_buffer(filter, ")");
+	if (filter == NULL) {
+		return NT_STATUS_NO_MEMORY;
 	}
 
-	if ( (count = ads_count_replies(ctx->ads, res)) == 0 ) {
-		DEBUG(10, ("No IDs found\n"));
+	DBG_DEBUG("Filter: [%s]\n", filter);
+
+	rc = tldap_search(ctx->ld, ctx->default_nc, TLDAP_SCOPE_SUB, filter,
+			  attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0,
+			  0, 0, 0, talloc_tos(), &msgs);
+	if (!TLDAP_RC_IS_SUCCESS(rc)) {
+		return NT_STATUS_LDAP(TLDAP_RC_V(rc));
 	}
 
-	entry = res;
-	for (i = 0; (i < count) && entry; i++) {
+	TALLOC_FREE(filter);
+
+	num_msgs = talloc_array_length(msgs);
+
+	for (i=0; i<num_msgs; i++) {
+		struct tldap_message *msg = msgs[i];
+		char *dn;
+		struct id_map *map;
 		struct dom_sid sid;
+		size_t j;
+		bool ok;
+		uint32_t atype, xid;
 		enum id_type type;
-		struct id_map *map;
-		uint32_t id;
-		uint32_t atype;
 
-		if (i == 0) { /* first entry */
-			entry = ads_first_entry(ctx->ads, entry);
-		} else { /* following ones */
-			entry = ads_next_entry(ctx->ads, entry);
-		}
-
-		if ( !entry ) {
-			DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
-			break;
+		if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+			continue;
 		}
 
-		/* first check if the SID is present */
-		if (!ads_pull_sid(ctx->ads, entry, "objectSid", &sid)) {
-			DEBUG(2, ("Could not retrieve SID from entry\n"));
+		ok = tldap_entry_dn(msg, &dn);
+		if (!ok) {
+			DBG_DEBUG("No dn found in msg %zu\n", i);
 			continue;
 		}
 
-		/* get type */
-		if (!ads_pull_uint32(ctx->ads, entry, "sAMAccountType", &atype)) {
-			DEBUG(1, ("could not get SAM account type\n"));
+		ok = tldap_pull_uint32(msg, "sAMAccountType", &atype);
+		if (!ok) {
+			DBG_DEBUG("No atype in object %s\n", dn);
 			continue;
 		}
 
 		switch (atype & 0xF0000000) {
-		case ATYPE_SECURITY_GLOBAL_GROUP:
-		case ATYPE_SECURITY_LOCAL_GROUP:
-			type = ID_TYPE_GID;
-			break;
-		case ATYPE_NORMAL_ACCOUNT:
-		case ATYPE_WORKSTATION_TRUST:
-		case ATYPE_INTERDOMAIN_TRUST:
-			type = ID_TYPE_UID;
-			break;
-		default:
-			DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
+		    case ATYPE_SECURITY_GLOBAL_GROUP:
+		    case ATYPE_SECURITY_LOCAL_GROUP:
+			    type = ID_TYPE_GID;
+			    break;
+		    case ATYPE_NORMAL_ACCOUNT:
+		    case ATYPE_WORKSTATION_TRUST:
+		    case ATYPE_INTERDOMAIN_TRUST:
+			    type = ID_TYPE_UID;
+			    break;
+		    default:
+			    DBG_WARNING("unrecognized SAM account type %08x\n",
+					atype);
 			continue;
 		}
 
-		if (!ads_pull_uint32(ctx->ads, entry, (type==ID_TYPE_UID) ?
-				                 ctx->ad_schema->posix_uidnumber_attr :
-				                 ctx->ad_schema->posix_gidnumber_attr,
-				     &id)) 
-		{
-			DEBUG(1, ("Could not get unix ID for SID %s\n",
-				  dom_sid_string(talloc_tos(), &sid)));
+		ok = tldap_pull_uint32(msg, (type == ID_TYPE_UID) ?
+				       ctx->schema->uid : ctx->schema->gid,
+				       &xid);
+		if (!ok) {
+			DBG_WARNING("No unix id in object %s\n", dn);
 			continue;
 		}
 
-		if (!idmap_unix_id_is_in_range(id, dom)) {
-			DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
-				id, dom->low_id, dom->high_id));
+		ok = tldap_pull_binsid(msg, "objectSid", &sid);
+		if (!ok) {
+			DBG_DEBUG("No objectSid in object %s\n", dn);
 			continue;
 		}
 
-		map = idmap_find_map_by_id(&ids[bidx], type, id);
-		if (!map) {
-			DEBUG(2, ("WARNING: couldn't match result with requested ID\n"));
+		map = NULL;
+		for (j=0; ids[j]; j++) {
+			if ((type == ids[j]->xid.type) &&
+			    (xid == ids[j]->xid.id)) {
+				map = ids[j];
+				break;
+			}
+		}
+		if (map == NULL) {
+			DBG_DEBUG("Got unexpected sid %s from object %s\n",
+				  sid_string_tos(&sid), dn);
 			continue;
 		}
 
 		sid_copy(map->sid, &sid);
-
-		/* mapped */
 		map->status = ID_MAPPED;
 
-		DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
-			   (unsigned long)map->xid.id,
-			   map->xid.type));
-	}
-
-	if (res) {
-		ads_msgfree(ctx->ads, res);
-	}
-
-	if (ids[idx]) { /* still some values to map */
-		goto again;
+		DBG_DEBUG("Mapped %s -> %ju (%d)\n", sid_string_dbg(map->sid),
+			  (uintmax_t)map->xid.id, map->xid.type);
 	}
 
-	ret = NT_STATUS_OK;
+	TALLOC_FREE(msgs);
 
-	/* mark all unknown/expired ones as unmapped */
-	for (i = 0; ids[i]; i++) {
-		if (ids[i]->status != ID_MAPPED) 
-			ids[i]->status = ID_UNMAPPED;
-	}
-
-done:
-	talloc_free(memctx);
-	return ret;
+	return NT_STATUS_OK;
 }
 
-/************************************************************************
- ***********************************************************************/
-
-static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
+static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom,
+					 struct id_map **ids)
 {
-	NTSTATUS ret;
-	TALLOC_CTX *memctx;
 	struct idmap_ad_context *ctx;
-	ADS_STATUS rc;
-	const char *attrs[] = { "sAMAccountType", 
-				"objectSid",
-				NULL, /* attr_uidnumber */
-				NULL, /* attr_gidnumber */
-				NULL };
-	LDAPMessage *res = NULL;
-	LDAPMessage *entry = NULL;
-	char *filter = NULL;
-	int idx = 0;
-	int bidx = 0;
-	int count;
-	int i;
-	char *sidstr;
-
-	/* initialize the status to avoid suprise */
-	for (i = 0; ids[i]; i++) {
-		ids[i]->status = ID_UNKNOWN;
-	}
-
-	/* Only do query if we are online */
-	if (idmap_is_offline())	{
-		return NT_STATUS_FILE_IS_OFFLINE;
+	TLDAPRC rc;
+	NTSTATUS status;
+	struct tldap_message **msgs;
+
+	char *filter;
+	size_t i, num_msgs;
+
+	const char *attrs[] = {
+		"sAMAccountType",
+		"objectSid",
+		NULL, /* attr_uidnumber */
+		NULL, /* attr_gidnumber */
+	};
+
+	status = idmap_ad_get_context(dom, &ctx);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
 
-	ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);	
+	attrs[2] = ctx->schema->uid;
+	attrs[3] = ctx->schema->gid;
 
-	if ( (memctx = talloc_new(ctx)) == NULL ) {		
-		DEBUG(0, ("Out of memory!\n"));
+	filter = talloc_asprintf(
+		talloc_tos(),
+		"(&(|(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)"
+		"(sAMAccountType=%d)(sAMAccountType=%d))(|",
+		ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST,
+		ATYPE_INTERDOMAIN_TRUST, ATYPE_SECURITY_GLOBAL_GROUP,
+		ATYPE_SECURITY_LOCAL_GROUP);
+	if (filter == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
 
-	rc = ad_idmap_cached_connection(dom);
-	if (!ADS_ERR_OK(rc)) {
-		DEBUG(1, ("ADS uninitialized: %s\n", ads_errstr(rc)));
-		ret = NT_STATUS_UNSUCCESSFUL;
-		/* ret = ads_ntstatus(rc); */
-		goto done;
-	}
+	for (i=0; ids[i]; i++) {
+		char *sidstr;
 
-	if (ctx->ad_schema == NULL) {
-		DEBUG(0, ("haven't got ctx->ad_schema ! \n"));
-		ret = NT_STATUS_UNSUCCESSFUL;
-		goto done;
-	}
-
-	attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
-	attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
-
-again:
-	filter = talloc_asprintf(memctx, "(&(|"
-				 "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
-				 "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */
-				 ")(|",
-				 ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
-				 ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
-
-	CHECK_ALLOC_DONE(filter);
-
-	bidx = idx;
-	for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) {
-
-		ids[idx]->status = ID_UNKNOWN;
+		ids[i]->status = ID_UNKNOWN;
 
-		sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), ids[idx]->sid);
-		filter = talloc_asprintf_append_buffer(filter, "(objectSid=%s)", sidstr);
+		sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), ids[i]->sid);
+		if (sidstr == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
 
+		filter = talloc_asprintf_append_buffer(
+			filter, "(objectSid=%s)", sidstr);
 		TALLOC_FREE(sidstr);
-		CHECK_ALLOC_DONE(filter);
+		if (filter == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
 	}
-	filter = talloc_asprintf_append_buffer(filter, "))");
-	CHECK_ALLOC_DONE(filter);
-	DEBUG(10, ("Filter: [%s]\n", filter));
 
-	rc = ads_search_retry(ctx->ads, &res, filter, attrs);
-	if (!ADS_ERR_OK(rc)) {
-		DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
-		ret = NT_STATUS_UNSUCCESSFUL;
-		goto done;
+	filter = talloc_asprintf_append_buffer(filter, "))");
+	if (filter == NULL) {
+		return NT_STATUS_NO_MEMORY;
 	}
 
-	if ( (count = ads_count_replies(ctx->ads, res)) == 0 ) {
-		DEBUG(10, ("No IDs found\n"));
+	DBG_DEBUG("Filter: [%s]\n", filter);
+
+	rc = tldap_search(ctx->ld, ctx->default_nc, TLDAP_SCOPE_SUB, filter,
+			  attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0,
+			  0, 0, 0, talloc_tos(), &msgs);
+	if (!TLDAP_RC_IS_SUCCESS(rc)) {
+		return NT_STATUS_LDAP(TLDAP_RC_V(rc));
 	}
 
-	entry = res;	
-	for (i = 0; (i < count) && entry; i++) {
+	TALLOC_FREE(filter);
+
+	num_msgs = talloc_array_length(msgs);
+
+	for (i=0; i<num_msgs; i++) {
+		struct tldap_message *msg = msgs[i];
+		char *dn;
+		struct id_map *map;
 		struct dom_sid sid;
+		size_t j;
+		bool ok;
+		uint64_t account_type, xid;
 		enum id_type type;
-		struct id_map *map;
-		uint32_t id;
-		uint32_t atype;
 
-		if (i == 0) { /* first entry */
-			entry = ads_first_entry(ctx->ads, entry);
-		} else { /* following ones */
-			entry = ads_next_entry(ctx->ads, entry);
+		if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+			continue;
 		}
 
-		if ( !entry ) {
-			DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
-			break;
+		ok = tldap_entry_dn(msg, &dn);
+		if (!ok) {
+			DBG_DEBUG("No dn found in msg %zu\n", i);
+			continue;
 		}
 
-		/* first check if the SID is present */
-		if (!ads_pull_sid(ctx->ads, entry, "objectSid", &sid)) {
-			DEBUG(2, ("Could not retrieve SID from entry\n"));
+		ok = tldap_pull_binsid(msg, "objectSid", &sid);
+		if (!ok) {
+			DBG_DEBUG("No objectSid in object %s\n", dn);
 			continue;
 		}
 
-		map = idmap_find_map_by_sid(&ids[bidx], &sid);
-		if (!map) {
-			DEBUG(2, ("WARNING: couldn't match result with requested SID\n"));
+		map = NULL;
+		for (j=0; ids[j]; j++) {
+			if (dom_sid_equal(&sid, ids[j]->sid)) {
+				map = ids[j];
+				break;
+			}
+		}
+		if (map == NULL) {
+			DBG_DEBUG("Got unexpected sid %s from object %s\n",
+				  sid_string_tos(&sid), dn);
 			continue;
 		}
 
-		/* get type */
-		if (!ads_pull_uint32(ctx->ads, entry, "sAMAccountType", &atype)) {
-			DEBUG(1, ("could not get SAM account type\n"));
+		ok = tldap_pull_uint64(msg, "sAMAccountType", &account_type);
+		if (!ok) {
+			DBG_DEBUG("No sAMAccountType in %s\n", dn);
 			continue;
 		}
 
-		switch (atype & 0xF0000000) {
+		switch (account_type & 0xF0000000) {
 		case ATYPE_SECURITY_GLOBAL_GROUP:
 		case ATYPE_SECURITY_LOCAL_GROUP:
 			type = ID_TYPE_GID;
@@ -511,91 +762,89 @@ again:
 			type = ID_TYPE_UID;
 			break;
 		default:
-			DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
+			DBG_WARNING("unrecognized SAM account type %"PRIu64"\n",
+				    account_type);
 			continue;
 		}
 
-		if (!ads_pull_uint32(ctx->ads, entry, (type==ID_TYPE_UID) ?
-				                 ctx->ad_schema->posix_uidnumber_attr :
-				                 ctx->ad_schema->posix_gidnumber_attr,
-				     &id)) 
-		{
-			DEBUG(1, ("Could not get unix ID for SID %s\n",
-				sid_string_dbg(map->sid)));
-			continue;
-		}
-		if (!idmap_unix_id_is_in_range(id, dom)) {
-			DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
-				id, dom->low_id, dom->high_id));
+		ok = tldap_pull_uint64(msg,
+				       type == ID_TYPE_UID ?
+				       ctx->schema->uid : ctx->schema->gid,
+				       &xid);
+		if (!ok) {
+			DBG_DEBUG("No xid in %s\n", dn);
 			continue;
 		}
 
 		/* mapped */
 		map->xid.type = type;
-		map->xid.id = id;
+		map->xid.id = xid;
 		map->status = ID_MAPPED;
 
 		DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
-			   (unsigned long)map->xid.id,
-			   map->xid.type));
+			   (unsigned long)map->xid.id, map->xid.type));
 	}
 
-	if (res) {
-		ads_msgfree(ctx->ads, res);
-	}
+	TALLOC_FREE(msgs);
 
-	if (ids[idx]) { /* still some values to map */
-		goto again;
-	}
+	return NT_STATUS_OK;
+}
 
-	ret = NT_STATUS_OK;
+static NTSTATUS idmap_ad_unixids_to_sids_retry(struct idmap_domain *dom,
+					       struct id_map **ids)
+{
+	const NTSTATUS status_server_down =
+		NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN));
+	NTSTATUS status;
+
+	status = idmap_ad_unixids_to_sids(dom, ids);
 
-	/* mark all unknown/expired ones as unmapped */
-	for (i = 0; ids[i]; i++) {
-		if (ids[i]->status != ID_MAPPED) 
-			ids[i]->status = ID_UNMAPPED;
+	if (NT_STATUS_EQUAL(status, status_server_down)) {
+		TALLOC_FREE(dom->private_data);
+		status = idmap_ad_unixids_to_sids(dom, ids);
 	}
 
-done:
-	talloc_free(memctx);
-	return ret;
+	return status;
 }
 
-/************************************************************************
- Function dispatch tables for the idmap and nss plugins
- ***********************************************************************/
+static NTSTATUS idmap_ad_sids_to_unixids_retry(struct idmap_domain *dom,
+					       struct id_map **ids)
+{
+	const NTSTATUS status_server_down =
+		NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN));
+	NTSTATUS status;
+
+	status = idmap_ad_sids_to_unixids(dom, ids);
+
+	if (NT_STATUS_EQUAL(status, status_server_down)) {
+		TALLOC_FREE(dom->private_data);
+		status = idmap_ad_sids_to_unixids(dom, ids);
+	}
+
+	return status;
+}
 
 static struct idmap_methods ad_methods = {
 	.init            = idmap_ad_initialize,
-	.unixids_to_sids = idmap_ad_unixids_to_sids,
-	.sids_to_unixids = idmap_ad_sids_to_unixids,
+	.unixids_to_sids = idmap_ad_unixids_to_sids_retry,
+	.sids_to_unixids = idmap_ad_sids_to_unixids_retry,
 };
 
-/************************************************************************
- Initialize the plugins
- ***********************************************************************/
-
 static_decl_idmap;
 NTSTATUS idmap_ad_init(void)
 {
-	static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL;
-	static NTSTATUS status_ad_nss = NT_STATUS_UNSUCCESSFUL;
-
-	/* Always register the AD method first in order to get the
-	   idmap_domain interface called */
+	NTSTATUS status;
 
-	if ( !NT_STATUS_IS_OK(status_idmap_ad) ) {
-		status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, 
-						     "ad", &ad_methods);
-		if ( !NT_STATUS_IS_OK(status_idmap_ad) )
-			return status_idmap_ad;		
+	status = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
+				    "ad", &ad_methods);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
 
-	if ( !NT_STATUS_IS_OK( status_ad_nss ) ) {
-		status_ad_nss = idmap_ad_nss_init();
-		if ( !NT_STATUS_IS_OK(status_ad_nss) )
-			return status_ad_nss;
+	status = idmap_ad_nss_init();
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
 
-	return NT_STATUS_OK;	
+	return NT_STATUS_OK;
 }
diff --git a/source3/winbindd/wscript_build b/source3/winbindd/wscript_build
index eab788f..1a2c497 100644
--- a/source3/winbindd/wscript_build
+++ b/source3/winbindd/wscript_build
@@ -28,7 +28,7 @@ bld.SAMBA3_MODULE('idmap_ad',
                  subsystem='idmap',
                  allow_undefined_symbols=True,
                  source='',
-                 deps='IDMAP_AD',
+                 deps='IDMAP_AD TLDAP LIBNMB',
                  init_function='',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_ad'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad'))
-- 
1.7.9.5



More information about the samba-technical mailing list