--- 3.0.1/include/ads.h Thu Aug 28 21:42:42 2003 +++ 3.0.1/include/ads.h Mon Feb 2 17:06:16 2004 @@ -4,6 +4,11 @@ basically this is a wrapper around ldap */ +/* there are 3 possible types of errors the ads subsystem can produce */ +enum nss_group_recursion {NSS_NO_GROUP_RECURSION, + NSS_CONTEXT_GROUP_RECURSION, + NSS_FULL_GROUP_RECURSION}; + typedef struct { void *ld; /* the active ldap structure */ struct in_addr ldap_ip; /* the ip of the active connection, if any */ @@ -35,6 +40,18 @@ char *bind_path; char *ldap_server_name; time_t current_time; + /* nss info derived from the servers config and smb.conf */ + struct { + enum nss_group_recursion group_recursion; + int groups_limited; + char *group_bind_path; + int group_scope; + char *group_filter; + int passwds_limited; + char *passwd_bind_path; + int passwd_scope; + char *passwd_filter; + } nss; } config; } ADS_STRUCT; --- 3.0.1/libads/ads_ldap.c Tue Jul 1 20:44:25 2003 +++ 3.0.1/libads/ads_ldap.c Mon Feb 2 21:23:50 2004 @@ -55,28 +55,33 @@ rc = ads_search_retry(ads, &res, ldap_exp, attrs); free(ldap_exp); if (!ADS_ERR_OK(rc)) { - DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc))); + DEBUG(1,("ads_name_to_sid ads_search: %s\n", ads_errstr(rc))); goto done; } count = ads_count_replies(ads, res); if (count != 1) { - DEBUG(1,("name_to_sid: %s not found\n", name)); + DEBUG(1,("ads_name_to_sid: %s not found\n", name)); goto done; } if (!ads_pull_sid(ads, res, "objectSid", sid)) { - DEBUG(1,("No sid for %s !?\n", name)); + DEBUG(1,("ads_name_to_sid: No sid for %s !?\n", name)); goto done; } if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) { - DEBUG(1,("No sAMAccountType for %s !?\n", name)); + DEBUG(1,("ads_name_to_sid: No sAMAccountType for %s !?\n", name)); goto done; } *type = ads_atype_map(t); + if (! ads_sid_in_context(ads, *type, sid)) { + DEBUG(5,("ads_name_to_sid: Name %s found, but outside context.\n", name)); + goto done; + } + status = NT_STATUS_OK; DEBUG(3,("ads name_to_sid mapped %s\n", name)); @@ -127,6 +132,7 @@ } if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) { + DEBUG(1,("ads_sid_to_name: No sAMAccountType for %s !?\n", name)); goto done; } @@ -139,6 +145,11 @@ *type = ads_atype_map(atype); + if (! ads_name_in_context(ads, *type, *name)) { + DEBUG(5,("ads_sid_to_name: Name %s found, but outside context.\n", name)); + goto done; + } + status = NT_STATUS_OK; DEBUG(3,("ads sid_to_name mapped %s\n", *name)); @@ -152,4 +163,113 @@ return status; } +/* determine if a name of a given type is within the context defined by + the configuration file "nss group ..." and "nss passwd ..." options. */ +BOOL ads_name_in_context(ADS_STRUCT *ads, enum SID_NAME_USE type, char *name) +{ + ADS_STATUS rc; + BOOL in_context = True; + char *ldap_exp = NULL; + char *bind_path = NULL; + char *filter = NULL; + const char *attrs[] = {"sAMAccountName", NULL}; + void *res = NULL; + int scope; + int check; + + if (! name) + return False; + + if (type == SID_NAME_USER) { + bind_path = ads->config.nss.passwd_bind_path; + scope = ads->config.nss.passwd_scope; + filter = ads->config.nss.passwd_filter; + check = ads->config.nss.passwds_limited; + } else { + bind_path = ads->config.nss.group_bind_path; + scope = ads->config.nss.group_scope; + filter = ads->config.nss.group_filter; + check = ads->config.nss.groups_limited; + } + + if (check) { + if (asprintf(&ldap_exp, "(&(sAMAccountName=%s)%s)", name, filter) == -1) { + DEBUG(1,("ads_name_in_context (name=%s): asprintf failed!\n", name)); + goto done; + } + + rc = ads_do_search_retry(ads, bind_path, scope, ldap_exp, attrs, &res); + if (!ADS_ERR_OK(rc) || !res) { + DEBUG(1,("ads_name_in_context (name=%s) ads_search: %s\n", name, ads_errstr(rc))); + goto done; + } + + in_context = (ads_count_replies(ads, res) > 0); + } + +done: + if (res) ads_msgfree(ads, res); + + SAFE_FREE(ldap_exp); + + return in_context; +} + +/* determine if a sid of a given type is within the context defined by + the configuration file "nss group ..." and "nss passwd ..." options. */ +BOOL ads_sid_in_context(ADS_STRUCT *ads, enum SID_NAME_USE type, DOM_SID *sid) +{ + ADS_STATUS rc; + BOOL in_context = True; + char *sid_string = NULL; + char *ldap_exp = NULL; + char *sidstr = NULL; + char *bind_path = NULL; + char *filter = NULL; + const char *attrs[] = {"sAMAccountName", NULL}; + void *res = NULL; + int scope; + int check; + + if (type == SID_NAME_USER) { + bind_path = ads->config.nss.passwd_bind_path; + scope = ads->config.nss.passwd_scope; + filter = ads->config.nss.passwd_filter; + check = ads->config.nss.passwds_limited; + } else { + bind_path = ads->config.nss.group_bind_path; + scope = ads->config.nss.group_scope; + filter = ads->config.nss.group_filter; + check = ads->config.nss.groups_limited; + } + + if (check) { + if (!(sidstr = sid_binstring(sid))) { + DEBUG(1,("ads_sid_in_context: sid_binstring failed!\n")); + goto done; + } + + if (asprintf(&ldap_exp, "(&(objectSid=%s)%s)", sidstr, filter) == -1) { + DEBUG(1,("ads_sid_in_context (sidstr=%s): asprintf failed!\n", sid_to_string(sid_string, sid))); + goto done; + } + + rc = ads_do_search_retry(ads, bind_path, scope, ldap_exp, attrs, &res); + + if (!ADS_ERR_OK(rc) || !res) { + DEBUG(1,("ads_sid_in_context (sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc))); + goto done; + } + + in_context = (ads_count_replies(ads, res) > 0); + } + +done: + if (res) ads_msgfree(ads, res); + + SAFE_FREE(ldap_exp); + SAFE_FREE(sidstr); + + return in_context; +} #endif --- 3.0.1/libads/ads_struct.c Fri Nov 7 17:37:35 2003 +++ 3.0.1/libads/ads_struct.c Tue Jan 27 20:37:34 2004 @@ -134,6 +134,11 @@ SAFE_FREE((*ads)->config.bind_path); SAFE_FREE((*ads)->config.ldap_server_name); + SAFE_FREE((*ads)->config.nss.group_bind_path); + SAFE_FREE((*ads)->config.nss.group_filter); + SAFE_FREE((*ads)->config.nss.passwd_bind_path); + SAFE_FREE((*ads)->config.nss.passwd_filter); + ZERO_STRUCTP(*ads); SAFE_FREE(*ads); } --- 3.0.1/libads/ldap.c Thu Dec 4 21:38:37 2003 +++ 3.0.1/libads/ldap.c Fri Jan 30 07:43:26 2004 @@ -1893,6 +1893,62 @@ DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset)); } + SAFE_FREE(ads->config.nss.group_bind_path); + SAFE_FREE(ads->config.nss.group_filter); + + p = lp_nss_group_bind_path(); + if (p[0]) + { + if (strstr(p, ads->config.bind_path)) + ads->config.nss.group_bind_path = strdup(p); + else + asprintf(&ads->config.nss.group_bind_path, "%s,%s", p, ads->config.bind_path); + } + else + ads->config.nss.group_bind_path = strdup(ads->config.bind_path); + + ads->config.nss.group_filter = strdup(lp_nss_group_filter()); + ads->config.nss.group_scope = lp_nss_group_scope(); + ads->config.nss.group_recursion = lp_nss_group_recursion(); + + ads->config.nss.groups_limited + = ads->config.nss.group_scope != LDAP_SCOPE_SUBTREE + || ads->config.nss.group_filter[0] + || StrCaseCmp(ads->config.nss.group_bind_path, ads->config.bind_path); + + DEBUG(3,("got ldap group recursion \"%d\", bind path \"%s\", scope %i, and filter \"%s\".\n", + ads->config.nss.group_recursion, + ads->config.nss.group_bind_path, + ads->config.nss.group_scope, + ads->config.nss.group_filter)); + + SAFE_FREE(ads->config.nss.passwd_bind_path); + SAFE_FREE(ads->config.nss.passwd_filter); + + p = lp_nss_passwd_bind_path(); + if (p[0]) + { + if (strstr(p, ads->config.bind_path)) + ads->config.nss.passwd_bind_path = strdup(p); + else + asprintf(&ads->config.nss.passwd_bind_path, "%s,%s", p, ads->config.bind_path); + } + else + ads->config.nss.passwd_bind_path = strdup(ads->config.bind_path); + + ads->config.nss.passwd_filter = strdup(lp_nss_passwd_filter()); + ads->config.nss.passwd_scope = lp_nss_passwd_scope(); + + ads->config.nss.passwds_limited + = ads->config.nss.passwd_scope != LDAP_SCOPE_SUBTREE + || ads->config.nss.passwd_filter[0] + || StrCaseCmp(ads->config.nss.passwd_bind_path, ads->config.bind_path); + + DEBUG(3,("got ldap passwd bind path \"%s\", scope %i, and filter \"%s\".\n", + ads->config.nss.passwd_bind_path, + ads->config.nss.passwd_scope, + ads->config.nss.passwd_filter)); + talloc_destroy(ctx); return ADS_SUCCESS; --- 3.0.1/nsswitch/winbindd.h Fri Nov 14 03:40:36 2003 +++ 3.0.1/nsswitch/winbindd.h Fri Jan 30 08:52:11 2004 @@ -196,6 +196,10 @@ /* setup the list of alternate names for the domain, if any */ NTSTATUS (*alternate_name)(struct winbindd_domain *domain); + + /* Determines if the netsamlogon_cache can used for group + membership determination. */ + BOOL (*use_netsamlogon_cache)(struct winbindd_domain *domain); }; /* Used to glue a policy handle and cli_state together */ --- 3.0.1/nsswitch/winbindd_ads.c Fri Nov 14 03:40:36 2003 +++ 3.0.1/nsswitch/winbindd_ads.c Mon Feb 2 21:17:05 2004 @@ -97,6 +97,7 @@ "sAMAccountType", NULL}; int i, count; ADS_STATUS rc; + char *ldap_exp; void *res = NULL; void *msg = NULL; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; @@ -112,7 +113,13 @@ goto done; } - rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs); + if (asprintf(&ldap_exp, "(&(objectCategory=user)%s)", ads->config.nss.passwd_filter) == -1) { + DEBUG(1,("query_user_list: asprintf failed!\n")); + return NT_STATUS_NO_MEMORY; + } + rc = ads_do_search_retry(ads, ads->config.nss.passwd_bind_path, ads->config.nss.passwd_scope, ldap_exp, attrs, &res); + SAFE_FREE(ldap_exp); + if (!ADS_ERR_OK(rc) || !res) { DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc))); goto done; @@ -193,6 +200,7 @@ struct acct_info **info) { ADS_STRUCT *ads = NULL; + char *ldap_exp; const char *attrs[] = {"userPrincipalName", "sAMAccountName", "name", "objectSid", "sAMAccountType", NULL}; @@ -214,7 +222,13 @@ goto done; } - rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs); + if (asprintf(&ldap_exp, "(&(objectCategory=group)%s)", ads->config.nss.group_filter) == -1) { + DEBUG(1,("enum_dom_groups: asprintf failed!\n")); + return NT_STATUS_NO_MEMORY; + } + rc = ads_do_search_retry(ads, ads->config.nss.group_bind_path, ads->config.nss.group_scope, ldap_exp, attrs, &res); + SAFE_FREE(ldap_exp); + if (!ADS_ERR_OK(rc) || !res) { DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc))); goto done; @@ -411,8 +425,8 @@ ADS_STATUS rc; int count; void *msg = NULL; - char *ldap_exp; - char *sidstr; + char *ldap_exp = NULL; + char *sidstr = NULL; uint32 group_rid; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; DOM_SID *sid2; @@ -427,11 +441,19 @@ goto done; } - sidstr = sid_binstring(sid); - asprintf(&ldap_exp, "(objectSid=%s)", sidstr); - rc = ads_search_retry(ads, &msg, ldap_exp, attrs); - free(ldap_exp); - free(sidstr); + if (!(sidstr = sid_binstring(sid))) { + DEBUG(1,("query_user: sid_binstring failed!\n")); + status = NT_STATUS_NO_MEMORY; + goto done; + } + + if (asprintf(&ldap_exp, "(&(objectSid=%s)%s)", sidstr, ads->config.nss.passwd_filter) == -1) { + DEBUG(1,("query_user (sidstr=%s): asprintf failed!\n", sidstr)); + status = NT_STATUS_NO_MEMORY; + goto done; + } + rc = ads_do_search_retry(ads, ads->config.nss.passwd_bind_path, ads->config.nss.passwd_scope, ldap_exp, attrs, &msg); + if (!ADS_ERR_OK(rc) || !msg) { DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc))); goto done; @@ -469,63 +491,91 @@ if (msg) ads_msgfree(ads, msg); + SAFE_FREE(ldap_exp); + SAFE_FREE(sidstr); + return status; } -/* Lookup groups a user is a member of - alternate method, for when - tokenGroups are not available. */ -static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - const char *user_dn, - DOM_SID *primary_group, - uint32 *num_groups, DOM_SID ***user_gids) +static NTSTATUS lookup_usergroups_alt_recursion(ADS_STRUCT *ads, + TALLOC_CTX *mem_ctx, + const char *member_dn, + DOM_SID *primary_group, + uint32 *num_groups, + DOM_SID ***user_gids, + int *talloc_cnt, + hash_table *group_hash) { - ADS_STATUS rc; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - int count; + const char *no_recursion_attrs[] = {"objectSid", NULL}; + const char *recursion_attrs[] = {"objectSid", "distinguishedName", NULL}; + const char *chk_attrs[] = {"name", NULL}; + const char **attrs; + ADS_STATUS rc; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; void *res = NULL; void *msg = NULL; + void *chk = NULL; char *ldap_exp; - ADS_STRUCT *ads; - const char *group_attrs[] = {"objectSid", NULL}; + char *ldap_exp_str; + char *bind_path; + char *dn; + int count; + int gbp_len; + int dn_len; + int store_group; + int scope; + int recurse; + + DEBUG(3,("ads: lookup_usergroups_alt_recursion\n")); + + /* Define the initial search attributes based on recursion type */ + if (ads->config.nss.group_recursion == NSS_FULL_GROUP_RECURSION) { + ldap_exp_str = "(&(member=%s)(objectClass=group))"; + bind_path = ads->config.bind_path; + scope = LDAP_SCOPE_SUBTREE; + gbp_len = strlen(ads->config.nss.group_bind_path); + } else { + ldap_exp_str = "(&(member=%s)(objectClass=group)%s)"; + bind_path = ads->config.nss.group_bind_path; + scope = ads->config.nss.group_scope; + } + if (ads->config.nss.group_recursion == NSS_NO_GROUP_RECURSION) + attrs = no_recursion_attrs; + else + attrs = recursion_attrs; - DEBUG(3,("ads: lookup_usergroups_alt\n")); + if (asprintf(&ldap_exp, ldap_exp_str, member_dn, ads->config.nss.group_filter) == -1) { + DEBUG(1,("lookup_usergroups_recursion(dn=%s) asprintf failed!\n", member_dn)); + return NT_STATUS_NO_MEMORY; + } - ads = ads_cached_connection(domain); + rc = ads_do_search_retry(ads, bind_path, scope, ldap_exp, attrs, &res); - if (!ads) { - domain->last_status = NT_STATUS_SERVER_DISABLED; - goto done; - } + SAFE_FREE(ldap_exp); - /* buggy server, no tokenGroups. Instead lookup what groups this user - is a member of by DN search on member*/ - if (asprintf(&ldap_exp, "(&(member=%s)(objectClass=group))", user_dn) == -1) { - DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn)); - return NT_STATUS_NO_MEMORY; - } - - rc = ads_search_retry(ads, &res, ldap_exp, group_attrs); - free(ldap_exp); - if (!ADS_ERR_OK(rc) || !res) { - DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc))); + DEBUG(1,("lookup_usergroups_recursion ads_search member=%s: %s\n", member_dn, ads_errstr(rc))); return ads_ntstatus(rc); } count = ads_count_replies(ads, res); if (count == 0) { - DEBUG(5,("lookup_usergroups: No supp groups found\n")); - status = ads_ntstatus(rc); goto done; } - (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1)); - (*user_gids)[0] = primary_group; + if (*talloc_cnt == 0) { + (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (1 + count)); + (*user_gids)[0] = primary_group; - *num_groups = 1; + *num_groups = 1; + *talloc_cnt = 1; + } else { + (*user_gids) = talloc_realloc(mem_ctx, (*user_gids), sizeof(**user_gids) * (count + *talloc_cnt)); + } + *talloc_cnt += count; + for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { DOM_SID group_sid; @@ -536,21 +586,79 @@ if (sid_equal(&group_sid, primary_group)) continue; - (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids)); - if (!(*user_gids)[*num_groups]) { - status = NT_STATUS_NO_MEMORY; - goto done; + store_group = 1; + recurse = 0; + + if (ads->config.nss.group_recursion != NSS_NO_GROUP_RECURSION) { + dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName"); + if (dn == 0 || hash_lookup(group_hash, dn)) + store_group = 0; + else { + hash_insert(group_hash, strdup(dn), dn); + recurse = 1; + + if (ads->config.nss.group_recursion == NSS_FULL_GROUP_RECURSION) { + /* + * Since this group is potentially outside the + * specified "nss group" context, we must ensure + * it should be added before doing so. + */ + + /* Save an ADs lookup, if we can */ + dn_len = strlen(dn); + store_group = (dn_len >= gbp_len) && (StrCaseCmp(dn+(dn_len-gbp_len), ads->config.nss.group_bind_path) == 0); + + /* If still possibly in the group context, now check with AD */ + if (store_group) { + if (asprintf(&ldap_exp, "(&(distinguishedName=%s)%s)", dn, ads->config.nss.group_filter) == -1) { + DEBUG(1,("lookup_usergroups_recursion(dn=%s) asprintf failed!\n", member_dn)); + return NT_STATUS_NO_MEMORY; + } + rc = ads_do_search_retry(ads, ads->config.nss.group_bind_path, ads->config.nss.group_scope, ldap_exp, chk_attrs, &chk); + SAFE_FREE(ldap_exp); + + if (!ADS_ERR_OK(rc) || !res) { + DEBUG(1,("lookup_usergroups_recursion context check dn=%s\n", dn)); + store_group = 0; + } + else + store_group = ads_count_replies(ads, chk); + + if (chk) + ads_msgfree(ads, chk); + } + } + } } - sid_copy((*user_gids)[*num_groups], &group_sid); + /* The hash lookup prevents going down circular references */ + if (store_group) { + (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids)); + if (!(*user_gids)[*num_groups]) { + status = NT_STATUS_NO_MEMORY; + goto done; + } - (*num_groups)++; - + sid_copy((*user_gids)[*num_groups], &group_sid); + + (*num_groups)++; + } + + if (recurse) { + status = lookup_usergroups_alt_recursion(ads, + mem_ctx, + dn, + primary_group, + num_groups, + user_gids, + talloc_cnt, + group_hash); + } } status = NT_STATUS_OK; - DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn)); + DEBUG(3,("ads lookup_usergroups (alt_context_recursion) for dn=%s\n", member_dn)); done: if (res) ads_msgfree(ads, res); @@ -560,6 +668,45 @@ return status; } +/* Lookup groups a user is a member of - alternate method, for when + tokenGroups are not available. */ +static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *user_dn, + DOM_SID *primary_group, + uint32 *num_groups, DOM_SID ***user_gids) +{ + ADS_STRUCT *ads; + hash_table group_hash; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + int talloc_cnt = 0; + + DEBUG(3,("ads: lookup_usergroups_alt\n")); + + ads = ads_cached_connection(domain); + + if (ads) { + hash_table_init (&group_hash, 512, (compare_function)(strcmp)); + + status = lookup_usergroups_alt_recursion(ads, + mem_ctx, + user_dn, + primary_group, + num_groups, + user_gids, + &talloc_cnt, + &group_hash); + + if (NT_STATUS_EQUAL(status, NT_STATUS_OK) && *num_groups == 0) + DEBUG(5,("lookup_usergroups_alt: No supp groups found\n")); + + hash_clear(&group_hash); + } else + domain->last_status = NT_STATUS_SERVER_DISABLED; + + return status; +} + /* Lookup groups a user is a member of. */ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, @@ -569,13 +716,18 @@ ADS_STRUCT *ads = NULL; const char *attrs[] = {"distinguishedName", NULL}; const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL}; + const char *attrs3[] = {"name", NULL}; + const char *attrs4[] = {"primaryGroupID", NULL}; ADS_STATUS rc; - int count; + int count = 0; + void *res = NULL; void *msg = NULL; char *ldap_exp; char *user_dn; DOM_SID *sids; int i; + int group_found; + int use_tokenGroups; DOM_SID *primary_group; uint32 primary_group_rid; char *sidstr; @@ -624,7 +776,22 @@ if (msg) ads_msgfree(ads, msg); - rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2); + /* + * tokenGroups are requested from the ADs if full recursion is + * specified in the configuration file, or if limited recursion is + * requested, but no group limitations exist (which is the same + * as full recursion). Otherwise, tokenGroups are not requested, + * significantly lowering the overhead in the LDAP call. + */ + use_tokenGroups = (ads->config.nss.group_recursion == NSS_FULL_GROUP_RECURSION + || (! ads->config.nss.groups_limited + && ads->config.nss.group_recursion == NSS_CONTEXT_GROUP_RECURSION)); + + if (use_tokenGroups) + rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2); + else + rc = ads_search_retry_dn(ads, &msg, user_dn, attrs4); + if (!ADS_ERR_OK(rc) || !msg) { DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc))); goto done; @@ -637,14 +804,19 @@ primary_group = rid_to_talloced_sid(domain, mem_ctx, primary_group_rid); - count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids); + if (use_tokenGroups) + count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids); if (msg) ads_msgfree(ads, msg); - /* there must always be at least one group in the token, - unless we are talking to a buggy Win2k server */ - if (count == 0) { + /* + * If there are no tokenGroups in the AD for this user, or we can't + * use the tokenGroups due to the group recursion method or context, + * we call lookup_usergroups_alt to manually find the user's groups. + */ + if (!use_tokenGroups || count == 0) + { return lookup_usergroups_alt(domain, mem_ctx, user_dn, primary_group, num_groups, user_gids); @@ -658,6 +830,41 @@ for (i=0;iconfig.nss.groups_limited) { + DEBUG(5,("lookup_usergroups (sid=%s): Limited Groups - checking inclusion rules.\n", sid_to_string(sid_string, &sids[i]))); + if (!(sidstr = sid_binstring(&sids[i]))) { + DEBUG(1,("lookup_usergroups(sid=%s) sid_binstring returned NULL\n", sid_to_string(sid_string, &sids[i]))); + return NT_STATUS_NO_MEMORY; + } + + if (asprintf(&ldap_exp, "(&(objectSid=%s)%s)", sidstr, ads->config.nss.group_filter) == -1) { + DEBUG(1,("lookup_usergroups (sid=%s [%s]): asprintf failed!\n", sidstr, sid_to_string(sid_string, &sids[i]))); + return NT_STATUS_NO_MEMORY; + } + rc = ads_do_search_retry(ads, ads->config.nss.group_bind_path, ads->config.nss.group_scope, ldap_exp, attrs3, &res); + SAFE_FREE(ldap_exp); + + if (!ADS_ERR_OK(rc) || !res) { + DEBUG(1,("lookup_usergroups ads_search: %s\n", ads_errstr(rc))); + continue; + } + + group_found = ads_count_replies(ads, res); + + if (res) + ads_msgfree(ads, res); + + if (group_found == 0) { + DEBUG(7,("lookup_usergroups (sid=%s): Group Excluded.\n", sid_to_string(sid_string, &sids[i]))); + continue; + } + } + (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids)); if (!(*user_gids)[*num_groups]) { status = NT_STATUS_NO_MEMORY; @@ -709,10 +916,13 @@ sidstr = sid_binstring(group_sid); /* search for all members of the group */ - asprintf(&ldap_exp, "(objectSid=%s)",sidstr); - rc = ads_search_retry(ads, &res, ldap_exp, attrs); - free(ldap_exp); - free(sidstr); + if (asprintf(&ldap_exp, "(&(objectSid=%s)%s)", sidstr, ads->config.nss.group_filter) == -1) { + DEBUG(1,("lookup_groupmem (sidstr=%s): asprintf failed!\n", sidstr)); + return NT_STATUS_NO_MEMORY; + } + rc = ads_do_search_retry(ads, ads->config.nss.group_bind_path, ads->config.nss.group_scope, ldap_exp, attrs, &res); + SAFE_FREE(ldap_exp); + SAFE_FREE(sidstr); if (!ADS_ERR_OK(rc) || !res) { DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc))); @@ -963,6 +1173,20 @@ return ads_ntstatus(rc); } +/* The netsamlogon_cache can only be used if group limitations do + not exist and group recursion is not turned off. */ +static BOOL use_netsamlogon_cache(struct winbindd_domain *domain) +{ + ADS_STRUCT *ads = ads_cached_connection(domain); + + if (!ads) + domain->last_status = NT_STATUS_SERVER_DISABLED; + + return (!ads + || (!ads->config.nss.groups_limited + && ads->config.nss.group_recursion != NSS_NO_GROUP_RECURSION)); +} + /* the ADS backend methods are exposed via this structure */ struct winbindd_methods ads_methods = { True, @@ -977,7 +1201,8 @@ sequence_number, trusted_domains, domain_sid, - alternate_name + alternate_name, + use_netsamlogon_cache }; #endif --- 3.0.1/nsswitch/winbindd_cache.c Fri Nov 14 03:40:36 2003 +++ 3.0.1/nsswitch/winbindd_cache.c Fri Jan 30 08:55:29 2004 @@ -1349,6 +1349,12 @@ } } +/* The netsamlogon_cache can always be queried for user groups */ +static BOOL use_netsamlogon_cache(struct winbindd_domain *domain) +{ + return True; +} + /* the ADS backend methods are exposed via this structure */ struct winbindd_methods cache_methods = { True, @@ -1363,5 +1369,6 @@ sequence_number, trusted_domains, domain_sid, - alternate_name + alternate_name, + use_netsamlogon_cache }; --- 3.0.1/nsswitch/winbindd_group.c Thu Dec 4 21:38:37 2003 +++ 3.0.1/nsswitch/winbindd_group.c Mon Feb 2 17:16:23 2004 @@ -25,6 +25,8 @@ #include "includes.h" #include "winbindd.h" +extern BOOL opt_nocache; + #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND @@ -967,10 +969,14 @@ goto done; } - /* Treat the info3 cache as authoritative as the - lookup_usergroups() function may return cached data. */ - - if ((info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) { + /* + * Treat the info3 cache as authoritative as the + * lookup_usergroups() function may return cached data, + * unless the domain's backend does not want to use the cache. + */ + if (! opt_nocache + && domain->backend->use_netsamlogon_cache(domain) + && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) { DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n", info3->num_groups2, info3->num_other_sids)); --- 3.0.1/nsswitch/winbindd_rpc.c Thu Dec 4 21:38:37 2003 +++ 3.0.1/nsswitch/winbindd_rpc.c Fri Jan 30 08:56:00 2004 @@ -972,6 +972,12 @@ return NT_STATUS_OK; } +/* The netsamlogon_cache can always be queried for user groups */ +static BOOL use_netsamlogon_cache(struct winbindd_domain *domain) +{ + return True; +} + /* the rpc backend methods are exposed via this structure */ struct winbindd_methods msrpc_methods = { @@ -987,5 +993,6 @@ sequence_number, trusted_domains, domain_sid, - alternate_name + alternate_name, + use_netsamlogon_cache }; --- 3.0.1/param/loadparm.c Thu Dec 4 21:38:38 2003 +++ 3.0.1/param/loadparm.c Fri Jan 30 23:18:39 2004 @@ -177,6 +177,13 @@ BOOL bWinbindTrustedDomainsOnly; char *szWinbindBackend; char *szIdmapBackend; + int NssGroupRecursion; + char *szNssGroupBindPath; + int NssGroupScope; + char *szNssGroupFilter; + char *szNssPasswdBindPath; + int NssPasswdScope; + char *szNssPasswdFilter; char *szAddShareCommand; char *szChangeShareCommand; char *szDeleteShareCommand; @@ -564,6 +571,9 @@ static BOOL handle_netbios_aliases( const char *pszParmValue, char **ptr ); static BOOL handle_netbios_scope( const char *pszParmValue, char **ptr ); static BOOL handle_charset( const char *pszParmValue, char **ptr ); +static BOOL handle_nss_group_scope( const char *pszParmValue, char **ptr ); +static BOOL handle_nss_group_recursion( const char *pszParmValue, char **ptr ); +static BOOL handle_nss_passwd_scope( const char *pszParmValue, char **ptr ); static BOOL handle_acl_compatibility(const char *pszParmValue, char **ptr); @@ -1152,6 +1162,13 @@ {"enable rid algorithm", P_BOOL, P_GLOBAL, &Globals.bEnableRidAlgorithm, NULL, NULL, FLAG_DEPRECATED}, {"idmap backend", P_STRING, P_GLOBAL, &Globals.szIdmapBackend, NULL, NULL, FLAG_ADVANCED}, + {"nss group recursion", P_INTEGER, P_GLOBAL, &Globals.NssGroupRecursion, handle_nss_group_recursion, NULL, FLAG_ADVANCED}, + {"nss group bind path", P_STRING, P_GLOBAL, &Globals.szNssGroupBindPath, NULL, NULL, FLAG_ADVANCED}, + {"nss group scope", P_INTEGER, P_GLOBAL, &Globals.NssGroupScope, handle_nss_group_scope, NULL, FLAG_ADVANCED}, + {"nss group filter", P_STRING, P_GLOBAL, &Globals.szNssGroupFilter, NULL, NULL, FLAG_ADVANCED}, + {"nss passwd bind path", P_STRING, P_GLOBAL, &Globals.szNssPasswdBindPath, NULL, NULL, FLAG_ADVANCED}, + {"nss passwd scope", P_INTEGER, P_GLOBAL, &Globals.NssPasswdScope, handle_nss_passwd_scope, NULL, FLAG_ADVANCED}, + {"nss passwd filter", P_STRING, P_GLOBAL, &Globals.szNssPasswdFilter, NULL, NULL, FLAG_ADVANCED}, {"idmap uid", P_STRING, P_GLOBAL, &Globals.szIdmapUID, handle_idmap_uid, NULL, FLAG_ADVANCED}, {"winbind uid", P_STRING, P_GLOBAL, &Globals.szIdmapUID, handle_idmap_uid, NULL, FLAG_HIDE}, {"idmap gid", P_STRING, P_GLOBAL, &Globals.szIdmapGID, handle_idmap_gid, NULL, FLAG_ADVANCED}, @@ -1470,6 +1487,14 @@ Globals.ldap_passwd_sync = LDAP_PASSWD_SYNC_OFF; Globals.ldap_delete_dn = False; + string_set(&Globals.szNssGroupBindPath, ""); + string_set(&Globals.szNssGroupFilter, ""); + Globals.NssGroupScope = LDAP_SCOPE_SUBTREE; + Globals.NssGroupRecursion = NSS_FULL_GROUP_RECURSION; + string_set(&Globals.szNssPasswdBindPath, ""); + Globals.NssPasswdScope = LDAP_SCOPE_SUBTREE; + string_set(&Globals.szNssPasswdFilter, ""); + /* these parameters are set to defaults that are more appropriate for the increasing samba install base: @@ -1688,6 +1713,14 @@ FN_GLOBAL_STRING(lp_idmap_backend, &Globals.szIdmapBackend) FN_GLOBAL_BOOL(lp_enable_rid_algorithm, &Globals.bEnableRidAlgorithm) +FN_GLOBAL_STRING(lp_nss_group_bind_path, &Globals.szNssGroupBindPath) +FN_GLOBAL_STRING(lp_nss_group_filter, &Globals.szNssGroupFilter) +FN_GLOBAL_INTEGER(lp_nss_group_scope, &Globals.NssGroupScope) +FN_GLOBAL_INTEGER(lp_nss_group_recursion, &Globals.NssGroupRecursion) +FN_GLOBAL_STRING(lp_nss_passwd_bind_path, &Globals.szNssPasswdBindPath) +FN_GLOBAL_STRING(lp_nss_passwd_filter, &Globals.szNssPasswdFilter) +FN_GLOBAL_INTEGER(lp_nss_passwd_scope, &Globals.NssPasswdScope) + #ifdef WITH_LDAP_SAMCONFIG FN_GLOBAL_STRING(lp_ldap_server, &Globals.szLdapServer) FN_GLOBAL_INTEGER(lp_ldap_port, &Globals.ldap_port) @@ -3042,6 +3075,76 @@ return append_ldap_suffix(Globals.szLdapIdmapSuffix); return lp_string(Globals.szLdapSuffix); +} + +/*************************************************************************** + * Convert the group recursion string to the appropriate enum value. + * Unknown or empty values default to full group recursion. + **************************************************************************/ +static BOOL handle_nss_group_recursion (const char *pszParmValue, char **ptr) +{ + if (strequal(pszParmValue, "context")) + Globals.NssGroupRecursion = NSS_CONTEXT_GROUP_RECURSION; + else if (strequal(pszParmValue, "none")) + Globals.NssGroupRecursion = NSS_NO_GROUP_RECURSION; + else + Globals.NssGroupRecursion = NSS_FULL_GROUP_RECURSION; + + return True; +} + +/*************************************************************************** + * Convert the group scope string to the appropriate LDAP value. An + * empty string defaults to LDAP_SCOPE_SUBTREE. Tests "sub" first, since + * it is the most likely to occur. + **************************************************************************/ +static BOOL handle_nss_group_scope(const char *pszParmValue, char **ptr) +{ + if (!pszParmValue[0] || strequal(pszParmValue, "sub")) + Globals.NssGroupScope = LDAP_SCOPE_SUBTREE; + else if (strequal(pszParmValue, "base")) + Globals.NssGroupScope = LDAP_SCOPE_BASE; + else if (strequal(pszParmValue, "one")) + Globals.NssGroupScope = LDAP_SCOPE_ONELEVEL; + else +#ifdef LDAP_SCOPE_UNKNOWN + Globals.NssGroupScope = LDAP_SCOPE_UNKNOWN; +#else + #ifdef LDAP_SCOPE_DEFAULT + Globals.NssGroupScope = LDAP_SCOPE_DEFAULT; + #else + Globals.NssGroupScope = LDAP_SCOPE_BASE; + #endif +#endif + + return True; +} + +/*************************************************************************** + * Convert the passwd scope string to the appropriate LDAP value. An + * empty string defaults to LDAP_SCOPE_SUBTREE. Tests "sub" first, since + * it is the most likely to occur. + **************************************************************************/ +static BOOL handle_nss_passwd_scope(const char *pszParmValue, char **ptr) +{ + if (!pszParmValue[0] || strequal(pszParmValue, "sub")) + Globals.NssPasswdScope = LDAP_SCOPE_SUBTREE; + else if (strequal(pszParmValue, "base")) + Globals.NssPasswdScope = LDAP_SCOPE_BASE; + else if (strequal(pszParmValue, "one")) + Globals.NssPasswdScope = LDAP_SCOPE_ONELEVEL; + else +#ifdef LDAP_SCOPE_UNKNOWN + Globals.NssPasswdScope = LDAP_SCOPE_UNKNOWN; +#else + #ifdef LDAP_SCOPE_DEFAULT + Globals.NssPasswdScope = LDAP_SCOPE_DEFAULT; + #else + Globals.NssPasswdScope = LDAP_SCOPE_BASE; + #endif +#endif + + return True; } /***************************************************************************