From 81d8eefc0d7620d5835b991d28bafe3d4f30eee2 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Mon, 23 Jun 2003 05:10:07 +0000 Subject: * set domain->last_status = NT_STATUS_SERVER_DISABLED on an ads_connect() failure * Fix code to use winbind_rpc methods for trusted mixed mode or NT4 domains ( does no one ever test this? ) * add in LDAP code to get the sequence number for rpc based seqnum update. ( this is needed if the DC is upgraded and samba is not reconfigured to use security = ads; it's not pretty but it works (from app_head) ) * fix bug that caused us to enumerate domain local groups in domains other than our own (This used to be commit 14f2cd139a22454571cea8475d3b7c5c2787d378) --- source3/lib/username.c | 13 +-- source3/nsswitch/winbindd_ads.c | 77 ++++++++++++++--- source3/nsswitch/winbindd_cache.c | 20 +++-- source3/nsswitch/winbindd_group.c | 24 +++-- source3/nsswitch/winbindd_rpc.c | 178 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 276 insertions(+), 36 deletions(-) diff --git a/source3/lib/username.c b/source3/lib/username.c index 8130b93c3f..0005372a8f 100644 --- a/source3/lib/username.c +++ b/source3/lib/username.c @@ -589,13 +589,14 @@ BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_gr fstrcpy(domain, *list); domain[PTR_DIFF(p, *list)] = 0; - /* Check to see if name is a Windows group; Win2k native mode DCs - will return domain local groups; while NT4 or mixed mode 2k DCs - will not */ + /* Check to see if name is a Windows group; Win2k native mode DCs + will return domain local groups; while NT4 or mixed mode 2k DCs + will not */ - if ( winbind_lookup_name(NULL, *list, &g_sid, &name_type) - && ( name_type==SID_NAME_DOM_GRP || name_type==SID_NAME_ALIAS ) ) - { + if ( winbind_lookup_name(NULL, *list, &g_sid, &name_type) + && ( name_type==SID_NAME_DOM_GRP || + (strequal(lp_workgroup(), domain) && name_type==SID_NAME_ALIAS) ) ) + { /* Check if user name is in the Windows group */ ret = user_in_winbind_group_list(user, *list, &winbind_answered); diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c index 04eb65637c..b54392a92e 100644 --- a/source3/nsswitch/winbindd_ads.c +++ b/source3/nsswitch/winbindd_ads.c @@ -116,7 +116,11 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, DEBUG(3,("ads: query_user_list\n")); ads = ads_cached_connection(domain); - if (!ads) goto done; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs); if (!ADS_ERR_OK(rc)) { @@ -213,7 +217,11 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, DEBUG(3,("ads: enum_dom_groups\n")); ads = ads_cached_connection(domain); - if (!ads) goto done; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs); if (!ADS_ERR_OK(rc)) { @@ -236,7 +244,9 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, i = 0; group_flags = ATYPE_GLOBAL_GROUP; - if ( domain->native_mode ) + + /* only grab domain local groups for our domain */ + if ( domain->native_mode && strequal(lp_realm(), domain->alt_name) ) group_flags |= ATYPE_LOCAL_GROUP; for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { @@ -286,7 +296,7 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain, { /* * This is a stub function only as we returned the domain - * ocal groups in enum_dom_groups() if the domain->native field + * local groups in enum_dom_groups() if the domain->native field * was true. This is a simple performance optimization when * using LDAP. * @@ -311,8 +321,11 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain, DEBUG(3,("ads: name_to_sid\n")); ads = ads_cached_connection(domain); - if (!ads) + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; return NT_STATUS_UNSUCCESSFUL; + } return ads_name_to_sid(ads, name, sid, type); } @@ -326,9 +339,13 @@ static NTSTATUS sid_to_name(struct winbindd_domain *domain, { ADS_STRUCT *ads = NULL; DEBUG(3,("ads: sid_to_name\n")); + ads = ads_cached_connection(domain); - if (!ads) + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; return NT_STATUS_UNSUCCESSFUL; + } return ads_sid_to_name(ads, mem_ctx, sid, name, type); } @@ -408,7 +425,11 @@ static NTSTATUS query_user(struct winbindd_domain *domain, DEBUG(3,("ads: query_user\n")); ads = ads_cached_connection(domain); - if (!ads) goto done; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } sidstr = sid_binstring(sid); asprintf(&exp, "(objectSid=%s)", sidstr); @@ -474,7 +495,11 @@ static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain, DEBUG(3,("ads: lookup_usergroups_alt\n")); ads = ads_cached_connection(domain); - if (!ads) goto done; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } /* buggy server, no tokenGroups. Instead lookup what groups this user is a member of by DN search on member*/ @@ -562,7 +587,11 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, *num_groups = 0; ads = ads_cached_connection(domain); - if (!ads) goto done; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } if (!(sidstr = sid_binstring(sid))) { DEBUG(1,("lookup_usergroups(sid=%s) sid_binstring returned NULL\n", sid_to_string(sid_string, sid))); @@ -669,7 +698,11 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, *num_names = 0; ads = ads_cached_connection(domain); - if (!ads) goto done; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + goto done; + } sidstr = sid_binstring(group_sid); @@ -745,7 +778,11 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) *seq = DOM_SEQUENCE_NONE; ads = ads_cached_connection(domain); - if (!ads) return NT_STATUS_UNSUCCESSFUL; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + return NT_STATUS_UNSUCCESSFUL; + } rc = ads_USN(ads, seq); if (!ADS_ERR_OK(rc)) { @@ -773,7 +810,11 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, *names = NULL; ads = ads_cached_connection(domain); - if (!ads) return NT_STATUS_UNSUCCESSFUL; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + return NT_STATUS_UNSUCCESSFUL; + } rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids); @@ -789,7 +830,11 @@ static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid) DEBUG(3,("ads: domain_sid\n")); ads = ads_cached_connection(domain); - if (!ads) return NT_STATUS_UNSUCCESSFUL; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + return NT_STATUS_UNSUCCESSFUL; + } rc = ads_domain_sid(ads, sid); @@ -815,7 +860,11 @@ static NTSTATUS alternate_name(struct winbindd_domain *domain) DEBUG(3,("ads: alternate_name\n")); ads = ads_cached_connection(domain); - if (!ads) return NT_STATUS_UNSUCCESSFUL; + + if (!ads) { + domain->last_status = NT_STATUS_SERVER_DISABLED; + return NT_STATUS_UNSUCCESSFUL; + } if (!(ctx = talloc_init("alternate_name"))) { return NT_STATUS_NO_MEMORY; diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index 5464e765d1..9f7d3686a5 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -105,9 +105,19 @@ static struct winbind_cache *get_cache(struct winbindd_domain *domain) #ifdef HAVE_ADS case SEC_ADS: { extern struct winbindd_methods ads_methods; - domain->backend = &ads_methods; - break; - } + /* always obey the lp_security parameter for our domain */ + if ( strequal(lp_realm(), domain->alt_name) ) { + domain->backend = &ads_methods; + break; + } + + if ( domain->native_mode ) { + domain->backend = &ads_methods; + break; + } + + /* fall through */ + } #endif default: domain->backend = &msrpc_methods; @@ -990,10 +1000,6 @@ static NTSTATUS sid_to_name(struct winbindd_domain *domain, do_query: *name = NULL; - if (wcache_server_down(domain)) { - return NT_STATUS_SERVER_DISABLED; - } - /* If the seq number check indicated that there is a problem * with this DC, then return that status... except for * access_denied. This is special because the dc may be in diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c index 41f594fe61..6749f55bff 100644 --- a/source3/nsswitch/winbindd_group.c +++ b/source3/nsswitch/winbindd_group.c @@ -75,7 +75,9 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, *num_gr_mem = 0; - if ((group_name_type!=SID_NAME_DOM_GRP) && (group_name_type!=SID_NAME_ALIAS)) { + if ( !((group_name_type==SID_NAME_DOM_GRP) || + ((group_name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) ) + { DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", sid_to_string(sid_string, group_sid), domain->name, group_name_type)); @@ -228,7 +230,9 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) return WINBINDD_ERROR; } - if ((name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_DOM_GRP)) { + if ( !((name_type==SID_NAME_DOM_GRP) || + ((name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) ) + { DEBUG(1, ("name '%s' is not a local or domain group: %d\n", name_group, name_type)); return WINBINDD_ERROR; @@ -292,8 +296,9 @@ enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state) return WINBINDD_ERROR; } - if (!((name_type == SID_NAME_ALIAS) || - (name_type == SID_NAME_DOM_GRP))) { + if ( !((name_type==SID_NAME_DOM_GRP) || + ((name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) ) + { DEBUG(1, ("name '%s' is not a local or domain group: %d\n", group_name, name_type)); return WINBINDD_ERROR; @@ -451,10 +456,10 @@ static BOOL get_sam_group_entries(struct getent_state *ent) ent->num_sam_entries = num_entries; - /* get the domain local groups if we are a member of a native win2k domain */ + /* get the domain local groups if we are a member of a native win2k domain + and are not using LDAP to get the groups */ - if ( domain->native_mode - && domain->methods->enum_local_groups + if ( lp_security != SEC_ADS && domain->native_mode && strequal(lp_workgroup(), domain->name) ) { DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n")); @@ -891,8 +896,9 @@ enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) /* Check it is a domain group or an alias (domain local group) in a win2k native mode domain. */ - if ( !(sid_type == SID_NAME_DOM_GRP || sid_type == SID_NAME_ALIAS) ) { - + if ( !((sid_type==SID_NAME_DOM_GRP) || + ((sid_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) ) + { DEBUG(10, ("winbindd_getgroups: sid type %d " "for %s is not a domain group\n", sid_type, diff --git a/source3/nsswitch/winbindd_rpc.c b/source3/nsswitch/winbindd_rpc.c index 5710a323e8..7d6055006d 100644 --- a/source3/nsswitch/winbindd_rpc.c +++ b/source3/nsswitch/winbindd_rpc.c @@ -681,6 +681,168 @@ done: return result; } +#ifdef HAVE_LDAP + +#include + +static SIG_ATOMIC_T gotalarm; + +/*************************************************************** + Signal function to tell us we timed out. +****************************************************************/ + +static void gotalarm_sig(void) +{ + gotalarm = 1; +} + +static LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to) +{ + LDAP *ldp = NULL; + + /* Setup timeout */ + gotalarm = 0; + CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); + alarm(to); + /* End setup timeout. */ + + ldp = ldap_open(server, port); + + /* Teardown timeout. */ + CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); + alarm(0); + + return ldp; +} + +static int get_ldap_seq(const char *server, uint32 *seq) +{ + int ret = -1; + struct timeval to; + char *attrs[] = {"highestCommittedUSN", NULL}; + LDAPMessage *res = NULL; + char **values = NULL; + LDAP *ldp = NULL; + + *seq = DOM_SEQUENCE_NONE; + + /* + * 10 second timeout on open. This is needed as the search timeout + * doesn't seem to apply to doing an open as well. JRA. + */ + + if ((ldp = ldap_open_with_timeout(server, LDAP_PORT, 10)) == NULL) + return -1; + +#if 0 + /* As per tridge comment this doesn't seem to be needed. JRA */ + if ((err = ldap_simple_bind_s(ldp, NULL, NULL)) != 0) + goto done; +#endif + + /* Timeout if no response within 20 seconds. */ + to.tv_sec = 10; + to.tv_usec = 0; + + if (ldap_search_st(ldp, "", LDAP_SCOPE_BASE, "(objectclass=*)", &attrs[0], 0, &to, &res)) + goto done; + + if (ldap_count_entries(ldp, res) != 1) + goto done; + + values = ldap_get_values(ldp, res, "highestCommittedUSN"); + if (!values || !values[0]) + goto done; + + *seq = atoi(values[0]); + ret = 0; + + done: + + if (values) + ldap_value_free(values); + if (res) + ldap_msgfree(res); + if (ldp) + ldap_unbind(ldp); + return ret; +} + +/********************************************************************** + Get the sequence number for a Windows AD native mode domain using + LDAP queries +**********************************************************************/ + +int get_ldap_sequence_number( const char* domain, uint32 *seq) +{ + int ret = -1; + int i; + struct in_addr *ip_list = NULL; + int count; + BOOL list_ordered; + + if ( !get_dc_list( domain, &ip_list, &count, &list_ordered ) ) { + DEBUG(3, ("Could not look up dc's for domain %s\n", domain)); + return False; + } + + if ( !list_ordered ) + { + /* + * Pick a nice close server. Look for DC on local net + * (assuming we don't have a list of preferred DC's) + */ + + for (i = 0; i < count; i++) { + if (is_zero_ip(ip_list[i])) + continue; + + if ( !is_local_net(ip_list[i]) ) + continue; + + if ( (ret = get_ldap_seq( inet_ntoa(ip_list[i]), seq)) == 0 ) + goto done; + + zero_ip(&ip_list[i]); + } + + + /* + * Secondly try and contact a random PDC/BDC. + */ + + i = (sys_random() % count); + + if ( !is_zero_ip(ip_list[i]) ) { + if ( (ret = get_ldap_seq( inet_ntoa(ip_list[i]), seq)) == 0 ) + goto done; + } + zero_ip(&ip_list[i]); /* Tried and failed. */ + } + + /* Finally return first DC that we can contact */ + + for (i = 0; i < count; i++) { + if (is_zero_ip(ip_list[i])) + continue; + + if ( (ret = get_ldap_seq( inet_ntoa(ip_list[i]), seq)) == 0 ) + goto done; + } + +done: + if ( ret == 0 ) { + DEBUG(3, ("get_ldap_sequence_number: Retrieved sequence number for Domain (%s) from DC (%s)\n", + domain, inet_ntoa(ip_list[i]))); + } + + SAFE_FREE(ip_list); + + return ret; +} + +#endif /* HAVE_LDAP */ + /* find the sequence number for a domain */ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) { @@ -704,6 +866,22 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) retry = 0; do { +#ifdef HAVE_LDAP + if ( domain->native_mode ) + { + DEBUG(8,("using get_ldap_seq() to retrieve the sequence number\n")); + + if ( get_ldap_sequence_number( domain->name, seq ) == 0 ) { + result = NT_STATUS_OK; + DEBUG(10,("domain_sequence_number: LDAP for domain %s is %u\n", + domain->name, *seq)); + goto done; + } + + DEBUG(10,("domain_sequence_number: failed to get LDAP sequence number for domain %s\n", + domain->name )); + } +#endif /* HAVE_LDAP */ /* Get sam handle */ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain->name, &hnd))) goto done; -- cgit