summaryrefslogtreecommitdiff
path: root/source3/nsswitch
diff options
context:
space:
mode:
authorGerald Carter <jerry@samba.org>2007-05-06 19:17:30 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:21:47 -0500
commit7cb2a4be354c23b4228d67fe2bba68067ea619cf (patch)
tree782cd4a9fe6e8deffca6a44e8b0a64aada7f878e /source3/nsswitch
parent879b84362715e8796f7ef92124007f5673338b37 (diff)
downloadsamba-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)
Diffstat (limited to 'source3/nsswitch')
-rw-r--r--source3/nsswitch/winbindd.c4
-rw-r--r--source3/nsswitch/winbindd.h2
-rw-r--r--source3/nsswitch/winbindd_ads.c82
-rw-r--r--source3/nsswitch/winbindd_misc.c33
-rw-r--r--source3/nsswitch/winbindd_util.c219
5 files changed, 319 insertions, 21 deletions
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() );