diff options
author | Gerald Carter <jerry@samba.org> | 2007-05-06 19:17:30 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:21:47 -0500 |
commit | 7cb2a4be354c23b4228d67fe2bba68067ea619cf (patch) | |
tree | 782cd4a9fe6e8deffca6a44e8b0a64aada7f878e | |
parent | 879b84362715e8796f7ef92124007f5673338b37 (diff) | |
download | samba-7cb2a4be354c23b4228d67fe2bba68067ea619cf.tar.gz samba-7cb2a4be354c23b4228d67fe2bba68067ea619cf.tar.bz2 samba-7cb2a4be354c23b4228d67fe2bba68067ea619cf.zip |
r22704: Implement three step method for enumerating domain trusts.
(a) Query our primary domain for trusts
(b) Query all tree roots in our forest
(c) Query all forest roots in trusted forests.
This will give us a complete trust topology including
domains via transitive Krb5 trusts. We also store the
trust type, flags, and attributes so we can determine
one-way trusted domains (outgoing only trust path).
Patch for one-way trusts coming in a later check-in.
"wbinfo -m" now lists all domains in the domain_list() as held
by the main winbindd process.
(This used to be commit 9cf6068f1e0a1063d331af17aa493140497b96ef)
-rw-r--r-- | source3/include/rpc_ds.h | 26 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.c | 4 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.h | 2 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_ads.c | 82 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_misc.c | 33 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_util.c | 219 |
6 files changed, 339 insertions, 27 deletions
diff --git a/source3/include/rpc_ds.h b/source3/include/rpc_ds.h index 4ca49871f6..05258fb306 100644 --- a/source3/include/rpc_ds.h +++ b/source3/include/rpc_ds.h @@ -30,7 +30,6 @@ #define DS_ENUM_DOM_TRUSTS 0x28 - /* macros for RPC's */ /* DSROLE_PRIMARY_DOMAIN_INFO_BASIC */ @@ -56,8 +55,6 @@ #define DS_DOMAIN_FUCNTION_2003_MIXED 1 #define DS_DOMAIN_FUNCTION_2003 2 - - typedef struct { uint16 machine_role; @@ -81,7 +78,6 @@ typedef struct #define DsRolePrimaryDomainInfoBasic 1 - /* DS_Q_GETPRIMDOMINFO - DsGetPrimaryDomainInformation() request */ typedef struct { @@ -139,15 +135,33 @@ typedef struct { } DS_DOMAIN_TRUSTS_CTR; +/* Trust flags */ + #define DS_DOMAIN_IN_FOREST 0x0001 /* domains in the forest to which we belong; even different domain trees */ #define DS_DOMAIN_DIRECT_OUTBOUND 0x0002 /* trusted domains */ -#define DS_DOMAIN_TREE_ROOT 0x0004 /* root of our forest; also available in - DsRoleGetPrimaryDomainInfo() */ +#define DS_DOMAIN_TREE_ROOT 0x0004 /* root of a forest */ #define DS_DOMAIN_PRIMARY 0x0008 /* our domain */ #define DS_DOMAIN_NATIVE_MODE 0x0010 /* native mode AD servers */ #define DS_DOMAIN_DIRECT_INBOUND 0x0020 /* trusting domains */ +/* Trust types */ + +#define DS_DOMAIN_TRUST_TYPE_DOWNLEVEL 0x00000001 +#define DS_DOMAIN_TRUST_TYPE_UPLEVEL 0x00000002 + +/* Trust attributes */ + +#define DS_DOMAIN_TRUST_ATTRIB_NON_TRANSITIVE 0x00000001 +#define DS_DOMAIN_TRUST_ATTRIB_UPLEVEL_ONLY 0x00000002 +#define DS_DOMAIN_TRUST_ATTRIB_QUARANTINED_DOMAIN 0x00000004 +#define DS_DOMAIN_TRUST_ATTRIB_FOREST_TRANSITIVE 0x00000008 +#define DS_DOMAIN_TRUST_ATTRIB_CROSS_ORG 0x00000010 +#define DS_DOMAIN_TRUST_ATTRIB_IN_FOREST 0x00000020 +#define DS_DOMAIN_TRUST_ATTRIB_EXTERNAL 0x00000040 + + + /* DS_Q_ENUM_DOM_TRUSTS - DsEnumerateDomainTrusts() request */ typedef struct { diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index ed4a23681b..9c5cd3b0e3 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -1139,6 +1139,10 @@ int main(int argc, char **argv, char **envp) netsamlogon_cache_init(); /* Non-critical */ + /* clear the cached list of trusted domains */ + + wcache_tdc_clear(); + if (!init_domain_list()) { DEBUG(0,("unable to initalize domain list\n")); exit(1); diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h index f5e1c71317..b316e988b8 100644 --- a/source3/nsswitch/winbindd.h +++ b/source3/nsswitch/winbindd.h @@ -350,7 +350,7 @@ struct winbindd_tdc_domain { #include "nsswitch/winbindd_proto.h" #define WINBINDD_ESTABLISH_LOOP 30 -#define WINBINDD_RESCAN_FREQ 300 +#define WINBINDD_RESCAN_FREQ lp_winbind_cache_time() #define WINBINDD_PAM_AUTH_KRB5_RENEW_TIME 2592000 /* one month */ #define DOM_SEQUENCE_NONE ((uint32)-1) diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c index 355a093855..111736244a 100644 --- a/source3/nsswitch/winbindd_ads.c +++ b/source3/nsswitch/winbindd_ads.c @@ -1020,8 +1020,10 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, struct ds_domain_trust *domains = NULL; int count = 0; int i; - uint32 flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND; + uint32 flags; 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")); @@ -1030,6 +1032,20 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, *names = NULL; *dom_sids = NULL; + /* If this is our primary domain or a root in our forest, + query for all trusts. If not, then just look for domain + trusts in the target forest */ + + if ( domain->primary || + ((domain->domain_flags&fr_flags) == fr_flags) ) + { + flags = DS_DOMAIN_DIRECT_OUTBOUND | + DS_DOMAIN_DIRECT_INBOUND | + DS_DOMAIN_IN_FOREST; + } else { + flags = DS_DOMAIN_IN_FOREST; + } + result = cm_connect_netlogon(domain, &cli); if (!NT_STATUS_IS_OK(result)) { @@ -1067,14 +1083,70 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, /* Copy across names and sids */ + + ret_count = 0; for (i = 0; i < count; i++) { - (*names)[i] = domains[i].netbios_domain; - (*alt_names)[i] = domains[i].dns_domain; + struct winbindd_domain d; + + /* drop external trusts if this is not our primary + domain. This means that the returned number of + domains may be less that the ones actually trusted + by the DC. */ + + if ( (domains[i].trust_attributes == DS_DOMAIN_TRUST_ATTRIB_QUARANTINED_DOMAIN) && + !domain->primary ) + { + DEBUG(10,("trusted_domains: Skipping external trusted domain " + "%s because it is outside of our primary domain\n", + domains[i].netbios_domain)); + continue; + } + + (*names)[ret_count] = domains[i].netbios_domain; + (*alt_names)[ret_count] = domains[i].dns_domain; + sid_copy(&(*dom_sids)[ret_count], &domains[i].sid); + + /* add to the trusted domain cache */ + + fstrcpy( d.name, domains[i].netbios_domain ); + fstrcpy( d.alt_name, domains[i].dns_domain ); + sid_copy( &d.sid, &domains[i].sid ); + + /* This gets a little tricky. If we are + following a transitive forest trust, then + innerit the flags, type, and attrins from + the domain we queried to make sure we don't + record the view of the trust from the wrong + side. Always view it from the side of our + primary domain. --jerry */ + if ( domain->primary || + ((domain->domain_flags&fr_flags) == fr_flags) ) + { + DEBUG(10,("trusted_domains(ads): Storing trust " + "flags for domain %s\n", d.alt_name)); + + /* Look this up in cache to make sure + we have the current trust flags and + attributes */ + + d.domain_flags = domains[i].flags; + d.domain_type = domains[i].trust_type; + d.domain_trust_attribs = domains[i].trust_attributes; + } else { + DEBUG(10,("trusted_domains(ads): Inheriting trust " + "flags for domain %s\n", d.alt_name)); + d.domain_flags = domain->domain_flags; + d.domain_type = domain->domain_type; + d.domain_trust_attribs = domain->domain_trust_attribs; + } + + wcache_tdc_add_domain( &d ); + + ret_count++; - sid_copy(&(*dom_sids)[i], &domains[i].sid); } - *num_domains = count; + *num_domains = ret_count; } return result; diff --git a/source3/nsswitch/winbindd_misc.c b/source3/nsswitch/winbindd_misc.c index f5363cad1a..ac751bf2a8 100644 --- a/source3/nsswitch/winbindd_misc.c +++ b/source3/nsswitch/winbindd_misc.c @@ -100,10 +100,41 @@ enum winbindd_result winbindd_dual_check_machine_acct(struct winbindd_domain *do void winbindd_list_trusted_domains(struct winbindd_cli_state *state) { + struct winbindd_domain *d = NULL; + int extra_data_len = 0; + char *extra_data = NULL; + DEBUG(3, ("[%5lu]: list trusted domains\n", (unsigned long)state->pid)); - sendto_domain(state, find_our_domain()); + for ( d=domain_list(); d; d=d->next ) { + if ( !extra_data ) { + extra_data = talloc_asprintf(state->mem_ctx, "%s\\%s\\%s", + d->name, + d->alt_name ? d->alt_name : d->name, + sid_string_static(&d->sid)); + } else { + extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s", + extra_data, + d->name, + d->alt_name ? d->alt_name : d->name, + sid_string_static(&d->sid)); + } + } + + extra_data_len = 0; + if (extra_data != NULL) { + extra_data_len = strlen(extra_data); + } + + if (extra_data_len > 0) { + state->response.extra_data.data = SMB_STRDUP(extra_data); + state->response.length += extra_data_len+1; + } + + TALLOC_FREE( extra_data ); + + request_ok(state); } enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *domain, diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c index 49d381913a..56de808c2d 100644 --- a/source3/nsswitch/winbindd_util.c +++ b/source3/nsswitch/winbindd_util.c @@ -112,24 +112,42 @@ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const init_domain_list() and we'll get stuck in a loop. */ for (domain = _domain_list; domain; domain = domain->next) { if (strequal(domain_name, domain->name) || - strequal(domain_name, domain->alt_name)) { - return domain; + strequal(domain_name, domain->alt_name)) + { + break; } - if (alternative_name && *alternative_name) { + + if (alternative_name && *alternative_name) + { if (strequal(alternative_name, domain->name) || - strequal(alternative_name, domain->alt_name)) { - return domain; + strequal(alternative_name, domain->alt_name)) + { + break; } } - if (sid) { + + if (sid) + { if (is_null_sid(sid)) { + continue; + } - } else if (sid_equal(sid, &domain->sid)) { - return domain; + if (sid_equal(sid, &domain->sid)) { + break; } } } + /* See if we found a match. Check if we need to update the + SID. */ + + if ( domain ) { + if ( sid_equal( &domain->sid, &global_sid_NULL ) ) + sid_copy( &domain->sid, sid ); + + return domain; + } + /* Create new domain entry */ if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL) @@ -165,6 +183,8 @@ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const /* Link to domain list */ DLIST_ADD(_domain_list, domain); + wcache_tdc_add_domain( domain ); + DEBUG(2,("Added domain %s %s %s\n", domain->name, domain->alt_name, &domain->sid?sid_string_static(&domain->sid):"")); @@ -178,16 +198,21 @@ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const struct trustdom_state { TALLOC_CTX *mem_ctx; + BOOL primary; + BOOL forest_root; struct winbindd_response *response; }; static void trustdom_recv(void *private_data, BOOL success); +static void rescan_forest_root_trusts( void ); +static void rescan_forest_trusts( void ); static void add_trusted_domains( struct winbindd_domain *domain ) { TALLOC_CTX *mem_ctx; struct winbindd_request *request; struct winbindd_response *response; + uint32 fr_flags = (DS_DOMAIN_TREE_ROOT|DS_DOMAIN_IN_FOREST); struct trustdom_state *state; @@ -210,6 +235,11 @@ static void add_trusted_domains( struct winbindd_domain *domain ) state->mem_ctx = mem_ctx; state->response = response; + /* Flags used to know how to continue the forest trust search */ + + state->primary = domain->primary; + state->forest_root = ((domain->domain_flags & fr_flags) == fr_flags ); + request->length = sizeof(*request); request->cmd = WINBINDD_LIST_TRUSTDOM; @@ -235,6 +265,8 @@ static void trustdom_recv(void *private_data, BOOL success) while ((p != NULL) && (*p != '\0')) { char *q, *sidstr, *alt_name; DOM_SID sid; + struct winbindd_domain *domain; + char *alternate_name = NULL; alt_name = strchr(p, '\\'); if (alt_name == NULL) { @@ -268,15 +300,21 @@ static void trustdom_recv(void *private_data, BOOL success) } } - if (find_domain_from_name_noinit(p) == NULL) { - struct winbindd_domain *domain; - char *alternate_name = NULL; - /* use the real alt_name if we have one, else pass in NULL */ if ( !strequal( alt_name, "(null)" ) ) alternate_name = alt_name; + /* If we have an existing domain structure, calling + add_trusted_domain() will update the SID if + necessary. This is important because we need the + SID for sibling domains */ + + if ( find_domain_from_name_noinit(p) != NULL ) { + domain = add_trusted_domain(p, alternate_name, + &cache_methods, + &sid); + } else { domain = add_trusted_domain(p, alternate_name, &cache_methods, &sid); @@ -288,13 +326,161 @@ static void trustdom_recv(void *private_data, BOOL success) } SAFE_FREE(response->extra_data.data); + + /* + Cases to consider when scanning trusts: + (a) we are calling from a child domain (primary && !forest_root) + (b) we are calling from the root of the forest (primary && forest_root) + (c) we are calling from a trusted forest domain (!primary + && !forest_root) + */ + + if ( state->primary ) { + /* If this is our primary domain and we are not the in the + forest root, we have to scan the root trusts first */ + + if ( !state->forest_root ) + rescan_forest_root_trusts(); + else + rescan_forest_trusts(); + + } else if ( state->forest_root ) { + /* Once we have done root forest trust search, we can + go on to search thing trusted forests */ + + rescan_forest_trusts(); + } + talloc_destroy(state->mem_ctx); + + return; } /******************************************************************** - Periodically we need to refresh the trusted domain cache for smbd + Scan the trusts of our forest root ********************************************************************/ +static void rescan_forest_root_trusts( void ) +{ + struct winbindd_tdc_domain *dom_list = NULL; + size_t num_trusts = 0; + int i; + + /* The only transitive trusts supported by Windows 2003 AD are + (a) Parent-Child, (b) Tree-Root, and (c) Forest. The + first two are handled in forest and listed by + DsEnumerateDomainTrusts(). Forest trusts are not so we + have to do that ourselves. */ + + if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) ) + return; + + for ( i=0; i<num_trusts; i++ ) { + struct winbindd_domain *d = NULL; + + /* Find the forest root. Don't necessarily trust + the domain_list() as our primary domain may not + have been initialized. */ + + if ( !(dom_list[i].trust_flags & DS_DOMAIN_TREE_ROOT) ) { + continue; + } + + /* Here's the forest root */ + + d = find_domain_from_name_noinit( dom_list[i].domain_name ); + + if ( !d ) { + d = add_trusted_domain( dom_list[i].domain_name, + dom_list[i].dns_name, + &cache_methods, + &dom_list[i].sid ); + } + + DEBUG(10,("rescan_forest_root_trusts: Following trust path " + "for domain tree root %s (%s)\n", + d->name, d->alt_name )); + + d->domain_flags = dom_list[i].trust_flags; + d->domain_type = dom_list[i].trust_type; + d->domain_trust_attribs = dom_list[i].trust_attribs; + + add_trusted_domains( d ); + + break; + } + + TALLOC_FREE( dom_list ); + + return; +} + +/******************************************************************** + scan the transitive forest trists (not our own) +********************************************************************/ + + +static void rescan_forest_trusts( void ) +{ + struct winbindd_domain *d = NULL; + struct winbindd_tdc_domain *dom_list = NULL; + size_t num_trusts = 0; + int i; + + /* The only transitive trusts supported by Windows 2003 AD are + (a) Parent-Child, (b) Tree-Root, and (c) Forest. The + first two are handled in forest and listed by + DsEnumerateDomainTrusts(). Forest trusts are not so we + have to do that ourselves. */ + + if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) ) + return; + + for ( i=0; i<num_trusts; i++ ) { + uint32 flags = dom_list[i].trust_flags; + uint32 type = dom_list[i].trust_type; + uint32 attribs = dom_list[i].trust_attribs; + + d = find_domain_from_name_noinit( dom_list[i].domain_name ); + + /* ignore our primary and internal domains */ + + if ( d && (d->internal || d->primary ) ) + continue; + + if ( (flags & DS_DOMAIN_DIRECT_INBOUND) && + (type == DS_DOMAIN_TRUST_TYPE_UPLEVEL) && + (attribs == DS_DOMAIN_TRUST_ATTRIB_FOREST_TRANSITIVE) ) + { + /* add the trusted domain if we don't know + about it */ + + if ( !d ) { + d = add_trusted_domain( dom_list[i].domain_name, + dom_list[i].dns_name, + &cache_methods, + &dom_list[i].sid ); + } + + DEBUG(10,("Following trust path for domain %s (%s)\n", + d->name, d->alt_name )); + add_trusted_domains( d ); + } + } + + TALLOC_FREE( dom_list ); + + return; +} + +/********************************************************************* + The process of updating the trusted domain list is a three step + async process: + (a) ask our domain + (b) ask the root domain in our forest + (c) ask the a DC in any Win2003 trusted forests +*********************************************************************/ + void rescan_trusted_domains( void ) { time_t now = time(NULL); @@ -305,7 +491,12 @@ void rescan_trusted_domains( void ) ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) ) return; - /* this will only add new domains we didn't already know about */ + /* clear the TRUSTDOM cache first */ + + wcache_tdc_clear(); + + /* this will only add new domains we didn't already know about + in the domain_list()*/ add_trusted_domains( find_our_domain() ); |