X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fwinbindd%2Fwinbindd_cm.c;h=134b6da97d425e4cbeb6b66a54cd9294ba459344;hb=33dcb69eaae058933f7cf9cc7e353af0786a7bb1;hp=adb631b57b85b930ac6fee5b9c3d2ff9f25e446a;hpb=bbded540b689d3f0c6ed3ddb61eae3b8a5569372;p=obnox%2Fsamba-ctdb.git diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c index adb631b57b..134b6da97d 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -8,17 +8,17 @@ 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 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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, see . */ @@ -27,14 +27,14 @@ We need to manage connections to domain controllers without having to mess up the main winbindd code with other issues. The aim of the connection manager is to: - + - make connections to domain controllers and cache them - re-establish connections when networks or servers go down - centralise the policy on connection timeouts, domain controller selection etc - manage re-entrancy for when winbindd becomes able to handle multiple outstanding rpc requests - + Why not have connection management as part of the rpc layer like tng? Good question. This code may morph into libsmb/rpc_cache.c or something like that but at the moment it's simply staying as part of winbind. I @@ -171,20 +171,31 @@ 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(); + char *lfile = NULL; - /* Stop zombies */ - CatchChild(); + if (domain->dc_probe_pid != (pid_t)-1) { + /* + * We might already have a DC probe + * child working, check. + */ + if (process_exists_by_pid(domain->dc_probe_pid)) { + DEBUG(10,("fork_child_dc_connect: pid %u already " + "checking for DC's.\n", + (unsigned int)domain->dc_probe_pid)); + return true; + } + domain->dc_probe_pid = (pid_t)-1; + } - child_pid = sys_fork(); + domain->dc_probe_pid = sys_fork(); - if (child_pid == -1) { + if (domain->dc_probe_pid == (pid_t)-1) { DEBUG(0, ("fork_child_dc_connect: Could not fork: %s\n", strerror(errno))); return False; } - if (child_pid != 0) { + if (domain->dc_probe_pid != (pid_t)0) { /* Parent */ messaging_register(winbind_messaging_context(), NULL, MSG_WINBIND_TRY_TO_GO_ONLINE, @@ -199,27 +210,32 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain) /* 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) { - char *logfile; - if (asprintf(&logfile, "%s/log.winbindd-dc-connect", get_dyn_LOGFILEBASE()) > 0) { - lp_set_logfile(logfile); - SAFE_FREE(logfile); - reopen_logs(); + if (asprintf(&lfile, "%s/log.winbindd-dc-connect", get_dyn_LOGFILEBASE()) == -1) { + DEBUG(0, ("fork_child_dc_connect: out of memory.\n")); + _exit(1); } } + if (!winbindd_reinit_after_fork(lfile)) { + 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(1); + } + SAFE_FREE(lfile); + mem_ctx = talloc_init("fork_child_dc_connect"); if (!mem_ctx) { DEBUG(0,("talloc_init failed.\n")); - _exit(0); + 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(1); } if ((!get_dcs(mem_ctx, domain, &dcs, &num_dcs)) || (num_dcs == 0)) { @@ -249,7 +265,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain) static void check_domain_online_handler(struct event_context *ctx, struct timed_event *te, - const struct timeval *now, + struct timeval now, void *private_data) { struct winbindd_domain *domain = @@ -263,7 +279,7 @@ static void check_domain_online_handler(struct event_context *ctx, /* Are we still in "startup" mode ? */ - if (domain->startup && (now->tv_sec > domain->startup_time + 30)) { + if (domain->startup && (now.tv_sec > domain->startup_time + 30)) { /* No longer in "startup" mode. */ DEBUG(10,("check_domain_online_handler: domain %s no longer in 'startup' mode.\n", domain->name )); @@ -292,12 +308,12 @@ static void check_domain_online_handler(struct event_context *ctx, static void calc_new_online_timeout_check(struct winbindd_domain *domain) { - int wbc = lp_winbind_cache_time(); + int wbr = lp_winbind_reconnect_delay(); if (domain->startup) { domain->check_online_timeout = 10; - } else if (domain->check_online_timeout < wbc) { - domain->check_online_timeout = wbc; + } else if (domain->check_online_timeout < wbr) { + domain->check_online_timeout = wbr; } } @@ -337,14 +353,13 @@ 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). */ + lp_winbind_reconnect_delay() seconds (which is 30 seconds by default). */ 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); @@ -361,7 +376,7 @@ void set_domain_offline(struct winbindd_domain *domain) if ( domain->primary ) { struct winbindd_child *idmap = idmap_child(); - + if ( idmap->pid != 0 ) { messaging_send_buf(winbind_messaging_context(), pid_to_procid(idmap->pid), @@ -380,8 +395,6 @@ void set_domain_offline(struct winbindd_domain *domain) static void set_domain_online(struct winbindd_domain *domain) { - struct timeval now; - DEBUG(10,("set_domain_online: called for domain %s\n", domain->name )); @@ -400,9 +413,7 @@ static void set_domain_online(struct winbindd_domain *domain) winbindd_set_locator_kdc_envs(domain); /* If we are waiting to get a krb5 ticket, trigger immediately. */ - GetTimeOfDay(&now); - set_event_dispatch_time(winbind_event_context(), - "krb5_ticket_gain_handler", now); + ccache_regain_all_now(); /* Ok, we're out of any startup mode now... */ domain->startup = False; @@ -440,7 +451,7 @@ static void set_domain_online(struct winbindd_domain *domain) if ( domain->primary ) { struct winbindd_child *idmap = idmap_child(); - + if ( idmap->pid != 0 ) { messaging_send_buf(winbind_messaging_context(), pid_to_procid(idmap->pid), @@ -475,6 +486,15 @@ void set_domain_online_request(struct winbindd_domain *domain) because network manager seems to lie. Wait at least 5 seconds. Heuristics suck... */ + + GetTimeOfDay(&tev); + + /* Go into "startup" mode again. */ + domain->startup_time = tev.tv_sec; + domain->startup = True; + + tev.tv_sec += 5; + if (!domain->check_online_event) { /* If we've come from being globally offline we don't have a check online event handler set. @@ -483,29 +503,20 @@ void set_domain_online_request(struct winbindd_domain *domain) DEBUG(10,("set_domain_online_request: domain %s was globally offline.\n", domain->name )); - - 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); - - /* 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; + TALLOC_FREE(domain->check_online_event); - tev.tv_sec += 5; + domain->check_online_event = event_add_timed(winbind_event_context(), + NULL, + tev, + check_domain_online_handler, + domain); - set_event_dispatch_time(winbind_event_context(), "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"); + } } /**************************************************************** @@ -531,7 +542,7 @@ void winbind_add_failed_connection_entry(const struct winbindd_domain *domain, an authenticated connection if DCs have the RestrictAnonymous registry entry set > 0, or the "Additional restrictions for anonymous connections" set in the win2k Local Security Policy. - + Caller to free() result in domain, username, password */ @@ -540,12 +551,12 @@ static void cm_get_ipc_userpass(char **username, char **domain, char **password) *username = (char *)secrets_fetch(SECRETS_AUTH_USER, NULL); *domain = (char *)secrets_fetch(SECRETS_AUTH_DOMAIN, NULL); *password = (char *)secrets_fetch(SECRETS_AUTH_PASSWORD, NULL); - + if (*username && **username) { if (!*domain || !**domain) *domain = smb_xstrdup(lp_workgroup()); - + if (!*password || !**password) *password = smb_xstrdup(""); @@ -599,7 +610,7 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain, /* 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); + orig_timeout = rpccli_set_timeout(netlogon_pipe, 35000); if (our_domain->active_directory) { struct netr_DsRGetDCNameInfo *domain_info = NULL; @@ -613,7 +624,7 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain, DS_RETURN_DNS_NAME, &domain_info, &werr); - if (W_ERROR_IS_OK(werr)) { + if (NT_STATUS_IS_OK(result) && W_ERROR_IS_OK(werr)) { tmp = talloc_strdup( mem_ctx, domain_info->dc_unc); if (tmp == NULL) { @@ -639,7 +650,7 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain, } /* And restore our original timeout. */ - cli_set_timeout(netlogon_pipe->cli, orig_timeout); + rpccli_set_timeout(netlogon_pipe, orig_timeout); if (!NT_STATUS_IS_OK(result)) { DEBUG(10,("rpccli_netr_GetAnyDCName failed: %s\n", @@ -650,19 +661,13 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain, if (!W_ERROR_IS_OK(werr)) { DEBUG(10,("rpccli_netr_GetAnyDCName failed: %s\n", - dos_errstr(werr))); + win_errstr(werr))); talloc_destroy(mem_ctx); return false; } /* rpccli_netr_GetAnyDCName gives us a name with \\ */ - p = tmp; - if (*p == '\\') { - p+=1; - } - if (*p == '\\') { - p+=1; - } + p = strip_hostname(tmp); fstrcpy(dcname, p); @@ -687,7 +692,7 @@ static NTSTATUS get_trust_creds(const struct winbindd_domain *domain, { const char *account_name; const char *name = NULL; - + /* If we are a DC and this is not our own domain */ if (IS_DC) { @@ -697,10 +702,10 @@ static NTSTATUS get_trust_creds(const struct winbindd_domain *domain, if (!our_domain) return NT_STATUS_INVALID_SERVER_STATE; - + name = our_domain->name; } - + if (!get_trust_pw_clear(name, machine_password, &account_name, NULL)) { @@ -713,12 +718,18 @@ static NTSTATUS get_trust_creds(const struct winbindd_domain *domain, return NT_STATUS_NO_MEMORY; } - /* this is at least correct when domain is our domain, - * which is the only case, when this is currently used: */ + /* For now assume our machine account only exists in our domain */ + if (machine_krb5_principal != NULL) { + struct winbindd_domain *our_domain = find_our_domain(); + + if (!our_domain) { + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + if (asprintf(machine_krb5_principal, "%s$@%s", - account_name, domain->alt_name) == -1) + account_name, our_domain->alt_name) == -1) { return NT_STATUS_NO_MEMORY; } @@ -792,30 +803,14 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, goto done; } - if (ntohs(peeraddr_in->sin_port) == 139) { - struct nmb_name calling; - struct nmb_name called; + result = cli_negprot(*cli); - make_nmb_name(&calling, global_myname(), 0x0); - make_nmb_name(&called, "*SMBSERVER", 0x20); - - if (!cli_session_request(*cli, &calling, &called)) { - DEBUG(8, ("cli_session_request failed for %s\n", - controller)); - result = NT_STATUS_UNSUCCESSFUL; - goto done; - } - } - - cli_setup_signing_state(*cli, Undefined); - - if (!cli_negprot(*cli)) { - DEBUG(1, ("cli_negprot failed\n")); - result = NT_STATUS_UNSUCCESSFUL; + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1, ("cli_negprot failed: %s\n", nt_errstr(result))); goto done; } - if (!is_trusted_domain_situation(domain->name) && + if (!is_dc_trusted_domain_situation(domain->name) && (*cli)->protocol >= PROTOCOL_NT1 && (*cli)->capabilities & CAP_EXTENDED_SECURITY) { @@ -834,15 +829,16 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, (*cli)->use_kerberos = True; DEBUG(5, ("connecting to %s from %s with kerberos principal " - "[%s]\n", controller, global_myname(), - machine_krb5_principal)); + "[%s] and realm [%s]\n", controller, global_myname(), + machine_krb5_principal, domain->alt_name)); winbindd_set_locator_kdc_envs(domain); ads_status = cli_session_setup_spnego(*cli, machine_krb5_principal, - machine_password, - domain->name); + machine_password, + lp_workgroup(), + domain->alt_name); if (!ADS_ERR_OK(ads_status)) { DEBUG(4,("failed kerberos session setup with %s\n", @@ -852,7 +848,10 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, result = ads_ntstatus(ads_status); if (NT_STATUS_IS_OK(result)) { /* Ensure creds are stored for NTLMSSP authenticated pipe access. */ - cli_init_creds(*cli, machine_account, domain->name, machine_password); + result = cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } goto session_setup_done; } } @@ -862,12 +861,13 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, DEBUG(5, ("connecting to %s from %s with username " "[%s]\\[%s]\n", controller, global_myname(), - domain->name, machine_account)); + lp_workgroup(), machine_account)); ads_status = cli_session_setup_spnego(*cli, machine_account, machine_password, - domain->name); + lp_workgroup(), + NULL); if (!ADS_ERR_OK(ads_status)) { DEBUG(4, ("authenticated session setup failed with %s\n", ads_errstr(ads_status))); @@ -876,7 +876,10 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, result = ads_ntstatus(ads_status); if (NT_STATUS_IS_OK(result)) { /* Ensure creds are stored for NTLMSSP authenticated pipe access. */ - cli_init_creds(*cli, machine_account, domain->name, machine_password); + result = cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } goto session_setup_done; } } @@ -902,7 +905,10 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, ipc_password, strlen(ipc_password)+1, ipc_domain))) { /* Successful logon with given username. */ - cli_init_creds(*cli, ipc_username, ipc_domain, ipc_password); + result = cli_init_creds(*cli, ipc_username, ipc_domain, ipc_password); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } goto session_setup_done; } else { DEBUG(4, ("authenticated session setup with user %s\\%s failed.\n", @@ -913,11 +919,17 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, anon_fallback: /* Fall back to anonymous connection, this might fail later */ + DEBUG(10,("cm_prepare_connection: falling back to anonymous " + "connection for DC %s\n", + controller )); if (NT_STATUS_IS_OK(cli_session_setup(*cli, "", NULL, 0, NULL, 0, ""))) { DEBUG(5, ("Connected anonymously\n")); - cli_init_creds(*cli, "", "", ""); + result = cli_init_creds(*cli, "", "", ""); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } goto session_setup_done; } @@ -941,15 +953,10 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, winbindd_set_locator_kdc_envs(domain); - if (!cli_send_tconX(*cli, "IPC$", "IPC", "", 0)) { - - result = cli_nt_error(*cli); + result = cli_tcon_andx(*cli, "IPC$", "IPC", "", 0); + if (!NT_STATUS_IS_OK(result)) { DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result))); - - if (NT_STATUS_IS_OK(result)) - result = NT_STATUS_UNSUCCESSFUL; - goto done; } @@ -957,8 +964,11 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, *retry = False; /* set the domain if empty; needed for schannel connections */ - if ( !*(*cli)->domain ) { - fstrcpy( (*cli)->domain, domain->name ); + if ( !(*cli)->domain[0] ) { + result = cli_set_domain((*cli), domain->name); + if (!NT_STATUS_IS_OK(result)) { + return result; + } } result = NT_STATUS_OK; @@ -983,15 +993,37 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, return result; } +/******************************************************************* + Add a dcname and sockaddr_storage pair to the end of a dc_name_ip + array. + + Keeps the list unique by not adding duplicate entries. + + @param[in] mem_ctx talloc memory context to allocate from + @param[in] domain_name domain of the DC + @param[in] dcname name of the DC to add to the list + @param[in] pss Internet address and port pair to add to the list + @param[in,out] dcs array of dc_name_ip structures to add to + @param[in,out] num_dcs number of dcs returned in the dcs array + @return true if the list was added to, false otherwise +*******************************************************************/ + static bool add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name, const char *dcname, struct sockaddr_storage *pss, struct dc_name_ip **dcs, int *num) { + int i = 0; + if (!NT_STATUS_IS_OK(check_negative_conn_cache(domain_name, dcname))) { DEBUG(10, ("DC %s was in the negative conn cache\n", dcname)); return False; } + /* Make sure there's no duplicates in the list */ + for (i=0; i<*num; i++) + if (sockaddr_equal((struct sockaddr *)&(*dcs)[i].ss, (struct sockaddr *)pss)) + return False; + *dcs = TALLOC_REALLOC_ARRAY(mem_ctx, *dcs, struct dc_name_ip, (*num)+1); if (*dcs == NULL) @@ -1015,7 +1047,7 @@ static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx, } (*addrs)[*num] = *pss; - set_sockaddr_port(&(*addrs)[*num], port); + set_sockaddr_port((struct sockaddr *)&(*addrs)[*num], port); *num += 1; return True; @@ -1025,11 +1057,13 @@ static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx, convert an ip to a name *******************************************************************/ -static bool dcip_to_name(const struct winbindd_domain *domain, +static bool dcip_to_name(TALLOC_CTX *mem_ctx, + const struct winbindd_domain *domain, struct sockaddr_storage *pss, fstring name ) { struct ip_service ip_list; + uint32_t nt_version = NETLOGON_NT_VERSION_1; ip_list.ss = *pss; ip_list.port = 0; @@ -1040,21 +1074,23 @@ static bool dcip_to_name(const struct winbindd_domain *domain, if (lp_security() == SEC_ADS) { ADS_STRUCT *ads; + ADS_STATUS ads_status; char addr[INET6_ADDRSTRLEN]; print_sockaddr(addr, sizeof(addr), pss); - ads = ads_init(domain->alt_name, domain->name, NULL); + ads = ads_init(domain->alt_name, domain->name, addr); ads->auth.flags |= ADS_AUTH_NO_BIND; - if (ads_try_connect(ads, addr)) { + ads_status = ads_connect(ads); + if (ADS_ERR_OK(ads_status)) { /* We got a cldap packet. */ fstrcpy(name, ads->config.ldap_server_name); namecache_store(name, 0x20, 1, &ip_list); DEBUG(10,("dcip_to_name: flags = 0x%x\n", (unsigned int)ads->config.flags)); - if (domain->primary && (ads->config.flags & ADS_KDC)) { + if (domain->primary && (ads->config.flags & NBT_SERVER_KDC)) { if (ads_closest_dc(ads)) { char *sitename = sitename_fetch(ads->config.realm); @@ -1092,12 +1128,17 @@ static bool dcip_to_name(const struct winbindd_domain *domain, /* try GETDC requests next */ - if (send_getdc_request(winbind_messaging_context(), - pss, domain->name, &domain->sid)) { + if (send_getdc_request(mem_ctx, winbind_messaging_context(), + pss, domain->name, &domain->sid, + nt_version)) { + const char *dc_name = NULL; int i; smb_msleep(100); for (i=0; i<5; i++) { - if (receive_getdc_response(pss, domain->name, name)) { + if (receive_getdc_response(mem_ctx, pss, domain->name, + &nt_version, + &dc_name, NULL)) { + fstrcpy(name, dc_name); namecache_store(name, 0x20, 1, &ip_list); return True; } @@ -1115,8 +1156,15 @@ static bool dcip_to_name(const struct winbindd_domain *domain, } /******************************************************************* - Retreive a list of IP address for domain controllers. Fill in - the dcs[] with results. + Retrieve a list of IP addresses for domain controllers. + + The array is sorted in the preferred connection order. + + @param[in] mem_ctx talloc memory context to allocate from + @param[in] domain domain to retrieve DCs for + @param[out] dcs array of dcs that will be returned + @param[out] num_dcs number of dcs returned in the dcs array + @return always true *******************************************************************/ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, @@ -1132,9 +1180,11 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, is_our_domain = strequal(domain->name, lp_workgroup()); + /* If not our domain, get the preferred DC, by asking our primary DC */ if ( !is_our_domain && get_dc_name_via_netlogon(domain, dcname, &ss) - && add_one_dc_unique(mem_ctx, domain->name, dcname, &ss, dcs, num_dcs) ) + && add_one_dc_unique(mem_ctx, domain->name, dcname, &ss, dcs, + num_dcs) ) { char addr[INET6_ADDRSTRLEN]; print_sockaddr(addr, sizeof(addr), &ss); @@ -1161,8 +1211,13 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, if (sitename) { /* Do the site-specific AD dns lookup first. */ - get_sorted_dc_list(domain->alt_name, sitename, &ip_list, &iplist_size, True); + get_sorted_dc_list(domain->alt_name, sitename, &ip_list, + &iplist_size, True); + /* Add ips to the DC array. We don't look up the name + of the DC in this function, but we fill in the char* + of the ip now to make the failed connection cache + work */ for ( i=0; ialt_name, NULL, &ip_list, &iplist_size, True); + /* 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, NULL, &ip_list, &iplist_size, False); - } + SAFE_FREE(ip_list); + iplist_size = 0; + } - /* FIXME!! this is where we should re-insert the GETDC requests --jerry */ + /* Try standard netbios queries if no ADS */ + if (*num_dcs == 0) { + get_sorted_dc_list(domain->name, NULL, &ip_list, &iplist_size, + False); - /* now add to the dc array. We'll wait until the last minute - to look up the name of the DC. But we fill in the char* for - the ip now in to make the failed connection cache work */ + for ( i=0; iname, + addr, + &ip_list[i].ss, + dcs, + num_dcs); + } - for ( i=0; iname, addr, - &ip_list[i].ss, dcs, num_dcs); + SAFE_FREE(ip_list); + iplist_size = 0; } - SAFE_FREE( ip_list ); - return True; } +/******************************************************************* + Find and make a connection to a DC in the given domain. + + @param[in] mem_ctx talloc memory context to allocate from + @param[in] domain domain to find a dc in + @param[out] dcname NetBIOS or FQDN of DC that's connected to + @param[out] pss DC Internet address and port + @param[out] fd fd of the open socket connected to the newly found dc + @return true when a DC connection is made, false otherwise +*******************************************************************/ + static bool find_new_dc(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, fstring dcname, struct sockaddr_storage *pss, int *fd) @@ -1234,7 +1303,12 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx, struct sockaddr_storage *addrs = NULL; int num_addrs = 0; - int i, fd_index; + int i; + size_t fd_index; + + NTSTATUS status; + + *fd = -1; again: if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs) || (num_dcs == 0)) @@ -1250,15 +1324,6 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx, &addrs, &num_addrs)) { return False; } - - if (!add_string_to_array(mem_ctx, dcs[i].name, - &dcnames, &num_dcnames)) { - return False; - } - if (!add_sockaddr_to_array(mem_ctx, &dcs[i].ss, 139, - &addrs, &num_addrs)) { - return False; - } } if ((num_dcnames == 0) || (num_dcnames != num_addrs)) @@ -1267,14 +1332,15 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx, if ((addrs == NULL) || (dcnames == NULL)) return False; - /* 5 second timeout. */ - if (!open_any_socket_out(addrs, num_addrs, 5000, &fd_index, fd) ) { + status = smbsock_any_connect(addrs, dcnames, num_addrs, + fd, &fd_index, NULL); + if (!NT_STATUS_IS_OK(status)) { for (i=0; iname, ab, strerror(errno) )); + domain->name, ab, nt_errstr(status) )); winbind_add_failed_connection_entry(domain, dcs[i].name, NT_STATUS_UNSUCCESSFUL); } @@ -1290,16 +1356,112 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx, } /* Try to figure out the name */ - if (dcip_to_name(domain, pss, dcname)) { + if (dcip_to_name(mem_ctx, domain, pss, dcname)) { return True; } /* We can not continue without the DC's name */ winbind_add_failed_connection_entry(domain, dcs[fd_index].name, NT_STATUS_UNSUCCESSFUL); + + /* Throw away all arrays as we're doing this again. */ + TALLOC_FREE(dcs); + num_dcs = 0; + + TALLOC_FREE(dcnames); + num_dcnames = 0; + + TALLOC_FREE(addrs); + num_addrs = 0; + + close(*fd); + *fd = -1; + goto again; } +static char *current_dc_key(TALLOC_CTX *mem_ctx, const char *domain_name) +{ + return talloc_asprintf_strupper_m(mem_ctx, "CURRENT_DCNAME/%s", + domain_name); +} + +static void store_current_dc_in_gencache(const char *domain_name, + const char *dc_name, + struct cli_state *cli) +{ + char addr[INET6_ADDRSTRLEN]; + char *key, *value; + + if (cli == NULL) { + return; + } + if (cli->fd == -1) { + return; + } + get_peer_addr(cli->fd, addr, sizeof(addr)); + + key = current_dc_key(talloc_tos(), domain_name); + if (key == NULL) { + goto done; + } + + value = talloc_asprintf(talloc_tos(), "%s %s", addr, dc_name); + if (value == NULL) { + goto done; + } + + gencache_set(key, value, 0x7ffffffff); +done: + TALLOC_FREE(value); + TALLOC_FREE(key); +} + +bool fetch_current_dc_from_gencache(TALLOC_CTX *mem_ctx, + const char *domain_name, + char **p_dc_name, char **p_dc_ip) +{ + char *key, *value, *p; + bool ret = false; + char *dc_name = NULL; + char *dc_ip = NULL; + + key = current_dc_key(talloc_tos(), domain_name); + if (key == NULL) { + goto done; + } + if (!gencache_get(key, &value, NULL)) { + goto done; + } + p = strchr(value, ' '); + if (p == NULL) { + goto done; + } + dc_ip = talloc_strndup(mem_ctx, value, p - value); + if (dc_ip == NULL) { + goto done; + } + dc_name = talloc_strdup(mem_ctx, p+1); + if (dc_name == NULL) { + goto done; + } + + if (p_dc_ip != NULL) { + *p_dc_ip = dc_ip; + dc_ip = NULL; + } + if (p_dc_name != NULL) { + *p_dc_name = dc_name; + dc_name = NULL; + } + ret = true; +done: + TALLOC_FREE(dc_name); + TALLOC_FREE(dc_ip); + TALLOC_FREE(key); + return ret; +} + static NTSTATUS cm_open_connection(struct winbindd_domain *domain, struct winbindd_cm_conn *new_conn) { @@ -1316,7 +1478,7 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, /* we have to check the server affinity cache here since later we selecte a DC based on response time and not preference */ - + /* Check the negative connection cache before talking to it. It going down may have triggered the reconnection. */ @@ -1335,7 +1497,7 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, AI_NUMERICHOST)) { return NT_STATUS_UNSUCCESSFUL; } - if (dcip_to_name( domain, &ss, saf_name )) { + if (dcip_to_name(mem_ctx, domain, &ss, saf_name )) { fstrcpy( domain->dcname, saf_name ); } else { winbind_add_failed_connection_entry( @@ -1362,23 +1524,11 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname)) && (resolve_name(domain->dcname, &domain->dcaddr, 0x20))) { - struct sockaddr_storage *addrs = NULL; - int num_addrs = 0; - int dummy = 0; - - if (!add_sockaddr_to_array(mem_ctx, &domain->dcaddr, 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, 139, &addrs, &num_addrs)) { - set_domain_offline(domain); - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; - } + NTSTATUS status; - /* 5 second timeout. */ - if (!open_any_socket_out(addrs, num_addrs, 5000, &dummy, &fd)) { + status = smbsock_connect(&domain->dcaddr, NULL, NULL, + &fd, NULL); + if (!NT_STATUS_IS_OK(status)) { fd = -1; } } @@ -1412,6 +1562,17 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, set_global_winbindd_state_online(); } set_domain_online(domain); + + /* + * Much as I hate global state, this seems to be the point + * where we can be certain that we have a proper connection to + * a DC. wbinfo --dc-info needs that information, store it in + * gencache with a looong timeout. This will need revisiting + * once we start to connect to multiple DCs, wbcDcInfo is + * already prepared for that. + */ + store_current_dc_in_gencache(domain->name, domain->dcname, + new_conn->cli); } else { /* Ensure we setup the retry handler. */ set_domain_offline(domain); @@ -1433,33 +1594,35 @@ void invalidate_cm_connection(struct winbindd_cm_conn *conn) } if (conn->samr_pipe != NULL) { - if (!cli_rpc_pipe_close(conn->samr_pipe)) { - /* Ok, it must be dead. Drop timeout to 0.5 sec. */ - if (conn->cli) { - cli_set_timeout(conn->cli, 500); - } + TALLOC_FREE(conn->samr_pipe); + /* Ok, it must be dead. Drop timeout to 0.5 sec. */ + if (conn->cli) { + cli_set_timeout(conn->cli, 500); } - conn->samr_pipe = NULL; } if (conn->lsa_pipe != NULL) { - if (!cli_rpc_pipe_close(conn->lsa_pipe)) { - /* Ok, it must be dead. Drop timeout to 0.5 sec. */ - if (conn->cli) { - cli_set_timeout(conn->cli, 500); - } + TALLOC_FREE(conn->lsa_pipe); + /* Ok, it must be dead. Drop timeout to 0.5 sec. */ + if (conn->cli) { + cli_set_timeout(conn->cli, 500); + } + } + + if (conn->lsa_pipe_tcp != NULL) { + TALLOC_FREE(conn->lsa_pipe_tcp); + /* Ok, it must be dead. Drop timeout to 0.5 sec. */ + if (conn->cli) { + cli_set_timeout(conn->cli, 500); } - conn->lsa_pipe = NULL; } if (conn->netlogon_pipe != NULL) { - if (!cli_rpc_pipe_close(conn->netlogon_pipe)) { - /* Ok, it must be dead. Drop timeout to 0.5 sec. */ - if (conn->cli) { - cli_set_timeout(conn->cli, 500); - } + TALLOC_FREE(conn->netlogon_pipe); + /* Ok, it must be dead. Drop timeout to 0.5 sec. */ + if (conn->cli) { + cli_set_timeout(conn->cli, 500); } - conn->netlogon_pipe = NULL; } if (conn->cli) { @@ -1487,21 +1650,11 @@ void close_conns_after_fork(void) static bool connection_ok(struct winbindd_domain *domain) { - if (domain->conn.cli == NULL) { - DEBUG(8, ("connection_ok: Connection to %s for domain %s has NULL " - "cli!\n", domain->dcname, domain->name)); - return False; - } - - if (!domain->conn.cli->initialised) { - DEBUG(3, ("connection_ok: Connection to %s for domain %s was never " - "initialised!\n", domain->dcname, domain->name)); - return False; - } + bool ok; - if (domain->conn.cli->fd == -1) { - DEBUG(3, ("connection_ok: Connection to %s for domain %s has died or was " - "never started (fd == -1)\n", + ok = cli_state_is_connected(domain->conn.cli); + if (!ok) { + DEBUG(3, ("connection_ok: Connection to %s for domain %s is not connected\n", domain->dcname, domain->name)); return False; } @@ -1527,6 +1680,12 @@ static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain) return NT_STATUS_OK; } + if (!winbindd_can_contact_domain(domain)) { + invalidate_cm_connection(&domain->conn); + domain->initialized = True; + return NT_STATUS_OK; + } + if (connection_ok(domain)) { if (!domain->initialized) { set_dc_type_and_flags(domain); @@ -1555,6 +1714,23 @@ NTSTATUS init_dc_connection(struct winbindd_domain *domain) return init_dc_connection_network(domain); } +static NTSTATUS init_dc_connection_rpc(struct winbindd_domain *domain) +{ + NTSTATUS status; + + status = init_dc_connection(domain); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!domain->internal && domain->conn.cli == NULL) { + /* happens for trusted domains without inbound trust */ + return NT_STATUS_TRUSTED_DOMAIN_FAILURE; + } + + return NT_STATUS_OK; +} + /****************************************************************************** Set the trust flags (direction and forest location) for a domain ******************************************************************************/ @@ -1572,26 +1748,26 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain ) 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 */ @@ -1610,7 +1786,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain ) } result = rpccli_netr_DsrEnumerateDomainTrusts(cli, mem_ctx, - cli->cli->desthost, + cli->desthost, flags, &trusts, NULL); @@ -1650,15 +1826,12 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain ) domain->initialized = True; - if ( !winbindd_can_contact_domain( domain) ) - domain->internal = True; - break; } } - + talloc_destroy( mem_ctx ); - + return domain->initialized; } @@ -1675,8 +1848,8 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain ) NTSTATUS result; WERROR werr; TALLOC_CTX *mem_ctx = NULL; - struct rpc_pipe_client *cli; - POLICY_HND pol; + struct rpc_pipe_client *cli = NULL; + struct policy_handle pol; union dssetup_DsRoleInfo info; union lsa_PolicyInformation *lsa_info = NULL; @@ -1693,10 +1866,11 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain ) DEBUG(5, ("set_dc_type_and_flags_connect: domain %s\n", domain->name )); - cli = cli_rpc_pipe_open_noauth(domain->conn.cli, PI_DSSETUP, - &result); + result = cli_rpc_pipe_open_noauth(domain->conn.cli, + &ndr_table_dssetup.syntax_id, + &cli); - if (cli == NULL) { + if (!NT_STATUS_IS_OK(result)) { DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to " "PI_DSSETUP on domain %s: (%s)\n", domain->name, nt_errstr(result))); @@ -1712,7 +1886,7 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain ) DS_ROLE_BASIC_INFORMATION, &info, &werr); - cli_rpc_pipe_close(cli); + TALLOC_FREE(cli); if (!NT_STATUS_IS_OK(result)) { DEBUG(5, ("set_dc_type_and_flags_connect: rpccli_ds_getprimarydominfo " @@ -1740,20 +1914,21 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain ) } no_dssetup: - cli = cli_rpc_pipe_open_noauth(domain->conn.cli, PI_LSARPC, &result); + result = cli_rpc_pipe_open_noauth(domain->conn.cli, + &ndr_table_lsarpc.syntax_id, &cli); - if (cli == NULL) { + if (!NT_STATUS_IS_OK(result)) { 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); + TALLOC_FREE(cli); TALLOC_FREE(mem_ctx); return; } result = rpccli_lsa_open_policy2(cli, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, &pol); - + SEC_FLAG_MAXIMUM_ALLOWED, &pol); + if (NT_STATUS_IS_OK(result)) { /* This particular query is exactly what Win2k clients use to determine that the DC is active directory */ @@ -1794,7 +1969,7 @@ no_dssetup: domain->active_directory = False; result = rpccli_lsa_open_policy(cli, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, + SEC_FLAG_MAXIMUM_ALLOWED, &pol); if (!NT_STATUS_IS_OK(result)) { @@ -1826,7 +2001,9 @@ done: 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); + domain->can_do_ncacn_ip_tcp = domain->active_directory; + + TALLOC_FREE(cli); TALLOC_FREE(mem_ctx); @@ -1882,32 +2059,37 @@ static bool cm_get_schannel_dcinfo(struct winbindd_domain *domain, /* Return a pointer to the struct dcinfo from the netlogon pipe. */ + if (!domain->conn.netlogon_pipe->dc) { + return false; + } + *ppdc = domain->conn.netlogon_pipe->dc; return True; } NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, - struct rpc_pipe_client **cli, POLICY_HND *sam_handle) + struct rpc_pipe_client **cli, struct policy_handle *sam_handle) { struct winbindd_cm_conn *conn; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - fstring conn_pwd; struct dcinfo *p_dcinfo; char *machine_password = NULL; char *machine_account = NULL; char *domain_name = NULL; - result = init_dc_connection(domain); + result = init_dc_connection_rpc(domain); if (!NT_STATUS_IS_OK(result)) { return result; } conn = &domain->conn; - if (conn->samr_pipe != NULL) { + if (rpccli_is_connected(conn->samr_pipe)) { goto done; } + TALLOC_FREE(conn->samr_pipe); + /* * No SAMR pipe yet. Attempt to get an NTLMSSP SPNEGO authenticated * sign and sealed pipe using the machine account password by @@ -1915,10 +2097,9 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, * anonymous. */ - pwd_get_cleartext(&conn->cli->pwd, conn_pwd); if ((conn->cli->user_name[0] == '\0') || (conn->cli->domain[0] == '\0') || - (conn_pwd[0] == '\0')) + (conn->cli->password == NULL || conn->cli->password[0] == '\0')) { result = get_trust_creds(domain, &machine_password, &machine_account, NULL); @@ -1929,7 +2110,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, } domain_name = domain->name; } else { - machine_password = SMB_STRDUP(conn_pwd); + machine_password = SMB_STRDUP(conn->cli->password); machine_account = SMB_STRDUP(conn->cli->user_name); domain_name = conn->cli->domain; } @@ -1941,14 +2122,16 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, /* We have an authenticated connection. Use a NTLMSSP SPNEGO authenticated SAMR pipe with sign & seal. */ - conn->samr_pipe = - cli_rpc_pipe_open_spnego_ntlmssp(conn->cli, PI_SAMR, - PIPE_AUTH_LEVEL_PRIVACY, - domain_name, - machine_account, - machine_password, &result); - - if (conn->samr_pipe == NULL) { + result = cli_rpc_pipe_open_spnego_ntlmssp(conn->cli, + &ndr_table_samr.syntax_id, + NCACN_NP, + PIPE_AUTH_LEVEL_PRIVACY, + domain_name, + machine_account, + machine_password, + &conn->samr_pipe); + + if (!NT_STATUS_IS_OK(result)) { DEBUG(10,("cm_connect_sam: failed to connect to SAMR " "pipe for domain %s using NTLMSSP " "authenticated pipe: user %s\\%s. Error was " @@ -1963,8 +2146,8 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, domain_name, machine_account)); result = rpccli_samr_Connect2(conn->samr_pipe, mem_ctx, - conn->samr_pipe->cli->desthost, - SEC_RIGHTS_MAXIMUM_ALLOWED, + conn->samr_pipe->desthost, + SEC_FLAG_MAXIMUM_ALLOWED, &conn->sam_connect_handle); if (NT_STATUS_IS_OK(result)) { goto open_domain; @@ -1972,7 +2155,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, DEBUG(10,("cm_connect_sam: ntlmssp-sealed rpccli_samr_Connect2 " "failed for domain %s, error was %s. Trying schannel\n", domain->name, nt_errstr(result) )); - cli_rpc_pipe_close(conn->samr_pipe); + TALLOC_FREE(conn->samr_pipe); schannel: @@ -1984,11 +2167,12 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, "for domain %s, trying anon\n", domain->name)); goto anonymous; } - conn->samr_pipe = cli_rpc_pipe_open_schannel_with_key - (conn->cli, PI_SAMR, PIPE_AUTH_LEVEL_PRIVACY, - domain->name, p_dcinfo, &result); + result = cli_rpc_pipe_open_schannel_with_key + (conn->cli, &ndr_table_samr.syntax_id, NCACN_NP, + PIPE_AUTH_LEVEL_PRIVACY, + domain->name, p_dcinfo, &conn->samr_pipe); - if (conn->samr_pipe == NULL) { + if (!NT_STATUS_IS_OK(result)) { DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for " "domain %s using schannel. Error was %s\n", domain->name, nt_errstr(result) )); @@ -1998,8 +2182,8 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, "schannel.\n", domain->name )); result = rpccli_samr_Connect2(conn->samr_pipe, mem_ctx, - conn->samr_pipe->cli->desthost, - SEC_RIGHTS_MAXIMUM_ALLOWED, + conn->samr_pipe->desthost, + SEC_FLAG_MAXIMUM_ALLOWED, &conn->sam_connect_handle); if (NT_STATUS_IS_OK(result)) { goto open_domain; @@ -2007,22 +2191,21 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, DEBUG(10,("cm_connect_sam: schannel-sealed rpccli_samr_Connect2 failed " "for domain %s, error was %s. Trying anonymous\n", domain->name, nt_errstr(result) )); - cli_rpc_pipe_close(conn->samr_pipe); + TALLOC_FREE(conn->samr_pipe); anonymous: /* Finally fall back to anonymous. */ - conn->samr_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_SAMR, - &result); + result = cli_rpc_pipe_open_noauth(conn->cli, &ndr_table_samr.syntax_id, + &conn->samr_pipe); - if (conn->samr_pipe == NULL) { - result = NT_STATUS_PIPE_NOT_AVAILABLE; + if (!NT_STATUS_IS_OK(result)) { goto done; } result = rpccli_samr_Connect2(conn->samr_pipe, mem_ctx, - conn->samr_pipe->cli->desthost, - SEC_RIGHTS_MAXIMUM_ALLOWED, + conn->samr_pipe->desthost, + SEC_FLAG_MAXIMUM_ALLOWED, &conn->sam_connect_handle); if (!NT_STATUS_IS_OK(result)) { DEBUG(10,("cm_connect_sam: rpccli_samr_Connect2 failed " @@ -2035,13 +2218,24 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, result = rpccli_samr_OpenDomain(conn->samr_pipe, mem_ctx, &conn->sam_connect_handle, - SEC_RIGHTS_MAXIMUM_ALLOWED, + SEC_FLAG_MAXIMUM_ALLOWED, &domain->sid, &conn->sam_domain_handle); done: - if (!NT_STATUS_IS_OK(result)) { + if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) { + /* + * if we got access denied, we might just have no access rights + * to talk to the remote samr server server (e.g. when we are a + * PDC and we are connecting a w2k8 pdc via an interdomain + * trust). In that case do not invalidate the whole connection + * stack + */ + TALLOC_FREE(conn->samr_pipe); + ZERO_STRUCT(conn->sam_domain_handle); + return result; + } else if (!NT_STATUS_IS_OK(result)) { invalidate_cm_connection(conn); return result; } @@ -2053,28 +2247,80 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, return result; } +/********************************************************************** + open an schanneld ncacn_ip_tcp connection to LSA +***********************************************************************/ + +NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + struct rpc_pipe_client **cli) +{ + struct winbindd_cm_conn *conn; + NTSTATUS status; + + DEBUG(10,("cm_connect_lsa_tcp\n")); + + status = init_dc_connection_rpc(domain); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + conn = &domain->conn; + + if (conn->lsa_pipe_tcp && + conn->lsa_pipe_tcp->transport->transport == NCACN_IP_TCP && + conn->lsa_pipe_tcp->auth->auth_level == PIPE_AUTH_LEVEL_PRIVACY && + rpccli_is_connected(conn->lsa_pipe_tcp)) { + goto done; + } + + TALLOC_FREE(conn->lsa_pipe_tcp); + + status = cli_rpc_pipe_open_schannel(conn->cli, + &ndr_table_lsarpc.syntax_id, + NCACN_IP_TCP, + PIPE_AUTH_LEVEL_PRIVACY, + domain->name, + &conn->lsa_pipe_tcp); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("cli_rpc_pipe_open_schannel failed: %s\n", + nt_errstr(status))); + goto done; + } + + done: + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(conn->lsa_pipe_tcp); + return status; + } + + *cli = conn->lsa_pipe_tcp; + + return status; +} + NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, - struct rpc_pipe_client **cli, POLICY_HND *lsa_policy) + struct rpc_pipe_client **cli, struct policy_handle *lsa_policy) { struct winbindd_cm_conn *conn; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - fstring conn_pwd; struct dcinfo *p_dcinfo; - result = init_dc_connection(domain); + result = init_dc_connection_rpc(domain); if (!NT_STATUS_IS_OK(result)) return result; conn = &domain->conn; - if (conn->lsa_pipe != NULL) { + if (rpccli_is_connected(conn->lsa_pipe)) { goto done; } - pwd_get_cleartext(&conn->cli->pwd, conn_pwd); + TALLOC_FREE(conn->lsa_pipe); + if ((conn->cli->user_name[0] == '\0') || (conn->cli->domain[0] == '\0') || - (conn_pwd[0] == '\0')) { + (conn->cli->password == NULL || conn->cli->password[0] == '\0')) { DEBUG(10, ("cm_connect_lsa: No no user available for " "domain %s, trying schannel\n", conn->cli->domain)); goto schannel; @@ -2082,11 +2328,13 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, /* We have an authenticated connection. Use a NTLMSSP SPNEGO * authenticated LSA pipe with sign & seal. */ - conn->lsa_pipe = cli_rpc_pipe_open_spnego_ntlmssp - (conn->cli, PI_LSARPC, PIPE_AUTH_LEVEL_PRIVACY, - conn->cli->domain, conn->cli->user_name, conn_pwd, &result); + result = cli_rpc_pipe_open_spnego_ntlmssp + (conn->cli, &ndr_table_lsarpc.syntax_id, NCACN_NP, + PIPE_AUTH_LEVEL_PRIVACY, + conn->cli->domain, conn->cli->user_name, conn->cli->password, + &conn->lsa_pipe); - if (conn->lsa_pipe == NULL) { + if (!NT_STATUS_IS_OK(result)) { DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for " "domain %s using NTLMSSP authenticated pipe: user " "%s\\%s. Error was %s. Trying schannel.\n", @@ -2100,7 +2348,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, domain->name, conn->cli->domain, conn->cli->user_name )); result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, + SEC_FLAG_MAXIMUM_ALLOWED, &conn->lsa_policy); if (NT_STATUS_IS_OK(result)) { goto done; @@ -2109,7 +2357,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying " "schannel\n")); - cli_rpc_pipe_close(conn->lsa_pipe); + TALLOC_FREE(conn->lsa_pipe); schannel: @@ -2121,11 +2369,12 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, "for domain %s, trying anon\n", domain->name)); goto anonymous; } - conn->lsa_pipe = cli_rpc_pipe_open_schannel_with_key - (conn->cli, PI_LSARPC, PIPE_AUTH_LEVEL_PRIVACY, - domain->name, p_dcinfo, &result); + result = cli_rpc_pipe_open_schannel_with_key + (conn->cli, &ndr_table_lsarpc.syntax_id, NCACN_NP, + PIPE_AUTH_LEVEL_PRIVACY, + domain->name, p_dcinfo, &conn->lsa_pipe); - if (conn->lsa_pipe == NULL) { + if (!NT_STATUS_IS_OK(result)) { DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for " "domain %s using schannel. Error was %s\n", domain->name, nt_errstr(result) )); @@ -2135,7 +2384,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, "schannel.\n", domain->name )); result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, + SEC_FLAG_MAXIMUM_ALLOWED, &conn->lsa_policy); if (NT_STATUS_IS_OK(result)) { goto done; @@ -2144,19 +2393,20 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying " "anonymous\n")); - cli_rpc_pipe_close(conn->lsa_pipe); + TALLOC_FREE(conn->lsa_pipe); anonymous: - conn->lsa_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_LSARPC, - &result); - if (conn->lsa_pipe == NULL) { + result = cli_rpc_pipe_open_noauth(conn->cli, + &ndr_table_lsarpc.syntax_id, + &conn->lsa_pipe); + if (!NT_STATUS_IS_OK(result)) { result = NT_STATUS_PIPE_NOT_AVAILABLE; goto done; } result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, + SEC_FLAG_MAXIMUM_ALLOWED, &conn->lsa_policy); done: if (!NT_STATUS_IS_OK(result)) { @@ -2188,21 +2438,24 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, *cli = NULL; - result = init_dc_connection(domain); + result = init_dc_connection_rpc(domain); if (!NT_STATUS_IS_OK(result)) { return result; } conn = &domain->conn; - if (conn->netlogon_pipe != NULL) { + if (rpccli_is_connected(conn->netlogon_pipe)) { *cli = conn->netlogon_pipe; return NT_STATUS_OK; } - netlogon_pipe = cli_rpc_pipe_open_noauth(conn->cli, PI_NETLOGON, - &result); - if (netlogon_pipe == NULL) { + TALLOC_FREE(conn->netlogon_pipe); + + result = cli_rpc_pipe_open_noauth(conn->cli, + &ndr_table_netlogon.syntax_id, + &netlogon_pipe); + if (!NT_STATUS_IS_OK(result)) { return result; } @@ -2219,7 +2472,7 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, if (!get_trust_pw_hash(domain->name, mach_pwd, &account_name, &sec_chan_type)) { - cli_rpc_pipe_close(netlogon_pipe); + TALLOC_FREE(netlogon_pipe); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } @@ -2234,14 +2487,14 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, &neg_flags); if (!NT_STATUS_IS_OK(result)) { - cli_rpc_pipe_close(netlogon_pipe); + TALLOC_FREE(netlogon_pipe); return result; } if ((lp_client_schannel() == True) && ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) { DEBUG(3, ("Server did not offer schannel\n")); - cli_rpc_pipe_close(netlogon_pipe); + TALLOC_FREE(netlogon_pipe); return NT_STATUS_ACCESS_DENIED; } @@ -2265,30 +2518,112 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, part of the new pipe auth struct. */ - conn->netlogon_pipe = - cli_rpc_pipe_open_schannel_with_key(conn->cli, - PI_NETLOGON, - PIPE_AUTH_LEVEL_PRIVACY, - domain->name, - netlogon_pipe->dc, - &result); + result = cli_rpc_pipe_open_schannel_with_key( + conn->cli, &ndr_table_netlogon.syntax_id, NCACN_NP, + PIPE_AUTH_LEVEL_PRIVACY, domain->name, netlogon_pipe->dc, + &conn->netlogon_pipe); /* We can now close the initial netlogon pipe. */ - cli_rpc_pipe_close(netlogon_pipe); + TALLOC_FREE(netlogon_pipe); - if (conn->netlogon_pipe == NULL) { + if (!NT_STATUS_IS_OK(result)) { DEBUG(3, ("Could not open schannel'ed NETLOGON pipe. Error " "was %s\n", nt_errstr(result))); - - /* make sure we return something besides OK */ - return !NT_STATUS_IS_OK(result) ? result : NT_STATUS_PIPE_NOT_AVAILABLE; + + invalidate_cm_connection(conn); + return result; } /* - * Try NetSamLogonEx for AD domains + * Always try netr_LogonSamLogonEx. We will fall back for NT4 + * which gives DCERPC_FAULT_OP_RNG_ERROR (function not + * supported). We used to only try SamLogonEx for AD, but + * Samba DCs can also do it. And because we don't distinguish + * between Samba and NT4, always try it once. */ - domain->can_do_samlogon_ex = domain->active_directory; + domain->can_do_samlogon_ex = true; *cli = conn->netlogon_pipe; return NT_STATUS_OK; } + +void winbind_msg_ip_dropped(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_domain *domain; + char *freeit = NULL; + char *addr; + + if ((data == NULL) + || (data->data == NULL) + || (data->length == 0) + || (data->data[data->length-1] != '\0')) { + DEBUG(1, ("invalid msg_ip_dropped message: not a valid " + "string\n")); + return; + } + + addr = (char *)data->data; + DEBUG(10, ("IP %s dropped\n", addr)); + + if (!is_ipaddress(addr)) { + char *slash; + /* + * Some code sends us ip addresses with the /netmask + * suffix + */ + slash = strchr(addr, '/'); + if (slash == NULL) { + DEBUG(1, ("invalid msg_ip_dropped message: %s", + addr)); + return; + } + freeit = talloc_strndup(talloc_tos(), addr, slash-addr); + if (freeit == NULL) { + DEBUG(1, ("talloc failed\n")); + return; + } + addr = freeit; + DEBUG(10, ("Stripped /netmask to IP %s\n", addr)); + } + + for (domain = domain_list(); domain != NULL; domain = domain->next) { + char sockaddr[INET6_ADDRSTRLEN]; + if (domain->conn.cli == NULL) { + continue; + } + if (domain->conn.cli->fd == -1) { + continue; + } + client_socket_addr(domain->conn.cli->fd, sockaddr, + sizeof(sockaddr)); + if (strequal(sockaddr, addr)) { + close(domain->conn.cli->fd); + domain->conn.cli->fd = -1; + } + } + TALLOC_FREE(freeit); +} + +extern struct winbindd_child *children; + +void winbind_msg_ip_dropped_parent(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct winbindd_child *child; + + winbind_msg_ip_dropped(msg_ctx, private_data, msg_type, + server_id, data); + + + for (child = children; child != NULL; child = child->next) { + messaging_send_buf(msg_ctx, pid_to_procid(child->pid), + msg_type, data->data, data->length); + } +}