[PATCH] Start to fix bug 8630

Volker Lendecke vl at samba.org
Sun Jan 1 16:17:47 UTC 2017


On Fri, Dec 30, 2016 at 05:01:30PM +0100, Volker Lendecke wrote:
> On Fri, Dec 30, 2016 at 02:50:20PM +0100, Volker Lendecke wrote:
> > The attached patchset is a rewrite of how winbind puts together user
> > information. Its goal is to remove domain_list dependency and make us
> > work better in complex trust scenarios. It is waiting for the last few
> > tests in autobuild, but all the winbind ones have passwd, autobuild
> > has already found some quirks in early versions.
> 
> Gna. Failed in samba.blackbox.wbinfo after 3h22m12s. So more fixups
> needed, but I would still appreciate some review already.

This one just survived an autobuild for me. The diff against the
previous version is also attached.

Volker
-------------- next part --------------
>From 85a343a412e816d3df492bfc96adf8474b22f06d Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 30 Dec 2016 11:08:22 +0000
Subject: [PATCH 01/17] winbind: Initialize user list info to 0

Further down wbint_userinfo will be extended. Make sure we don't
have uninitialized memory hanging around

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

diff --git a/source3/winbindd/winbindd_rpc.c b/source3/winbindd/winbindd_rpc.c
index 7b355ba..cd6174a 100644
--- a/source3/winbindd/winbindd_rpc.c
+++ b/source3/winbindd/winbindd_rpc.c
@@ -109,6 +109,8 @@ NTSTATUS rpc_query_user_list(TALLOC_CTX *mem_ctx,
 			src = &(disp_info.info1.entries[j]);
 			dst = &(info[i]);
 
+			*dst = (struct wbint_userinfo) {0};
+
 			dst->acct_name = talloc_strdup(info,
 						       src->account_name.string);
 			if (dst->acct_name == NULL) {
-- 
2.1.4


>From c2d99388730240f77b26c0bb0556670fac7c0912 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 27 Dec 2016 14:01:13 +0000
Subject: [PATCH 02/17] winbind4: Remove unused code

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source4/winbind/wb_async_helpers.c | 494 -------------------------------------
 source4/winbind/wb_async_helpers.h |  37 ---
 source4/winbind/wb_utils.c         |   1 -
 source4/winbind/wscript_build      |   2 +-
 4 files changed, 1 insertion(+), 533 deletions(-)
 delete mode 100644 source4/winbind/wb_async_helpers.c
 delete mode 100644 source4/winbind/wb_async_helpers.h

diff --git a/source4/winbind/wb_async_helpers.c b/source4/winbind/wb_async_helpers.c
deleted file mode 100644
index 5f0455a..0000000
--- a/source4/winbind/wb_async_helpers.c
+++ /dev/null
@@ -1,494 +0,0 @@
-/* 
-   Unix SMB/CIFS implementation.
-
-   Copyright (C) Volker Lendecke 2005
-   
-   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/>.
-*/
-/*
-  a composite API for finding a DC and its name
-*/
-
-#include "includes.h"
-#include <tevent.h>
-#include "libcli/composite/composite.h"
-#include "winbind/wb_async_helpers.h"
-
-#include "libcli/security/security.h"
-#include "librpc/gen_ndr/ndr_lsa_c.h"
-#include "librpc/gen_ndr/ndr_samr_c.h"
-
-#include "winbind/wb_helper.h"
-
-
-struct lsa_lookupsids_state {
-	struct composite_context *ctx;
-	uint32_t num_sids;
-	struct lsa_LookupSids r;
-	struct lsa_SidArray sids;
-	struct lsa_TransNameArray names;
-	struct lsa_RefDomainList *domains;
-	uint32_t count;
-	struct wb_sid_object **result;
-};
-
-static void lsa_lookupsids_recv_names(struct tevent_req *subreq);
-
-struct composite_context *wb_lsa_lookupsids_send(TALLOC_CTX *mem_ctx,
-						 struct tevent_context *ev,
-						 struct dcerpc_binding_handle *lsa_binding,
-						 struct policy_handle *handle,
-						 uint32_t num_sids,
-						 const struct dom_sid **sids)
-{
-	struct composite_context *result;
-	struct lsa_lookupsids_state *state;
-	uint32_t i;
-	struct tevent_req *subreq;
-
-	result = composite_create(mem_ctx, ev);
-	if (result == NULL) goto failed;
-
-	state = talloc(result, struct lsa_lookupsids_state);
-	if (state == NULL) goto failed;
-	result->private_data = state;
-	state->ctx = result;
-
-	state->sids.num_sids = num_sids;
-	state->sids.sids = talloc_array(state, struct lsa_SidPtr, num_sids);
-	if (state->sids.sids == NULL) goto failed;
-
-	for (i=0; i<num_sids; i++) {
-		state->sids.sids[i].sid = dom_sid_dup(state->sids.sids,
-						      sids[i]);
-		if (state->sids.sids[i].sid == NULL) goto failed;
-	}
-
-	state->domains = talloc(state, struct lsa_RefDomainList);
-	if (state->domains == NULL) goto failed;
-
-	state->count = 0;
-	state->num_sids = num_sids;
-	state->names.count = 0;
-	state->names.names = NULL;
-
-	state->r.in.handle = handle;
-	state->r.in.sids = &state->sids;
-	state->r.in.names = &state->names;
-	state->r.in.level = 1;
-	state->r.in.count = &state->count;
-	state->r.out.names = &state->names;
-	state->r.out.count = &state->count;
-	state->r.out.domains = &state->domains;
-
-	subreq = dcerpc_lsa_LookupSids_r_send(state, ev,
-					      lsa_binding,
-					      &state->r);
-	if (subreq == NULL) goto failed;
-	tevent_req_set_callback(subreq, lsa_lookupsids_recv_names, state);
-
-	return result;
-
- failed:
-	talloc_free(result);
-	return NULL;
-}
-
-static void lsa_lookupsids_recv_names(struct tevent_req *subreq)
-{
-	struct lsa_lookupsids_state *state =
-		tevent_req_callback_data(subreq,
-		struct lsa_lookupsids_state);
-	uint32_t i;
-
-	state->ctx->status = dcerpc_lsa_LookupSids_r_recv(subreq, state);
-	TALLOC_FREE(subreq);
-	if (!composite_is_ok(state->ctx)) return;
-	state->ctx->status = state->r.out.result;
-	if (!NT_STATUS_IS_OK(state->ctx->status) &&
-	    !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) {
-		composite_error(state->ctx, state->ctx->status);
-		return;
-	}
-
-	if (state->names.count != state->num_sids) {
-		composite_error(state->ctx,
-				NT_STATUS_INVALID_NETWORK_RESPONSE);
-		return;
-	}
-
-	state->result = talloc_array(state, struct wb_sid_object *,
-				     state->num_sids);
-	if (composite_nomem(state->result, state->ctx)) return;
-
-	for (i=0; i<state->num_sids; i++) {
-		struct lsa_TranslatedName *name =
-			&state->r.out.names->names[i];
-		struct lsa_DomainInfo *dom;
-		struct lsa_RefDomainList *domains =
-			state->domains;
-
-		state->result[i] = talloc_zero(state->result,
-					       struct wb_sid_object);
-		if (composite_nomem(state->result[i], state->ctx)) return;
-
-		state->result[i]->type = name->sid_type;
-		if (state->result[i]->type == SID_NAME_UNKNOWN) {
-			continue;
-		}
-
-		if (domains == NULL) {
-			composite_error(state->ctx,
-					NT_STATUS_INVALID_NETWORK_RESPONSE);
-			return;
-		}
-		if (name->sid_index >= domains->count) {
-			composite_error(state->ctx,
-					NT_STATUS_INVALID_NETWORK_RESPONSE);
-			return;
-		}
-
-		dom = &domains->domains[name->sid_index];
-		state->result[i]->domain = talloc_reference(state->result[i],
-							    dom->name.string);
-		if ((name->sid_type == SID_NAME_DOMAIN) ||
-		    (name->name.string == NULL)) {
-			state->result[i]->name =
-				talloc_strdup(state->result[i], "");
-		} else {
-			state->result[i]->name =
-				talloc_steal(state->result[i],
-					     name->name.string);
-		}
-
-		if (composite_nomem(state->result[i]->name, state->ctx)) {
-			return;
-		}
-	}
-
-	composite_done(state->ctx);
-}
-
-NTSTATUS wb_lsa_lookupsids_recv(struct composite_context *c,
-				TALLOC_CTX *mem_ctx,
-				struct wb_sid_object ***names)
-{
-	NTSTATUS status = composite_wait(c);
-	if (NT_STATUS_IS_OK(status)) {
-		struct lsa_lookupsids_state *state =
-			talloc_get_type(c->private_data,
-					struct lsa_lookupsids_state);
-		*names = talloc_steal(mem_ctx, state->result);
-	}
-	talloc_free(c);
-	return status;
-}
-
-
-struct lsa_lookupnames_state {
-	struct composite_context *ctx;
-	uint32_t num_names;
-	struct lsa_LookupNames r;
-	struct lsa_TransSidArray sids;
-	struct lsa_RefDomainList *domains;
-	uint32_t count;
-	struct wb_sid_object **result;
-};
-
-static void lsa_lookupnames_recv_sids(struct tevent_req *subreq);
-
-struct composite_context *wb_lsa_lookupnames_send(TALLOC_CTX *mem_ctx,
-						  struct tevent_context *ev,
-						  struct dcerpc_binding_handle *lsa_binding,
-						  struct policy_handle *handle,
-						  uint32_t num_names,
-						  const char **names)
-{
-	struct composite_context *result;
-	struct lsa_lookupnames_state *state;
-	struct tevent_req *subreq;
-
-	struct lsa_String *lsa_names;
-	uint32_t i;
-
-	result = composite_create(mem_ctx, ev);
-	if (result == NULL) goto failed;
-
-	state = talloc(result, struct lsa_lookupnames_state);
-	if (state == NULL) goto failed;
-	result->private_data = state;
-	state->ctx = result;
-
-	state->sids.count = 0;
-	state->sids.sids = NULL;
-	state->num_names = num_names;
-	state->count = 0;
-
-	lsa_names = talloc_array(state, struct lsa_String, num_names);
-	if (lsa_names == NULL) goto failed;
-
-	for (i=0; i<num_names; i++) {
-		lsa_names[i].string = names[i];
-	}
-
-	state->domains = talloc(state, struct lsa_RefDomainList);
-	if (state->domains == NULL) goto failed;
-
-	state->r.in.handle = handle;
-	state->r.in.num_names = num_names;
-	state->r.in.names = lsa_names;
-	state->r.in.sids = &state->sids;
-	state->r.in.level = 1;
-	state->r.in.count = &state->count;
-	state->r.out.count = &state->count;
-	state->r.out.sids = &state->sids;
-	state->r.out.domains = &state->domains;
-
-	subreq = dcerpc_lsa_LookupNames_r_send(state, ev,
-					       lsa_binding,
-					       &state->r);
-	if (subreq == NULL) goto failed;
-	tevent_req_set_callback(subreq, lsa_lookupnames_recv_sids, state);
-
-	return result;
-
- failed:
-	talloc_free(result);
-	return NULL;
-}
-
-static void lsa_lookupnames_recv_sids(struct tevent_req *subreq)
-{
-	struct lsa_lookupnames_state *state =
-		tevent_req_callback_data(subreq,
-		struct lsa_lookupnames_state);
-	uint32_t i;
-
-	state->ctx->status = dcerpc_lsa_LookupNames_r_recv(subreq, state);
-	TALLOC_FREE(subreq);
-	if (!composite_is_ok(state->ctx)) return;
-	state->ctx->status = state->r.out.result;
-	if (!NT_STATUS_IS_OK(state->ctx->status) &&
-	    !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) {
-		composite_error(state->ctx, state->ctx->status);
-		return;
-	}
-
-	if (state->sids.count != state->num_names) {
-		composite_error(state->ctx,
-				NT_STATUS_INVALID_NETWORK_RESPONSE);
-		return;
-	}
-
-	state->result = talloc_array(state, struct wb_sid_object *,
-				     state->num_names);
-	if (composite_nomem(state->result, state->ctx)) return;
-
-	for (i=0; i<state->num_names; i++) {
-		struct lsa_TranslatedSid *sid = &state->r.out.sids->sids[i];
-		struct lsa_RefDomainList *domains = state->domains;
-		struct lsa_DomainInfo *dom;
-
-		state->result[i] = talloc_zero(state->result,
-					       struct wb_sid_object);
-		if (composite_nomem(state->result[i], state->ctx)) return;
-
-		state->result[i]->type = sid->sid_type;
-		if (state->result[i]->type == SID_NAME_UNKNOWN) {
-			continue;
-		}
-
-		if (domains == NULL) {
-			composite_error(state->ctx,
-					NT_STATUS_INVALID_NETWORK_RESPONSE);
-			return;
-		}
-		if (sid->sid_index >= domains->count) {
-			composite_error(state->ctx,
-					NT_STATUS_INVALID_NETWORK_RESPONSE);
-			return;
-		}
-
-		dom = &domains->domains[sid->sid_index];
-
-		state->result[i]->sid = dom_sid_add_rid(state->result[i],
-							dom->sid, sid->rid);
-	}
-
-	composite_done(state->ctx);
-}
-
-NTSTATUS wb_lsa_lookupnames_recv(struct composite_context *c,
-				 TALLOC_CTX *mem_ctx,
-				 struct wb_sid_object ***sids)
-{
-	NTSTATUS status = composite_wait(c);
-	if (NT_STATUS_IS_OK(status)) {
-		struct lsa_lookupnames_state *state =
-			talloc_get_type(c->private_data,
-					struct lsa_lookupnames_state);
-		*sids = talloc_steal(mem_ctx, state->result);
-	}
-	talloc_free(c);
-	return status;
-}
-struct samr_getuserdomgroups_state {
-	struct composite_context *ctx;
-	struct dcerpc_binding_handle *samr_binding;
-
-	uint32_t num_rids;
-	uint32_t *rids;
-
-	struct samr_RidWithAttributeArray *rid_array;
-
-	struct policy_handle *user_handle;
-	struct samr_OpenUser o;
-	struct samr_GetGroupsForUser g;
-	struct samr_Close c;
-};
-
-static void samr_usergroups_recv_open(struct tevent_req *subreq);
-static void samr_usergroups_recv_groups(struct tevent_req *subreq);
-static void samr_usergroups_recv_close(struct tevent_req *subreq);
-
-struct composite_context *wb_samr_userdomgroups_send(TALLOC_CTX *mem_ctx,
-						     struct tevent_context *ev,
-						     struct dcerpc_binding_handle *samr_binding,
-						     struct policy_handle *domain_handle,
-						     uint32_t rid)
-{
-	struct composite_context *result;
-	struct samr_getuserdomgroups_state *state;
-	struct tevent_req *subreq;
-
-	result = composite_create(mem_ctx, ev);
-	if (result == NULL) goto failed;
-
-	state = talloc(result, struct samr_getuserdomgroups_state);
-	if (state == NULL) goto failed;
-	result->private_data = state;
-	state->ctx = result;
-
-	state->samr_binding = samr_binding;
-
-	state->user_handle = talloc(state, struct policy_handle);
-	if (state->user_handle == NULL) goto failed;
-
-	state->o.in.domain_handle = domain_handle;
-	state->o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
-	state->o.in.rid = rid;
-	state->o.out.user_handle = state->user_handle;
-
-	subreq = dcerpc_samr_OpenUser_r_send(state,
-					     state->ctx->event_ctx,
-					     state->samr_binding,
-					     &state->o);
-	if (subreq == NULL) goto failed;
-	tevent_req_set_callback(subreq, samr_usergroups_recv_open, state);
-
-	return result;
-
- failed:
-	talloc_free(result);
-	return NULL;
-}
-					      
-static void samr_usergroups_recv_open(struct tevent_req *subreq)
-{
-	struct samr_getuserdomgroups_state *state =
-		tevent_req_callback_data(subreq,
-		struct samr_getuserdomgroups_state);
-
-	state->ctx->status = dcerpc_samr_OpenUser_r_recv(subreq, state);
-	TALLOC_FREE(subreq);
-	if (!composite_is_ok(state->ctx)) return;
-	state->ctx->status = state->o.out.result;
-	if (!composite_is_ok(state->ctx)) return;
-
-	state->g.in.user_handle = state->user_handle;
-	state->g.out.rids = &state->rid_array;
-
-	subreq = dcerpc_samr_GetGroupsForUser_r_send(state,
-						     state->ctx->event_ctx,
-						     state->samr_binding,
-						     &state->g);
-	if (composite_nomem(subreq, state->ctx)) return;
-	tevent_req_set_callback(subreq, samr_usergroups_recv_groups, state);
-}
-
-static void samr_usergroups_recv_groups(struct tevent_req *subreq)
-{
-	struct samr_getuserdomgroups_state *state =
-		tevent_req_callback_data(subreq,
-		struct samr_getuserdomgroups_state);
-
-	state->ctx->status = dcerpc_samr_GetGroupsForUser_r_recv(subreq, state);
-	TALLOC_FREE(subreq);
-	if (!composite_is_ok(state->ctx)) return;
-	state->ctx->status = state->g.out.result;
-	if (!composite_is_ok(state->ctx)) return;
-
-	state->c.in.handle = state->user_handle;
-	state->c.out.handle = state->user_handle;
-
-	subreq = dcerpc_samr_Close_r_send(state,
-					  state->ctx->event_ctx,
-					  state->samr_binding,
-					  &state->c);
-	if (composite_nomem(subreq, state->ctx)) return;
-	tevent_req_set_callback(subreq, samr_usergroups_recv_close, state);
-}
-
-static void samr_usergroups_recv_close(struct tevent_req *subreq)
-{
-        struct samr_getuserdomgroups_state *state =
-		tevent_req_callback_data(subreq,
-		struct samr_getuserdomgroups_state);
-
-	state->ctx->status = dcerpc_samr_Close_r_recv(subreq, state);
-	TALLOC_FREE(subreq);
-        if (!composite_is_ok(state->ctx)) return;
-        state->ctx->status = state->c.out.result;
-        if (!composite_is_ok(state->ctx)) return;
-
-	composite_done(state->ctx);
-}
-
-NTSTATUS wb_samr_userdomgroups_recv(struct composite_context *ctx,
-				    TALLOC_CTX *mem_ctx,
-				    uint32_t *num_rids, uint32_t **rids)
-{
-        struct samr_getuserdomgroups_state *state =
-                talloc_get_type(ctx->private_data,
-                                struct samr_getuserdomgroups_state);
-
-	uint32_t i;
-	NTSTATUS status = composite_wait(ctx);
-	if (!NT_STATUS_IS_OK(status)) goto done;
-
-	*num_rids = state->rid_array->count;
-	*rids = talloc_array(mem_ctx, uint32_t, *num_rids);
-	if (*rids == NULL) {
-		status = NT_STATUS_NO_MEMORY;
-		goto done;
-	}
-
-	for (i=0; i<*num_rids; i++) {
-		(*rids)[i] = state->rid_array->rids[i].rid;
-	}
-
- done:
-	talloc_free(ctx);
-	return status;
-}
diff --git a/source4/winbind/wb_async_helpers.h b/source4/winbind/wb_async_helpers.h
deleted file mode 100644
index 359696d..0000000
--- a/source4/winbind/wb_async_helpers.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* 
-   Unix SMB/CIFS implementation.
-
-   SMB composite request interfaces
-
-   Copyright (C) Volker Lendecke 2005
-   
-   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 __WB_ASYNC_HELPERS_H__
-#define __WB_ASYNC_HELPERS_H__
-
-struct dcerpc_pipe;
-struct dcerpc_binding_handle;
-
-#include "librpc/gen_ndr/lsa.h"
-
-struct wb_sid_object {
-	enum lsa_SidType type;
-	struct dom_sid *sid;
-	const char *domain;
-	const char *name;
-};
-
-#endif /* __WB_ASYNC_HELPERS_H__ */
diff --git a/source4/winbind/wb_utils.c b/source4/winbind/wb_utils.c
index 0d1f228..535d1ec 100644
--- a/source4/winbind/wb_utils.c
+++ b/source4/winbind/wb_utils.c
@@ -22,7 +22,6 @@
 #include "includes.h"
 #include "param/param.h"
 #include "libcli/security/dom_sid.h"
-#include "winbind/wb_async_helpers.h"
 #include "winbind/wb_helper.h"
 
 
diff --git a/source4/winbind/wscript_build b/source4/winbind/wscript_build
index e242a6a..61adeb5 100644
--- a/source4/winbind/wscript_build
+++ b/source4/winbind/wscript_build
@@ -11,7 +11,7 @@ bld.SAMBA_MODULE('service_winbindd',
 
 
 bld.SAMBA_SUBSYSTEM('WB_HELPER',
-	source='wb_async_helpers.c wb_utils.c',
+	source='wb_utils.c',
 	autoproto='wb_helper.h',
 	public_deps='RPC_NDR_LSA dcerpc-samr'
 	)
-- 
2.1.4


>From 2498648e398125f155bd74089e4d5744afc2e051 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 25 Dec 2016 11:33:53 +0000
Subject: [PATCH 03/17] winbind: Fix wb_lookupsids for AD DCs

Not yet a fix, but the IS_DC macro also contains the
ROLE_ACTIVE_DIRECTORY_DC, and once we start to fully do this we'll
need it.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_lookupsids.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/source3/winbindd/wb_lookupsids.c b/source3/winbindd/wb_lookupsids.c
index a4bcbad..c395f54 100644
--- a/source3/winbindd/wb_lookupsids.c
+++ b/source3/winbindd/wb_lookupsids.c
@@ -272,8 +272,7 @@ static bool wb_lookupsids_bulk(const struct dom_sid *sid)
 		return true;
 	}
 
-	if ((lp_server_role() == ROLE_DOMAIN_PDC) ||
-	    (lp_server_role() == ROLE_DOMAIN_BDC)) {
+	if (IS_DC) {
 		/*
 		 * Bulk lookups to trusted DCs
 		 */
-- 
2.1.4


>From 5ae08cbb826765ee95de9fcf7426266b3c226704 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sat, 17 Dec 2016 15:03:59 +0100
Subject: [PATCH 04/17] idmap: Simplify idmap_ad_nss_init()

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/idmap_ad_nss.c | 31 +++++++++++++------------------
 1 file changed, 13 insertions(+), 18 deletions(-)

diff --git a/source3/winbindd/idmap_ad_nss.c b/source3/winbindd/idmap_ad_nss.c
index 8c5a13d..d979231 100644
--- a/source3/winbindd/idmap_ad_nss.c
+++ b/source3/winbindd/idmap_ad_nss.c
@@ -502,29 +502,24 @@ static struct nss_info_methods nss_sfu20_methods = {
 
 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;
+	NTSTATUS status;
 
-	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;
+	status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+					"rfc2307",  &nss_rfc2307_methods);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
 
-	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;
+	status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+					"sfu",  &nss_sfu_methods);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
 
-	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;
+	status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+					"sfu20",  &nss_sfu20_methods);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
 
 	return NT_STATUS_OK;
-- 
2.1.4


>From d96bd2b52c74d62612705ecf51246428e8f2b457 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 25 Dec 2016 10:12:59 +0000
Subject: [PATCH 05/17] winbind: It's legitmate to have 0 groups in info3

At least a Samba DC can send an info3 struct with base.groups.count==0. We
should not fail with that and just return 0 groups.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/winbindd_util.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
index c98b3ef..bd9403f 100644
--- a/source3/winbindd/winbindd_util.c
+++ b/source3/winbindd/winbindd_util.c
@@ -1295,11 +1295,6 @@ NTSTATUS lookup_usergroups_cached(TALLOC_CTX *mem_ctx,
 		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 	}
 
-	if (info3->base.groups.count == 0) {
-		TALLOC_FREE(info3);
-		return NT_STATUS_UNSUCCESSFUL;
-	}
-
 	/*
 	 * Before bug #7843 the "Domain Local" groups were added with a
 	 * lookupuseraliases call, but this isn't done anymore for our domain
-- 
2.1.4


>From d4353a492d180426a9a4529d6ef64775496b83d1 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 29 Dec 2016 09:54:56 +0000
Subject: [PATCH 06/17] winbind: Make "idmap_find_domain" public

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/idmap.c       | 2 +-
 source3/winbindd/idmap_proto.h | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/source3/winbindd/idmap.c b/source3/winbindd/idmap.c
index 84834f1..6a52633 100644
--- a/source3/winbindd/idmap.c
+++ b/source3/winbindd/idmap.c
@@ -500,7 +500,7 @@ fail:
  * add_trusted_domain.
  */
 
-static struct idmap_domain *idmap_find_domain(const char *domname)
+struct idmap_domain *idmap_find_domain(const char *domname)
 {
 	bool ok;
 	int i;
diff --git a/source3/winbindd/idmap_proto.h b/source3/winbindd/idmap_proto.h
index 84cc2f0..0e25963 100644
--- a/source3/winbindd/idmap_proto.h
+++ b/source3/winbindd/idmap_proto.h
@@ -36,6 +36,7 @@ NTSTATUS idmap_allocate_uid(struct unixid *id);
 NTSTATUS idmap_allocate_gid(struct unixid *id);
 NTSTATUS idmap_backend_unixids_to_sids(struct id_map **maps,
 				       const char *domain_name);
+struct idmap_domain *idmap_find_domain(const char *domname);
 
 /* The following definitions come from winbindd/idmap_nss.c  */
 
-- 
2.1.4


>From 448ada8492e2b08310f4dc6c99319292db133f4c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 29 Dec 2016 09:56:29 +0000
Subject: [PATCH 07/17] winbind: Add a GetNssInfo parent/child call

This call will be done in the idmap child. It is not 100% the right place,
but there is no better one available to me. It will become a replacement
for the "winbind nss info" parameter: This global parameter is good
for just one domain. It might be possible to have idmap backend AD for
different domains, and the NSS info like primary gid, homedir and shell
might be done with different policies per domain. As we already have a
domain-specific idmap configuration, doing the NSS info configuration
there also is the closest way to do it.

The alternative, if we did not want to put this call into the idmap child
would be to establish an equivalent engine like the whole "idmap config
*" just for the nss info. But as I believe this is closely related,
I'll just keep it in the idmap child.

This also extends the wbint_userinfo structure with pretty much all user
related fields. The idea is that the GetNssInfo call can do whatever it
wants with it.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 librpc/idl/winbind.idl               |  7 +++++++
 source3/include/idmap.h              |  4 ++++
 source3/winbindd/winbindd_dual_srv.c | 14 ++++++++++++++
 3 files changed, 25 insertions(+)

diff --git a/librpc/idl/winbind.idl b/librpc/idl/winbind.idl
index ec472c5..ab1a32e 100644
--- a/librpc/idl/winbind.idl
+++ b/librpc/idl/winbind.idl
@@ -72,11 +72,14 @@ interface winbind
 	);
 
     typedef [public] struct {
+	[string,charset(UTF8)] char *domain_name;
 	[string,charset(UTF8)] char *acct_name;
 	[string,charset(UTF8)] char *full_name;
 	[string,charset(UTF8)] char *homedir;
 	[string,charset(UTF8)] char *shell;
+	hyper uid;
 	hyper primary_gid;
+	[string,charset(UTF8)] char *primary_group_name;
 	dom_sid user_sid;
 	dom_sid group_sid;
     } wbint_userinfo;
@@ -86,6 +89,10 @@ interface winbind
 	[out] wbint_userinfo *info
 	);
 
+    NTSTATUS wbint_GetNssInfo(
+	[in,out] wbint_userinfo *info
+	);
+
     typedef [public] struct {
 	uint32 num_sids;
 	[size_is(num_sids)] dom_sid sids[];
diff --git a/source3/include/idmap.h b/source3/include/idmap.h
index 800e694..c379eba 100644
--- a/source3/include/idmap.h
+++ b/source3/include/idmap.h
@@ -32,9 +32,13 @@
 
 #include "librpc/gen_ndr/idmap.h"
 
+struct wbint_userinfo;
+
 struct idmap_domain {
 	const char *name;
 	struct idmap_methods *methods;
+	NTSTATUS (*query_user)(struct idmap_domain *domain,
+			       struct wbint_userinfo *info);
 	uint32_t low_id;
 	uint32_t high_id;
 	bool read_only;
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index 7b80418..cf4846d 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -283,6 +283,20 @@ NTSTATUS _wbint_QueryUser(struct pipes_struct *p, struct wbint_QueryUser *r)
 	return status;
 }
 
+NTSTATUS _wbint_GetNssInfo(struct pipes_struct *p, struct wbint_GetNssInfo *r)
+{
+	struct idmap_domain *domain;
+	NTSTATUS status;
+
+	domain = idmap_find_domain(r->in.info->domain_name);
+	if ((domain == NULL) || (domain->query_user == NULL)) {
+		return NT_STATUS_REQUEST_NOT_ACCEPTED;
+	}
+
+	status = domain->query_user(domain, r->in.info);
+	return status;
+}
+
 NTSTATUS _wbint_LookupUserAliases(struct pipes_struct *p,
 				  struct wbint_LookupUserAliases *r)
 {
-- 
2.1.4


>From 2dabe595409a5995d570c041c957f4a1cc43dde8 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 30 Dec 2016 10:57:50 +0000
Subject: [PATCH 08/17] winbind: Adapt cache to extended wbint_userinfo

Separate commit, UL/ was missing some fields already

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

diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c
index 05b356f..5171e28 100644
--- a/source3/winbindd/winbindd_cache.c
+++ b/source3/winbindd/winbindd_cache.c
@@ -1020,11 +1020,14 @@ static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
 	centry = centry_start(domain, status);
 	if (!centry)
 		return;
+	centry_put_string(centry, info->domain_name);
 	centry_put_string(centry, info->acct_name);
 	centry_put_string(centry, info->full_name);
 	centry_put_string(centry, info->homedir);
 	centry_put_string(centry, info->shell);
+	centry_put_uint32(centry, info->uid);
 	centry_put_uint32(centry, info->primary_gid);
+	centry_put_string(centry, info->primary_group_name);
 	centry_put_sid(centry, &info->user_sid);
 	centry_put_sid(centry, &info->group_sid);
 	centry_end(centry, "U/%s", sid_to_fstring(sid_string,
@@ -1483,10 +1486,14 @@ do_fetch_cache:
 		smb_panic_fn("query_user_list out of memory");
 	}
 	for (i=0; i<(*num_entries); i++) {
+		(*info)[i].domain_name = centry_string(centry, mem_ctx);
 		(*info)[i].acct_name = centry_string(centry, mem_ctx);
 		(*info)[i].full_name = centry_string(centry, mem_ctx);
 		(*info)[i].homedir = centry_string(centry, mem_ctx);
 		(*info)[i].shell = centry_string(centry, mem_ctx);
+		(*info)[i].uid = centry_uint32(centry);
+		(*info)[i].primary_gid = centry_uint32(centry);
+		(*info)[i].primary_group_name = centry_string(centry, mem_ctx);
 		centry_sid(centry, &(*info)[i].user_sid);
 		centry_sid(centry, &(*info)[i].group_sid);
 	}
@@ -1576,10 +1583,14 @@ do_query:
 		goto skip_save;
 	centry_put_uint32(centry, *num_entries);
 	for (i=0; i<(*num_entries); i++) {
+		centry_put_string(centry, (*info)[i].domain_name);
 		centry_put_string(centry, (*info)[i].acct_name);
 		centry_put_string(centry, (*info)[i].full_name);
 		centry_put_string(centry, (*info)[i].homedir);
 		centry_put_string(centry, (*info)[i].shell);
+		centry_put_uint32(centry, (*info)[i].uid);
+		centry_put_uint32(centry, (*info)[i].primary_gid);
+		centry_put_string(centry, (*info)[i].primary_group_name);
 		centry_put_sid(centry, &(*info)[i].user_sid);
 		centry_put_sid(centry, &(*info)[i].group_sid);
 		if (domain->backend && domain->backend->consistent) {
@@ -2308,11 +2319,14 @@ NTSTATUS wcache_query_user(struct winbindd_domain *domain,
 	   and the rest of the data doesn't matter */
 	status = centry->status;
 	if (NT_STATUS_IS_OK(status)) {
+		info->domain_name = centry_string(centry, mem_ctx);
 		info->acct_name = centry_string(centry, mem_ctx);
 		info->full_name = centry_string(centry, mem_ctx);
 		info->homedir = centry_string(centry, mem_ctx);
 		info->shell = centry_string(centry, mem_ctx);
+		info->uid = centry_uint32(centry);
 		info->primary_gid = centry_uint32(centry);
+		info->primary_group_name = centry_string(centry, mem_ctx);
 		centry_sid(centry, &info->user_sid);
 		centry_sid(centry, &info->group_sid);
 	}
@@ -3762,7 +3776,10 @@ static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 	(void)centry_string(centry, mem_ctx);
 	(void)centry_string(centry, mem_ctx);
 	(void)centry_string(centry, mem_ctx);
+	(void)centry_string(centry, mem_ctx);
+	(void)centry_uint32(centry);
 	(void)centry_uint32(centry);
+	(void)centry_string(centry, mem_ctx);
 	(void)centry_sid(centry, &sid);
 	(void)centry_sid(centry, &sid);
 
@@ -3865,6 +3882,10 @@ static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 		(void)centry_string(centry, mem_ctx);
 		(void)centry_string(centry, mem_ctx);
 		(void)centry_string(centry, mem_ctx);
+		(void)centry_string(centry, mem_ctx);
+		(void)centry_uint32(centry);
+		(void)centry_uint32(centry);
+		(void)centry_string(centry, mem_ctx);
 		(void)centry_sid(centry, &sid);
 		(void)centry_sid(centry, &sid);
 	}
-- 
2.1.4


>From 331a718528eb6764e1ec3ee382e0f45884a07312 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 29 Dec 2016 10:05:28 +0000
Subject: [PATCH 09/17] winbind: Restructure wb_getpwsid

This patch moves the responsibility to create a winbind user from the
winbind backends into wb_queryuser.c. The name comes from lsa_lookupsids,
the uid from idmap. If we have a netsamlogon_cache, we get the primary
group sid from there. Without netsamlogon_cache, we default to -513, as
we do right now as default for non-reachable ADS domains anyway. Shell
and homedir default to template. This can all be done in the parent
without contacting any LDAP-related calls and is correct once we have
a netsamlogon_cache.

Once the parent has filled in the userinfo, the idmap child is queried
with the GetNssInfo call, taking the userinfo [in,out]. The child is
free to override the whole thing, something the AD backend will do in
the next patch.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_getpwsid.c  | 117 +++++-----------
 source3/winbindd/wb_queryuser.c | 286 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 304 insertions(+), 99 deletions(-)

diff --git a/source3/winbindd/wb_getpwsid.c b/source3/winbindd/wb_getpwsid.c
index 5e218ac..8c764f7 100644
--- a/source3/winbindd/wb_getpwsid.c
+++ b/source3/winbindd/wb_getpwsid.c
@@ -30,8 +30,6 @@ struct wb_getpwsid_state {
 };
 
 static void wb_getpwsid_queryuser_done(struct tevent_req *subreq);
-static void wb_getpwsid_lookupsid_done(struct tevent_req *subreq);
-static void wb_getpwsid_done(struct tevent_req *subreq);
 
 struct tevent_req *wb_getpwsid_send(TALLOC_CTX *mem_ctx,
 				    struct tevent_context *ev,
@@ -63,108 +61,57 @@ static void wb_getpwsid_queryuser_done(struct tevent_req *subreq)
 		subreq, struct tevent_req);
 	struct wb_getpwsid_state *state = tevent_req_data(
 		req, struct wb_getpwsid_state);
+	struct winbindd_pw *pw = state->pw;
+	struct wbint_userinfo *info;
+	fstring acct_name, output_username;
+	char *tmp;
 	NTSTATUS status;
 
 	status = wb_queryuser_recv(subreq, state, &state->userinfo);
 	TALLOC_FREE(subreq);
-	if (NT_STATUS_IS_OK(status)
-	    && (state->userinfo->acct_name != NULL)
-	    && (state->userinfo->acct_name[0] != '\0'))
-	{
-		/*
-		 * QueryUser got us a name, let's go directly to the
-		 * fill_pwent step
-		 */
-		subreq = wb_fill_pwent_send(state, state->ev, state->userinfo,
-					    state->pw);
-		if (tevent_req_nomem(subreq, req)) {
-			return;
-		}
-		tevent_req_set_callback(subreq, wb_getpwsid_done, req);
+	if (tevent_req_nterror(req, status)) {
 		return;
 	}
+	info = state->userinfo;
 
-	/*
-	 * Either query_user did not succeed, or it
-	 * succeeded but did not return an acct_name.
-	 * (TODO: Can this happen at all???)
-	 * ==> Try lsa_lookupsids.
-	 */
-	if (state->userinfo == NULL) {
-		state->userinfo = talloc_zero(state, struct wbint_userinfo);
-		if (tevent_req_nomem(state->userinfo, req)) {
-			return;
-		}
-
-		/* a successful query_user call would have filled these */
-		sid_copy(&state->userinfo->user_sid, &state->sid);
-		state->userinfo->homedir = NULL;
-		state->userinfo->shell = NULL;
-		state->userinfo->primary_gid = (gid_t)-1;
-	}
+	pw->pw_uid = info->uid;
+	pw->pw_gid = info->primary_gid;
 
-	subreq = wb_lookupsid_send(state, state->ev, &state->sid);
-	if (tevent_req_nomem(subreq, req)) {
+	fstrcpy(acct_name, info->acct_name);
+	if (!strlower_m(acct_name)) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 		return;
 	}
-	tevent_req_set_callback(subreq, wb_getpwsid_lookupsid_done, req);
-}
 
-static void wb_getpwsid_lookupsid_done(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct wb_getpwsid_state *state = tevent_req_data(
-		req, struct wb_getpwsid_state);
-	NTSTATUS status;
-	enum lsa_SidType type;
-	const char *domain;
+	fill_domain_username(output_username, info->domain_name,
+			     acct_name, true);
+	strlcpy(pw->pw_name, output_username, sizeof(pw->pw_name));
 
-	status = wb_lookupsid_recv(subreq, state->userinfo, &type, &domain,
-				   &state->userinfo->acct_name);
-	TALLOC_FREE(subreq);
-	if (tevent_req_nterror(req, status)) {
-		return;
-	}
+	strlcpy(pw->pw_gecos, info->full_name ? info->full_name : "",
+		sizeof(pw->pw_gecos));
 
-	switch (type) {
-	case SID_NAME_USER:
-	case SID_NAME_COMPUTER:
-		/*
-		 * user case: we only need the account name from lookup_sids
-		 */
-		break;
-	case SID_NAME_DOM_GRP:
-	case SID_NAME_ALIAS:
-	case SID_NAME_WKN_GRP:
-		/*
-		 * also treat group-type SIDs (they might map to ID_TYPE_BOTH)
-		 */
-		sid_copy(&state->userinfo->group_sid, &state->sid);
-		break;
-	default:
-		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+	tmp = talloc_sub_specified(
+		state, info->homedir, acct_name,
+		info->primary_group_name, info->domain_name,
+		pw->pw_uid, pw->pw_gid);
+	if (tevent_req_nomem(tmp, req)) {
 		return;
 	}
-
-	subreq = wb_fill_pwent_send(state, state->ev, state->userinfo,
-				    state->pw);
-	if (tevent_req_nomem(subreq, req)) {
+	strlcpy(pw->pw_dir, tmp, sizeof(pw->pw_dir));
+	TALLOC_FREE(tmp);
+
+	tmp = talloc_sub_specified(
+		state, info->shell, info->acct_name,
+		info->primary_group_name, info->domain_name,
+		pw->pw_uid, pw->pw_gid);
+	if (tevent_req_nomem(tmp, req)) {
 		return;
 	}
-	tevent_req_set_callback(subreq, wb_getpwsid_done, req);
-}
+	strlcpy(pw->pw_shell, tmp, sizeof(pw->pw_dir));
+	TALLOC_FREE(tmp);
 
-static void wb_getpwsid_done(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	NTSTATUS status;
+	strlcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd));
 
-	status = wb_fill_pwent_recv(subreq);
-	if (tevent_req_nterror(req, status)) {
-		return;
-	}
 	tevent_req_done(req);
 }
 
diff --git a/source3/winbindd/wb_queryuser.c b/source3/winbindd/wb_queryuser.c
index 974687a..be4d3d3 100644
--- a/source3/winbindd/wb_queryuser.c
+++ b/source3/winbindd/wb_queryuser.c
@@ -21,12 +21,19 @@
 #include "winbindd.h"
 #include "librpc/gen_ndr/ndr_winbind_c.h"
 #include "../libcli/security/security.h"
+#include "libsmb/samlogon_cache.h"
 
 struct wb_queryuser_state {
-	struct dom_sid sid;
+	struct tevent_context *ev;
 	struct wbint_userinfo *info;
+	bool tried_dclookup;
 };
 
+static void wb_queryuser_got_uid(struct tevent_req *subreq);
+static void wb_queryuser_got_domain(struct tevent_req *subreq);
+static void wb_queryuser_got_dc(struct tevent_req *subreq);
+static void wb_queryuser_got_gid(struct tevent_req *subreq);
+static void wb_queryuser_got_group_name(struct tevent_req *subreq);
 static void wb_queryuser_done(struct tevent_req *subreq);
 
 struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
@@ -35,46 +42,297 @@ struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
 {
 	struct tevent_req *req, *subreq;
 	struct wb_queryuser_state *state;
-	struct winbindd_domain *domain;
+	struct wbint_userinfo *info;
 
 	req = tevent_req_create(mem_ctx, &state, struct wb_queryuser_state);
 	if (req == NULL) {
 		return NULL;
 	}
-	sid_copy(&state->sid, user_sid);
+	state->ev = ev;
 
-	domain = find_domain_from_sid_noinit(user_sid);
-	if (domain == NULL) {
-		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
-		return tevent_req_post(req, ev);
+	if (lp_winbind_trusted_domains_only()) {
+		struct winbindd_domain *our_domain = find_our_domain();
+
+		if (dom_sid_compare_domain(user_sid, &our_domain->sid) == 0) {
+			char buf[DOM_SID_STR_BUFLEN];
+			dom_sid_string_buf(user_sid, buf, sizeof(buf));
+			DBG_NOTICE("My domain -- rejecting %s\n", buf);
+			tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+			return tevent_req_post(req, ev);
+		}
 	}
 
-	state->info = talloc(state, struct wbint_userinfo);
+	state->info = talloc_zero(state, struct wbint_userinfo);
 	if (tevent_req_nomem(state->info, req)) {
 		return tevent_req_post(req, ev);
 	}
+	info = state->info;
+
+	info->primary_gid = (gid_t)-1;
 
-	subreq = dcerpc_wbint_QueryUser_send(state, ev, dom_child_handle(domain),
-					     &state->sid, state->info);
+	sid_copy(&info->user_sid, user_sid);
+
+	subreq = wb_sids2xids_send(
+		state, state->ev, &state->info->user_sid, 1);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
-	tevent_req_set_callback(subreq, wb_queryuser_done, req);
+	tevent_req_set_callback(subreq, wb_queryuser_got_uid, req);
 	return req;
 }
 
+static void wb_queryuser_got_uid(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_queryuser_state *state = tevent_req_data(
+		req, struct wb_queryuser_state);
+	struct wbint_userinfo *info = state->info;
+	struct netr_SamInfo3 *info3;
+	struct winbindd_child *child;
+	struct unixid xid;
+	NTSTATUS status;
+
+	status = wb_sids2xids_recv(subreq, &xid, 1);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if ((xid.type != ID_TYPE_UID) && (xid.type != ID_TYPE_BOTH)) {
+		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+		return;
+	}
+
+	info->uid = xid.id;
+
+	/*
+	 * Default the group sid to "Domain Users" in the user's
+	 * domain. The samlogon cache or the query_user call later on
+	 * can override this.
+	 */
+	sid_copy(&info->group_sid, &info->user_sid);
+	sid_split_rid(&info->group_sid, NULL);
+	sid_append_rid(&info->group_sid, DOMAIN_RID_USERS);
+
+	info->homedir = talloc_strdup(info, lp_template_homedir());
+	if (tevent_req_nomem(info->homedir, req)) {
+		return;
+	}
+
+	info->shell = talloc_strdup(info, lp_template_shell());
+	if (tevent_req_nomem(info->shell, req)) {
+		return;
+	}
+
+	info3 = netsamlogon_cache_get(state, &info->user_sid);
+	if (info3 != NULL) {
+
+		sid_compose(&info->group_sid, info3->base.domain_sid,
+			    info3->base.primary_gid);
+		info->acct_name = talloc_move(
+			info, &info3->base.account_name.string);
+		info->full_name = talloc_move(
+			info, &info3->base.full_name.string);
+
+		info->domain_name = talloc_move(
+			state, &info3->base.logon_domain.string);
+
+		TALLOC_FREE(info3);
+	}
+
+	if (info->domain_name == NULL) {
+		subreq = wb_lookupsid_send(state, state->ev, &info->user_sid);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, wb_queryuser_got_domain, req);
+		return;
+	}
+
+	child = idmap_child();
+
+	subreq = dcerpc_wbint_GetNssInfo_send(
+		state, state->ev, child->binding_handle, info);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, wb_queryuser_done, req);
+}
+
+static void wb_queryuser_got_domain(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_queryuser_state *state = tevent_req_data(
+		req, struct wb_queryuser_state);
+	struct wbint_userinfo *info = state->info;
+	enum lsa_SidType type;
+	struct winbindd_child *child;
+	NTSTATUS status;
+
+	status = wb_lookupsid_recv(subreq, state, &type,
+				   &info->domain_name, &info->acct_name);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if (type != SID_NAME_USER) {
+		/* allow SID_NAME_COMPUTER? */
+		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+		return;
+	}
+
+	child = idmap_child();
+
+	subreq = dcerpc_wbint_GetNssInfo_send(
+		state, state->ev, child->binding_handle, info);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, wb_queryuser_done, req);
+}
+
 static void wb_queryuser_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
 	struct wb_queryuser_state *state = tevent_req_data(
 		req, struct wb_queryuser_state);
+	struct wbint_userinfo *info = state->info;
 	NTSTATUS status, result;
 
-	status = dcerpc_wbint_QueryUser_recv(subreq, state->info, &result);
+	status = dcerpc_wbint_GetNssInfo_recv(subreq, info, &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) {
+		subreq = wb_dsgetdcname_send(
+			state, state->ev, state->info->domain_name, NULL, NULL,
+			DS_RETURN_DNS_NAME);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, wb_queryuser_got_dc, req);
+		return;
+	}
+
+	/*
+	 * Ignore failure in "result" here. We'll try to fill in stuff
+	 * that misses further down.
+	 */
+
+	if (state->info->primary_gid == (gid_t)-1) {
+		subreq = wb_sids2xids_send(
+			state, state->ev, &info->group_sid, 1);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, wb_queryuser_got_gid, req);
+		return;
+	}
+
+	if (state->info->primary_group_name == NULL) {
+		subreq = wb_lookupsid_send(state, state->ev, &info->group_sid);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, wb_queryuser_got_group_name,
+					req);
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+static void wb_queryuser_got_dc(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_queryuser_state *state = tevent_req_data(
+		req, struct wb_queryuser_state);
+	struct wbint_userinfo *info = state->info;
+	struct netr_DsRGetDCNameInfo *dcinfo;
+	struct winbindd_child *child;
+	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(info->domain_name, dcinfo);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	child = idmap_child();
+
+	subreq = dcerpc_wbint_GetNssInfo_send(
+		state, state->ev, child->binding_handle, info);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, wb_queryuser_done, req);
+}
+
+static void wb_queryuser_got_gid(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_queryuser_state *state = tevent_req_data(
+		req, struct wb_queryuser_state);
+	struct unixid xid;
+	NTSTATUS status;
+
+	status = wb_sids2xids_recv(subreq, &xid, 1);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if ((xid.type != ID_TYPE_GID) && (xid.type != ID_TYPE_BOTH)) {
+		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+		return;
+	}
+
+	state->info->primary_gid = xid.id;
+
+	if (state->info->primary_group_name == NULL) {
+		subreq = wb_lookupsid_send(state, state->ev,
+					   &state->info->group_sid);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, wb_queryuser_got_group_name,
+					req);
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+static void wb_queryuser_got_group_name(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_queryuser_state *state = tevent_req_data(
+		req, struct wb_queryuser_state);
+	enum lsa_SidType type;
+	NTSTATUS status;
+	const char *domain_name;
+
+	status = wb_lookupsid_recv(subreq, state, &type, &domain_name,
+				   &state->info->primary_group_name);
 	TALLOC_FREE(subreq);
-	if (any_nt_status_not_ok(status, result, &status)) {
-		tevent_req_nterror(req, status);
+	if (tevent_req_nterror(req, status)) {
 		return;
 	}
 	tevent_req_done(req);
-- 
2.1.4


>From b1331f64bbb7ee58a21a7cac0ed85893ab37efae Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 29 Dec 2016 10:27:58 +0000
Subject: [PATCH 10/17] idmap_ad: Restore querying SFU nss info

With the last commit the getpwsid call did not look at the winbind
nss info parameter anymore. This restores it for the idmap ad backend
with slightly different semantics and configuration: We now have the
unix_primary_group and unix_nss_info domain-specific parameters for
idmap config. This enables overriding the Windows primary group with
the unix one.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 docs-xml/manpages/idmap_ad.8.xml |  14 +++++
 source3/winbindd/idmap_ad.c      | 110 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 124 insertions(+)

diff --git a/docs-xml/manpages/idmap_ad.8.xml b/docs-xml/manpages/idmap_ad.8.xml
index 5876c46..58e7f52 100644
--- a/docs-xml/manpages/idmap_ad.8.xml
+++ b/docs-xml/manpages/idmap_ad.8.xml
@@ -74,6 +74,20 @@
 			via the "primaryGroupID" LDAP attribute.
 		</para></listitem>
 		</varlistentry>
+		<varlistentry>
+		<term>unix_primary_group = yes/no</term>
+		<listitem><para>
+		  Defines whether to retrieve the user's primary group
+		  from the SFU attributes.
+		</para></listitem>
+		</varlistentry>
+		<varlistentry>
+		<term>unix_nss_info = yes/no</term>
+		<listitem><para>
+		  Defines whether to retrieve the login shell and
+		  home directory from the SFU attributes.
+		</para></listitem>
+		</varlistentry>
 	</variablelist>
 </refsect1>
 
diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
index c385cf0..f406392 100644
--- a/source3/winbindd/idmap_ad.c
+++ b/source3/winbindd/idmap_ad.c
@@ -39,8 +39,14 @@ struct idmap_ad_context {
 	struct tldap_context *ld;
 	struct idmap_ad_schema_names *schema;
 	const char *default_nc;
+
+	bool unix_primary_group;
+	bool unix_nss_info;
 };
 
+static NTSTATUS idmap_ad_get_context(struct idmap_domain *dom,
+				     struct idmap_ad_context **pctx);
+
 static char *get_schema_path(TALLOC_CTX *mem_ctx, struct tldap_context *ld)
 {
 	struct tldap_message *rootdse;
@@ -396,6 +402,11 @@ static NTSTATUS idmap_ad_context_create(TALLOC_CTX *mem_ctx,
 		return NT_STATUS_NO_MEMORY;
 	}
 
+	ctx->unix_primary_group = lp_parm_bool(
+		-1, schema_config_option, "unix_primary_group", false);
+	ctx->unix_nss_info = lp_parm_bool(
+		-1, schema_config_option, "unix_nss_info", false);
+
 	schema_mode = lp_parm_const_string(
 		-1, schema_config_option, "schema_mode", "rfc2307");
 	TALLOC_FREE(schema_config_option);
@@ -412,8 +423,107 @@ static NTSTATUS idmap_ad_context_create(TALLOC_CTX *mem_ctx,
 	return NT_STATUS_OK;
 }
 
+static NTSTATUS idmap_ad_query_user(struct idmap_domain *domain,
+				    struct wbint_userinfo *info)
+{
+	struct idmap_ad_context *ctx;
+	TLDAPRC rc;
+	NTSTATUS status;
+	char *sidstr, *filter;
+	const char *attrs[4];
+	size_t i, num_msgs;
+	struct tldap_message **msgs;
+
+	status = idmap_ad_get_context(domain, &ctx);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	if (!(ctx->unix_primary_group || ctx->unix_nss_info)) {
+		return NT_STATUS_OK;
+	}
+
+	attrs[0] = ctx->schema->gid;
+	attrs[1] = ctx->schema->gecos;
+	attrs[2] = ctx->schema->dir;
+	attrs[3] = ctx->schema->shell;
+
+	sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), &info->user_sid);
+	if (sidstr == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	filter = talloc_asprintf(talloc_tos(), "(objectsid=%s)", sidstr);
+	TALLOC_FREE(sidstr);
+	if (filter == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	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));
+	}
+
+	TALLOC_FREE(filter);
+
+	num_msgs = talloc_array_length(msgs);
+
+	for (i=0; i<num_msgs; i++) {
+		struct tldap_message *msg = msgs[i];
+
+		if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+			continue;
+		}
+
+		if (ctx->unix_primary_group) {
+			bool ok;
+			uint32_t gid;
+
+			ok = tldap_pull_uint32(msg, ctx->schema->gid, &gid);
+			if (ok) {
+				DBG_DEBUG("Setting primary group "
+					  "to %"PRIu32" from attr %s\n",
+					  gid, ctx->schema->gid);
+				info->primary_gid = gid;
+			}
+		}
+
+		if (ctx->unix_nss_info) {
+			char *attr;
+
+			attr = tldap_talloc_single_attribute(
+				msg, ctx->schema->dir, talloc_tos());
+			if (attr != NULL) {
+				info->homedir = talloc_move(info, &attr);
+			}
+			TALLOC_FREE(attr);
+
+			attr = tldap_talloc_single_attribute(
+				msg, ctx->schema->shell, talloc_tos());
+			if (attr != NULL) {
+				info->shell = talloc_move(info, &attr);
+			}
+			TALLOC_FREE(attr);
+
+			attr = tldap_talloc_single_attribute(
+				msg, ctx->schema->gecos, talloc_tos());
+			if (attr != NULL) {
+				info->full_name = talloc_move(info, &attr);
+			}
+			TALLOC_FREE(attr);
+		}
+	}
+
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom)
 {
+	dom->query_user = idmap_ad_query_user;
 	dom->private_data = NULL;
 	return NT_STATUS_OK;
 }
-- 
2.1.4


>From a51330499fa0cf9f365c4e9b9272d9eceb2c9186 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 25 Dec 2016 10:16:31 +0000
Subject: [PATCH 11/17] winbind: Don't do supplementary group lookup manually

This can never be done successfully without a valid samlogon_cache entry.

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

diff --git a/source3/winbindd/wb_gettoken.c b/source3/winbindd/wb_gettoken.c
index 5271998..a2643b5 100644
--- a/source3/winbindd/wb_gettoken.c
+++ b/source3/winbindd/wb_gettoken.c
@@ -35,7 +35,7 @@ static bool wb_add_rids_to_sids(TALLOC_CTX *mem_ctx,
 				const struct dom_sid *domain_sid,
 				int num_rids, uint32_t *rids);
 
-static void wb_gettoken_gotgroups(struct tevent_req *subreq);
+static void wb_gettoken_gotuser(struct tevent_req *subreq);
 static void wb_gettoken_gotlocalgroups(struct tevent_req *subreq);
 static void wb_gettoken_gotbuiltins(struct tevent_req *subreq);
 
@@ -45,7 +45,6 @@ struct tevent_req *wb_gettoken_send(TALLOC_CTX *mem_ctx,
 {
 	struct tevent_req *req, *subreq;
 	struct wb_gettoken_state *state;
-	struct winbindd_domain *domain;
 
 	req = tevent_req_create(mem_ctx, &state, struct wb_gettoken_state);
 	if (req == NULL) {
@@ -54,30 +53,15 @@ struct tevent_req *wb_gettoken_send(TALLOC_CTX *mem_ctx,
 	sid_copy(&state->usersid, sid);
 	state->ev = ev;
 
-	domain = find_domain_from_sid_noinit(sid);
-	if (domain == NULL) {
-		DEBUG(5, ("Could not find domain from SID %s\n",
-			  sid_string_dbg(sid)));
-		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
-		return tevent_req_post(req, ev);
-	}
-
-	if (lp_winbind_trusted_domains_only() && domain->primary) {
-		DEBUG(7, ("wb_gettoken: My domain -- rejecting getgroups() "
-			  "for %s.\n", sid_string_tos(sid)));
-		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
-		return tevent_req_post(req, ev);
-	}
-
-	subreq = wb_lookupusergroups_send(state, ev, domain, &state->usersid);
+	subreq = wb_queryuser_send(state, ev, &state->usersid);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
-	tevent_req_set_callback(subreq, wb_gettoken_gotgroups, req);
+	tevent_req_set_callback(subreq, wb_gettoken_gotuser, req);
 	return req;
 }
 
-static void wb_gettoken_gotgroups(struct tevent_req *subreq)
+static void wb_gettoken_gotuser(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
@@ -85,25 +69,52 @@ static void wb_gettoken_gotgroups(struct tevent_req *subreq)
 		req, struct wb_gettoken_state);
         struct dom_sid *sids;
 	struct winbindd_domain *domain;
+	struct wbint_userinfo *info;
+	uint32_t num_groups;
+	struct dom_sid *groups;
 	NTSTATUS status;
 
-	status = wb_lookupusergroups_recv(subreq, state, &state->num_sids,
-					  &state->sids);
+	status = wb_queryuser_recv(subreq, state, &info);
 	TALLOC_FREE(subreq);
 	if (tevent_req_nterror(req, status)) {
 		return;
 	}
 
+	sids = talloc_array(state, struct dom_sid, 2);
+	if (tevent_req_nomem(sids, req)) {
+		return;
+	}
+	state->sids = sids;
+	state->num_sids = 2;
+
+	sid_copy(&state->sids[0], &info->user_sid);
+	sid_copy(&state->sids[1], &info->group_sid);
+
+	status = lookup_usergroups_cached(
+		state, &info->user_sid, &num_groups, &groups);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("lookup_usergroups_cached failed (%s), not doing "
+			  "supplementary group lookups\n", nt_errstr(status));
+		tevent_req_done(req);
+		return;
+	}
+
+	if (num_groups + state->num_sids < num_groups) {
+		tevent_req_nterror(req, NT_STATUS_INTEGER_OVERFLOW);
+		return;
+	}
+
 	sids = talloc_realloc(state, state->sids, struct dom_sid,
-			      state->num_sids + 1);
+			      state->num_sids+num_groups);
 	if (tevent_req_nomem(sids, req)) {
 		return;
 	}
-	memmove(&sids[1], &sids[0], state->num_sids * sizeof(sids[0]));
-	sid_copy(&sids[0], &state->usersid);
-	state->num_sids += 1;
 	state->sids = sids;
 
+	memcpy(&state->sids[state->num_sids], groups,
+	       num_groups * sizeof(struct dom_sid));
+	state->num_sids += num_groups;
+
 	/*
 	 * Expand our domain's aliases
 	 */
-- 
2.1.4


>From 76e90de17394f3a7408210440046abcd5bb84af7 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 25 Dec 2016 10:19:38 +0000
Subject: [PATCH 12/17] winbind: Simplify wb_gettoken

All we need from the domain struct is it's sid. Directly use it.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_gettoken.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/source3/winbindd/wb_gettoken.c b/source3/winbindd/wb_gettoken.c
index a2643b5..1386c7d 100644
--- a/source3/winbindd/wb_gettoken.c
+++ b/source3/winbindd/wb_gettoken.c
@@ -148,13 +148,8 @@ static void wb_gettoken_gotlocalgroups(struct tevent_req *subreq)
 	if (tevent_req_nterror(req, status)) {
 		return;
 	}
-	domain = find_domain_from_sid_noinit(get_global_sam_sid());
-	if (domain == NULL) {
-		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
-		return;
-	}
 	if (!wb_add_rids_to_sids(state, &state->num_sids, &state->sids,
-				 &domain->sid, num_rids, rids)) {
+				 get_global_sam_sid(), num_rids, rids)) {
 		tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
 		return;
 	}
-- 
2.1.4


>From 35f855ecb15192d59dd294ff323f95ef29c130b2 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 29 Dec 2016 15:34:41 +0000
Subject: [PATCH 13/17] winbind: Fix a confusing indentation

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/winbindd_cache.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c
index 5171e28..a130b03 100644
--- a/source3/winbindd/winbindd_cache.c
+++ b/source3/winbindd/winbindd_cache.c
@@ -2140,7 +2140,7 @@ NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
 						names, types);
 
 	if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
