From 3f5fa7c458dbc673b35827bb588e424cd14332c7 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 2 Mar 2017 14:53:47 +0100 Subject: [PATCH] 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 Reviewed-by: Stefan Metzmacher Reviewed-by: Ralph Boehme --- 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 0f9570338d9..c22057366cc 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 cde9099b14d..d2e1ac42b18 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; iname)); + + /* 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;iname)); + + /* 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 d23ffcfb8de..bbb5a37f390 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 17ea9d28c0c..3bb8b5ecba1 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 1a73fc4fcc6..aedb77bdee9 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.34.1