From ab9ff0fa73f33c064a39859e39fa79af77cc088f Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 5 Aug 2002 02:47:46 +0000 Subject: This fixes a number of ADS problems, particularly with netbiosless setups. - split up the ads structure into logical pieces. This makes it much easier to keep things like the authentication realm and the server realm separate (they can be different). - allow ads callers to specify that no sasl bind should be performed (used by "net ads info" for example) - fix an error with handing ADS_ERROR_SYSTEM() when errno is 0 - completely rewrote the code for finding the LDAP server. Now try DNS methods first, and try all DNS servers returned from the SRV DNS query, sorted by closeness to our interfaces (using the same sort code as we use in replies from WINS servers). This allows us to cope with ADS DCs that are down, and ensures we don't pick one that is on the other side of the country unless absolutely necessary. - recognise dnsRecords as binary when displaying them - cope with the realm not being configured in smb.conf (work it out from the LDAP server) - look at the trustDirection when looking up trusted domains and don't include trusts that trust our domains but we don't trust theirs. - use LDAP to query the alternate (netbios) name for a realm, and make sure that both and long and short forms of the name are accepted by winbindd. Use the short form by default for listing users/groups. - rescan the list of trusted domains every 5 minutes in case new trust relationships are added while winbindd is running - include transient trust relationships (ie. C trusts B, B trusts A, so C trusts A) in winbindd. - don't do a gratuituous node status lookup when finding an ADS DC (we don't need it and it could fail) - remove unused sid_to_distinguished_name function - make sure we find the allternate name of our primary domain when operating with a netbiosless ADS DC (using LDAP to do the lookup) - fixed the rpc trusted domain enumeration to support up to approx 2000 trusted domains (the old limit was 3) - use the IP for the remote_machine (%m) macro when the client doesn't supply us with a name via a netbios session request (eg. port 445) - if the client uses SPNEGO then use the machine name from the SPNEGO auth packet for remote_machine (%m) macro - add new 'net ads workgroup' command to find the netbios workgroup name for a realm (This used to be commit e358d7b24c86a46d8c361b9e32a25d4f71a6dc00) --- source3/nsswitch/winbindd.h | 8 ++- source3/nsswitch/winbindd_ads.c | 119 ++++++++++++++++---------------------- source3/nsswitch/winbindd_cache.c | 15 ++++- source3/nsswitch/winbindd_cm.c | 22 ++++--- source3/nsswitch/winbindd_rpc.c | 15 ++++- source3/nsswitch/winbindd_util.c | 118 ++++++++++++++++++++++++------------- 6 files changed, 172 insertions(+), 125 deletions(-) (limited to 'source3/nsswitch') diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h index 11d399be49..dd92ecefe6 100644 --- a/source3/nsswitch/winbindd.h +++ b/source3/nsswitch/winbindd.h @@ -88,7 +88,7 @@ typedef struct { struct winbindd_domain { fstring name; /* Domain name */ - fstring full_name; /* full Domain name (realm) */ + fstring alt_name; /* alt Domain name (if any) */ DOM_SID sid; /* SID for this domain */ /* Lookup methods for this domain (LDAP or RPC) */ @@ -170,11 +170,15 @@ struct winbindd_methods { TALLOC_CTX *mem_ctx, uint32 *num_domains, char ***names, + char ***alt_names, DOM_SID **dom_sids); /* find the domain sid */ NTSTATUS (*domain_sid)(struct winbindd_domain *domain, DOM_SID *sid); + + /* setup the list of alternate names for the domain, if any */ + NTSTATUS (*alternate_name)(struct winbindd_domain *domain); }; /* Used to glue a policy handle and cli_state together */ @@ -190,6 +194,8 @@ typedef struct { #include "rpc_client.h" #define WINBINDD_ESTABLISH_LOOP 30 +#define WINBINDD_RESCAN_FREQ 300 + #define DOM_SEQUENCE_NONE ((uint32)-1) /* SETENV */ diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c index b61348adfe..b0b70178a4 100644 --- a/source3/nsswitch/winbindd_ads.c +++ b/source3/nsswitch/winbindd_ads.c @@ -61,8 +61,8 @@ ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope if (*res) ads_msgfree(ads, *res); *res = NULL; - DEBUG(3,("Reopening ads connection to %s after error %s\n", - ads->ldap_server, ads_errstr(status))); + DEBUG(3,("Reopening ads connection to realm '%s' after error %s\n", + ads->config.realm, ads_errstr(status))); if (ads->ld) { ldap_unbind(ads->ld); } @@ -87,7 +87,7 @@ ADS_STATUS ads_search_retry(ADS_STRUCT *ads, void **res, const char *exp, const char **attrs) { - return ads_do_search_retry(ads, ads->bind_path, LDAP_SCOPE_SUBTREE, + return ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, exp, attrs, res); } @@ -108,8 +108,6 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) ADS_STRUCT *ads; ADS_STATUS status; char *ccache; - struct in_addr server_ip; - char *sname; if (domain->private) { return (ADS_STRUCT *)domain->private; @@ -120,30 +118,23 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) SETENV("KRB5CCNAME", ccache, 1); unlink(ccache); - if (resolve_name(domain->name, &server_ip, 0x1b)) { - sname = inet_ntoa(server_ip); - } else if (resolve_name(domain->name, &server_ip, 0x1c)) { - sname = inet_ntoa(server_ip); - } else { - if (strcasecmp(domain->name, lp_workgroup()) != 0) { - DEBUG(1,("can't find domain controller for %s\n", domain->name)); - return NULL; - } - sname = NULL; - } - - ads = ads_init(primary_realm, domain->name, NULL, NULL, NULL); + ads = ads_init(domain->alt_name, domain->name, NULL); if (!ads) { DEBUG(1,("ads_init for domain %s failed\n", domain->name)); return NULL; } /* the machine acct password might have change - fetch it every time */ - SAFE_FREE(ads->password); - ads->password = secrets_fetch_machine_password(); + SAFE_FREE(ads->auth.password); + ads->auth.password = secrets_fetch_machine_password(); + + if (primary_realm) { + SAFE_FREE(ads->auth.realm); + ads->auth.realm = strdup(primary_realm); + } status = ads_connect(ads); - if (!ADS_ERR_OK(status) || !ads->realm) { + if (!ADS_ERR_OK(status) || !ads->config.realm) { extern struct winbindd_methods msrpc_methods; DEBUG(1,("ads_connect for domain %s failed: %s\n", domain->name, ads_errstr(status))); @@ -161,11 +152,9 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) /* remember our primary realm for trusted domain support */ if (!primary_realm) { - primary_realm = strdup(ads->realm); + primary_realm = strdup(ads->config.realm); } - fstrcpy(domain->full_name, ads->server_realm); - domain->private = (void *)ads; return ads; } @@ -405,7 +394,7 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain, /* accept either the win2000 or the pre-win2000 username */ asprintf(&exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))", - name, name, ads->realm); + name, name, ads->config.realm); rc = ads_search_retry(ads, &res, exp, attrs); free(exp); if (!ADS_ERR_OK(rc)) { @@ -535,49 +524,6 @@ failed: return False; } - -/* convert a sid to a distnguished name */ -static NTSTATUS sid_to_distinguished_name(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - DOM_SID *sid, - char **dn) -{ - ADS_STRUCT *ads = NULL; - const char *attrs[] = {"distinguishedName", NULL}; - ADS_STATUS rc; - void *msg = NULL; - char *exp; - char *sidstr; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - - DEBUG(3,("ads: sid_to_distinguished_name\n")); - - ads = ads_cached_connection(domain); - if (!ads) goto done; - - sidstr = sid_binstring(sid); - asprintf(&exp, "(objectSid=%s)", sidstr); - rc = ads_search_retry(ads, &msg, exp, attrs); - free(exp); - free(sidstr); - if (!ADS_ERR_OK(rc)) { - DEBUG(1,("sid_to_distinguished_name ads_search: %s\n", ads_errstr(rc))); - goto done; - } - - *dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName"); - - status = NT_STATUS_OK; - - DEBUG(3,("ads sid_to_distinguished_name mapped %s\n", *dn)); - -done: - if (msg) ads_msgfree(ads, msg); - - return status; -} - - /* Lookup user information from a rid */ static NTSTATUS query_user(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, @@ -831,6 +777,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_domains, char ***names, + char ***alt_names, DOM_SID **dom_sids) { ADS_STRUCT *ads; @@ -842,7 +789,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, ads = ads_cached_connection(domain); if (!ads) return NT_STATUS_UNSUCCESSFUL; - rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, dom_sids); + rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids); return ads_ntstatus(rc); } @@ -867,6 +814,37 @@ static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid) return ads_ntstatus(rc); } + +/* find alternate names list for the domain - for ADS this is the + netbios name */ +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + TALLOC_CTX *ctx; + char *workgroup; + + ads = ads_cached_connection(domain); + if (!ads) return NT_STATUS_UNSUCCESSFUL; + + if (!(ctx = talloc_init_named("alternate_name"))) { + return NT_STATUS_NO_MEMORY; + } + + rc = ads_workgroup_name(ads, ctx, &workgroup); + + if (ADS_ERR_OK(rc)) { + fstrcpy(domain->name, workgroup); + fstrcpy(domain->alt_name, ads->config.realm); + strupper(domain->alt_name); + strupper(domain->name); + } + + talloc_destroy(ctx); + + return ads_ntstatus(rc); +} + /* the ADS backend methods are exposed via this structure */ struct winbindd_methods ads_methods = { True, @@ -879,7 +857,8 @@ struct winbindd_methods ads_methods = { lookup_groupmem, sequence_number, trusted_domains, - domain_sid + domain_sid, + alternate_name }; #endif diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index a607727867..060139af3e 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -873,13 +873,14 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_domains, char ***names, + char ***alt_names, DOM_SID **dom_sids) { struct winbind_cache *cache = get_cache(domain); /* we don't cache this call */ return cache->backend->trusted_domains(domain, mem_ctx, num_domains, - names, dom_sids); + names, alt_names, dom_sids); } /* find the domain sid */ @@ -891,6 +892,15 @@ static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid) return cache->backend->domain_sid(domain, sid); } +/* find the alternate names for the domain, if any */ +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + struct winbind_cache *cache = get_cache(domain); + + /* we don't cache this call */ + return cache->backend->alternate_name(domain); +} + /* the ADS backend methods are exposed via this structure */ struct winbindd_methods cache_methods = { True, @@ -903,5 +913,6 @@ struct winbindd_methods cache_methods = { lookup_groupmem, sequence_number, trusted_domains, - domain_sid + domain_sid, + alternate_name }; diff --git a/source3/nsswitch/winbindd_cm.c b/source3/nsswitch/winbindd_cm.c index 3175860a79..be28984791 100644 --- a/source3/nsswitch/winbindd_cm.c +++ b/source3/nsswitch/winbindd_cm.c @@ -97,12 +97,15 @@ struct get_dc_name_cache { static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name) { ADS_STRUCT *ads; - ads = ads_init_simple(); + ads = ads_init(domain, domain, NULL); if (!ads) { return False; } - DEBUG(4,("cm_ads_find_dc: realm=%s\n", ads->realm)); + /* we don't need to bind, just connect */ + ads->auth.no_bind = 1; + + DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain)); #ifdef HAVE_ADS /* a full ads_connect() is actually overkill, as we don't srictly need @@ -111,15 +114,15 @@ static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring sr ads_connect(ads); #endif - fstrcpy(srv_name, ads->ldap_server_name); + if (!ads->config.realm) { + return False; + } + + fstrcpy(srv_name, ads->config.ldap_server_name); strupper(srv_name); *dc_ip = ads->ldap_ip; ads_destroy(&ads); - if (!*srv_name || is_zero_ip(*dc_ip)) { - return False; - } - DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n", srv_name, inet_ntoa(*dc_ip))); @@ -247,9 +250,12 @@ static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr zero_ip(&dc_ip); + ret = False; if (lp_security() == SEC_ADS) { ret = cm_ads_find_dc(domain, &dc_ip, srv_name); - } else { + } + if (!ret) { + /* fall back on rpc methods if the ADS methods fail */ ret = cm_rpc_find_dc(domain, &dc_ip, srv_name); } diff --git a/source3/nsswitch/winbindd_rpc.c b/source3/nsswitch/winbindd_rpc.c index 2bb0e8c49f..5ec34f663d 100644 --- a/source3/nsswitch/winbindd_rpc.c +++ b/source3/nsswitch/winbindd_rpc.c @@ -575,22 +575,23 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_domains, char ***names, + char ***alt_names, DOM_SID **dom_sids) { CLI_POLICY_HND *hnd; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; uint32 enum_ctx = 0; - uint32 pref_num_domains = 5; DEBUG(3,("rpc: trusted_domains\n")); *num_domains = 0; + *alt_names = NULL; if (!(hnd = cm_get_lsa_handle(lp_workgroup()))) goto done; result = cli_lsa_enum_trust_dom(hnd->cli, mem_ctx, - &hnd->pol, &enum_ctx, &pref_num_domains, + &hnd->pol, &enum_ctx, num_domains, names, dom_sids); done: return result; @@ -621,6 +622,13 @@ done: return status; } +/* find alternate names list for the domain - none for rpc */ +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + return NT_STATUS_OK; +} + + /* the rpc backend methods are exposed via this structure */ struct winbindd_methods msrpc_methods = { False, @@ -633,5 +641,6 @@ struct winbindd_methods msrpc_methods = { lookup_groupmem, sequence_number, trusted_domains, - domain_sid + domain_sid, + alternate_name }; diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c index b927380af8..daa3abb340 100644 --- a/source3/nsswitch/winbindd_util.c +++ b/source3/nsswitch/winbindd_util.c @@ -74,19 +74,17 @@ void free_domain_list(void) } /* Add a trusted domain to our list of domains */ - -static struct winbindd_domain *add_trusted_domain(char *domain_name, - struct winbindd_methods *methods) +static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name, + struct winbindd_methods *methods, + DOM_SID *sid) { struct winbindd_domain *domain; /* We can't call domain_list() as this function is called from init_domain_list() and we'll get stuck in a loop. */ - for (domain = _domain_list; domain; domain = domain->next) { - if (strcmp(domain_name, domain->name) == 0) { - DEBUG(3, ("domain %s already in domain list\n", - domain_name)); + if (strcmp(domain_name, domain->name) == 0 || + strcmp(domain_name, domain->alt_name) == 0) { return domain; } } @@ -101,40 +99,95 @@ static struct winbindd_domain *add_trusted_domain(char *domain_name, ZERO_STRUCTP(domain); + /* prioritise the short name */ + if (strchr_m(domain_name, '.') && alt_name && *alt_name) { + fstrcpy(domain->name, alt_name); + fstrcpy(domain->alt_name, domain_name); + } else { fstrcpy(domain->name, domain_name); + if (alt_name) { + fstrcpy(domain->alt_name, alt_name); + } + } + domain->methods = methods; domain->sequence_number = DOM_SEQUENCE_NONE; domain->last_seq_check = 0; + if (sid) { + sid_copy(&domain->sid, sid); + } /* Link to domain list */ - DLIST_ADD(_domain_list, domain); + DEBUG(1,("Added domain %s %s %s\n", + domain->name, domain->alt_name, + sid?sid_string_static(&domain->sid):"")); + return domain; } -/* Look up global info for the winbind daemon */ +/* + rescan our domains looking for new trusted domains + */ +void rescan_trusted_domains(void) +{ + struct winbindd_domain *domain; + TALLOC_CTX *mem_ctx; + static time_t last_scan; + time_t t = time(NULL); + + /* ony rescan every few minutes */ + if ((unsigned)(t - last_scan) < WINBINDD_RESCAN_FREQ) { + return; + } + last_scan = time(NULL); + + DEBUG(1, ("scanning trusted domain list\n")); + + if (!(mem_ctx = talloc_init_named("init_domain_list"))) + return; + + for (domain = _domain_list; domain; domain = domain->next) { + NTSTATUS result; + char **names; + char **alt_names; + int num_domains = 0; + DOM_SID *dom_sids; + int i; + + result = domain->methods->trusted_domains(domain, mem_ctx, &num_domains, + &names, &alt_names, &dom_sids); + if (!NT_STATUS_IS_OK(result)) { + continue; + } + + /* Add each domain to the trusted domain list. Each domain inherits + the access methods of its parent */ + for(i = 0; i < num_domains; i++) { + DEBUG(10,("Found domain %s\n", names[i])); + add_trusted_domain(names[i], + alt_names?alt_names[i]:NULL, + domain->methods, &dom_sids[i]); + } + } + + talloc_destroy(mem_ctx); +} + +/* Look up global info for the winbind daemon */ BOOL init_domain_list(void) { NTSTATUS result; - TALLOC_CTX *mem_ctx; extern struct winbindd_methods cache_methods; struct winbindd_domain *domain; - DOM_SID *dom_sids; - char **names; - uint32 num_domains = 0; - - if (!(mem_ctx = talloc_init_named("init_domain_list"))) - return False; /* Free existing list */ - free_domain_list(); /* Add ourselves as the first entry */ - - domain = add_trusted_domain(lp_workgroup(), &cache_methods); + domain = add_trusted_domain(lp_workgroup(), NULL, &cache_methods, NULL); /* Now we *must* get the domain sid for our primary domain. Go into a holding pattern until that is available */ @@ -147,29 +200,12 @@ BOOL init_domain_list(void) result = cache_methods.domain_sid(domain, &domain->sid); } - DEBUG(1,("Added domain %s (%s)\n", - domain->name, - sid_string_static(&domain->sid))); - - DEBUG(1, ("getting trusted domain list\n")); + /* get any alternate name for the primary domain */ + cache_methods.alternate_name(domain); - result = cache_methods.trusted_domains(domain, mem_ctx, &num_domains, - &names, &dom_sids); + /* do an initial scan for trusted domains */ + rescan_trusted_domains(); - /* Add each domain to the trusted domain list */ - if (NT_STATUS_IS_OK(result)) { - int i; - for(i = 0; i < num_domains; i++) { - domain = add_trusted_domain(names[i], &cache_methods); - if (!domain) continue; - sid_copy(&domain->sid, &dom_sids[i]); - DEBUG(1,("Added domain %s (%s)\n", - domain->name, - sid_string_static(&domain->sid))); - } - } - - talloc_destroy(mem_ctx); return True; } @@ -184,7 +220,7 @@ struct winbindd_domain *find_domain_from_name(const char *domain_name) for (domain = domain_list(); domain != NULL; domain = domain->next) { if (strequal(domain_name, domain->name) || - strequal(domain_name, domain->full_name)) + (domain->alt_name[0] && strequal(domain_name, domain->alt_name))) return domain; } -- cgit