Winbind changes in 4.6/Change for 4.6.0?

Karolin Seeger kseeger at samba.org
Fri Mar 3 11:12:33 UTC 2017


Hi,

we just had some internal discussions about the winbind changes in Samba
4.6.0. The removal of the token groups fallback will break exististing
setups (e.g. domain members where people access files without Samba
(nfs, ...). There is no workaround!

What about re-adding this feature cleanly and for local domains only and
disable it by default?

Please find attached a patchset from Volker.
"winbind : ask token groups = yes" would restore the old behaviour.
(I would prefer a documented parameter, but that could be changed.)

Unfortunately, it's pretty late in the release process, but since the
code is disabled by default, it should not be a big deal...

The planned release date for the final release still is Tuesday, March 7.
Some patches have been added sinc rc4, but it seems to be ok to go ahead
with rc5.

Opinions?

Cheers,
Karo

-- 
Karolin Seeger			https://samba.org/~kseeger/
Release Manager Samba Team	https://samba.org
Team Lead Samba SerNet		https://sernet.de
-------------- next part --------------
From a930cb8f0994966f4daf09ed8e8aa5cc67170b14 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 14:52:49 +0100
Subject: [PATCH 1/9] Revert "winbind: Remove rpc_lookup_usergroups"

This reverts commit 91b73b1e93bb8fb38e2f1cea6c1cbd012c952542.
---
 source3/winbindd/winbindd_rpc.c | 74 +++++++++++++++++++++++++++++++++++++++++
 source3/winbindd/winbindd_rpc.h |  9 +++++
 2 files changed, 83 insertions(+)

diff --git a/source3/winbindd/winbindd_rpc.c b/source3/winbindd/winbindd_rpc.c
index 261906f..3dd4f77 100644
--- a/source3/winbindd/winbindd_rpc.c
+++ b/source3/winbindd/winbindd_rpc.c
@@ -424,6 +424,80 @@ NTSTATUS rpc_rids_to_names(TALLOC_CTX *mem_ctx,
 	return NT_STATUS_OK;
 }
 
+/* Lookup groups a user is a member of. */
+NTSTATUS rpc_lookup_usergroups(TALLOC_CTX *mem_ctx,
+			       struct rpc_pipe_client *samr_pipe,
+			       struct policy_handle *samr_policy,
+			       const struct dom_sid *domain_sid,
+			       const struct dom_sid *user_sid,
+			       uint32_t *pnum_groups,
+			       struct dom_sid **puser_grpsids)
+{
+	struct policy_handle user_policy;
+	struct samr_RidWithAttributeArray *rid_array = NULL;
+	struct dom_sid *user_grpsids = NULL;
+	uint32_t num_groups = 0, i;
+	uint32_t user_rid;
+	NTSTATUS status, result;
+	struct dcerpc_binding_handle *b = samr_pipe->binding_handle;
+
+	if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+		return NT_STATUS_UNSUCCESSFUL;
+	}
+
+	/* Get user handle */
+	status = dcerpc_samr_OpenUser(b,
+				      mem_ctx,
+				      samr_policy,
+				      SEC_FLAG_MAXIMUM_ALLOWED,
+				      user_rid,
+				      &user_policy,
+				      &result);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+	if (!NT_STATUS_IS_OK(result)) {
+		return result;
+	}
+
+	/* Query user rids */
+	status = dcerpc_samr_GetGroupsForUser(b,
+					      mem_ctx,
+					      &user_policy,
+					      &rid_array,
+					      &result);
+	{
+		NTSTATUS _result;
+		dcerpc_samr_Close(b, mem_ctx, &user_policy, &_result);
+	}
+
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+	if (!NT_STATUS_IS_OK(result)) {
+		return result;
+	}
+
+	num_groups = rid_array->count;
+
+	user_grpsids = talloc_array(mem_ctx, struct dom_sid, num_groups);
+	if (user_grpsids == NULL) {
+		status = NT_STATUS_NO_MEMORY;
+		return status;
+	}
+
+	for (i = 0; i < num_groups; i++) {
+		sid_compose(&(user_grpsids[i]), domain_sid,
+			    rid_array->rids[i].rid);
+	}
+
+	*pnum_groups = num_groups;
+
+	*puser_grpsids = user_grpsids;
+
+	return NT_STATUS_OK;
+}
+
 NTSTATUS rpc_lookup_useraliases(TALLOC_CTX *mem_ctx,
 				struct rpc_pipe_client *samr_pipe,
 				struct policy_handle *samr_policy,
diff --git a/source3/winbindd/winbindd_rpc.h b/source3/winbindd/winbindd_rpc.h
index ee4b210..162f1ef 100644
--- a/source3/winbindd/winbindd_rpc.h
+++ b/source3/winbindd/winbindd_rpc.h
@@ -78,6 +78,15 @@ NTSTATUS rpc_rids_to_names(TALLOC_CTX *mem_ctx,
 			   char ***pnames,
 			   enum lsa_SidType **ptypes);
 
+/* Lookup groups a user is a member of. */
+NTSTATUS rpc_lookup_usergroups(TALLOC_CTX *mem_ctx,
+			       struct rpc_pipe_client *samr_pipe,
+			       struct policy_handle *samr_policy,
+			       const struct dom_sid *domain_sid,
+			       const struct dom_sid *user_sid,
+			       uint32_t *pnum_groups,
+			       struct dom_sid **puser_grpsids);
+
 NTSTATUS rpc_lookup_useraliases(TALLOC_CTX *mem_ctx,
 				struct rpc_pipe_client *samr_pipe,
 				struct policy_handle *samr_policy,
-- 
2.1.4


From 2f77ace0ca9b3c2f5e7033b6f9aaf59c624be1ad Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 14:53:47 +0100
Subject: [PATCH 2/9] Revert "winbind: Remove "lookup_usergroups" winbind
 method"

This reverts commit b231814c6b0ad17255139bc8934f269610348b2b.
---
 source3/winbindd/winbindd.h               |   8 +
 source3/winbindd/winbindd_ads.c           | 377 ++++++++++++++++++++++++++++++
 source3/winbindd/winbindd_msrpc.c         |  72 ++++++
 source3/winbindd/winbindd_reconnect.c     |  21 ++
 source3/winbindd/winbindd_reconnect_ads.c |  22 ++
 source3/winbindd/winbindd_samr.c          |  65 ++++++
 6 files changed, 565 insertions(+)

diff --git a/source3/winbindd/winbindd.h b/source3/winbindd/winbindd.h
index 0f95703..c220573 100644
--- a/source3/winbindd/winbindd.h
+++ b/source3/winbindd/winbindd.h
@@ -262,6 +262,14 @@ struct winbindd_methods {
 				  char ***names,
 				  enum lsa_SidType **types);
 
+	/* lookup all groups that a user is a member of. The backend
+	   can also choose to lookup by username or rid for this
+	   function */
+	NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain,
+				      TALLOC_CTX *mem_ctx,
+				      const struct dom_sid *user_sid,
+				      uint32_t *num_groups, struct dom_sid **user_gids);
+
 	/* Lookup all aliases that the sids delivered are member of. This is
 	 * to implement 'domain local groups' correctly */
 	NTSTATUS (*lookup_useraliases)(struct winbindd_domain *domain,
diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c
index 05ef2ec..c408ef8 100644
--- a/source3/winbindd/winbindd_ads.c
+++ b/source3/winbindd/winbindd_ads.c
@@ -574,6 +574,382 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 					   domain_name, names, types);
 }
 