-		NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+	    NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
 		if (!domain->internal && old_status) {
 			set_domain_offline(domain);
 		}
-- 
2.1.4


>From 68bdf214c4558d39e69f0699aaa6a4d7f3ff1ad8 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 29 Dec 2016 18:13:28 +0000
Subject: [PATCH 14/17] Add wbint_QueryUserRidList

This is an equivalent of QueryUserList with simpler output. The next
commit will use it to go through wb_getpwsid for getent passwd, to
make sure we get the same results. Eventually, this might get a simpler
backend.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 librpc/idl/winbind.idl               |  4 +++
 source3/winbindd/winbindd_dual_srv.c | 47 ++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/librpc/idl/winbind.idl b/librpc/idl/winbind.idl
index ab1a32e..d38b17a 100644
--- a/librpc/idl/winbind.idl
+++ b/librpc/idl/winbind.idl
@@ -147,6 +147,10 @@ interface winbind
 	[out] wbint_Principals *groups
 	);
 
+    NTSTATUS wbint_QueryUserRidList(
+	[out] wbint_RidArray *rids
+	);
+
     NTSTATUS wbint_DsGetDcName(
 	[in,string,charset(UTF8)]		char *domain_name,
 	[in,unique]				GUID *domain_guid,
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index cf4846d..a75e577 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -511,6 +511,53 @@ NTSTATUS _wbint_QueryGroupList(struct pipes_struct *p,
 	return NT_STATUS_OK;
 }
 
+NTSTATUS _wbint_QueryUserRidList(struct pipes_struct *p,
+				 struct wbint_QueryUserRidList *r)
+{
+	struct winbindd_domain *domain = wb_child_domain();
+	uint32_t i, num_userinfos;
+	struct wbint_userinfo *userinfos;
+	NTSTATUS status;
+
+	if (domain == NULL) {
+		return NT_STATUS_REQUEST_NOT_ACCEPTED;
+	}
+
+	/*
+	 * Right now this is overkill. We should add a backend call
+	 * just querying the rids.
+	 */
+
+	status = wb_cache_query_user_list(domain, p->mem_ctx,
+					  &num_userinfos, &userinfos);
+	reset_cm_connection_on_error(domain, status);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	r->out.rids->rids = talloc_array(r->out.rids, uint32_t, num_userinfos);
+	if (r->out.rids->rids == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	for (i=0; i<num_userinfos; i++) {
+		struct wbint_userinfo *info = &userinfos[i];
+
+		if (!dom_sid_in_domain(&domain->sid, &info->user_sid)) {
+			fstring sidstr, domstr;
+			DBG_WARNING("Got sid %s in domain %s\n",
+				    sid_to_fstring(sidstr, &info->user_sid),
+				    sid_to_fstring(domstr, &domain->sid));
+			continue;
+		}
+		sid_split_rid(&info->user_sid,
+			      &r->out.rids->rids[r->out.rids->num_rids++]);
+	}
+
+	return status;
+}
+
 NTSTATUS _wbint_DsGetDcName(struct pipes_struct *p, struct wbint_DsGetDcName *r)
 {
 	struct winbindd_domain *domain = wb_child_domain();
-- 
2.1.4


>From c0ac2e56eb6aec9855953345f5ea41313a250623 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 29 Dec 2016 19:05:40 +0000
Subject: [PATCH 15/17] winbind: Go throught wb_getpwsid for listing users

This makes sure we get the same results for getpwnam and getpwent.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_next_pwent.c | 36 +++++++++++++++++++++---------------
 source3/winbindd/winbindd.h      |  5 ++---
 2 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/source3/winbindd/wb_next_pwent.c b/source3/winbindd/wb_next_pwent.c
index af5e8d9..dae1621 100644
--- a/source3/winbindd/wb_next_pwent.c
+++ b/source3/winbindd/wb_next_pwent.c
@@ -20,11 +20,13 @@
 #include "includes.h"
 #include "winbindd.h"
 #include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "libcli/security/dom_sid.h"
 #include "passdb/machine_sid.h"
 
 struct wb_next_pwent_state {
 	struct tevent_context *ev;
 	struct getpwent_state *gstate;
+	struct dom_sid next_sid;
 	struct winbindd_pw *pw;
 };
 
@@ -36,8 +38,9 @@ static void wb_next_pwent_send_do(struct tevent_req *req,
 {
 	struct tevent_req *subreq;
 
-	if (state->gstate->next_user >= state->gstate->num_users) {
-		TALLOC_FREE(state->gstate->users);
+	if (state->gstate->next_user >= state->gstate->rids.num_rids) {
+		TALLOC_FREE(state->gstate->rids.rids);
+		state->gstate->rids.num_rids = 0;
 
 		state->gstate->domain = wb_next_domain(state->gstate->domain);
 		if (state->gstate->domain == NULL) {
@@ -45,8 +48,10 @@ static void wb_next_pwent_send_do(struct tevent_req *req,
 			return;
 		}
 
-		subreq = wb_query_user_list_send(state, state->ev,
-						 state->gstate->domain);
+		subreq = dcerpc_wbint_QueryUserRidList_send(
+			state, state->ev,
+			dom_child_handle(state->gstate->domain),
+			&state->gstate->rids);
 		if (tevent_req_nomem(subreq, req)) {
 			return;
 		}
@@ -55,9 +60,11 @@ static void wb_next_pwent_send_do(struct tevent_req *req,
 		return;
 	}
 
-	subreq = wb_fill_pwent_send(state, state->ev,
-				&state->gstate->users[state->gstate->next_user],
-				state->pw);
+	sid_compose(&state->next_sid, &state->gstate->domain->sid,
+		    state->gstate->rids.rids[state->gstate->next_user]);
+
+	subreq = wb_getpwsid_send(state, state->ev, &state->next_sid,
+				  state->pw);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
@@ -95,18 +102,17 @@ static void wb_next_pwent_fetch_done(struct tevent_req *subreq)
 		subreq, struct tevent_req);
 	struct wb_next_pwent_state *state = tevent_req_data(
 		req, struct wb_next_pwent_state);
-	NTSTATUS status;
+	NTSTATUS status, result;
 
-	status = wb_query_user_list_recv(subreq, state->gstate,
-					 &state->gstate->num_users,
-					 &state->gstate->users);
+	status = dcerpc_wbint_QueryUserRidList_recv(subreq, state->gstate,
+						    &result);
 	TALLOC_FREE(subreq);
-	if (!NT_STATUS_IS_OK(status)) {
+	if (any_nt_status_not_ok(status, result, &status)) {
 		/* Ignore errors here, just log it */
 		DEBUG(10, ("query_user_list for domain %s returned %s\n",
 			   state->gstate->domain->name,
 			   nt_errstr(status)));
-		state->gstate->num_users = 0;
+		state->gstate->rids.num_rids = 0;
 	}
 
 	state->gstate->next_user = 0;
@@ -122,13 +128,13 @@ static void wb_next_pwent_fill_done(struct tevent_req *subreq)
 		req, struct wb_next_pwent_state);
 	NTSTATUS status;
 
-	status = wb_fill_pwent_recv(subreq);
+	status = wb_getpwsid_recv(subreq);
 	TALLOC_FREE(subreq);
 	/*
 	 * When you try to enumerate users with 'getent passwd' and the user
 	 * doesn't have a uid set we should just move on.
 	 */
-	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
 		state->gstate->next_user += 1;
 
 		wb_next_pwent_send_do(req, state);
diff --git a/source3/winbindd/winbindd.h b/source3/winbindd/winbindd.h
index fce2e46..535252b 100644
--- a/source3/winbindd/winbindd.h
+++ b/source3/winbindd/winbindd.h
@@ -75,9 +75,8 @@ struct winbindd_cli_state {
 
 struct getpwent_state {
 	struct winbindd_domain *domain;
-	int next_user;
-	int num_users;
-	struct wbint_userinfo *users;
+	uint32_t next_user;
+	struct wbint_RidArray rids;
 };
 
 struct getgrent_state {
-- 
2.1.4


>From b9706d16dde04dd61a9dff28530ec08e4e66959b Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 30 Dec 2016 11:47:45 +0000
Subject: [PATCH 16/17] winbind: Remove wb_fill_pwent

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_fill_pwent.c  | 248 --------------------------------------
 source3/winbindd/winbindd_proto.h |   7 --
 source3/winbindd/wscript_build    |   1 -
 3 files changed, 256 deletions(-)
 delete mode 100644 source3/winbindd/wb_fill_pwent.c

diff --git a/source3/winbindd/wb_fill_pwent.c b/source3/winbindd/wb_fill_pwent.c
deleted file mode 100644
index 2229b05..0000000
--- a/source3/winbindd/wb_fill_pwent.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-   async fill_pwent
-   Copyright (C) Volker Lendecke 2009
-
-   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 "librpc/gen_ndr/ndr_winbind_c.h"
-
-struct wb_fill_pwent_state {
-	struct tevent_context *ev;
-	const struct wbint_userinfo *info;
-	struct winbindd_pw *pw;
-};
-
-static bool fillup_pw_field(const char *lp_template,
-			    const char *username,
-			    const char *grpname,
-			    const char *domname,
-			    uid_t uid,
-			    gid_t gid,
-			    const char *in,
-			    fstring out);
-
-static void wb_fill_pwent_sid2uid_done(struct tevent_req *subreq);
-static void wb_fill_pwent_getgrsid_done(struct tevent_req *subreq);
-
-struct tevent_req *wb_fill_pwent_send(TALLOC_CTX *mem_ctx,
-				      struct tevent_context *ev,
-				      const struct wbint_userinfo *info,
-				      struct winbindd_pw *pw)
-{
-	struct tevent_req *req, *subreq;
-	struct wb_fill_pwent_state *state;
-
-	req = tevent_req_create(mem_ctx, &state, struct wb_fill_pwent_state);
-	if (req == NULL) {
-		return NULL;
-	}
-	state->ev = ev;
-	state->info = info;
-	state->pw = pw;
-
-	subreq = wb_sids2xids_send(state, state->ev, &state->info->user_sid, 1);
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, wb_fill_pwent_sid2uid_done, req);
-	return req;
-}
-
-static void wb_fill_pwent_sid2uid_done(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct wb_fill_pwent_state *state = tevent_req_data(
-		req, struct wb_fill_pwent_state);
-	NTSTATUS status;
-	struct unixid xids[1];
-
-	status = wb_sids2xids_recv(subreq, xids, ARRAY_SIZE(xids));
-	TALLOC_FREE(subreq);
-	if (tevent_req_nterror(req, status)) {
-		return;
-	}
-
-	/*
-	 * We are filtering further down in sids2xids, but that filtering
-	 * depends on the actual type of the sid handed in (as determined
-	 * by lookupsids). Here we need to filter for the type of object
-	 * actually requested, in this case uid.
-	 */
-	if (!(xids[0].type == ID_TYPE_UID || xids[0].type == ID_TYPE_BOTH)) {
-		tevent_req_nterror(req, NT_STATUS_NONE_MAPPED);
-		return;
-	}
-
-	state->pw->pw_uid = (uid_t)xids[0].id;
-
-	subreq = wb_getgrsid_send(state, state->ev, &state->info->group_sid, 0);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, wb_fill_pwent_getgrsid_done, req);
-}
-
-static void wb_fill_pwent_getgrsid_done(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct wb_fill_pwent_state *state = tevent_req_data(
-		req, struct wb_fill_pwent_state);
-	struct winbindd_domain *domain;
-	const char *dom_name;
-	const char *grp_name;
-	fstring user_name, output_username;
-	char *mapped_name = NULL;
-	struct talloc_dict *members;
-	TALLOC_CTX *tmp_ctx = talloc_stackframe();
-	NTSTATUS status;
-	bool ok;
-
-	/* xid handling is done in getgrsid() */
-	status = wb_getgrsid_recv(subreq,
-				  tmp_ctx,
-				  &dom_name,
-				  &grp_name,
-				  &state->pw->pw_gid,
-				  &members);
-	TALLOC_FREE(subreq);
-	if (tevent_req_nterror(req, status)) {
-		talloc_free(tmp_ctx);
-		return;
-	}
-
-	domain = find_domain_from_sid_noinit(&state->info->user_sid);
-	if (domain == NULL) {
-		talloc_free(tmp_ctx);
-		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
-		return;
-	}
-	dom_name = domain->name;
-
-	/* Username */
-
-	fstrcpy(user_name, state->info->acct_name);
-	if (!strlower_m(user_name)) {
-		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return;
-	}
-	status = normalize_name_map(state, domain, user_name, &mapped_name);
-
-	/* Basic removal of whitespace */
-	if (NT_STATUS_IS_OK(status)) {
-		fill_domain_username(output_username, dom_name, mapped_name,
-				     true);
-	}
-	/* Complete name replacement */
-	else if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
-		fstrcpy(output_username, mapped_name);
-	}
-	/* No change at all */
-	else {
-		fill_domain_username(output_username, dom_name, user_name,
-				     true);
-	}
-
-	strlcpy(state->pw->pw_name,
-		output_username,
-		sizeof(state->pw->pw_name));
-	/* FIXME The full_name can be longer than 255 chars */
-	strlcpy(state->pw->pw_gecos,
-		state->info->full_name != NULL ? state->info->full_name : "",
-		sizeof(state->pw->pw_gecos));
-
-	/* Home directory and shell */
-	ok = fillup_pw_field(lp_template_homedir(),
-			     user_name,
-			     grp_name,
-			     dom_name,
-			     state->pw->pw_uid,
-			     state->pw->pw_gid,
-			     state->info->homedir,
-			     state->pw->pw_dir);
-	if (!ok) {
-		talloc_free(tmp_ctx);
-		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
-		return;
-	}
-
-	ok = fillup_pw_field(lp_template_shell(),
-			     user_name,
-			     grp_name,
-			     dom_name,
-			     state->pw->pw_uid,
-			     state->pw->pw_gid,
-			     state->info->shell,
-			     state->pw->pw_shell);
-	talloc_free(tmp_ctx);
-	if (!ok) {
-		tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
-		return;
-	}
-
-	/* Password - set to "*" as we can't generate anything useful here.
-	   Authentication can be done using the pam_winbind module. */
-
-	fstrcpy(state->pw->pw_passwd, "*");
-	tevent_req_done(req);
-}
-
-NTSTATUS wb_fill_pwent_recv(struct tevent_req *req)
-{
-	return tevent_req_simple_recv_ntstatus(req);
-}
-
-static bool fillup_pw_field(const char *lp_template,
-			    const char *username,
-			    const char *grpname,
-			    const char *domname,
-			    uid_t uid,
-			    gid_t gid,
-			    const char *in,
-			    fstring out)
-{
-	const char *templ;
-	char *result;
-
-	if (out == NULL)
-		return False;
-
-	templ = lp_template;
-
-	if ((in != NULL) && (in[0] != '\0') && (lp_security() == SEC_ADS)) {
-		/*
-		 * The backend has already filled in the required value. Use
-		 * that instead of the template.
-		 */
-		templ = in;
-	}
-
-	result = talloc_sub_specified(talloc_tos(), templ,
-				      username, grpname, domname,
-				      uid, gid);
-	if (result == NULL) {
-		return False;
-	}
-
-	fstrcpy(out, result);
-	TALLOC_FREE(result);
-
-	return True;
-
-}
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 675d27f..a2810c9 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -783,13 +783,6 @@ NTSTATUS wb_query_group_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 				  int *num_users,
 				  struct wbint_Principal **groups);
 
-
-struct tevent_req *wb_fill_pwent_send(TALLOC_CTX *mem_ctx,
-				      struct tevent_context *ev,
-				      const struct wbint_userinfo *info,
-				      struct winbindd_pw *pw);
-NTSTATUS wb_fill_pwent_recv(struct tevent_req *req);
-
 struct tevent_req *wb_next_pwent_send(TALLOC_CTX *mem_ctx,
 				      struct tevent_context *ev,
 				      struct getpwent_state *gstate,
diff --git a/source3/winbindd/wscript_build b/source3/winbindd/wscript_build
index 3f78093..51264e9 100644
--- a/source3/winbindd/wscript_build
+++ b/source3/winbindd/wscript_build
@@ -215,7 +215,6 @@ bld.SAMBA3_BINARY('winbindd',
                  wb_getgrsid.c
                  wb_query_user_list.c
                  wb_query_group_list.c
-                 wb_fill_pwent.c
                  wb_next_pwent.c
                  wb_next_grent.c
                  wb_dsgetdcname.c
-- 
2.1.4


>From 77bd3be9bf419fbbc113ee52125a641739514df1 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 30 Dec 2016 11:51:37 +0000
Subject: [PATCH 17/17] winbind: Remove find_builtin_domain helper function

There was only one caller, and the function was pretty small anyway.

This makes a "git grep find_domain_from" more obvious :-)

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_gettoken.c    |  2 +-
 source3/winbindd/winbindd_proto.h |  1 -
 source3/winbindd/winbindd_util.c  | 12 ------------
 3 files changed, 1 insertion(+), 14 deletions(-)

diff --git a/source3/winbindd/wb_gettoken.c b/source3/winbindd/wb_gettoken.c
index 1386c7d..1c99121 100644
--- a/source3/winbindd/wb_gettoken.c
+++ b/source3/winbindd/wb_gettoken.c
@@ -159,7 +159,7 @@ static void wb_gettoken_gotlocalgroups(struct tevent_req *subreq)
 	 * Now expand the builtin groups
 	 */
 
-	domain = find_builtin_domain();
+	domain = find_domain_from_sid(&global_sid_Builtin);
 	if (domain == NULL) {
 		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
 		return;
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index a2810c9..2b6f26e 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -483,7 +483,6 @@ struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid);
 struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid);
 struct winbindd_domain *find_our_domain(void);
 struct winbindd_domain *find_root_domain(void);
-struct winbindd_domain *find_builtin_domain(void);
 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid);
 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name);
 bool parse_domain_user(const char *domuser, fstring domain, fstring user);
diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
index bd9403f..ffcb09d 100644
--- a/source3/winbindd/winbindd_util.c
+++ b/source3/winbindd/winbindd_util.c
@@ -994,18 +994,6 @@ struct winbindd_domain *find_root_domain(void)
 	return find_domain_from_name( ours->forest_name );
 }
 
-struct winbindd_domain *find_builtin_domain(void)
-{
-	struct winbindd_domain *domain;
-
-	domain = find_domain_from_sid(&global_sid_Builtin);
-	if (domain == NULL) {
-		smb_panic("Could not find BUILTIN domain");
-	}
-
-	return domain;
-}
-
 /* Find the appropriate domain to lookup a name or SID */
 
 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
-- 
2.1.4

-------------- next part --------------
 source3/winbindd/wb_getpwsid.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/source3/winbindd/wb_getpwsid.c b/source3/winbindd/wb_getpwsid.c
index f0a2cbf3634..8c764f77b08 100644
--- a/source3/winbindd/wb_getpwsid.c
+++ b/source3/winbindd/wb_getpwsid.c
@@ -63,7 +63,7 @@ static void wb_getpwsid_queryuser_done(struct tevent_req *subreq)
 		req, struct wb_getpwsid_state);
 	struct winbindd_pw *pw = state->pw;
 	struct wbint_userinfo *info;
-	fstring output_username;
+	fstring acct_name, output_username;
 	char *tmp;
 	NTSTATUS status;
 
@@ -77,15 +77,21 @@ static void wb_getpwsid_queryuser_done(struct tevent_req *subreq)
 	pw->pw_uid = info->uid;
 	pw->pw_gid = info->primary_gid;
 
+	fstrcpy(acct_name, info->acct_name);
+	if (!strlower_m(acct_name)) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		return;
+	}
+
 	fill_domain_username(output_username, info->domain_name,
-			     info->acct_name, true);
+			     acct_name, true);
 	strlcpy(pw->pw_name, output_username, sizeof(pw->pw_name));
 
 	strlcpy(pw->pw_gecos, info->full_name ? info->full_name : "",
 		sizeof(pw->pw_gecos));
 
 	tmp = talloc_sub_specified(
-		state, info->homedir, info->acct_name,
+		state, info->homedir, acct_name,
 		info->primary_group_name, info->domain_name,
 		pw->pw_uid, pw->pw_gid);
 	if (tevent_req_nomem(tmp, req)) {


More information about the samba-technical mailing list