X-Git-Url: http://git.samba.org/samba.git/?p=tprouty%2Fsamba.git;a=blobdiff_plain;f=source%2Fnsswitch%2Fwinbindd_cm.c;h=269dab0389b976eb52db0e59ac5b8996a3114c48;hp=a9f6ea30004c1dfc047b4c284ca58202d4c20442;hb=b0132e94fc5fef936aa766fb99a306b3628e9f07;hpb=27b063078dff0d8c5eb552dd73825f6858d04e4b diff --git a/source/nsswitch/winbindd_cm.c b/source/nsswitch/winbindd_cm.c index a9f6ea3000..269dab0389 100644 --- a/source/nsswitch/winbindd_cm.c +++ b/source/nsswitch/winbindd_cm.c @@ -7,10 +7,11 @@ Copyright (C) Andrew Bartlett 2002 Copyright (C) Gerald (Jerry) Carter 2003-2005. Copyright (C) Volker Lendecke 2004-2005 + Copyright (C) Jeremy Allison 2006 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -19,8 +20,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . */ /* @@ -64,25 +64,200 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND -static NTSTATUS init_dc_connection(struct winbindd_domain *domain); +struct dc_name_ip { + fstring name; + struct in_addr ip; +}; + +extern struct winbindd_methods reconnect_methods; +extern BOOL override_logfile; + +static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain); +static void set_dc_type_and_flags( struct winbindd_domain *domain ); +static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain, + struct dc_name_ip **dcs, int *num_dcs); + +/**************************************************************** + Child failed to find DC's. Reschedule check. +****************************************************************/ + +static void msg_failed_to_go_online(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_domain *domain; + const char *domainname = (const char *)data->data; + + if (data->data == NULL || data->length == 0) { + return; + } + + DEBUG(5,("msg_fail_to_go_online: received for domain %s.\n", domainname)); + + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->internal) { + continue; + } + + if (strequal(domain->name, domainname)) { + if (domain->online) { + /* We're already online, ignore. */ + DEBUG(5,("msg_fail_to_go_online: domain %s " + "already online.\n", domainname)); + continue; + } + + /* Reschedule the online check. */ + set_domain_offline(domain); + break; + } + } +} + +/**************************************************************** + Actually cause a reconnect from a message. +****************************************************************/ + +static void msg_try_to_go_online(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_domain *domain; + const char *domainname = (const char *)data->data; + + if (data->data == NULL || data->length == 0) { + return; + } + + DEBUG(5,("msg_try_to_go_online: received for domain %s.\n", domainname)); + + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->internal) { + continue; + } + + if (strequal(domain->name, domainname)) { + + if (domain->online) { + /* We're already online, ignore. */ + DEBUG(5,("msg_try_to_go_online: domain %s " + "already online.\n", domainname)); + continue; + } + + /* This call takes care of setting the online + flag to true if we connected, or re-adding + the offline handler if false. Bypasses online + check so always does network calls. */ + + init_dc_connection_network(domain); + break; + } + } +} + +/**************************************************************** + Fork a child to try and contact a DC. Do this as contacting a + DC requires blocking lookups and we don't want to block our + parent. +****************************************************************/ + +static BOOL fork_child_dc_connect(struct winbindd_domain *domain) +{ + struct dc_name_ip *dcs = NULL; + int num_dcs = 0; + TALLOC_CTX *mem_ctx = NULL; + pid_t child_pid; + pid_t parent_pid = sys_getpid(); + + /* Stop zombies */ + CatchChild(); + + child_pid = sys_fork(); + + if (child_pid == -1) { + DEBUG(0, ("fork_child_dc_connect: Could not fork: %s\n", strerror(errno))); + return False; + } + + if (child_pid != 0) { + /* Parent */ + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_TRY_TO_GO_ONLINE, + msg_try_to_go_online); + messaging_register(winbind_messaging_context(), NULL, + MSG_WINBIND_FAILED_TO_GO_ONLINE, + msg_failed_to_go_online); + return True; + } + + /* Child. */ + + /* Leave messages blocked - we will never process one. */ + + /* tdb needs special fork handling */ + if (tdb_reopen_all(1) == -1) { + DEBUG(0,("tdb_reopen_all failed.\n")); + _exit(0); + } + + close_conns_after_fork(); + + if (!override_logfile) { + pstring logfile; + pstr_sprintf(logfile, "%s/log.winbindd-dc-connect", dyn_LOGFILEBASE); + lp_set_logfile(logfile); + reopen_logs(); + } + + mem_ctx = talloc_init("fork_child_dc_connect"); + if (!mem_ctx) { + DEBUG(0,("talloc_init failed.\n")); + _exit(0); + } + + if ((!get_dcs(mem_ctx, domain, &dcs, &num_dcs)) || (num_dcs == 0)) { + /* Still offline ? Can't find DC's. */ + messaging_send_buf(winbind_messaging_context(), + pid_to_procid(parent_pid), + MSG_WINBIND_FAILED_TO_GO_ONLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + _exit(0); + } + + /* We got a DC. Send a message to our parent to get it to + try and do the same. */ + + messaging_send_buf(winbind_messaging_context(), + pid_to_procid(parent_pid), + MSG_WINBIND_TRY_TO_GO_ONLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + _exit(0); +} /**************************************************************** Handler triggered if we're offline to try and detect a DC. ****************************************************************/ -static void check_domain_online_handler(struct timed_event *te, +static void check_domain_online_handler(struct event_context *ctx, + struct timed_event *te, const struct timeval *now, void *private_data) { struct winbindd_domain *domain = (struct winbindd_domain *)private_data; - DEBUG(10,("check_domain_online_handler: called for domain %s\n", - domain->name )); + DEBUG(10,("check_domain_online_handler: called for domain " + "%s (online = %s)\n", domain->name, + domain->online ? "True" : "False" )); - if (domain->check_online_event) { - TALLOC_FREE(domain->check_online_event); - } + TALLOC_FREE(domain->check_online_event); /* Are we still in "startup" mode ? */ @@ -102,10 +277,26 @@ static void check_domain_online_handler(struct timed_event *te, return; } - /* This call takes care of setting the online - flag to true if we connected, or re-adding - the offline handler if false. */ - init_dc_connection(domain); + /* Fork a child to test if it can contact a DC. + If it can then send ourselves a message to + cause a reconnect. */ + + fork_child_dc_connect(domain); +} + +/**************************************************************** + If we're still offline setup the timeout check. +****************************************************************/ + +static void calc_new_online_timeout_check(struct winbindd_domain *domain) +{ + int wbc = lp_winbind_cache_time(); + + if (domain->startup) { + domain->check_online_timeout = 10; + } else if (domain->check_online_timeout < wbc) { + domain->check_online_timeout = wbc; + } } /**************************************************************** @@ -118,12 +309,21 @@ void set_domain_offline(struct winbindd_domain *domain) DEBUG(10,("set_domain_offline: called for domain %s\n", domain->name )); - if (domain->check_online_event) { - TALLOC_FREE(domain->check_online_event); + TALLOC_FREE(domain->check_online_event); + + if (domain->internal) { + DEBUG(3,("set_domain_offline: domain %s is internal - logic error.\n", + domain->name )); + return; } domain->online = False; + /* Offline domains are always initialized. They're + re-initialized when they go back online. */ + + domain->initialized = True; + /* We only add the timeout handler that checks and allows us to go back online when we've not been told to remain offline. */ @@ -137,21 +337,39 @@ void set_domain_offline(struct winbindd_domain *domain) /* If we're in statup mode, check again in 10 seconds, not in lp_winbind_cache_time() seconds (which is 5 mins by default). */ - domain->check_online_event = add_timed_event( NULL, - domain->startup ? - timeval_current_ofs(10,0) : - timeval_current_ofs(lp_winbind_cache_time(), 0), + calc_new_online_timeout_check(domain); + + domain->check_online_event = event_add_timed(winbind_event_context(), + NULL, + timeval_current_ofs(domain->check_online_timeout,0), "check_domain_online_handler", check_domain_online_handler, domain); /* The above *has* to succeed for winbindd to work. */ if (!domain->check_online_event) { - smb_panic("set_domain_offline: failed to add online handler.\n"); + smb_panic("set_domain_offline: failed to add online handler"); } DEBUG(10,("set_domain_offline: added event handler for domain %s\n", domain->name )); + + /* Send an offline message to the idmap child when our + primary domain goes offline */ + + if ( domain->primary ) { + struct winbindd_child *idmap = idmap_child(); + + if ( idmap->pid != 0 ) { + messaging_send_buf(winbind_messaging_context(), + pid_to_procid(idmap->pid), + MSG_WINBIND_OFFLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + } + } + + return; } /**************************************************************** @@ -165,6 +383,12 @@ static void set_domain_online(struct winbindd_domain *domain) DEBUG(10,("set_domain_online: called for domain %s\n", domain->name )); + if (domain->internal) { + DEBUG(3,("set_domain_online: domain %s is internal - logic error.\n", + domain->name )); + return; + } + if (get_global_winbindd_state_offline()) { DEBUG(10,("set_domain_online: domain %s remaining globally offline\n", domain->name )); @@ -173,11 +397,56 @@ static void set_domain_online(struct winbindd_domain *domain) /* If we are waiting to get a krb5 ticket, trigger immediately. */ GetTimeOfDay(&now); - set_event_dispatch_time("krb5_ticket_gain_handler", now); - domain->online = True; + set_event_dispatch_time(winbind_event_context(), + "krb5_ticket_gain_handler", now); /* Ok, we're out of any startup mode now... */ domain->startup = False; + + if (domain->online == False) { + /* We were offline - now we're online. We default to + using the MS-RPC backend if we started offline, + and if we're going online for the first time we + should really re-initialize the backends and the + checks to see if we're talking to an AD or NT domain. + */ + + domain->initialized = False; + + /* 'reconnect_methods' is the MS-RPC backend. */ + if (domain->backend == &reconnect_methods) { + domain->backend = NULL; + } + } + + /* Ensure we have no online timeout checks. */ + domain->check_online_timeout = 0; + TALLOC_FREE(domain->check_online_event); + + /* Ensure we ignore any pending child messages. */ + messaging_deregister(winbind_messaging_context(), + MSG_WINBIND_TRY_TO_GO_ONLINE, NULL); + messaging_deregister(winbind_messaging_context(), + MSG_WINBIND_FAILED_TO_GO_ONLINE, NULL); + + domain->online = True; + + /* Send an online message to the idmap child when our + primary domain comes online */ + + if ( domain->primary ) { + struct winbindd_child *idmap = idmap_child(); + + if ( idmap->pid != 0 ) { + messaging_send_buf(winbind_messaging_context(), + pid_to_procid(idmap->pid), + MSG_WINBIND_ONLINE, + (uint8 *)domain->name, + strlen(domain->name)+1); + } + } + + return; } /**************************************************************** @@ -186,6 +455,8 @@ static void set_domain_online(struct winbindd_domain *domain) void set_domain_online_request(struct winbindd_domain *domain) { + struct timeval tev; + DEBUG(10,("set_domain_online_request: called for domain %s\n", domain->name )); @@ -201,20 +472,36 @@ void set_domain_online_request(struct winbindd_domain *domain) Wait at least 5 seconds. Heuristics suck... */ if (!domain->check_online_event) { - DEBUG(5,("set_domain_online_request: no check_domain_online_handler " - "registered. Were we online (%d) ?\n", (int)domain->online )); - } else { - struct timeval tev; + /* If we've come from being globally offline we + don't have a check online event handler set. + We need to add one now we're trying to go + back online. */ - GetTimeOfDay(&tev); + DEBUG(10,("set_domain_online_request: domain %s was globally offline.\n", + domain->name )); - /* Go into "startup" mode again. */ - domain->startup_time = tev.tv_sec; - domain->startup = True; + domain->check_online_event = event_add_timed(winbind_event_context(), + NULL, + timeval_current_ofs(5, 0), + "check_domain_online_handler", + check_domain_online_handler, + domain); - tev.tv_sec += 5; - set_event_dispatch_time("check_domain_online_handler", tev); + /* The above *has* to succeed for winbindd to work. */ + if (!domain->check_online_event) { + smb_panic("set_domain_online_request: failed to add online handler"); + } } + + GetTimeOfDay(&tev); + + /* Go into "startup" mode again. */ + domain->startup_time = tev.tv_sec; + domain->startup = True; + + tev.tv_sec += 5; + + set_event_dispatch_time(winbind_event_context(), "check_domain_online_handler", tev); } /**************************************************************** @@ -228,10 +515,10 @@ void winbind_add_failed_connection_entry(const struct winbindd_domain *domain, add_failed_connection_entry(domain->name, server, result); /* If this was the saf name for the last thing we talked to, remove it. */ - saf_delete(domain->name, server); + saf_delete(domain->name); if (*domain->alt_name) { add_failed_connection_entry(domain->alt_name, server, result); - saf_delete(domain->alt_name, server); + saf_delete(domain->alt_name); } } @@ -274,8 +561,9 @@ static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain, struct winbindd_domain *our_domain = NULL; struct rpc_pipe_client *netlogon_pipe = NULL; NTSTATUS result; + WERROR werr; TALLOC_CTX *mem_ctx; - + unsigned int orig_timeout; fstring tmp; char *p; @@ -298,21 +586,30 @@ static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain, result = cm_connect_netlogon(our_domain, &netlogon_pipe); if (!NT_STATUS_IS_OK(result)) { + talloc_destroy(mem_ctx); return False; } - result = rpccli_netlogon_getdcname(netlogon_pipe, mem_ctx, our_domain->dcname, - domain->name, tmp); + /* This call can take a long time - allow the server to time out. + 35 seconds should do it. */ + + orig_timeout = cli_set_timeout(netlogon_pipe->cli, 35000); + + werr = rpccli_netlogon_getanydcname(netlogon_pipe, mem_ctx, our_domain->dcname, + domain->name, tmp); + + /* And restore our original timeout. */ + cli_set_timeout(netlogon_pipe->cli, orig_timeout); talloc_destroy(mem_ctx); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10, ("rpccli_netlogon_getdcname failed: %s\n", - nt_errstr(result))); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(10, ("rpccli_netlogon_getanydcname failed: %s\n", + dos_errstr(werr))); return False; } - /* cli_netlogon_getdcname gives us a name with \\ */ + /* cli_netlogon_getanydcname gives us a name with \\ */ p = tmp; if (*p == '\\') { p+=1; @@ -323,7 +620,7 @@ static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain, fstrcpy(dcname, p); - DEBUG(10, ("rpccli_netlogon_getdcname returned %s\n", dcname)); + DEBUG(10, ("rpccli_netlogon_getanydcname returned %s\n", dcname)); if (!resolve_name(dcname, dc_ip, 0x20)) { return False; @@ -586,11 +883,6 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, return result; } -struct dc_name_ip { - fstring name; - struct in_addr ip; -}; - static BOOL add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name, const char *dcname, struct in_addr ip, struct dc_name_ip **dcs, int *num) @@ -617,8 +909,10 @@ static BOOL add_sockaddr_to_array(TALLOC_CTX *mem_ctx, { *addrs = TALLOC_REALLOC_ARRAY(mem_ctx, *addrs, struct sockaddr_in, (*num)+1); - if (*addrs == NULL) + if (*addrs == NULL) { + *num = 0; return False; + } (*addrs)[*num].sin_family = PF_INET; putip((char *)&((*addrs)[*num].sin_addr), (char *)&ip); @@ -680,7 +974,8 @@ static BOOL send_getdc_request(struct in_addr dc_ip, SSVAL(p, 6, 0xffff); p+=8; - return cli_send_mailslot(False, "\\MAILSLOT\\NET\\NTLOGON", 0, + return cli_send_mailslot(winbind_messaging_context(), + False, "\\MAILSLOT\\NET\\NTLOGON", 0, outbuf, PTR_DIFF(p, outbuf), global_myname(), 0, domain_name, 0x1c, dc_ip); @@ -758,8 +1053,7 @@ static BOOL receive_getdc_response(struct in_addr dc_ip, convert an ip to a name *******************************************************************/ -static BOOL dcip_to_name( const char *domainname, const char *realm, - const DOM_SID *sid, struct in_addr ip, fstring name ) +static BOOL dcip_to_name(const struct winbindd_domain *domain, struct in_addr ip, fstring name ) { struct ip_service ip_list; @@ -773,7 +1067,7 @@ static BOOL dcip_to_name( const char *domainname, const char *realm, if (lp_security() == SEC_ADS) { ADS_STRUCT *ads; - ads = ads_init(realm, domainname, NULL); + ads = ads_init(domain->alt_name, domain->name, NULL); ads->auth.flags |= ADS_AUTH_NO_BIND; if (ads_try_connect( ads, inet_ntoa(ip) ) ) { @@ -783,18 +1077,22 @@ static BOOL dcip_to_name( const char *domainname, const char *realm, DEBUG(10,("dcip_to_name: flags = 0x%x\n", (unsigned int)ads->config.flags)); - if ((ads->config.flags & ADS_KDC) && ads_sitename_match(ads)) { + if (domain->primary && (ads->config.flags & ADS_KDC) && ads_closest_dc(ads)) { + char *sitename = sitename_fetch(ads->config.realm); + /* We're going to use this KDC for this realm/domain. If we are using sites, then force the krb5 libs to use this KDC. */ - create_local_private_krb5_conf_for_domain(realm, - domainname, + create_local_private_krb5_conf_for_domain(domain->alt_name, + domain->name, + sitename, ip); + SAFE_FREE(sitename); /* Ensure we contact this DC also. */ - saf_store( domainname, name); - saf_store( realm, name); + saf_store( domain->name, name); + saf_store( domain->alt_name, name); } ads_destroy( &ads ); @@ -807,11 +1105,11 @@ static BOOL dcip_to_name( const char *domainname, const char *realm, /* try GETDC requests next */ - if (send_getdc_request(ip, domainname, sid)) { + if (send_getdc_request(ip, domain->name, &domain->sid)) { int i; smb_msleep(100); for (i=0; i<5; i++) { - if (receive_getdc_response(ip, domainname, name)) { + if (receive_getdc_response(ip, domain->name, name)) { namecache_store(name, 0x20, 1, &ip_list); return True; } @@ -821,7 +1119,7 @@ static BOOL dcip_to_name( const char *domainname, const char *realm, /* try node status request */ - if ( name_status_find(domainname, 0x1c, 0x20, ip, name) ) { + if ( name_status_find(domain->name, 0x1c, 0x20, ip, name) ) { namecache_store(name, 0x20, 1, &ip_list); return True; } @@ -856,6 +1154,8 @@ static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain, } if (sec == SEC_ADS) { + char *sitename = NULL; + /* We need to make sure we know the local site before doing any DNS queries, as this will restrict the get_sorted_dc_list() call below to only fetching @@ -864,16 +1164,39 @@ static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain, /* Find any DC to get the site record. We deliberately don't care about the return here. */ - get_dc_name(domain->name, lp_realm(), dcname, &ip); - /* Now do the site-specific AD dns lookup. */ - get_sorted_dc_list(domain->alt_name, &ip_list, &iplist_size, True); + get_dc_name(domain->name, domain->alt_name, dcname, &ip); + + sitename = sitename_fetch(domain->alt_name); + if (sitename) { + + /* Do the site-specific AD dns lookup first. */ + get_sorted_dc_list(domain->alt_name, sitename, &ip_list, &iplist_size, True); + + for ( i=0; iname, inet_ntoa(ip_list[i].ip), + ip_list[i].ip, dcs, num_dcs); + } + + SAFE_FREE(ip_list); + SAFE_FREE(sitename); + iplist_size = 0; + } + + /* Now we add DCs from the main AD dns lookup. */ + get_sorted_dc_list(domain->alt_name, NULL, &ip_list, &iplist_size, True); + + for ( i=0; iname, inet_ntoa(ip_list[i].ip), + ip_list[i].ip, dcs, num_dcs); + } } /* try standard netbios queries if no ADS */ - if (iplist_size==0) - get_sorted_dc_list(domain->name, &ip_list, &iplist_size, False); + if (iplist_size==0) { + get_sorted_dc_list(domain->name, NULL, &ip_list, &iplist_size, False); + } /* FIXME!! this is where we should re-insert the GETDC requests --jerry */ @@ -912,15 +1235,23 @@ static BOOL find_new_dc(TALLOC_CTX *mem_ctx, for (i=0; iname, domain->alt_name, &domain->sid, - addr->sin_addr, dcname )) { + if (dcip_to_name( domain, addr->sin_addr, dcname )) { return True; } @@ -994,8 +1324,7 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, struct in_addr ip; ip = *interpret_addr2( saf_servername ); - if (dcip_to_name( domain->name, domain->alt_name, - &domain->sid, ip, saf_name )) { + if (dcip_to_name( domain, ip, saf_name )) { fstrcpy( domain->dcname, saf_name ); } else { winbind_add_failed_connection_entry( @@ -1027,8 +1356,16 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, int num_addrs = 0; int dummy = 0; - add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 445, &addrs, &num_addrs); - add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 139, &addrs, &num_addrs); + if (!add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 445, &addrs, &num_addrs)) { + set_domain_offline(domain); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + if (!add_sockaddr_to_array(mem_ctx, domain->dcaddr.sin_addr, 139, &addrs, &num_addrs)) { + set_domain_offline(domain); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } /* 5 second timeout. */ if (!open_any_socket_out(addrs, num_addrs, 5000, &dummy, &fd)) { @@ -1163,17 +1500,146 @@ static BOOL connection_ok(struct winbindd_domain *domain) return True; } - -/* Initialize a new connection up to the RPC BIND. */ -static NTSTATUS init_dc_connection(struct winbindd_domain *domain) +/* Initialize a new connection up to the RPC BIND. + Bypass online status check so always does network calls. */ + +static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain) { - if (connection_ok(domain)) + NTSTATUS result; + + /* Internal connections never use the network. */ + if (domain->internal) { + domain->initialized = True; + return NT_STATUS_OK; + } + + if (connection_ok(domain)) { + if (!domain->initialized) { + set_dc_type_and_flags(domain); + } return NT_STATUS_OK; + } invalidate_cm_connection(&domain->conn); - return cm_open_connection(domain, &domain->conn); + result = cm_open_connection(domain, &domain->conn); + + if (NT_STATUS_IS_OK(result) && !domain->initialized) { + set_dc_type_and_flags(domain); + } + + return result; +} + +NTSTATUS init_dc_connection(struct winbindd_domain *domain) +{ + if (domain->initialized && !domain->online) { + /* We check for online status elsewhere. */ + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + } + + return init_dc_connection_network(domain); +} + +/****************************************************************************** + Set the trust flags (direction and forest location) for a domain +******************************************************************************/ + +static BOOL set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain ) +{ + struct winbindd_domain *our_domain; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + struct ds_domain_trust *domains = NULL; + int count = 0; + int i; + uint32 flags = (DS_DOMAIN_IN_FOREST | + DS_DOMAIN_DIRECT_OUTBOUND | + DS_DOMAIN_DIRECT_INBOUND); + struct rpc_pipe_client *cli; + TALLOC_CTX *mem_ctx = NULL; + + DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s\n", domain->name )); + + /* Our primary domain doesn't need to worry about trust flags. + Force it to go through the network setup */ + if ( domain->primary ) { + return False; + } + + our_domain = find_our_domain(); + + if ( !connection_ok(our_domain) ) { + DEBUG(3,("set_dc_type_and_flags_trustinfo: No connection to our domain!\n")); + return False; + } + + /* This won't work unless our domain is AD */ + + if ( !our_domain->active_directory ) { + return False; + } + + /* Use DsEnumerateDomainTrusts to get us the trust direction + and type */ + + result = cm_connect_netlogon(our_domain, &cli); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(5, ("set_dc_type_and_flags_trustinfo: Could not open " + "a connection to %s for PIPE_NETLOGON (%s)\n", + domain->name, nt_errstr(result))); + return False; + } + + if ( (mem_ctx = talloc_init("set_dc_type_and_flags_trustinfo")) == NULL ) { + DEBUG(0,("set_dc_type_and_flags_trustinfo: talloc_init() failed!\n")); + return False; + } + + result = rpccli_ds_enum_domain_trusts(cli, mem_ctx, + cli->cli->desthost, + flags, &domains, + (unsigned int *)&count); + + /* Now find the domain name and get the flags */ + + for ( i=0; iname, domains[i].netbios_domain ) ) { + domain->domain_flags = domains[i].flags; + domain->domain_type = domains[i].trust_type; + domain->domain_trust_attribs = domains[i].trust_attributes; + + if ( domain->domain_type == DS_DOMAIN_TRUST_TYPE_UPLEVEL ) + domain->active_directory = True; + + /* This flag is only set if the domain is *our* + primary domain and the primary domain is in + native mode */ + + domain->native_mode = (domain->domain_flags & DS_DOMAIN_NATIVE_MODE); + + DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s is %sin " + "native mode.\n", domain->name, + domain->native_mode ? "" : "NOT ")); + + DEBUG(5,("set_dc_type_and_flags_trustinfo: domain %s is %s" + "running active directory.\n", domain->name, + domain->active_directory ? "" : "NOT ")); + + + domain->initialized = True; + + if ( !winbindd_can_contact_domain( domain) ) + domain->internal = True; + + break; + } + } + + talloc_destroy( mem_ctx ); + + return domain->initialized; } /****************************************************************************** @@ -1184,7 +1650,7 @@ static NTSTATUS init_dc_connection(struct winbindd_domain *domain) is native mode. ******************************************************************************/ -void set_dc_type_and_flags( struct winbindd_domain *domain ) +static void set_dc_type_and_flags_connect( struct winbindd_domain *domain ) { NTSTATUS result; DS_DOMINFO_CTR ctr; @@ -1194,40 +1660,30 @@ void set_dc_type_and_flags( struct winbindd_domain *domain ) char *domain_name = NULL; char *dns_name = NULL; - DOM_SID *dom_sid = NULL; - int try_count = 0; + char *forest_name = NULL; + DOM_SID *dom_sid = NULL; ZERO_STRUCT( ctr ); - domain->native_mode = False; - domain->active_directory = False; - - if (domain->internal) { - domain->initialized = True; + if (!connection_ok(domain)) { return; } - try_again: - - result = init_dc_connection(domain); - if (!NT_STATUS_IS_OK(result) || try_count > 2) { - DEBUG(5, ("set_dc_type_and_flags: Could not open a connection " - "to %s: (%s)\n", domain->name, nt_errstr(result))); - domain->initialized = True; - return; - } + DEBUG(5, ("set_dc_type_and_flags_connect: domain %s\n", domain->name )); cli = cli_rpc_pipe_open_noauth(domain->conn.cli, PI_LSARPC_DS, &result); if (cli == NULL) { - DEBUG(5, ("set_dc_type_and_flags: Could not bind to " + DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to " "PI_LSARPC_DS on domain %s: (%s)\n", domain->name, nt_errstr(result))); - domain->initialized = True; - /* We want to detect network failures asap to try another dc. */ - try_count++; - goto try_again; + + /* if this is just a non-AD domain we need to continue + * identifying so that we can in the end return with + * domain->initialized = True - gd */ + + goto no_lsarpc_ds; } result = rpccli_ds_getprimarydominfo(cli, cli->cli->mem_ctx, @@ -1236,27 +1692,34 @@ void set_dc_type_and_flags( struct winbindd_domain *domain ) cli_rpc_pipe_close(cli); if (!NT_STATUS_IS_OK(result)) { - domain->initialized = True; + DEBUG(5, ("set_dc_type_and_flags_connect: rpccli_ds_getprimarydominfo " + "on domain %s failed: (%s)\n", + domain->name, nt_errstr(result))); return; } if ((ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) && - !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) ) + !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE)) { domain->native_mode = True; + } else { + domain->native_mode = False; + } +no_lsarpc_ds: cli = cli_rpc_pipe_open_noauth(domain->conn.cli, PI_LSARPC, &result); if (cli == NULL) { - domain->initialized = True; - /* We want to detect network failures asap to try another dc. */ - try_count++; - goto try_again; + DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to " + "PI_LSARPC on domain %s: (%s)\n", + domain->name, nt_errstr(result))); + cli_rpc_pipe_close(cli); + return; } mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", domain->name); if (!mem_ctx) { - DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n")); + DEBUG(1, ("set_dc_type_and_flags_connect: talloc_init() failed\n")); cli_rpc_pipe_close(cli); return; } @@ -1269,23 +1732,27 @@ void set_dc_type_and_flags( struct winbindd_domain *domain ) to determine that the DC is active directory */ result = rpccli_lsa_query_info_policy2(cli, mem_ctx, &pol, 12, &domain_name, - &dns_name, NULL, + &dns_name, &forest_name, NULL, &dom_sid); } if (NT_STATUS_IS_OK(result)) { + domain->active_directory = True; + if (domain_name) fstrcpy(domain->name, domain_name); if (dns_name) fstrcpy(domain->alt_name, dns_name); + if ( forest_name ) + fstrcpy(domain->forest_name, forest_name); + if (dom_sid) sid_copy(&domain->sid, dom_sid); - - domain->active_directory = True; } else { - + domain->active_directory = False; + result = rpccli_lsa_open_policy(cli, mem_ctx, True, SEC_RIGHTS_MAXIMUM_ALLOWED, &pol); @@ -1307,15 +1774,50 @@ void set_dc_type_and_flags( struct winbindd_domain *domain ) } done: + DEBUG(5, ("set_dc_type_and_flags_connect: domain %s is %sin native mode.\n", + domain->name, domain->native_mode ? "" : "NOT ")); + + DEBUG(5,("set_dc_type_and_flags_connect: domain %s is %srunning active directory.\n", + domain->name, domain->active_directory ? "" : "NOT ")); + cli_rpc_pipe_close(cli); talloc_destroy(mem_ctx); domain->initialized = True; - +} + +/********************************************************************** + Set the domain_flags (trust attributes, domain operating modes, etc... +***********************************************************************/ + +static void set_dc_type_and_flags( struct winbindd_domain *domain ) +{ + /* we always have to contact our primary domain */ + + if ( domain->primary ) { + DEBUG(10,("set_dc_type_and_flags: setting up flags for " + "primary domain\n")); + set_dc_type_and_flags_connect( domain ); + return; + } + + /* Use our DC to get the information if possible */ + + if ( !set_dc_type_and_flags_trustinfo( domain ) ) { + /* Otherwise, fallback to contacting the + domain directly */ + set_dc_type_and_flags_connect( domain ); + } + return; } + + +/********************************************************************** +***********************************************************************/ + static BOOL cm_get_schannel_dcinfo(struct winbindd_domain *domain, struct dcinfo **ppdc) { @@ -1342,7 +1844,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, struct rpc_pipe_client **cli, POLICY_HND *sam_handle) { struct winbindd_cm_conn *conn; - NTSTATUS result; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; fstring conn_pwd; struct dcinfo *p_dcinfo; @@ -1412,8 +1914,9 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, /* Fall back to schannel if it's a W2K pre-SP1 box. */ if (!cm_get_schannel_dcinfo(domain, &p_dcinfo)) { + /* If this call fails - conn->cli can now be NULL ! */ DEBUG(10, ("cm_connect_sam: Could not get schannel auth info " - "for domain %s, trying anon\n", conn->cli->domain)); + "for domain %s, trying anon\n", domain->name)); goto anonymous; } conn->samr_pipe = cli_rpc_pipe_open_schannel_with_key @@ -1485,7 +1988,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, struct rpc_pipe_client **cli, POLICY_HND *lsa_policy) { struct winbindd_cm_conn *conn; - NTSTATUS result; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; fstring conn_pwd; struct dcinfo *p_dcinfo; @@ -1544,8 +2047,9 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, /* Fall back to schannel if it's a W2K pre-SP1 box. */ if (!cm_get_schannel_dcinfo(domain, &p_dcinfo)) { + /* If this call fails - conn->cli can now be NULL ! */ DEBUG(10, ("cm_connect_lsa: Could not get schannel auth info " - "for domain %s, trying anon\n", conn->cli->domain)); + "for domain %s, trying anon\n", domain->name)); goto anonymous; } conn->lsa_pipe = cli_rpc_pipe_open_schannel_with_key @@ -1588,7 +2092,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, done: if (!NT_STATUS_IS_OK(result)) { invalidate_cm_connection(conn); - return NT_STATUS_UNSUCCESSFUL; + return result; } *cli = conn->lsa_pipe; @@ -1627,7 +2131,7 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, return NT_STATUS_OK; } - if (!get_trust_pw(domain->name, mach_pwd, &sec_chan_type)) { + if (domain->primary && !get_trust_pw(domain->name, mach_pwd, &sec_chan_type)) { return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } @@ -1637,6 +2141,12 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, return result; } + if ( !domain->primary ) { + /* Clear the schannel request bit and drop down */ + neg_flags &= ~NETLOGON_NEG_SCHANNEL; + goto no_schannel; + } + if (lp_client_schannel() != False) { neg_flags |= NETLOGON_NEG_SCHANNEL; } @@ -1681,8 +2191,15 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, return NT_STATUS_ACCESS_DENIED; } + no_schannel: if ((lp_client_schannel() == False) || ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) { + + /* + * NetSamLogonEx only works for schannel + */ + domain->can_do_samlogon_ex = False; + /* We're done - just keep the existing connection to NETLOGON * open */ conn->netlogon_pipe = netlogon_pipe; @@ -1714,6 +2231,11 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, return !NT_STATUS_IS_OK(result) ? result : NT_STATUS_PIPE_NOT_AVAILABLE; } + /* + * Try NetSamLogonEx for AD domains + */ + domain->can_do_samlogon_ex = domain->active_directory; + *cli = conn->netlogon_pipe; return NT_STATUS_OK; }