+/* Lookup groups a user is a member of - alternate method, for when
+   tokenGroups are not available. */
+static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
+					 TALLOC_CTX *mem_ctx,
+					 const char *user_dn, 
+					 struct dom_sid *primary_group,
+					 uint32_t *p_num_groups, struct dom_sid **user_sids)
+{
+	ADS_STATUS rc;
+	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+	int count;
+	LDAPMessage *res = NULL;
+	LDAPMessage *msg = NULL;
+	char *ldap_exp;
+	ADS_STRUCT *ads;
+	const char *group_attrs[] = {"objectSid", NULL};
+	char *escaped_dn;
+	uint32_t num_groups = 0;
+
+	DEBUG(3,("ads: lookup_usergroups_member\n"));
+
+	if ( !winbindd_can_contact_domain( domain ) ) {
+		DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
+			  domain->name));		
+		return NT_STATUS_OK;
+	}
+
+	ads = ads_cached_connection(domain);
+
+	if (!ads) {
+		domain->last_status = NT_STATUS_SERVER_DISABLED;
+		goto done;
+	}
+
+	if (!(escaped_dn = escape_ldap_string(talloc_tos(), user_dn))) {
+		status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	ldap_exp = talloc_asprintf(mem_ctx,
+		"(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
+		escaped_dn,
+		ADS_LDAP_MATCHING_RULE_BIT_AND,
+		GROUP_TYPE_SECURITY_ENABLED);
+	if (!ldap_exp) {
+		DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
+		TALLOC_FREE(escaped_dn);
+		status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	TALLOC_FREE(escaped_dn);
+
+	rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
+
+	if (!ADS_ERR_OK(rc)) {
+		DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
+		return ads_ntstatus(rc);
+	} else if (!res) {
+		DEBUG(1,("lookup_usergroups ads_search returned NULL res\n"));
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+
+	count = ads_count_replies(ads, res);
+
+	*user_sids = NULL;
+	num_groups = 0;
+
+	/* always add the primary group to the sid array */
+	status = add_sid_to_array(mem_ctx, primary_group, user_sids,
+				  &num_groups);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto done;
+	}
+
+	if (count > 0) {
+		for (msg = ads_first_entry(ads, res); msg;
+		     msg = ads_next_entry(ads, msg)) {
+			struct dom_sid group_sid;
+
+			if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
+				DEBUG(1,("No sid for this group ?!?\n"));
+				continue;
+			}
+
+			/* ignore Builtin groups from ADS - Guenther */
+			if (sid_check_is_in_builtin(&group_sid)) {
+				continue;
+			}
+
+			status = add_sid_to_array(mem_ctx, &group_sid,
+						  user_sids, &num_groups);
+			if (!NT_STATUS_IS_OK(status)) {
+				goto done;
+			}
+		}
+
+	}
+
+	*p_num_groups = num_groups;
+	status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
+
+	DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
+done:
+	if (res) 
+		ads_msgfree(ads, res);
+
+	return status;
+}
+
+/* Lookup groups a user is a member of - alternate method, for when
+   tokenGroups are not available. */
+static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
+					   TALLOC_CTX *mem_ctx,
+					   const char *user_dn,
+					   struct dom_sid *primary_group,
+					   uint32_t *p_num_groups,
+					   struct dom_sid **user_sids)
+{
+	ADS_STATUS rc;
+	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+	ADS_STRUCT *ads;
+	const char *attrs[] = {"memberOf", NULL};
+	uint32_t num_groups = 0;
+	struct dom_sid *group_sids = NULL;
+	int i;
+	char **strings = NULL;
+	size_t num_strings = 0, num_sids = 0;
+
+
+	DEBUG(3,("ads: lookup_usergroups_memberof\n"));
+
+	if ( !winbindd_can_contact_domain( domain ) ) {
+		DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
+			  "domain %s\n", domain->name));
+		return NT_STATUS_OK;
+	}
+
+	ads = ads_cached_connection(domain);
+
+	if (!ads) {
+		domain->last_status = NT_STATUS_SERVER_DISABLED;
+		return NT_STATUS_UNSUCCESSFUL;
+	}
+
+	rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
+						 ADS_EXTENDED_DN_HEX_STRING,
+						 &strings, &num_strings);
+
+	if (!ADS_ERR_OK(rc)) {
+		DEBUG(1,("lookup_usergroups_memberof ads_search "
+			"member=%s: %s\n", user_dn, ads_errstr(rc)));
+		return ads_ntstatus(rc);
+	}
+
+	*user_sids = NULL;
+	num_groups = 0;
+
+	/* always add the primary group to the sid array */
+	status = add_sid_to_array(mem_ctx, primary_group, user_sids,
+				  &num_groups);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto done;
+	}
+
+	group_sids = talloc_zero_array(mem_ctx, struct dom_sid, num_strings + 1);
+	if (!group_sids) {
+		status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	for (i=0; i<num_strings; i++) {
+		rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
+						  ADS_EXTENDED_DN_HEX_STRING,
+						  &(group_sids)[i]);
+		if (!ADS_ERR_OK(rc)) {
+			/* ignore members without SIDs */
+			if (NT_STATUS_EQUAL(ads_ntstatus(rc),
+			    NT_STATUS_NOT_FOUND)) {
+				continue;
+			}
+			else {
+				status = ads_ntstatus(rc);
+				goto done;
+			}
+		}
+		num_sids++;
+	}
+
+	if (i == 0) {
+		DEBUG(1,("No memberOf for this user?!?\n"));
+		status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	for (i=0; i<num_sids; i++) {
+
+		/* ignore Builtin groups from ADS - Guenther */
+		if (sid_check_is_in_builtin(&group_sids[i])) {
+			continue;
+		}
+
+		status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
+					  &num_groups);
+		if (!NT_STATUS_IS_OK(status)) {
+			goto done;
+		}
+
+	}
+
+	*p_num_groups = num_groups;
+	status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
+
+	DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
+		user_dn));
+
+done:
+	TALLOC_FREE(strings);
+	TALLOC_FREE(group_sids);
+
+	return status;
+}
+
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+				  TALLOC_CTX *mem_ctx,
+				  const struct dom_sid *sid,
+				  uint32_t *p_num_groups, struct dom_sid **user_sids)
+{
+	ADS_STRUCT *ads = NULL;
+	const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
+	ADS_STATUS rc;
+	int count;
+	LDAPMessage *msg = NULL;
+	char *user_dn = NULL;
+	struct dom_sid *sids;
+	int i;
+	struct dom_sid primary_group;
+	uint32_t primary_group_rid;
+	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+	uint32_t num_groups = 0;
+
+	DEBUG(3,("ads: lookup_usergroups\n"));
+	*p_num_groups = 0;
+
+	status = lookup_usergroups_cached(mem_ctx, sid,
+					  p_num_groups, user_sids);
+	if (NT_STATUS_IS_OK(status)) {
+		return NT_STATUS_OK;
+	}
+
+	if ( !winbindd_can_contact_domain( domain ) ) {
+		DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
+			  domain->name));
+
+		/* Tell the cache manager not to remember this one */
+
+		return NT_STATUS_SYNCHRONIZATION_REQUIRED;
+	}
+
+	ads = ads_cached_connection(domain);
+
+	if (!ads) {
+		domain->last_status = NT_STATUS_SERVER_DISABLED;
+		status = NT_STATUS_SERVER_DISABLED;
+		goto done;
+	}
+
+	rc = ads_search_retry_sid(ads, &msg, sid, attrs);
+
+	if (!ADS_ERR_OK(rc)) {
+		status = ads_ntstatus(rc);
+		DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
+			  "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
+		goto done;
+	}
+
+	count = ads_count_replies(ads, msg);
+	if (count != 1) {
+		status = NT_STATUS_UNSUCCESSFUL;
+		DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
+			 "invalid number of results (count=%d)\n", 
+			 sid_string_dbg(sid), count));
+		goto done;
+	}
+
+	if (!msg) {
+		DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
+			 sid_string_dbg(sid)));
+		status = NT_STATUS_UNSUCCESSFUL;
+		goto done;
+	}
+
+	user_dn = ads_get_dn(ads, mem_ctx, msg);
+	if (user_dn == NULL) {
+		status = NT_STATUS_NO_MEMORY;
+		goto done;
+	}
+
+	if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
+		DEBUG(1,("%s: No primary group for sid=%s !?\n", 
+			 domain->name, sid_string_dbg(sid)));
+		goto done;
+	}
+
+	sid_compose(&primary_group, &domain->sid, primary_group_rid);
+
+	count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
+
+	/* there must always be at least one group in the token, 
+	   unless we are talking to a buggy Win2k server */
+
+	/* actually this only happens when the machine account has no read
+	 * permissions on the tokenGroup attribute - gd */
+
+	if (count == 0) {
+
+		/* no tokenGroups */
+
+		/* lookup what groups this user is a member of by DN search on
+		 * "memberOf" */
+
+		status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
+						    &primary_group,
+						    &num_groups, user_sids);
+		*p_num_groups = num_groups;
+		if (NT_STATUS_IS_OK(status)) {
+			goto done;
+		}
+
+		/* lookup what groups this user is a member of by DN search on
+		 * "member" */
+
+		status = lookup_usergroups_member(domain, mem_ctx, user_dn, 
+						  &primary_group,
+						  &num_groups, user_sids);
+		*p_num_groups = num_groups;
+		goto done;
+	}
+
+	*user_sids = NULL;
+	num_groups = 0;
+
+	status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
+				  &num_groups);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto done;
+	}
+
+	for (i=0;i<count;i++) {
+
+		/* ignore Builtin groups from ADS - Guenther */
+		if (sid_check_is_in_builtin(&sids[i])) {
+			continue;
+		}
+
+		status = add_sid_to_array_unique(mem_ctx, &sids[i],
+						 user_sids, &num_groups);
+		if (!NT_STATUS_IS_OK(status)) {
+			goto done;
+		}
+	}
+
+	*p_num_groups = (uint32_t)num_groups;
+	status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
+
+	DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
+		 sid_string_dbg(sid)));
+done:
+	TALLOC_FREE(user_dn);
+	ads_msgfree(ads, msg);
+	return status;
+}
+
 /* Lookup aliases a user is member of - use rpc methods */
 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
 				   TALLOC_CTX *mem_ctx,
