Winbind changes in 4.6/Change for 4.6.0?

Stefan Metzmacher metze at samba.org
Mon Mar 6 12:12:15 UTC 2017


Am 06.03.2017 um 09:57 schrieb Andrew Bartlett:
> On Mon, 2017-03-06 at 07:33 +0100, Stefan Metzmacher wrote:
>> Am 03.03.2017 um 19:09 schrieb Andrew Bartlett:
>>> On Fri, 2017-03-03 at 12:12 +0100, Karolin Seeger wrote:
>>>> 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?
>>>
>>> I really appreciated the move to push this up in the WHATSNEW
>>> earlier
>>> in the week, and it certainly gave me the same gut feeling of
>>> 'ouch,
>>> did we really break this with no workaround?'.
>>>
>>> I would put it back without the smb.conf option myself, but I'll
>>> take
>>> anything to avoid dropping sites into unsupported.
>>
>> I'd also think we should restore the whole old behavior, also
>> returning
>> the broken values for trusted domains.
>>
>> I don't really care if we have no option at all, one option to enable
>> the old behavior or even 2 options to enable it for the primary
>> domain
>> and other domains separately. If we add options we should add them as
>> fully
>> documented options (and mark them as deprecated similar to "lsa over
>> netlogon").
>>
>> But I guess restoring this without option would be the simplest way
>> of doing it...
> 
> We can always try again for 4.7.  The case about NFS is really
> persuasive to me.  I think going back to how things were for 4.5 sounds
> the least disruptive, and we think about options to control this in the
> 4.7 release for September.  

So what about the following patch.

While testing I found https://bugzilla.samba.org/show_bug.cgi?id=12613,
the autorid module requires a valid stamp in netsamlogon_cache.tdb
in order to allocate a new domain range. But the following will
allow admins to work around the problem, if there're add a new domain
which doesn't already have an autorid range and no user will authenticate
against samba:

### get the existing ranges:
#> net idmap get ranges
RANGE 0: ALLOC
RANGE 1: S-1-5-21-2072033271-969857664-807811056
### use the next free range
#> net idmap set range 2 S-1-5-21-4053568372-2049667917-3384589010
#> net idmap get ranges
RANGE 0: ALLOC
RANGE 2: S-1-5-21-4053568372-2049667917-3384589010
RANGE 1: S-1-5-21-2072033271-969857664-807811056

I also have a patch (attached to the bug report) to improve
the autorid verification check, but that might be able to
wait for the first maintenance release of 4.6.

metze
-------------- next part --------------
From 62cbf261e2fbb08c447df2a23ed76794ebefabdc 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.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12612

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 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,
-- 
1.9.1


From 15189b24b44f3a383d65a8f6e9c7b491dcedd190 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.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12612

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 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 cde9099..d2e1ac4 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,
@@ -1157,6 +1533,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,
-- 
1.9.1


From 246ec690c2074ece7d4a96004d08ff5e1415e292 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.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12612

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 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},
-- 
1.9.1


From 0d22bdf1b1f3d1cd740fdba00bda8ab10500fb53 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.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12612

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 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);
-- 
1.9.1


From 224e2c448991d6df88f88005d64ebffd1e4a9a7e 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.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12612

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 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,
-- 
1.9.1


From 6cab3bbf2898c400140134677767330535b5e6d4 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.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12612

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 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)
 {
-- 
1.9.1


From 4e76ce92441e83491399e8acb4caff39c459d9a3 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.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12612

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 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
-- 
1.9.1


From 947ec5981c30898c005e2be3d598504a48ff82db Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 6 Mar 2017 10:30:52 +0100
Subject: [PATCH 8/9] winbindd: find the domain based on the sid within
 wb_lookupusergroups_send()

That simplifies the potential caller.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Volker Lendecke <vl at samba.org>
---
 source3/winbindd/wb_lookupusergroups.c | 11 ++++++++++-
 source3/winbindd/winbindd_proto.h      |  1 -
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/source3/winbindd/wb_lookupusergroups.c b/source3/winbindd/wb_lookupusergroups.c
index 2d39692..7647fbd 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,15 @@ struct tevent_req *wb_lookupusergroups_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	domain = find_domain_from_sid_noinit(&state->sid);
+	if (domain == NULL) {
+		char buf[DOM_SID_STR_BUFLEN];
+		dom_sid_string_buf(&state->sid, buf, sizeof(buf));
+		DEBUG(1,("could not find domain entry for sid %s\n", buf));
+		tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+		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);
-- 
1.9.1


From 21bb55ef0982078f0c2187ae6773f7683146ee16 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

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12612

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 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;
 	}
-- 
1.9.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20170306/746a88fa/signature.sig>


More information about the samba-technical mailing list