From dee2bce2af6aab8308dcef4109cc5248cfba5ef5 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Sun, 6 May 2007 20:16:12 +0000 Subject: [PATCH] r22710: Support one-way trusts. * Rely on the fact that name2sid will work for any name in a trusted domain will work against our primary domain (even in the absense of an incoming trust path) * Only logons will reliably work and the idmap backend is responsible for being able to manage id's without contacting the trusted domain * "getent passwd" and "getent group" for trusted users and groups will work but we cannot get the group membership of a user in any fashion without the user first logging on (via NTLM or krb5) and the netsamlogon_cache being updated. --- source/nsswitch/winbindd_ads.c | 113 ++++++++++++++++++++++++++++++++++++--- source/nsswitch/winbindd_async.c | 6 +++ source/nsswitch/winbindd_cache.c | 14 +++-- source/nsswitch/winbindd_dual.c | 8 +++ source/nsswitch/winbindd_pam.c | 6 +++ source/nsswitch/winbindd_rpc.c | 83 +++++++++++++++++++++++++--- source/nsswitch/winbindd_user.c | 37 ++++++++++++- source/nsswitch/winbindd_util.c | 43 +++++++++++++-- 8 files changed, 290 insertions(+), 20 deletions(-) diff --git a/source/nsswitch/winbindd_ads.c b/source/nsswitch/winbindd_ads.c index 4b6bca78344..1f9748c3731 100644 --- a/source/nsswitch/winbindd_ads.c +++ b/source/nsswitch/winbindd_ads.c @@ -168,6 +168,12 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, DEBUG(3,("ads: query_user_list\n")); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("query_user_list: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + ads = ads_cached_connection(domain); if (!ads) { @@ -213,7 +219,7 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, name = ads_pull_username(ads, mem_ctx, msg); if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) { - status = nss_get_info( domain->name, &user_sid, mem_ctx, + status = nss_get_info_cached( domain, &user_sid, mem_ctx, ads, msg, &homedir, &shell, &gecos, &primary_gid ); } @@ -274,6 +280,12 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, DEBUG(3,("ads: enum_dom_groups\n")); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + /* only grab domain local groups for our domain */ if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) { enum_dom_local_groups = True; @@ -449,9 +461,64 @@ static NTSTATUS query_user(struct winbindd_domain *domain, char *sidstr; uint32 group_rid; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + uint32 user_rid; + NET_USER_INFO_3 *user; DEBUG(3,("ads: query_user\n")); + info->homedir = NULL; + info->shell = NULL; + info->primary_gid = (gid_t)-1; + + /* try netsamlogon cache first */ + + if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL ) + { + + DEBUG(5,("query_user: Cache lookup succeeded for %s\n", + sid_string_static(sid))); + + sid_compose(&info->user_sid, &domain->sid, user_rid); + sid_compose(&info->group_sid, &domain->sid, user->group_rid); + + info->acct_name = unistr2_tdup(mem_ctx, &user->uni_user_name); + info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name); + + nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, + &info->homedir, &info->shell, &info->full_name, + &info->primary_gid ); + + SAFE_FREE(user); + + return NT_STATUS_OK; + } + + if ( !winbindd_can_contact_domain(domain)) { + DEBUG(8,("query_user: No incoming trust from domain %s\n", + domain->name)); + + /* We still need to generate some basic information + about the user even if we cannot contact the + domain. Most of this stuff we can deduce. */ + + sid_copy( &info->user_sid, sid ); + + /* Assume "Domain Users" for the primary group */ + + sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS ); + + /* Try to fill in what the nss_info backend can do */ + + nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, + &info->homedir, &info->shell, &info->full_name, + &info->primary_gid ); + + status = NT_STATUS_OK; + goto done; + } + + /* no cache...do the query */ + if ( (ads = ads_cached_connection(domain)) == NULL ) { domain->last_status = NT_STATUS_SERVER_DISABLED; goto done; @@ -477,9 +544,9 @@ static NTSTATUS query_user(struct winbindd_domain *domain, info->acct_name = ads_pull_username(ads, mem_ctx, msg); - info->primary_gid = (gid_t)-1; - nss_get_info( domain->name, sid, mem_ctx, ads, msg, - &info->homedir, &info->shell, &info->full_name, &info->primary_gid ); + nss_get_info_cached( domain, sid, mem_ctx, ads, msg, + &info->homedir, &info->shell, &info->full_name, + &info->primary_gid ); if (info->full_name == NULL) { info->full_name = ads_pull_string(ads, mem_ctx, msg, "name"); @@ -525,6 +592,12 @@ static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain, 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) { @@ -620,6 +693,12 @@ static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain, 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) { @@ -726,6 +805,15 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, 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) { @@ -866,6 +954,12 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, *num_names = 0; + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + ads = ads_cached_connection(domain); if (!ads) { @@ -982,6 +1076,13 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name)); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("sequence: No incoming trust for domain %s\n", + domain->name)); + *seq = time(NULL); + return NT_STATUS_OK; + } + *seq = DOM_SEQUENCE_NONE; ads = ads_cached_connection(domain); @@ -1024,7 +1125,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, struct rpc_pipe_client *cli; uint32 fr_flags = (DS_DOMAIN_IN_FOREST | DS_DOMAIN_TREE_ROOT); int ret_count; - + DEBUG(3,("ads: trusted_domains\n")); *num_domains = 0; @@ -1063,7 +1164,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, } if ( NT_STATUS_IS_OK(result) && count) { - + /* Allocate memory for trusted domain names and sids */ if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) { diff --git a/source/nsswitch/winbindd_async.c b/source/nsswitch/winbindd_async.c index 1a68005efc8..b674ef6ceec 100644 --- a/source/nsswitch/winbindd_async.c +++ b/source/nsswitch/winbindd_async.c @@ -1494,6 +1494,12 @@ static void query_user_recv(TALLOC_CTX *mem_ctx, BOOL success, return; } + if (response->result != WINBINDD_OK) { + DEBUG(5, ("query_user returned an error\n")); + cont(private_data, False, NULL, NULL, NULL, NULL, -1, -1); + return; + } + cont(private_data, True, response->data.user_info.acct_name, response->data.user_info.full_name, response->data.user_info.homedir, diff --git a/source/nsswitch/winbindd_cache.c b/source/nsswitch/winbindd_cache.c index 7538cddbdff..ceeb4988c21 100644 --- a/source/nsswitch/winbindd_cache.c +++ b/source/nsswitch/winbindd_cache.c @@ -483,9 +483,17 @@ static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force) goto done; /* important! make sure that we know if this is a native - mode domain or not */ + mode domain or not. And that we can contact it. */ + + if ( winbindd_can_contact_domain( domain ) ) { + status = domain->backend->sequence_number(domain, + &domain->sequence_number); + } else { + /* just use the current time */ + status = NT_STATUS_OK; + domain->sequence_number = time(NULL); + } - status = domain->backend->sequence_number(domain, &domain->sequence_number); /* the above call could have set our domain->backend to NULL when * coming from offline to online mode, make sure to reinitialize the @@ -2197,7 +2205,7 @@ void wcache_invalidate_cache(void) } } -static BOOL init_wcache(void) +BOOL init_wcache(void) { if (wcache == NULL) { wcache = SMB_XMALLOC_P(struct winbind_cache); diff --git a/source/nsswitch/winbindd_dual.c b/source/nsswitch/winbindd_dual.c index 8d475e6c9f9..aa6b356679e 100644 --- a/source/nsswitch/winbindd_dual.c +++ b/source/nsswitch/winbindd_dual.c @@ -653,6 +653,14 @@ static void account_lockout_policy_handler(struct event_context *ctx, TALLOC_FREE(child->lockout_policy_event); } + if ( !winbindd_can_contact_domain( child->domain ) ) { + DEBUG(10,("account_lockout_policy_handler: Removing myself since I " + "do not have an incoming trust to domain %s\n", + child->domain->name)); + + return; + } + methods = child->domain->methods; mem_ctx = talloc_init("account_lockout_policy_handler ctx"); diff --git a/source/nsswitch/winbindd_pam.c b/source/nsswitch/winbindd_pam.c index 97c1ac4b9c3..eb2da870c35 100644 --- a/source/nsswitch/winbindd_pam.c +++ b/source/nsswitch/winbindd_pam.c @@ -312,6 +312,12 @@ static NTSTATUS fillup_password_policy(struct winbindd_domain *domain, NTSTATUS status = NT_STATUS_UNSUCCESSFUL; SAM_UNK_INFO_1 password_policy; + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(5,("fillup_password_policy: No inbound trust to " + "contact domain %s\n", domain->name)); + return NT_STATUS_NOT_SUPPORTED; + } + methods = domain->methods; status = methods->password_policy(domain, state->mem_ctx, &password_policy); diff --git a/source/nsswitch/winbindd_rpc.c b/source/nsswitch/winbindd_rpc.c index a66b05b7917..0fafff196b3 100644 --- a/source/nsswitch/winbindd_rpc.c +++ b/source/nsswitch/winbindd_rpc.c @@ -48,6 +48,12 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, *num_entries = 0; *info = NULL; + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("query_user_list: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) return result; @@ -140,6 +146,12 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, DEBUG(3,("rpc: enum_dom_groups\n")); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("enum_domain_groups: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + status = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(status)) return status; @@ -196,6 +208,12 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain, DEBUG(3,("rpc: enum_local_groups\n")); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("enum_local_groups: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) return result; @@ -305,13 +323,20 @@ NTSTATUS msrpc_sid_to_name(struct winbindd_domain *domain, domain->name )); result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); - if (!NT_STATUS_IS_OK(result)) + if (!NT_STATUS_IS_OK(result)) { + DEBUG(2,("msrpc_sid_to_name: cm_connect_lsa() failed (%s)\n", + nt_errstr(result))); return result; + } + result = rpccli_lsa_lookup_sids(cli, mem_ctx, &lsa_policy, 1, sid, &domains, &names, &types); - if (!NT_STATUS_IS_OK(result)) + if (!NT_STATUS_IS_OK(result)) { + DEBUG(2,("msrpc_sid_to_name: rpccli_lsa_lookup_sids() failed (%s)\n", + nt_errstr(result))); return result; + } *type = (enum lsa_SidType)types[0]; *domain_name = domains[0]; @@ -401,6 +426,10 @@ static NTSTATUS query_user(struct winbindd_domain *domain, if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) return NT_STATUS_UNSUCCESSFUL; + user_info->homedir = NULL; + user_info->shell = NULL; + user_info->primary_gid = (gid_t)-1; + /* try netsamlogon cache first */ if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL ) @@ -418,12 +447,14 @@ static NTSTATUS query_user(struct winbindd_domain *domain, user_info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name); - user_info->homedir = NULL; - user_info->shell = NULL; - user_info->primary_gid = (gid_t)-1; - TALLOC_FREE(user); + + return NT_STATUS_OK; + } + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("query_user: No incoming trust for domain %s\n", + domain->name)); return NT_STATUS_OK; } @@ -496,6 +527,15 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, 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; + } + /* no cache; hit the wire */ result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); @@ -552,6 +592,12 @@ NTSTATUS msrpc_lookup_useraliases(struct winbindd_domain *domain, DEBUG(3,("rpc: lookup_useraliases\n")); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("msrpc_lookup_useraliases: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) return result; @@ -638,6 +684,12 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, DEBUG(10,("rpc: lookup_groupmem %s sid=%s\n", domain->name, sid_to_string(sid_string, group_sid))); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_OK; + } + if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) return NT_STATUS_UNSUCCESSFUL; @@ -822,6 +874,13 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) DEBUG(10,("rpc: fetch sequence_number for %s\n", domain->name)); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("sequence_number: No incoming trust for domain %s\n", + domain->name)); + *seq = time(NULL); + return NT_STATUS_OK; + } + *seq = DOM_SEQUENCE_NONE; if (!(mem_ctx = talloc_init("sequence_number[rpc]"))) @@ -967,6 +1026,12 @@ NTSTATUS msrpc_lockout_policy(struct winbindd_domain *domain, DEBUG(10,("rpc: fetch lockout policy for %s\n", domain->name)); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("msrpc_lockout_policy: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_NOT_SUPPORTED; + } + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) { goto done; @@ -999,6 +1064,12 @@ NTSTATUS msrpc_password_policy(struct winbindd_domain *domain, DEBUG(10,("rpc: fetch password policy for %s\n", domain->name)); + if ( !winbindd_can_contact_domain( domain ) ) { + DEBUG(10,("msrpc_password_policy: No incoming trust for domain %s\n", + domain->name)); + return NT_STATUS_NOT_SUPPORTED; + } + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) { goto done; diff --git a/source/nsswitch/winbindd_user.c b/source/nsswitch/winbindd_user.c index ce677198fff..74dc291e018 100644 --- a/source/nsswitch/winbindd_user.c +++ b/source/nsswitch/winbindd_user.c @@ -239,7 +239,30 @@ static void getpwsid_queryuser_recv(void *private_data, BOOL success, return; } + if ( acct_name && *acct_name ) { fstrcpy( username, acct_name ); + } else { + char *domain_name = NULL; + enum lsa_SidType type; + char *user_name = NULL; + struct winbindd_domain *domain = NULL; + + domain = find_lookup_domain_from_sid(&s->user_sid); + winbindd_lookup_name_by_sid(s->state->mem_ctx, domain, + &s->user_sid, &domain_name, + &user_name, &type ); + + /* If this still fails we ar4e done. Just error out */ + if ( !user_name ) { + DEBUG(5,("Could not obtain a name for SID %s\n", + sid_string_static(&s->user_sid))); + request_error(s->state); + return; + } + + fstrcpy( username, user_name ); + } + strlower_m( username ); s->username = talloc_strdup(s->state->mem_ctx, username); @@ -360,11 +383,14 @@ void winbindd_getpwnam(struct winbindd_cli_state *state) domain = find_domain_from_name(domname); if (domain == NULL) { - DEBUG(7, ("could not find domain entry for domain %s\n", - domname)); + DEBUG(7, ("could not find domain entry for domain %s. " + "Using primary domain\n", domname)); + if ( (domain = find_our_domain()) == NULL ) { + DEBUG(0,("Cannot find my primary domain structure!\n")); request_error(state); return; } + } if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) { DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n", @@ -384,6 +410,7 @@ static void getpwnam_name2sid_recv(void *private_data, BOOL success, { struct winbindd_cli_state *state = (struct winbindd_cli_state *)private_data; + fstring domname, username; if (!success) { DEBUG(5, ("Could not lookup name for user %s\n", @@ -398,6 +425,12 @@ static void getpwnam_name2sid_recv(void *private_data, BOOL success, return; } + if ( parse_domain_user(state->request.data.username, domname, username) ) { + check_domain_trusted( domname, sid ); + } + + + winbindd_getpwsid(state, sid); } diff --git a/source/nsswitch/winbindd_util.c b/source/nsswitch/winbindd_util.c index d58fffd0dbe..231e11ae458 100644 --- a/source/nsswitch/winbindd_util.c +++ b/source/nsswitch/winbindd_util.c @@ -554,11 +554,11 @@ enum winbindd_result init_child_connection(struct winbindd_domain *domain, state->continuation = continuation; state->private_data = private_data; - if (IS_DC || domain->primary) { + if (IS_DC || domain->primary || domain->internal ) { /* The primary domain has to find the DC name itself */ request->cmd = WINBINDD_INIT_CONNECTION; fstrcpy(request->domain_name, domain->name); - request->data.init_conn.is_primary = True; + request->data.init_conn.is_primary = domain->internal ? False : True; fstrcpy(request->data.init_conn.dcname, ""); async_request(mem_ctx, &domain->child, request, response, init_child_recv, state); @@ -572,7 +572,6 @@ enum winbindd_result init_child_connection(struct winbindd_domain *domain, fstrcpy(request->domain_name, domain->name); request_domain = find_our_domain(); - async_domain_request(mem_ctx, request_domain, request, response, init_child_getdc_recv, state); return WINBINDD_PENDING; @@ -728,6 +727,44 @@ BOOL init_domain_list(void) return True; } +void check_domain_trusted( const char *name, const DOM_SID *user_sid ) +{ + struct winbindd_domain *domain; + DOM_SID dom_sid; + uint32 rid; + + domain = find_domain_from_name_noinit( name ); + if ( domain ) + return; + + sid_copy( &dom_sid, user_sid ); + if ( !sid_split_rid( &dom_sid, &rid ) ) + return; + + /* add the newly discovered trusted domain */ + + domain = add_trusted_domain( name, NULL, &cache_methods, + &dom_sid); + + if ( !domain ) + return; + + /* assume this is a trust from a one-way transitive + forest trust */ + + domain->active_directory = True; + domain->domain_flags = DS_DOMAIN_DIRECT_OUTBOUND; + domain->domain_type = DS_DOMAIN_TRUST_TYPE_UPLEVEL; + domain->internal = False; + domain->online = True; + + setup_domain_child(domain, &domain->child, NULL); + + wcache_tdc_add_domain( domain ); + + return; +} + /** * Given a domain name, return the struct winbindd domain info for it * -- 2.11.4.GIT