@@ -1149,6 +1525,7 @@ struct winbindd_methods ads_methods = {
 	name_to_sid,
 	sid_to_name,
 	rids_to_names,
+	lookup_usergroups,
 	lookup_useraliases,
 	lookup_groupmem,
 	sequence_number,
diff --git a/source3/winbindd/winbindd_msrpc.c b/source3/winbindd/winbindd_msrpc.c
index 5ace4d1..cd7dfbc 100644
--- a/source3/winbindd/winbindd_msrpc.c
+++ b/source3/winbindd/winbindd_msrpc.c
@@ -393,6 +393,77 @@ static NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain,
 	return result;
 }
 
+/* Lookup groups a user is a member of.  I wish Unix had a call like this! */
+static NTSTATUS msrpc_lookup_usergroups(struct winbindd_domain *domain,
+					TALLOC_CTX *mem_ctx,
+					const struct dom_sid *user_sid,
+					uint32_t *pnum_groups,
+					struct dom_sid **puser_grpsids)
+{
+	struct rpc_pipe_client *samr_pipe;
+	struct policy_handle dom_pol;
+	struct dom_sid *user_grpsids = NULL;
+	uint32_t num_groups = 0;
+	TALLOC_CTX *tmp_ctx;
+	NTSTATUS status;
+
+	DEBUG(3,("msrpc_lookup_usergroups sid=%s\n", sid_string_dbg(user_sid)));
+
+	*pnum_groups = 0;
+
+	tmp_ctx = talloc_stackframe();
+	if (tmp_ctx == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	/* Check if we have a cached user_info_3 */
+	status = lookup_usergroups_cached(tmp_ctx,
+					  user_sid,
+					  &num_groups,
+					  &user_grpsids);
+	if (NT_STATUS_IS_OK(status)) {
+		goto cached;
+	}
+
+	if ( !winbindd_can_contact_domain( domain ) ) {
+		DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
+			  domain->name));
+
+		/* Tell the cache manager not to remember this one */
+		status = NT_STATUS_SYNCHRONIZATION_REQUIRED;
+		goto done;
+	}
+
+	/* no cache; hit the wire */
+	status = cm_connect_sam(domain, tmp_ctx, false, &samr_pipe, &dom_pol);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto done;
+	}
+
+	status = rpc_lookup_usergroups(tmp_ctx,
+				       samr_pipe,
+				       &dom_pol,
+				       &domain->sid,
+				       user_sid,
+				       &num_groups,
+				       &user_grpsids);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto done;
+	}
+
+cached:
+	*pnum_groups = num_groups;
+
+	if (puser_grpsids) {
+		*puser_grpsids = talloc_move(mem_ctx, &user_grpsids);
+	}
+
+done:
+	TALLOC_FREE(tmp_ctx);
+	return status;
+	return NT_STATUS_OK;
+}
+
 #define MAX_SAM_ENTRIES_W2K 0x400 /* 1024 */
 
 static NTSTATUS msrpc_lookup_useraliases(struct winbindd_domain *domain,
@@ -1090,6 +1161,7 @@ struct winbindd_methods msrpc_methods = {
 	msrpc_name_to_sid,
 	msrpc_sid_to_name,
 	msrpc_rids_to_names,
+	msrpc_lookup_usergroups,
 	msrpc_lookup_useraliases,
 	msrpc_lookup_groupmem,
 	msrpc_sequence_number,
diff --git a/source3/winbindd/winbindd_reconnect.c b/source3/winbindd/winbindd_reconnect.c
index d23ffcf..bbb5a37 100644
--- a/source3/winbindd/winbindd_reconnect.c
+++ b/source3/winbindd/winbindd_reconnect.c
@@ -199,6 +199,26 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 	return result;
 }
 
+/* Lookup groups a user is a member of.  I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+				  TALLOC_CTX *mem_ctx,
+				  const struct dom_sid *user_sid,
+				  uint32_t *num_groups, struct dom_sid **user_gids)
+{
+	NTSTATUS result;
+
+	result = msrpc_methods.lookup_usergroups(domain, mem_ctx,
+						 user_sid, num_groups,
+						 user_gids);
+
+	if (reconnect_need_retry(result, domain))
+		result = msrpc_methods.lookup_usergroups(domain, mem_ctx,
+							 user_sid, num_groups,
+							 user_gids);
+
+	return result;
+}
+
 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
 				   TALLOC_CTX *mem_ctx,
 				   uint32_t num_sids, const struct dom_sid *sids,
@@ -314,6 +334,7 @@ struct winbindd_methods reconnect_methods = {
 	name_to_sid,
 	sid_to_name,
 	rids_to_names,
+	lookup_usergroups,
 	lookup_useraliases,
 	lookup_groupmem,
 	sequence_number,
diff --git a/source3/winbindd/winbindd_reconnect_ads.c b/source3/winbindd/winbindd_reconnect_ads.c
index 17ea9d2..3bb8b5e 100644
--- a/source3/winbindd/winbindd_reconnect_ads.c
+++ b/source3/winbindd/winbindd_reconnect_ads.c
@@ -153,6 +153,27 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 	return result;
 }
 
+/* Lookup groups a user is a member of.  I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+				  TALLOC_CTX *mem_ctx,
+				  const struct dom_sid *user_sid,
+				  uint32_t *num_groups,
+				  struct dom_sid **user_gids)
+{
+	NTSTATUS result;
+
+	result = ads_methods.lookup_usergroups(domain, mem_ctx, user_sid,
+					       num_groups, user_gids);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.lookup_usergroups(domain, mem_ctx,
+						       user_sid, num_groups,
+						       user_gids);
+	}
+
+	return result;
+}
+
 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
 				   TALLOC_CTX *mem_ctx,
 				   uint32_t num_sids,
@@ -269,6 +290,7 @@ struct winbindd_methods reconnect_ads_methods = {
 	name_to_sid,
 	sid_to_name,
 	rids_to_names,
+	lookup_usergroups,
 	lookup_useraliases,
 	lookup_groupmem,
 	sequence_number,
diff --git a/source3/winbindd/winbindd_samr.c b/source3/winbindd/winbindd_samr.c
index 1a73fc4..aedb77b 100644
--- a/source3/winbindd/winbindd_samr.c
+++ b/source3/winbindd/winbindd_samr.c
@@ -758,6 +758,69 @@ error:
 	return status;
 }
 
+/* Lookup groups a user is a member of. */
+static NTSTATUS sam_lookup_usergroups(struct winbindd_domain *domain,
+				      TALLOC_CTX *mem_ctx,
+				      const struct dom_sid *user_sid,
+				      uint32_t *pnum_groups,
+				      struct dom_sid **puser_grpsids)
+{
+	struct rpc_pipe_client *samr_pipe;
+	struct policy_handle dom_pol;
+	struct dom_sid *user_grpsids = NULL;
+	uint32_t num_groups = 0;
+	TALLOC_CTX *tmp_ctx;
+	NTSTATUS status, result;
+	struct dcerpc_binding_handle *b = NULL;
+
+	DEBUG(3,("sam_lookup_usergroups\n"));
+
+	ZERO_STRUCT(dom_pol);
+
+	if (pnum_groups) {
+		*pnum_groups = 0;
+	}
+
+	tmp_ctx = talloc_stackframe();
+	if (tmp_ctx == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto done;
+	}
+
+	b = samr_pipe->binding_handle;
+
+	status = rpc_lookup_usergroups(tmp_ctx,
+				       samr_pipe,
+				       &dom_pol,
+				       &domain->sid,
+				       user_sid,
+				       &num_groups,
+				       &user_grpsids);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto done;
+	}
+
+	if (pnum_groups) {
+		*pnum_groups = num_groups;
+	}
+
+	if (puser_grpsids) {
+		*puser_grpsids = talloc_move(mem_ctx, &user_grpsids);
+	}
+
+done:
+	if (b && is_valid_policy_hnd(&dom_pol)) {
+		dcerpc_samr_Close(b, mem_ctx, &dom_pol, &result);
+	}
+
+	TALLOC_FREE(tmp_ctx);
+	return status;
+}
+
 static NTSTATUS sam_lookup_useraliases(struct winbindd_domain *domain,
 				       TALLOC_CTX *mem_ctx,
 				       uint32_t num_sids,
@@ -879,6 +942,7 @@ struct winbindd_methods builtin_passdb_methods = {
 	.name_to_sid           = sam_name_to_sid,
 	.sid_to_name           = sam_sid_to_name,
 	.rids_to_names         = sam_rids_to_names,
+	.lookup_usergroups     = sam_lookup_usergroups,
 	.lookup_useraliases    = sam_lookup_useraliases,
 	.lookup_groupmem       = sam_lookup_groupmem,
 	.sequence_number       = sam_sequence_number,
@@ -897,6 +961,7 @@ struct winbindd_methods sam_passdb_methods = {
 	.name_to_sid           = sam_name_to_sid,
 	.sid_to_name           = sam_sid_to_name,
 	.rids_to_names         = sam_rids_to_names,
+	.lookup_usergroups     = sam_lookup_usergroups,
 	.lookup_useraliases    = sam_lookup_useraliases,
 	.lookup_groupmem       = sam_lookup_groupmem,
 	.sequence_number       = sam_sequence_number,
-- 
2.1.4


From cdbee494f227c99479650d3526400c2bbb9ae115 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 14:54:09 +0100
Subject: [PATCH 3/9] Revert "winbind: Remove validate_ug"

This reverts commit 3f58a8cabab75a594cff9088d5dd8ea439b36178.
---
 source3/winbindd/winbindd_cache.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c
index 4431cb5..a343ad1 100644
--- a/source3/winbindd/winbindd_cache.c
+++ b/source3/winbindd/winbindd_cache.c
@@ -2917,6 +2917,11 @@ void wcache_invalidate_samlogon(struct winbindd_domain *domain,
 	DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
 	tdb_delete(cache->tdb, string_tdb_data(key_str));
 
+	/* Clear UG/SID cache entry */
+	fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
+	DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
+	tdb_delete(cache->tdb, string_tdb_data(key_str));
+
 	/* Samba/winbindd never needs this. */
 	netsamlogon_clear_cached_user(sid);
 }
@@ -3679,6 +3684,32 @@ static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 	return 0;
 }
 
+static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+		       struct tdb_validation_status *state)
+{
+	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+	int32_t num_groups, i;
+
+	if (!centry) {
+		return 1;
+	}
+
+	num_groups = centry_uint32(centry);
+
+	for (i=0; i< num_groups; i++) {
+		struct dom_sid sid;
+		centry_sid(centry, &sid);
+	}
+
+	centry_free(centry);
+
+	if (!(state->success)) {
+		return 1;
+	}
+	DEBUG(10,("validate_ug: %s ok\n", keystr));
+	return 0;
+}
+
 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 		       struct tdb_validation_status *state)
 {
@@ -3881,6 +3912,7 @@ struct key_val_struct {
 	{"CRED/", validate_cred},
 	{"UL/", validate_ul},
 	{"GL/", validate_gl},
+	{"UG/", validate_ug},
 	{"UA", validate_ua},
 	{"GM/", validate_gm},
 	{"DR/", validate_dr},
-- 
2.1.4


From 35d15f11609f0d04c633230326f2064f81e2dfac Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 14:54:23 +0100
Subject: [PATCH 4/9] Revert "winbind: Remove wcache_lookup_usergroups"

This reverts commit 876dc28b9cf13343a2962b1a1b035fe78c1858a6.
---
 source3/winbindd/winbindd_cache.c | 59 +++++++++++++++++++++++++++++++++++++++
 source3/winbindd/winbindd_proto.h |  5 ++++
 2 files changed, 64 insertions(+)

diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c
index a343ad1..e05e048 100644
--- a/source3/winbindd/winbindd_cache.c
+++ b/source3/winbindd/winbindd_cache.c
@@ -2320,6 +2320,65 @@ NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
 	return NT_STATUS_OK;
 }
 
+NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
+				  TALLOC_CTX *mem_ctx,
+				  const struct dom_sid *user_sid,
+				  uint32_t *pnum_sids,
+				  struct dom_sid **psids)
+{
+	struct winbind_cache *cache = get_cache(domain);
+	struct cache_entry *centry = NULL;
+	NTSTATUS status;
+	uint32_t i, num_sids;
+	struct dom_sid *sids;
+	fstring sid_string;
+
+	if (cache->tdb == NULL) {
+		return NT_STATUS_NOT_FOUND;
+	}
+
+	centry = wcache_fetch(cache, domain, "UG/%s",
+			      sid_to_fstring(sid_string, user_sid));
+	if (centry == NULL) {
+		return NT_STATUS_NOT_FOUND;
+	}
+
+	/* If we have an access denied cache entry and a cached info3 in the
+           samlogon cache then do a query.  This will force the rpc back end
+           to return the info3 data. */
+
+	if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
+	    && netsamlogon_cache_have(user_sid)) {
+		DEBUG(10, ("lookup_usergroups: cached access denied and have "
+			   "cached info3\n"));
+		domain->last_status = NT_STATUS_OK;
+		centry_free(centry);
+		return NT_STATUS_NOT_FOUND;
+	}
+
+	num_sids = centry_uint32(centry);
+	sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
+	if (sids == NULL) {
+		centry_free(centry);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	for (i=0; i<num_sids; i++) {
+		centry_sid(centry, &sids[i]);
+	}
+
+	status = centry->status;
+
+	DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
+		  "status: %s\n", domain->name, nt_errstr(status)));
+
+	centry_free(centry);
+
+	*pnum_sids = num_sids;
+	*psids = sids;
+	return status;
+}
+
 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
 				 const struct dom_sid *sids)
 {
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index d7dec3a..e1ded99 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -169,6 +169,11 @@ NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
 				   TALLOC_CTX *mem_ctx,
 				   uint32_t num_sids, const struct dom_sid *sids,
 				   uint32_t *pnum_aliases, uint32_t **paliases);
+NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
+				  TALLOC_CTX *mem_ctx,
+				  const struct dom_sid *user_sid,
+				  uint32_t *pnum_sids,
+				  struct dom_sid **psids);
 
 void wcache_flush_cache(void);
 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count);
-- 
2.1.4


From 92f26108771ee7897d04b0cfb615ce378c3c97f8 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 14:54:46 +0100
Subject: [PATCH 5/9] Revert "winbind: Remove wb_cache_lookup_usergroups"

This reverts commit f83863b4d1510a9519d15934c960fd1675235812.
---
 source3/winbindd/winbindd_cache.c | 71 +++++++++++++++++++++++++++++++++++++++
 source3/winbindd/winbindd_proto.h |  5 +++
 2 files changed, 76 insertions(+)

diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c
index e05e048..5787441 100644
--- a/source3/winbindd/winbindd_cache.c
+++ b/source3/winbindd/winbindd_cache.c
@@ -2379,6 +2379,77 @@ NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
 	return status;
 }
 
+/* Lookup groups a user is a member of. */
+NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
+				    TALLOC_CTX *mem_ctx,
+				    const struct dom_sid *user_sid,
+				    uint32_t *num_groups,
+				    struct dom_sid **user_gids)
+{
+	struct cache_entry *centry = NULL;
+	NTSTATUS status;
+	unsigned int i;
+	fstring sid_string;
+	bool old_status;
+
+	old_status = domain->online;
+	status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
+					  num_groups, user_gids);
+	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+		return status;
+	}
+
+	(*num_groups) = 0;
+	(*user_gids) = NULL;
+
+	/* Return status value returned by seq number check */
+
+	if (!NT_STATUS_IS_OK(domain->last_status))
+		return domain->last_status;
+
+	DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
+		domain->name ));
+
+	status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+		NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+		if (!domain->internal && old_status) {
+			set_domain_offline(domain);
+		}
+		if (!domain->internal &&
+			!domain->online &&
+			old_status) {
+			NTSTATUS cache_status;
+			cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
+							  num_groups, user_gids);
+			return cache_status;
+		}
+	}
+	if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
+		goto skip_save;
+
+	/* and save it */
+	refresh_sequence_number(domain);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+	centry = centry_start(domain, status);
+	if (!centry)
+		goto skip_save;
+
+	centry_put_uint32(centry, *num_groups);
+	for (i=0; i<(*num_groups); i++) {
+		centry_put_sid(centry, &(*user_gids)[i]);
+	}	
+
+	centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
+	centry_free(centry);
+
+skip_save:
+	return status;
+}
+
 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
 				 const struct dom_sid *sids)
 {
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index e1ded99..ca9897a 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -89,6 +89,11 @@ NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
 				char **domain_name,
 				char ***names,
 				enum lsa_SidType **types);
+NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
+				    TALLOC_CTX *mem_ctx,
+				    const struct dom_sid *user_sid,
+				    uint32_t *pnum_sids,
+				    struct dom_sid **psids);
 NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
 				     TALLOC_CTX *mem_ctx,
 				     uint32_t num_sids,
-- 
2.1.4


From a786982c02f7d4ee52fbdeaff99edf5d9cda1b88 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 14:55:15 +0100
Subject: [PATCH 6/9] Revert "winbind: Remove wbint_LookupUserGroups"

This reverts commit 256632ed3cc724bab0fc22132ca6b52faf680ab2.
---
 librpc/idl/winbind.idl               |  5 +++++
 source3/winbindd/winbindd_dual_srv.c | 17 +++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/librpc/idl/winbind.idl b/librpc/idl/winbind.idl
index 6245e13..05db6b9 100644
--- a/librpc/idl/winbind.idl
+++ b/librpc/idl/winbind.idl
@@ -103,6 +103,11 @@ interface winbind
 	[out] wbint_RidArray *rids
 	);
 
+    NTSTATUS wbint_LookupUserGroups(
+	[in] dom_sid *sid,
+	[out] wbint_SidArray *sids
+	);
+
     NTSTATUS wbint_QuerySequenceNumber(
 	[out] uint32 *sequence
 	);
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index b0b38a6..7b97f33 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -301,6 +301,23 @@ NTSTATUS _wbint_LookupUserAliases(struct pipes_struct *p,
 	return status;
 }
 
