]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
r22704: Implement three step method for enumerating domain trusts.
authorGerald Carter <jerry@samba.org>
Sun, 6 May 2007 19:17:30 +0000 (19:17 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:21:47 +0000 (12:21 -0500)
(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.

source/include/rpc_ds.h
source/nsswitch/winbindd.c
source/nsswitch/winbindd.h
source/nsswitch/winbindd_ads.c
source/nsswitch/winbindd_misc.c
source/nsswitch/winbindd_util.c

index 4ca49871f6db9c71bda53bdb18f7deb614ca852a..05258fb306cab1b67d7f9c54f7d3353eb13a4b85 100644 (file)
@@ -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 
 {
index ed4a23681b88c1aa7dd0986e12d222dfa2163b32..9c5cd3b0e380ce6bf7b66946d0b5974bc8397960 100644 (file)
@@ -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);
index f5e1c713179eb4f54d2b6b954bfb426afda698ef..b316e988b8850557d93f674143dbf8e05784f7ae 100644 (file)
@@ -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)
 
index 355a09385574d8ef9f046a0c55c10d54705978b9..111736244a9f4e011638577769a83f13f531a791 100644 (file)
@@ -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;
index f5363cad1a21447fd2eb44e498c703012ed6c6fc..ac751bf2a85e6e459425f411bd56d4c2c115e43f 100644 (file)
@@ -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,
index 49d381913a24f68836de8e9e1c5336a90b17eab5..56de808c2dc56cf13764c04932272f92313f162b 100644 (file)
@@ -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() );