+NTSTATUS _wbint_LookupUserGroups(struct pipes_struct *p,
+				 struct wbint_LookupUserGroups *r)
+{
+	struct winbindd_domain *domain = wb_child_domain();
+	NTSTATUS status;
+
+	if (domain == NULL) {
+		return NT_STATUS_REQUEST_NOT_ACCEPTED;
+	}
+
+	status = wb_cache_lookup_usergroups(domain, p->mem_ctx, r->in.sid,
+					    &r->out.sids->num_sids,
+					    &r->out.sids->sids);
+	reset_cm_connection_on_error(domain, status);
+	return status;
+}
+
 NTSTATUS _wbint_QuerySequenceNumber(struct pipes_struct *p,
 				    struct wbint_QuerySequenceNumber *r)
 {
-- 
2.1.4


From 0d16f94dd3c5291f6491bdf56b3df1ec90c0a6aa Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 14:56:09 +0100
Subject: [PATCH 7/9] Revert "winbind: Remove wb_lookupusergroups"

This reverts commit c0570e6ae8f8f0057ece48d764580897ff2b6f62.
---
 source3/winbindd/wb_lookupusergroups.c | 97 ++++++++++++++++++++++++++++++++++
 source3/winbindd/winbindd_proto.h      |  6 +++
 source3/winbindd/wscript_build         |  1 +
 3 files changed, 104 insertions(+)
 create mode 100644 source3/winbindd/wb_lookupusergroups.c

diff --git a/source3/winbindd/wb_lookupusergroups.c b/source3/winbindd/wb_lookupusergroups.c
new file mode 100644
index 0000000..2d39692
--- /dev/null
+++ b/source3/winbindd/wb_lookupusergroups.c
@@ -0,0 +1,97 @@
+/*
+   Unix SMB/CIFS implementation.
+   async lookupusergroups
+   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"
+#include "../libcli/security/security.h"
+
+struct wb_lookupusergroups_state {
+	struct tevent_context *ev;
+	struct dom_sid sid;
+	struct wbint_SidArray sids;
+};
+
+static void wb_lookupusergroups_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_lookupusergroups_send(TALLOC_CTX *mem_ctx,
+					    struct tevent_context *ev,
+					    struct winbindd_domain *domain,
+					    const struct dom_sid *sid)
+{
+	struct tevent_req *req, *subreq;
+	struct wb_lookupusergroups_state *state;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct wb_lookupusergroups_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	sid_copy(&state->sid, sid);
+
+	status = lookup_usergroups_cached(state,
+					  &state->sid,
+					  &state->sids.num_sids,
+					  &state->sids.sids);
+	if (NT_STATUS_IS_OK(status)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = dcerpc_wbint_LookupUserGroups_send(
+		state, ev, dom_child_handle(domain), &state->sid, &state->sids);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, wb_lookupusergroups_done, req);
+	return req;
+}
+
+static void wb_lookupusergroups_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_lookupusergroups_state *state = tevent_req_data(
+		req, struct wb_lookupusergroups_state);
+	NTSTATUS status, result;
+
+	status = dcerpc_wbint_LookupUserGroups_recv(subreq, state, &result);
+	TALLOC_FREE(subreq);
+	if (any_nt_status_not_ok(status, result, &status)) {
+		tevent_req_nterror(req, status);
+		return;
+	}
+	tevent_req_done(req);
+}
+
+NTSTATUS wb_lookupusergroups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+				  int *num_sids, struct dom_sid **sids)
+{
+	struct wb_lookupusergroups_state *state = tevent_req_data(
+		req, struct wb_lookupusergroups_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	*num_sids = state->sids.num_sids;
+	*sids = talloc_move(mem_ctx, &state->sids.sids);
+	return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index ca9897a..0faf0c6 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -663,6 +663,12 @@ struct tevent_req *winbindd_getsidaliases_send(TALLOC_CTX *mem_ctx,
 					       struct winbindd_request *request);
 NTSTATUS winbindd_getsidaliases_recv(struct tevent_req *req,
 				     struct winbindd_response *response);
+struct tevent_req *wb_lookupusergroups_send(TALLOC_CTX *mem_ctx,
+					    struct tevent_context *ev,
+					    struct winbindd_domain *domain,
+					    const struct dom_sid *sid);
+NTSTATUS wb_lookupusergroups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+				  int *num_sids, struct dom_sid **sids);
 
 struct tevent_req *winbindd_getuserdomgroups_send(TALLOC_CTX *mem_ctx,
 						  struct tevent_context *ev,
diff --git a/source3/winbindd/wscript_build b/source3/winbindd/wscript_build
index a5d0dd6..51264e9 100644
--- a/source3/winbindd/wscript_build
+++ b/source3/winbindd/wscript_build
@@ -206,6 +206,7 @@ bld.SAMBA3_BINARY('winbindd',
                  wb_xids2sids.c
                  wb_queryuser.c
                  wb_lookupuseraliases.c
+                 wb_lookupusergroups.c
                  wb_getpwsid.c
                  wb_gettoken.c
                  wb_seqnum.c
-- 
2.1.4


From e0fee438a151283ac9c7b8b6cffd554e7dc2de5b Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 15:08:02 +0100
Subject: [PATCH 8/9] winbind: Limit wb_lookupusergroups to our own domain

Also default it to off, enable it with "winbind : ask token groups = yes"
---
 source3/winbindd/wb_lookupusergroups.c | 19 ++++++++++++++++++-
 source3/winbindd/winbindd_proto.h      |  1 -
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/source3/winbindd/wb_lookupusergroups.c b/source3/winbindd/wb_lookupusergroups.c
index 2d39692..6c501f6 100644
--- a/source3/winbindd/wb_lookupusergroups.c
+++ b/source3/winbindd/wb_lookupusergroups.c
@@ -32,11 +32,11 @@ static void wb_lookupusergroups_done(struct tevent_req *subreq);
 
 struct tevent_req *wb_lookupusergroups_send(TALLOC_CTX *mem_ctx,
 					    struct tevent_context *ev,
-					    struct winbindd_domain *domain,
 					    const struct dom_sid *sid)
 {
 	struct tevent_req *req, *subreq;
 	struct wb_lookupusergroups_state *state;
+	struct winbindd_domain *domain;
 	NTSTATUS status;
 
 	req = tevent_req_create(mem_ctx, &state,
@@ -55,6 +55,23 @@ struct tevent_req *wb_lookupusergroups_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	if (!lp_parm_bool(-1, "winbind", "ask token groups", false)) {
+		DBG_DEBUG("lookup_usergroups_cached failed (%s), not doing "
+			  "supplementary group lookups\n", nt_errstr(status));
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	domain = find_our_domain();
+
+	if (dom_sid_compare_domain(sid, &domain->sid) != 0) {
+		DBG_DEBUG("Domain (%s) not ours, not doing "
+			  "supplementary group lookups\n",
+			  domain->name);
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
 	subreq = dcerpc_wbint_LookupUserGroups_send(
 		state, ev, dom_child_handle(domain), &state->sid, &state->sids);
 	if (tevent_req_nomem(subreq, req)) {
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 0faf0c6..46fb600 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -665,7 +665,6 @@ NTSTATUS winbindd_getsidaliases_recv(struct tevent_req *req,
 				     struct winbindd_response *response);
 struct tevent_req *wb_lookupusergroups_send(TALLOC_CTX *mem_ctx,
 					    struct tevent_context *ev,
-					    struct winbindd_domain *domain,
 					    const struct dom_sid *sid);
 NTSTATUS wb_lookupusergroups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 				  int *num_sids, struct dom_sid **sids);
-- 
2.1.4


From d4428dbe8b72f468e82f532e1e26d8b2355be354 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Mar 2017 15:14:51 +0100
Subject: [PATCH 9/9] Re-enable token groups fallback

---
 source3/winbindd/wb_gettoken.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/source3/winbindd/wb_gettoken.c b/source3/winbindd/wb_gettoken.c
index 07c7fc7..a393b0f 100644
--- a/source3/winbindd/wb_gettoken.c
+++ b/source3/winbindd/wb_gettoken.c
@@ -38,6 +38,7 @@ static NTSTATUS wb_add_rids_to_sids(TALLOC_CTX *mem_ctx,
 				    int num_rids, uint32_t *rids);
 
 static void wb_gettoken_gotuser(struct tevent_req *subreq);
+static void wb_gettoken_gotgroups(struct tevent_req *subreq);
 static void wb_gettoken_gotlocalgroups(struct tevent_req *subreq);
 static void wb_gettoken_gotbuiltins(struct tevent_req *subreq);
 
@@ -71,10 +72,7 @@ static void wb_gettoken_gotuser(struct tevent_req *subreq)
 		subreq, struct tevent_req);
 	struct wb_gettoken_state *state = tevent_req_data(
 		req, struct wb_gettoken_state);
-	struct winbindd_domain *domain;
 	struct wbint_userinfo *info;
-	uint32_t i, num_groups;
-	struct dom_sid *groups;
 	NTSTATUS status;
 
 	status = wb_queryuser_recv(subreq, state, &info);
@@ -92,11 +90,27 @@ static void wb_gettoken_gotuser(struct tevent_req *subreq)
 	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);
+	subreq = wb_lookupusergroups_send(state, state->ev, &info->user_sid);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, wb_gettoken_gotgroups, req);
+}
+
+static void wb_gettoken_gotgroups(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct wb_gettoken_state *state = tevent_req_data(
+		req, struct wb_gettoken_state);
+	int i, num_groups;
+	struct dom_sid *groups;
+	struct winbindd_domain *domain;
+	NTSTATUS status;
+
+	status = wb_lookupusergroups_recv(subreq, state, &num_groups, &groups);
+	TALLOC_FREE(subreq);
 	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;
 	}
-- 
2.1.4



More information about the samba-technical mailing list