CVE-2016-2115: s3:winbindd: use lp_client_ipc_signing()
[samba.git] / source3 / winbindd / winbindd_cm.c
index d190bb39a168c0aa39c48f4a9c03fed7668fd562..ff0e081fb08541bfb0ef0c410e34bbcce386d244 100644 (file)
 #include "messages.h"
 #include "auth/gensec/gensec.h"
 #include "../libcli/smb/smbXcli_base.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+#include "auth.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "auth/credentials/credentials.h"
+#include "lib/param/param.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -90,8 +95,9 @@ struct dc_name_ip {
 extern struct winbindd_methods reconnect_methods;
 extern bool override_logfile;
 
-static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain);
+static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain, bool need_rw_dc);
 static void set_dc_type_and_flags( struct winbindd_domain *domain );
+static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain );
 static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
                    struct dc_name_ip **dcs, int *num_dcs);
 
@@ -172,7 +178,7 @@ static void msg_try_to_go_online(struct messaging_context *msg,
                           the offline handler if false. Bypasses online
                           check so always does network calls. */
 
-                       init_dc_connection_network(domain);
+                       init_dc_connection_network(domain, true);
                        break;
                }
        }
@@ -243,7 +249,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
                messaging_send_buf(winbind_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_FAILED_TO_GO_ONLINE,
-                                  (uint8 *)domain->name,
+                                  (const uint8_t *)domain->name,
                                   strlen(domain->name)+1);
                _exit(1);
        }
@@ -255,7 +261,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
                messaging_send_buf(winbind_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_FAILED_TO_GO_ONLINE,
-                                  (uint8 *)domain->name,
+                                  (const uint8_t *)domain->name,
                                   strlen(domain->name)+1);
                _exit(1);
        }
@@ -265,7 +271,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
                messaging_send_buf(winbind_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_FAILED_TO_GO_ONLINE,
-                                  (uint8 *)domain->name,
+                                  (const uint8_t *)domain->name,
                                   strlen(domain->name)+1);
                _exit(0);
        }
@@ -276,7 +282,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
        messaging_send_buf(winbind_messaging_context(),
                           pid_to_procid(parent_pid),
                           MSG_WINBIND_TRY_TO_GO_ONLINE,
-                          (uint8 *)domain->name,
+                          (const uint8_t *)domain->name,
                           strlen(domain->name)+1);
        _exit(0);
 }
@@ -285,8 +291,8 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
  Handler triggered if we're offline to try and detect a DC.
 ****************************************************************/
 
-static void check_domain_online_handler(struct event_context *ctx,
-                                       struct timed_event *te,
+static void check_domain_online_handler(struct tevent_context *ctx,
+                                       struct tevent_timer *te,
                                        struct timeval now,
                                        void *private_data)
 {
@@ -339,6 +345,46 @@ static void calc_new_online_timeout_check(struct winbindd_domain *domain)
        }
 }
 
+void winbind_msg_domain_offline(struct messaging_context *msg_ctx,
+                               void *private_data,
+                               uint32_t msg_type,
+                               struct server_id server_id,
+                               DATA_BLOB *data)
+{
+       const char *domain_name = (const char *)data->data;
+       struct winbindd_domain *domain;
+
+       domain = find_domain_from_name_noinit(domain_name);
+       if (domain == NULL) {
+               return;
+       }
+
+       domain->online = false;
+
+       DEBUG(10, ("Domain %s is marked as offline now.\n",
+                  domain_name));
+}
+
+void winbind_msg_domain_online(struct messaging_context *msg_ctx,
+                               void *private_data,
+                               uint32_t msg_type,
+                               struct server_id server_id,
+                               DATA_BLOB *data)
+{
+       const char *domain_name = (const char *)data->data;
+       struct winbindd_domain *domain;
+
+       domain = find_domain_from_name_noinit(domain_name);
+       if (domain == NULL) {
+               return;
+       }
+
+       domain->online = true;
+
+       DEBUG(10, ("Domain %s is marked as online now.\n",
+                  domain_name));
+}
+
 /****************************************************************
  Set domain offline and also add handler to put us back online
  if we detect a DC.
@@ -346,6 +392,8 @@ static void calc_new_online_timeout_check(struct winbindd_domain *domain)
 
 void set_domain_offline(struct winbindd_domain *domain)
 {
+       pid_t parent_pid = getppid();
+
        DEBUG(10,("set_domain_offline: called for domain %s\n",
                domain->name ));
 
@@ -379,7 +427,7 @@ void set_domain_offline(struct winbindd_domain *domain)
 
        calc_new_online_timeout_check(domain);
 
-       domain->check_online_event = event_add_timed(winbind_event_context(),
+       domain->check_online_event = tevent_add_timer(winbind_event_context(),
                                                NULL,
                                                timeval_current_ofs(domain->check_online_timeout,0),
                                                check_domain_online_handler,
@@ -393,6 +441,15 @@ void set_domain_offline(struct winbindd_domain *domain)
        DEBUG(10,("set_domain_offline: added event handler for domain %s\n",
                domain->name ));
 
+       /* Send a message to the parent that the domain is offline. */
+       if (parent_pid > 1 && !domain->internal) {
+               messaging_send_buf(winbind_messaging_context(),
+                                  pid_to_procid(parent_pid),
+                                  MSG_WINBIND_DOMAIN_OFFLINE,
+                                  (uint8_t *)domain->name,
+                                  strlen(domain->name) + 1);
+       }
+
        /* Send an offline message to the idmap child when our
           primary domain goes offline */
 
@@ -403,7 +460,7 @@ void set_domain_offline(struct winbindd_domain *domain)
                        messaging_send_buf(winbind_messaging_context(),
                                           pid_to_procid(idmap->pid), 
                                           MSG_WINBIND_OFFLINE, 
-                                          (uint8 *)domain->name, 
+                                          (const uint8_t *)domain->name,
                                           strlen(domain->name)+1);
                }                       
        }
@@ -417,6 +474,8 @@ void set_domain_offline(struct winbindd_domain *domain)
 
 static void set_domain_online(struct winbindd_domain *domain)
 {
+       pid_t parent_pid = getppid();
+
        DEBUG(10,("set_domain_online: called for domain %s\n",
                domain->name ));
 
@@ -468,6 +527,15 @@ static void set_domain_online(struct winbindd_domain *domain)
 
        domain->online = True;
 
+       /* Send a message to the parent that the domain is online. */
+       if (parent_pid > 1 && !domain->internal) {
+               messaging_send_buf(winbind_messaging_context(),
+                                  pid_to_procid(parent_pid),
+                                  MSG_WINBIND_DOMAIN_ONLINE,
+                                  (uint8_t *)domain->name,
+                                  strlen(domain->name) + 1);
+       }
+
        /* Send an online message to the idmap child when our
           primary domain comes online */
 
@@ -478,7 +546,7 @@ static void set_domain_online(struct winbindd_domain *domain)
                        messaging_send_buf(winbind_messaging_context(),
                                           pid_to_procid(idmap->pid), 
                                           MSG_WINBIND_ONLINE, 
-                                          (uint8 *)domain->name, 
+                                          (const uint8_t *)domain->name,
                                           strlen(domain->name)+1);
                }                       
        }
@@ -535,7 +603,7 @@ void set_domain_online_request(struct winbindd_domain *domain)
 
        TALLOC_FREE(domain->check_online_event);
 
-       domain->check_online_event = event_add_timed(winbind_event_context(),
+       domain->check_online_event = tevent_add_timer(winbind_event_context(),
                                                     NULL,
                                                     tev,
                                                     check_domain_online_handler,
@@ -560,7 +628,7 @@ static void winbind_add_failed_connection_entry(
        /* If this was the saf name for the last thing we talked to,
           remove it. */
        saf_delete(domain->name);
-       if (*domain->alt_name) {
+       if (domain->alt_name != NULL) {
                add_failed_connection_entry(domain->alt_name, server, result);
                saf_delete(domain->alt_name);
        }
@@ -600,6 +668,105 @@ static void cm_get_ipc_userpass(char **username, char **domain, char **password)
        }
 }
 
+static NTSTATUS cm_get_ipc_credentials(TALLOC_CTX *mem_ctx,
+                                      struct cli_credentials **_creds)
+{
+
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       struct loadparm_context *lp_ctx;
+       char *username = NULL;
+       char *netbios_domain = NULL;
+       char *password = NULL;
+       struct cli_credentials *creds = NULL;
+       bool ok;
+
+       cm_get_ipc_userpass(&username, &netbios_domain, &password);
+
+       lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+       if (lp_ctx == NULL) {
+               DEBUG(1, ("loadparm_init_s3 failed\n"));
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto fail;
+       }
+
+       creds = cli_credentials_init(mem_ctx);
+       if (creds == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       cli_credentials_set_conf(creds, lp_ctx);
+       cli_credentials_set_kerberos_state(creds, CRED_DONT_USE_KERBEROS);
+
+       ok = cli_credentials_set_domain(creds, netbios_domain, CRED_SPECIFIED);
+       if (!ok) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       ok = cli_credentials_set_username(creds, username, CRED_SPECIFIED);
+       if (!ok) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       ok = cli_credentials_set_password(creds, password, CRED_SPECIFIED);
+       if (!ok) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       *_creds = creds;
+       creds = NULL;
+       status = NT_STATUS_OK;
+ fail:
+       TALLOC_FREE(creds);
+       SAFE_FREE(username);
+       SAFE_FREE(netbios_domain);
+       SAFE_FREE(password);
+       TALLOC_FREE(frame);
+       return status;
+}
+
+static bool cm_is_ipc_credentials(struct cli_credentials *creds)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       char *ipc_account = NULL;
+       char *ipc_domain = NULL;
+       char *ipc_password = NULL;
+       const char *creds_account = NULL;
+       const char *creds_domain = NULL;
+       const char *creds_password = NULL;
+       bool ret = false;
+
+       cm_get_ipc_userpass(&ipc_account, &ipc_domain, &ipc_password);
+
+       creds_account = cli_credentials_get_username(creds);
+       creds_domain = cli_credentials_get_domain(creds);
+       creds_password = cli_credentials_get_password(creds);
+
+       if (!strequal(ipc_domain, creds_domain)) {
+               goto done;
+       }
+
+       if (!strequal(ipc_account, creds_account)) {
+               goto done;
+       }
+
+       if (!strcsequal(ipc_password, creds_password)) {
+               goto done;
+       }
+
+       ret = true;
+ done:
+       SAFE_FREE(ipc_account);
+       SAFE_FREE(ipc_domain);
+       SAFE_FREE(ipc_password);
+       TALLOC_FREE(frame);
+       return ret;
+}
+
 static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
                                     fstring dcname,
                                     struct sockaddr_storage *dc_ss)
@@ -664,13 +831,23 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
                                talloc_destroy(mem_ctx);
                                return false;
                        }
-                       if (strlen(domain->alt_name) == 0) {
-                               fstrcpy(domain->alt_name,
-                                       domain_info->domain_name);
+                       if (domain->alt_name == NULL) {
+                               domain->alt_name = talloc_strdup(domain,
+                                                                domain_info->domain_name);
+                               if (domain->alt_name == NULL) {
+                                       DEBUG(0, ("talloc_strdup failed\n"));
+                                       talloc_destroy(mem_ctx);
+                                       return false;
+                               }
                        }
-                       if (strlen(domain->forest_name) == 0) {
-                               fstrcpy(domain->forest_name,
-                                       domain_info->forest_name);
+                       if (domain->forest_name == NULL) {
+                               domain->forest_name = talloc_strdup(domain,
+                                                                   domain_info->forest_name);
+                               if (domain->forest_name == NULL) {
+                                       DEBUG(0, ("talloc_strdup failed\n"));
+                                       talloc_destroy(mem_ctx);
+                                       return false;
+                               }
                        }
                }
        } else {
@@ -717,58 +894,81 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
 /**
  * Helper function to assemble trust password and account name
  */
-static NTSTATUS get_trust_creds(const struct winbindd_domain *domain,
-                               char **machine_password,
-                               char **machine_account,
-                               char **machine_krb5_principal)
+static NTSTATUS get_trust_credentials(struct winbindd_domain *domain,
+                                     TALLOC_CTX *mem_ctx,
+                                     bool netlogon,
+                                     struct cli_credentials **_creds)
 {
-       const char *account_name;
-       const char *name = NULL;
+       const struct winbindd_domain *creds_domain = NULL;
+       struct cli_credentials *creds;
+       NTSTATUS status;
+       bool force_machine_account = false;
 
        /* If we are a DC and this is not our own domain */
 
-       if (IS_DC) {
-               name = domain->name;
-       } else {
-               struct winbindd_domain *our_domain = find_our_domain();
-
-               if (!our_domain)
-                       return NT_STATUS_INVALID_SERVER_STATE;          
-
-               name = our_domain->name;                
-       }       
+       if (!domain->active_directory) {
+               if (!netlogon) {
+                       /*
+                        * For non active directory domains
+                        * we can only use NTLMSSP for SMB.
+                        *
+                        * But the trust account is not allowed
+                        * to use SMB with NTLMSSP.
+                        */
+                       force_machine_account = true;
+               }
+       }
 
-       if (!get_trust_pw_clear(name, machine_password,
-                               &account_name, NULL))
-       {
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       if (IS_DC && !force_machine_account) {
+               creds_domain = domain;
+       } else {
+               creds_domain = find_our_domain();
+               if (creds_domain == NULL) {
+                       return NT_STATUS_INVALID_SERVER_STATE;
+               }
        }
 
-       if ((machine_account != NULL) &&
-           (asprintf(machine_account, "%s$", account_name) == -1))
-       {
-               return NT_STATUS_NO_MEMORY;
+       status = pdb_get_trust_credentials(creds_domain->name,
+                                          creds_domain->alt_name,
+                                          mem_ctx,
+                                          &creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto ipc_fallback;
        }
 
-       /* For now assume our machine account only exists in our domain */
+       if (domain->primary && lp_security() == SEC_ADS) {
+               cli_credentials_set_kerberos_state(creds,
+                                                  CRED_AUTO_USE_KERBEROS);
+       } else if (domain->active_directory) {
+               cli_credentials_set_kerberos_state(creds,
+                                                  CRED_MUST_USE_KERBEROS);
+       } else {
+               cli_credentials_set_kerberos_state(creds,
+                                                  CRED_DONT_USE_KERBEROS);
+       }
 
-       if (machine_krb5_principal != NULL)
-       {
-               struct winbindd_domain *our_domain = find_our_domain();
+       if (creds_domain != domain) {
+               /*
+                * We can only use schannel against a direct trust
+                */
+               cli_credentials_set_secure_channel_type(creds,
+                                                       SEC_CHAN_NULL);
+       }
 
-               if (!our_domain) {
-                       return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;                       
-               }
+       *_creds = creds;
+       return NT_STATUS_OK;
 
-               if (asprintf(machine_krb5_principal, "%s$@%s",
-                            account_name, our_domain->alt_name) == -1)
-               {
-                       return NT_STATUS_NO_MEMORY;
-               }
+ ipc_fallback:
+       if (netlogon) {
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
 
-               strupper_m(*machine_krb5_principal);
+       status = cm_get_ipc_credentials(mem_ctx, &creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
+       *_creds = creds;
        return NT_STATUS_OK;
 }
 
@@ -777,27 +977,48 @@ static NTSTATUS get_trust_creds(const struct winbindd_domain *domain,
  to the pipe.
 ************************************************************************/
 
-static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
+static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                                      const int sockfd,
                                      const char *controller,
                                      struct cli_state **cli,
                                      bool *retry)
 {
-       bool try_spnego = false;
        bool try_ipc_auth = false;
-       char *machine_password = NULL;
-       char *machine_krb5_principal = NULL;
-       char *machine_account = NULL;
-       char *ipc_username = NULL;
-       char *ipc_domain = NULL;
-       char *ipc_password = NULL;
+       const char *machine_password = NULL;
+       const char *machine_krb5_principal = NULL;
+       const char *machine_account = NULL;
+       const char *machine_domain = NULL;
        int flags = 0;
-       uint16_t sec_mode = 0;
+       struct cli_credentials *creds = NULL;
+       enum credentials_use_kerberos krb5_state;
 
        struct named_mutex *mutex;
 
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
 
+       enum smb_signing_setting smb_sign_client_connections = lp_client_ipc_signing();
+
+       if (smb_sign_client_connections == SMB_SIGNING_DEFAULT) {
+               /*
+                * If we are connecting to our own AD domain, require
+                * smb signing to disrupt MITM attacks
+                */
+               if (domain->primary && lp_security() == SEC_ADS) {
+                       smb_sign_client_connections = SMB_SIGNING_REQUIRED;
+               /*
+                * If we are in or are an AD domain and connecting to another
+                * AD domain in our forest
+                * then require smb signing to disrupt MITM attacks
+                */
+               } else if ((lp_security() == SEC_ADS ||
+                           lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC)
+                          && domain->active_directory
+                          && (domain->domain_trust_attribs
+                              & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
+                       smb_sign_client_connections = SMB_SIGNING_REQUIRED;
+               }
+       }
+
        DEBUG(10,("cm_prepare_connection: connecting to DC %s for domain %s\n",
                controller, domain->name ));
 
@@ -817,7 +1038,7 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
        *cli = cli_state_create(NULL, sockfd,
                                controller, domain->alt_name,
-                               SMB_SIGNING_DEFAULT, flags);
+                               smb_sign_client_connections, flags);
        if (*cli == NULL) {
                close(sockfd);
                DEBUG(1, ("Could not cli_initialize\n"));
@@ -827,7 +1048,9 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
        cli_set_timeout(*cli, 10000); /* 10 seconds */
 
-       result = cli_negprot(*cli, PROTOCOL_SMB2_02);
+       result = smbXcli_negprot((*cli)->conn, (*cli)->timeout,
+                                lp_client_ipc_min_protocol(),
+                                lp_client_ipc_max_protocol());
 
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(1, ("cli_negprot failed: %s\n", nt_errstr(result)));
@@ -836,59 +1059,87 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
        if (smbXcli_conn_protocol((*cli)->conn) >= PROTOCOL_NT1 &&
            smb1cli_conn_capabilities((*cli)->conn) & CAP_EXTENDED_SECURITY) {
-               try_spnego = true;
+               try_ipc_auth = true;
        } else if (smbXcli_conn_protocol((*cli)->conn) >= PROTOCOL_SMB2_02) {
-               try_spnego = true;
+               try_ipc_auth = true;
+       } else if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+               /*
+                * If we are forcing on SMB signing, then we must
+                * require authentication unless this is a one-way
+                * trust, and we have no stored user/password
+                */
+               try_ipc_auth = true;
        }
 
-       if (!is_dc_trusted_domain_situation(domain->name) && try_spnego) {
-               result = get_trust_creds(domain, &machine_password,
-                                        &machine_account,
-                                        &machine_krb5_principal);
+       if (try_ipc_auth) {
+               result = get_trust_credentials(domain, talloc_tos(), false, &creds);
                if (!NT_STATUS_IS_OK(result)) {
-                       goto anon_fallback;
+                       DEBUG(1, ("get_trust_credentials(%s) failed: %s\n",
+                                 domain->name, nt_errstr(result)));
+                       goto done;
+               }
+       } else {
+               /*
+                * Without SPNEGO or NTLMSSP (perhaps via SMB2) we
+                * would try and authentication with our machine
+                * account password and fail.  This is very rare in
+                * the modern world however
+                */
+               creds = cli_credentials_init_anon(talloc_tos());
+               if (creds == NULL) {
+                       result = NT_STATUS_NO_MEMORY;
+                       DEBUG(1, ("cli_credentials_init_anon(%s) failed: %s\n",
+                                 domain->name, nt_errstr(result)));
+                       goto done;
                }
+       }
 
-               if (lp_security() == SEC_ADS) {
+       krb5_state = cli_credentials_get_kerberos_state(creds);
 
-                       /* Try a krb5 session */
+       machine_krb5_principal = cli_credentials_get_principal(creds,
+                                                       talloc_tos());
+       if (machine_krb5_principal == NULL) {
+               krb5_state = CRED_DONT_USE_KERBEROS;
+       }
 
-                       (*cli)->use_kerberos = True;
-                       DEBUG(5, ("connecting to %s from %s with kerberos principal "
-                                 "[%s] and realm [%s]\n", controller, lp_netbios_name(),
-                                 machine_krb5_principal, domain->alt_name));
+       machine_account = cli_credentials_get_username(creds);
+       machine_password = cli_credentials_get_password(creds);
+       machine_domain = cli_credentials_get_domain(creds);
 
-                       winbindd_set_locator_kdc_envs(domain);
+       if (krb5_state != CRED_DONT_USE_KERBEROS) {
 
-                       result = cli_session_setup(*cli,
-                                                  machine_krb5_principal,
-                                                  machine_password,
-                                                  strlen(machine_password)+1,
-                                                  machine_password,
-                                                  strlen(machine_password)+1,
-                                                  lp_workgroup());
+               /* Try a krb5 session */
 
-                       if (!NT_STATUS_IS_OK(result)) {
-                               DEBUG(4,("failed kerberos session setup with %s\n",
-                                       nt_errstr(result)));
-                       }
+               (*cli)->use_kerberos = True;
+               DEBUG(5, ("connecting to %s from %s with kerberos principal "
+                         "[%s] and realm [%s]\n", controller, lp_netbios_name(),
+                         machine_krb5_principal, domain->alt_name));
 
-                       if (NT_STATUS_IS_OK(result)) {
-                               /* Ensure creds are stored for NTLMSSP authenticated pipe access. */
-                               result = cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password);
-                               if (!NT_STATUS_IS_OK(result)) {
-                                       goto done;
-                               }
-                               goto session_setup_done;
-                       }
+               winbindd_set_locator_kdc_envs(domain);
+
+               result = cli_session_setup(*cli,
+                                          machine_krb5_principal,
+                                          machine_password,
+                                          strlen(machine_password)+1,
+                                          machine_password,
+                                          strlen(machine_password)+1,
+                                          machine_domain);
+
+               if (NT_STATUS_IS_OK(result)) {
+                       goto session_setup_done;
                }
 
+               DEBUG(4,("failed kerberos session setup with %s\n",
+                        nt_errstr(result)));
+       }
+
+       if (krb5_state != CRED_MUST_USE_KERBEROS) {
                /* Fall back to non-kerberos session setup using NTLMSSP SPNEGO with the machine account. */
                (*cli)->use_kerberos = False;
 
-               DEBUG(5, ("connecting to %s from %s with username "
+               DEBUG(5, ("connecting to %s from %s using NTLMSSP with username "
                          "[%s]\\[%s]\n",  controller, lp_netbios_name(),
-                         lp_workgroup(), machine_account));
+                         machine_domain, machine_account));
 
                result = cli_session_setup(*cli,
                                           machine_account,
@@ -896,76 +1147,113 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                                           strlen(machine_password)+1,
                                           machine_password,
                                           strlen(machine_password)+1,
-                                          lp_workgroup());
-               if (!NT_STATUS_IS_OK(result)) {
-                       DEBUG(4, ("authenticated session setup failed with %s\n",
-                               nt_errstr(result)));
+                                          machine_domain);
+       }
+
+       if (NT_STATUS_IS_OK(result)) {
+               goto session_setup_done;
+       }
+
+       /*
+        * If we are not going to validiate the conneciton
+        * with SMB signing, then allow us to fall back to
+        * anonymous
+        */
+       if (NT_STATUS_EQUAL(result, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)
+           || NT_STATUS_EQUAL(result, NT_STATUS_TRUSTED_DOMAIN_FAILURE)
+           || NT_STATUS_EQUAL(result, NT_STATUS_INVALID_ACCOUNT_NAME)
+           || NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)
+           || NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE))
+       {
+               if (cli_credentials_is_anonymous(creds)) {
+                       goto done;
                }
 
-               if (NT_STATUS_IS_OK(result)) {
-                       /* Ensure creds are stored for NTLMSSP authenticated pipe access. */
-                       result = cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password);
-                       if (!NT_STATUS_IS_OK(result)) {
-                               goto done;
-                       }
-                       goto session_setup_done;
+               if (!cm_is_ipc_credentials(creds)) {
+                       goto ipc_fallback;
                }
+
+               if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+                       goto done;
+               }
+
+               goto anon_fallback;
+       }
+
+       DEBUG(4, ("authenticated session setup failed with %s\n",
+               nt_errstr(result)));
+
+       goto done;
+
+ ipc_fallback:
+       result = cm_get_ipc_credentials(talloc_tos(), &creds);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
        }
 
-       /* Fall back to non-kerberos session setup with auth_user */
+       if (cli_credentials_is_anonymous(creds)) {
+               TALLOC_FREE(creds);
+               goto anon_fallback;
+       }
+
+       machine_account = cli_credentials_get_username(creds);
+       machine_password = cli_credentials_get_password(creds);
+       machine_domain = cli_credentials_get_domain(creds);
 
+       /* Fall back to non-kerberos session setup using NTLMSSP SPNEGO with the ipc creds. */
        (*cli)->use_kerberos = False;
 
-       cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
+       DEBUG(5, ("connecting to %s from %s using NTLMSSP with username "
+                 "[%s]\\[%s]\n",  controller, lp_netbios_name(),
+                 machine_domain, machine_account));
 
-       sec_mode = cli_state_security_mode(*cli);
+       result = cli_session_setup(*cli,
+                                  machine_account,
+                                  machine_password,
+                                  strlen(machine_password)+1,
+                                  machine_password,
+                                  strlen(machine_password)+1,
+                                  machine_domain);
 
-       try_ipc_auth = false;
-       if (try_spnego) {
-               try_ipc_auth = true;
-       } else if (sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
-               try_ipc_auth = true;
+       if (NT_STATUS_IS_OK(result)) {
+               goto session_setup_done;
        }
 
-       if (try_ipc_auth && (strlen(ipc_username) > 0)) {
+       /*
+        * If we are not going to validiate the conneciton
+        * with SMB signing, then allow us to fall back to
+        * anonymous
+        */
+       if (NT_STATUS_EQUAL(result, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)
+           || NT_STATUS_EQUAL(result, NT_STATUS_TRUSTED_DOMAIN_FAILURE)
+           || NT_STATUS_EQUAL(result, NT_STATUS_INVALID_ACCOUNT_NAME)
+           || NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)
+           || NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE))
+       {
+               goto anon_fallback;
+       }
 
-               /* Only try authenticated if we have a username */
+       DEBUG(4, ("authenticated session setup failed with %s\n",
+               nt_errstr(result)));
 
-               DEBUG(5, ("connecting to %s from %s with username "
-                         "[%s]\\[%s]\n",  controller, lp_netbios_name(),
-                         ipc_domain, ipc_username));
-
-               if (NT_STATUS_IS_OK(cli_session_setup(
-                                           *cli, ipc_username,
-                                           ipc_password, strlen(ipc_password)+1,
-                                           ipc_password, strlen(ipc_password)+1,
-                                           ipc_domain))) {
-                       /* Successful logon with given username. */
-                       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",
-                               ipc_domain, ipc_username ));
-               }
-       }
+       goto done;
 
  anon_fallback:
 
+       if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+               goto done;
+       }
+
        /* Fall back to anonymous connection, this might fail later */
        DEBUG(10,("cm_prepare_connection: falling back to anonymous "
                "connection for DC %s\n",
                controller ));
 
-       result = cli_session_setup(*cli, "", NULL, 0, NULL, 0, "");
+       (*cli)->use_kerberos = False;
+
+       result = cli_session_setup(*cli, "", "", 0, "", 0, "");
        if (NT_STATUS_IS_OK(result)) {
                DEBUG(5, ("Connected anonymously\n"));
-               result = cli_init_creds(*cli, "", "", "");
-               if (!NT_STATUS_IS_OK(result)) {
-                       goto done;
-               }
                goto session_setup_done;
        }
 
@@ -974,15 +1262,17 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
  session_setup_done:
 
-       /* cache the server name for later connections */
-
-       saf_store(domain->name, controller);
-       if (domain->alt_name && (*cli)->use_kerberos) {
-               saf_store(domain->alt_name, controller);
+       /*
+        * This should be a short term hack until
+        * dynamic re-authentication is implemented.
+        *
+        * See Bug 9175 - winbindd doesn't recover from
+        * NT_STATUS_NETWORK_SESSION_EXPIRED
+        */
+       if (smbXcli_conn_protocol((*cli)->conn) >= PROTOCOL_SMB2_02) {
+               smbXcli_session_set_disconnect_expired((*cli)->smb2.session);
        }
 
-       winbindd_set_locator_kdc_envs(domain);
-
        result = cli_tree_connect(*cli, "IPC$", "IPC", "", 0);
 
        if (!NT_STATUS_IS_OK(result)) {
@@ -990,27 +1280,22 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                goto done;
        }
 
-       TALLOC_FREE(mutex);
-       *retry = False;
+       /* cache the server name for later connections */
 
-       /* set the domain if empty; needed for schannel connections */
-       if ( !(*cli)->domain[0] ) {
-               result = cli_set_domain((*cli), domain->name);
-               if (!NT_STATUS_IS_OK(result)) {
-                       return result;
-               }
+       saf_store(domain->name, controller);
+       if (domain->alt_name && (*cli)->use_kerberos) {
+               saf_store(domain->alt_name, controller);
        }
 
+       winbindd_set_locator_kdc_envs(domain);
+
+       TALLOC_FREE(mutex);
+       *retry = False;
+
        result = NT_STATUS_OK;
 
  done:
        TALLOC_FREE(mutex);
-       SAFE_FREE(machine_account);
-       SAFE_FREE(machine_password);
-       SAFE_FREE(machine_krb5_principal);
-       SAFE_FREE(ipc_username);
-       SAFE_FREE(ipc_domain);
-       SAFE_FREE(ipc_password);
 
        if (!NT_STATUS_IS_OK(result)) {
                winbind_add_failed_connection_entry(domain, controller, result);
@@ -1068,7 +1353,7 @@ static bool add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name,
 }
 
 static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
-                                 struct sockaddr_storage *pss, uint16 port,
+                                 struct sockaddr_storage *pss, uint16_t port,
                                  struct sockaddr_storage **addrs, int *num)
 {
        *addrs = talloc_realloc(mem_ctx, *addrs, struct sockaddr_storage, (*num)+1);
@@ -1092,13 +1377,16 @@ static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
 static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                const struct winbindd_domain *domain,
                struct sockaddr_storage *pss,
-               fstring name )
+               char **name)
 {
        struct ip_service ip_list;
        uint32_t nt_version = NETLOGON_NT_VERSION_1;
        NTSTATUS status;
        const char *dc_name;
-
+       fstring nbtname;
+#ifdef HAVE_ADS
+       bool is_ad_domain = false;
+#endif
        ip_list.ss = *pss;
        ip_list.port = 0;
 
@@ -1106,7 +1394,13 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
        /* For active directory servers, try to get the ldap server name.
           None of these failures should be considered critical for now */
 
-       if (lp_security() == SEC_ADS) {
+       if ((lp_security() == SEC_ADS) && (domain->alt_name != NULL)) {
+               is_ad_domain = true;
+       } else if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+               is_ad_domain = domain->active_directory;
+       }
+
+       if (is_ad_domain) {
                ADS_STRUCT *ads;
                ADS_STATUS ads_status;
                char addr[INET6_ADDRSTRLEN];
@@ -1119,14 +1413,18 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                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);
+                       *name = talloc_strdup(mem_ctx,
+                                            ads->config.ldap_server_name);
+                       if (*name == NULL) {
+                               return false;
+                       }
+                       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 & NBT_SERVER_KDC)) {
                                if (ads_closest_dc(ads)) {
-                                       char *sitename = sitename_fetch(ads->config.realm);
+                                       char *sitename = sitename_fetch(mem_ctx, ads->config.realm);
 
                                        /* We're going to use this KDC for this realm/domain.
                                           If we are using sites, then force the krb5 libs
@@ -1135,23 +1433,21 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                                        create_local_private_krb5_conf_for_domain(domain->alt_name,
                                                                        domain->name,
                                                                        sitename,
-                                                                       pss,
-                                                                       name);
+                                                                       pss);
 
-                                       SAFE_FREE(sitename);
+                                       TALLOC_FREE(sitename);
                                } else {
                                        /* use an off site KDC */
                                        create_local_private_krb5_conf_for_domain(domain->alt_name,
                                                                        domain->name,
                                                                        NULL,
-                                                                       pss,
-                                                                       name);
+                                                                       pss);
                                }
                                winbindd_set_locator_kdc_envs(domain);
 
                                /* Ensure we contact this DC also. */
-                               saf_store( domain->name, name);
-                               saf_store( domain->alt_name, name);
+                               saf_store(domain->name, *name);
+                               saf_store(domain->alt_name, *name);
                        }
 
                        ads_destroy( &ads );
@@ -1167,16 +1463,27 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                           &domain->sid, nt_version, mem_ctx, &nt_version,
                           &dc_name, NULL);
        if (NT_STATUS_IS_OK(status)) {
-               fstrcpy(name, dc_name);
-               namecache_store(name, 0x20, 1, &ip_list);
+               *name = talloc_strdup(mem_ctx, dc_name);
+               if (*name == NULL) {
+                       return false;
+               }
+               namecache_store(*name, 0x20, 1, &ip_list);
                return True;
        }
 
        /* try node status request */
 
-       if ( name_status_find(domain->name, 0x1c, 0x20, pss, name) ) {
-               namecache_store(name, 0x20, 1, &ip_list);
-               return True;
+       if (name_status_find(domain->name, 0x1c, 0x20, pss, nbtname) ) {
+               namecache_store(nbtname, 0x20, 1, &ip_list);
+
+               if (name != NULL) {
+                       *name = talloc_strdup(mem_ctx, nbtname);
+                       if (*name == NULL) {
+                               return false;
+                       }
+               }
+
+               return true;
        }
        return False;
 }
@@ -1219,7 +1526,7 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
                return True;
        }
 
-       if (sec == SEC_ADS) {
+       if ((sec == SEC_ADS) && (domain->alt_name != NULL)) {
                char *sitename = NULL;
 
                /* We need to make sure we know the local site before
@@ -1233,7 +1540,7 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
 
                get_dc_name(domain->name, domain->alt_name, dcname, &ss);
 
-               sitename = sitename_fetch(domain->alt_name);
+               sitename = sitename_fetch(mem_ctx, domain->alt_name);
                if (sitename) {
 
                        /* Do the site-specific AD dns lookup first. */
@@ -1257,7 +1564,7 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
                        }
 
                        SAFE_FREE(ip_list);
-                       SAFE_FREE(sitename);
+                       TALLOC_FREE(sitename);
                        iplist_size = 0;
                }
 
@@ -1281,10 +1588,17 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
                iplist_size = 0;
         }
 
-       /* Try standard netbios queries if no ADS */
+       /* Try standard netbios queries if no ADS and fall back to DNS queries
+        * if alt_name is available */
        if (*num_dcs == 0) {
                get_sorted_dc_list(domain->name, NULL, &ip_list, &iplist_size,
-                      False);
+                      false);
+               if (iplist_size == 0) {
+                       if (domain->alt_name != NULL) {
+                               get_sorted_dc_list(domain->alt_name, NULL, &ip_list,
+                                      &iplist_size, true);
+                       }
+               }
 
                for ( i=0; i<iplist_size; i++ ) {
                        char addr[INET6_ADDRSTRLEN];
@@ -1318,13 +1632,13 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
 
 static bool find_new_dc(TALLOC_CTX *mem_ctx,
                        struct winbindd_domain *domain,
-                       fstring dcname, struct sockaddr_storage *pss, int *fd)
+                       char **dcname, struct sockaddr_storage *pss, int *fd)
 {
        struct dc_name_ip *dcs = NULL;
        int num_dcs = 0;
 
        const char **dcnames = NULL;
-       int num_dcnames = 0;
+       size_t num_dcnames = 0;
 
        struct sockaddr_storage *addrs = NULL;
        int num_addrs = 0;
@@ -1377,8 +1691,11 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
 
        if (*dcnames[fd_index] != '\0' && !is_ipaddress(dcnames[fd_index])) {
                /* Ok, we've got a name for the DC */
-               fstrcpy(dcname, dcnames[fd_index]);
-               return True;
+               *dcname = talloc_strdup(mem_ctx, dcnames[fd_index]);
+               if (*dcname == NULL) {
+                       return false;
+               }
+               return true;
        }
 
        /* Try to figure out the name */
@@ -1447,7 +1764,8 @@ 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;
+       char *key, *p;
+       char *value = NULL;
        bool ret = false;
        char *dc_name = NULL;
        char *dc_ip = NULL;
@@ -1456,7 +1774,7 @@ bool fetch_current_dc_from_gencache(TALLOC_CTX *mem_ctx,
        if (key == NULL) {
                goto done;
        }
-       if (!gencache_get(key, &value, NULL)) {
+       if (!gencache_get(key, mem_ctx, &value, NULL)) {
                goto done;
        }
        p = strchr(value, ' ');
@@ -1485,23 +1803,66 @@ done:
        TALLOC_FREE(dc_name);
        TALLOC_FREE(dc_ip);
        TALLOC_FREE(key);
+       TALLOC_FREE(value);
        return ret;
 }
 
+NTSTATUS wb_open_internal_pipe(TALLOC_CTX *mem_ctx,
+                              const struct ndr_interface_table *table,
+                              struct rpc_pipe_client **ret_pipe)
+{
+       struct rpc_pipe_client *cli = NULL;
+       const struct auth_session_info *session_info;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+
+       session_info = get_session_info_system();
+       SMB_ASSERT(session_info != NULL);
+
+       /* create a connection to the specified pipe */
+       if (lp_parm_bool(-1, "winbindd", "use external pipes", false)) {
+               status = rpc_pipe_open_interface(mem_ctx,
+                                                table,
+                                                session_info,
+                                                NULL,
+                                                winbind_messaging_context(),
+                                                &cli);
+       } else {
+               status = rpc_pipe_open_internal(mem_ctx,
+                                               &table->syntax_id,
+                                               session_info,
+                                               NULL,
+                                               winbind_messaging_context(),
+                                               &cli);
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("open_internal_pipe: Could not connect to %s pipe: %s\n",
+                         table->name, nt_errstr(status)));
+               return status;
+       }
+
+       if (ret_pipe) {
+               *ret_pipe = cli;
+       }
+
+       return NT_STATUS_OK;
+}
+
 static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                                   struct winbindd_cm_conn *new_conn)
 {
        TALLOC_CTX *mem_ctx;
        NTSTATUS result;
-       char *saf_servername = saf_fetch( domain->name );
+       char *saf_servername;
        int retries;
 
        if ((mem_ctx = talloc_init("cm_open_connection")) == NULL) {
-               SAFE_FREE(saf_servername);
                set_domain_offline(domain);
                return NT_STATUS_NO_MEMORY;
        }
 
+       saf_servername = saf_fetch(mem_ctx, domain->name );
+
        /* we have to check the server affinity cache here since 
           later we select a DC based on response time and not preference */
 
@@ -1516,37 +1877,46 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
 
                /* convert an ip address to a name */
                if (is_ipaddress( saf_servername ) ) {
-                       fstring saf_name;
+                       char *dcname = NULL;
                        struct sockaddr_storage ss;
 
                        if (!interpret_string_addr(&ss, saf_servername,
                                                AI_NUMERICHOST)) {
+                               TALLOC_FREE(mem_ctx);
                                return NT_STATUS_UNSUCCESSFUL;
                        }
-                       if (dcip_to_name(mem_ctx, domain, &ss, saf_name )) {
-                               strlcpy(domain->dcname, saf_name, sizeof(domain->dcname));
+                       if (dcip_to_name(mem_ctx, domain, &ss, &dcname)) {
+                               domain->dcname = talloc_strdup(domain,
+                                                              dcname);
+                               if (domain->dcname == NULL) {
+                                       TALLOC_FREE(mem_ctx);
+                                       return NT_STATUS_NO_MEMORY;
+                               }
                        } else {
                                winbind_add_failed_connection_entry(
                                        domain, saf_servername,
                                        NT_STATUS_UNSUCCESSFUL);
                        }
                } else {
-                       fstrcpy( domain->dcname, saf_servername );
+                       domain->dcname = talloc_strdup(domain, saf_servername);
+                       if (domain->dcname == NULL) {
+                               TALLOC_FREE(mem_ctx);
+                               return NT_STATUS_NO_MEMORY;
+                       }
                }
-
-               SAFE_FREE( saf_servername );
        }
 
        for (retries = 0; retries < 3; retries++) {
                int fd = -1;
                bool retry = False;
+               char *dcname = NULL;
 
                result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
 
                DEBUG(10,("cm_open_connection: dcname is '%s' for domain %s\n",
-                       domain->dcname, domain->name ));
+                       domain->dcname ? domain->dcname : "", domain->name ));
 
-               if (*domain->dcname 
+               if (domain->dcname != NULL
                        && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname))
                        && (resolve_name(domain->dcname, &domain->dcaddr, 0x20, true)))
                {
@@ -1560,8 +1930,8 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                        }
                }
 
-               if ((fd == -1) 
-                       && !find_new_dc(mem_ctx, domain, domain->dcname, &domain->dcaddr, &fd))
+               if ((fd == -1) &&
+                   !find_new_dc(mem_ctx, domain, &dcname, &domain->dcaddr, &fd))
                {
                        /* This is the one place where we will
                           set the global winbindd offline state
@@ -1570,17 +1940,31 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                        set_global_winbindd_state_offline();
                        break;
                }
+               if (dcname != NULL) {
+                       talloc_free(domain->dcname);
+
+                       domain->dcname = talloc_move(domain, &dcname);
+                       if (domain->dcname == NULL) {
+                               result = NT_STATUS_NO_MEMORY;
+                               break;
+                       }
+               }
 
                new_conn->cli = NULL;
 
                result = cm_prepare_connection(domain, fd, domain->dcname,
                        &new_conn->cli, &retry);
+               if (!NT_STATUS_IS_OK(result)) {
+                       /* Don't leak the smb connection socket */
+                       close(fd);
+               }
 
                if (!retry)
                        break;
        }
 
        if (NT_STATUS_IS_OK(result)) {
+               bool seal_pipes = true;
 
                winbindd_set_locator_kdc_envs(domain);
 
@@ -1600,6 +1984,17 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                 */
                store_current_dc_in_gencache(domain->name, domain->dcname,
                                             new_conn->cli);
+
+               seal_pipes = lp_winbind_sealed_pipes();
+               seal_pipes = lp_parm_bool(-1, "winbind sealed pipes",
+                                         domain->name,
+                                         seal_pipes);
+
+               if (seal_pipes) {
+                       new_conn->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+               } else {
+                       new_conn->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+               }
        } else {
                /* Ensure we setup the retry handler. */
                set_domain_offline(domain);
@@ -1611,9 +2006,10 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
 
 /* Close down all open pipes on a connection. */
 
-void invalidate_cm_connection(struct winbindd_cm_conn *conn)
+void invalidate_cm_connection(struct winbindd_domain *domain)
 {
        NTSTATUS result;
+       struct winbindd_cm_conn *conn = &domain->conn;
 
        /* We're closing down a possibly dead
           connection. Don't have impossibly long (10s) timeouts. */
@@ -1672,6 +2068,11 @@ void invalidate_cm_connection(struct winbindd_cm_conn *conn)
                }
        }
 
+       conn->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+       conn->netlogon_force_reauth = false;
+       conn->netlogon_flags = 0;
+       TALLOC_FREE(conn->netlogon_creds);
+
        if (conn->cli) {
                cli_shutdown(conn->cli);
        }
@@ -1691,10 +2092,10 @@ void close_conns_after_fork(void)
                 * requests in invalidate_cm_connection()
                 */
                if (cli_state_is_connected(domain->conn.cli)) {
-                       cli_state_disconnect(domain->conn.cli);
+                       smbXcli_conn_disconnect(domain->conn.cli->conn, NT_STATUS_OK);
                }
 
-               invalidate_cm_connection(&domain->conn);
+               invalidate_cm_connection(domain);
        }
 
        for (cli_state = winbindd_client_list();
@@ -1729,30 +2130,36 @@ static bool connection_ok(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)
+static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain, bool need_rw_dc)
 {
        NTSTATUS result;
-
-       /* Internal connections never use the network. */
-       if (domain->internal) {
-               domain->initialized = True;
-               return NT_STATUS_OK;
+       bool skip_connection = domain->internal;
+       if (need_rw_dc && domain->rodc) {
+               skip_connection = false;
        }
 
-       if (!winbindd_can_contact_domain(domain)) {
-               invalidate_cm_connection(&domain->conn);
-               domain->initialized = True;
-               return NT_STATUS_OK;
+       /* Internal connections never use the network. */
+       if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
-       if (connection_ok(domain)) {
+       /* Still ask the internal LSA and SAMR server about the local domain */
+       if (skip_connection || connection_ok(domain)) {
                if (!domain->initialized) {
                        set_dc_type_and_flags(domain);
                }
                return NT_STATUS_OK;
        }
 
-       invalidate_cm_connection(&domain->conn);
+       invalidate_cm_connection(domain);
+
+       if (!domain->primary && !domain->initialized) {
+               /*
+                * Before we connect to a trust, work out if it is an
+                * AD domain by asking our own domain.
+                */
+               set_dc_type_and_flags_trustinfo(domain);
+       }
 
        result = cm_open_connection(domain, &domain->conn);
 
@@ -1763,9 +2170,9 @@ static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain)
        return result;
 }
 
-NTSTATUS init_dc_connection(struct winbindd_domain *domain)
+NTSTATUS init_dc_connection(struct winbindd_domain *domain, bool need_rw_dc)
 {
-       if (domain->internal) {
+       if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
@@ -1774,14 +2181,14 @@ NTSTATUS init_dc_connection(struct winbindd_domain *domain)
                return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
        }
 
-       return init_dc_connection_network(domain);
+       return init_dc_connection_network(domain, need_rw_dc);
 }
 
-static NTSTATUS init_dc_connection_rpc(struct winbindd_domain *domain)
+static NTSTATUS init_dc_connection_rpc(struct winbindd_domain *domain, bool need_rw_dc)
 {
        NTSTATUS status;
 
-       status = init_dc_connection(domain);
+       status = init_dc_connection(domain, need_rw_dc);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -1805,7 +2212,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
        WERROR werr;
        struct netr_DomainTrustList trusts;
        int i;
-       uint32 flags = (NETR_TRUST_FLAG_IN_FOREST |
+       uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST |
                        NETR_TRUST_FLAG_OUTBOUND |
                        NETR_TRUST_FLAG_INBOUND);
        struct rpc_pipe_client *cli;
@@ -1820,38 +2227,46 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                return False;           
        }
 
+       mem_ctx = talloc_stackframe();
        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;
+       if (our_domain->internal) {
+               result = init_dc_connection(our_domain, false);
+               if (!NT_STATUS_IS_OK(result)) {
+                       DEBUG(3,("set_dc_type_and_flags_trustinfo: "
+                                "Not able to make a connection to our domain: %s\n",
+                                 nt_errstr(result)));
+                       TALLOC_FREE(mem_ctx);
+                       return false;
+               }
        }
 
        /* This won't work unless our domain is AD */
-
        if ( !our_domain->active_directory ) {
+               TALLOC_FREE(mem_ctx);
                return False;
        }
 
-       /* Use DsEnumerateDomainTrusts to get us the trust direction
-          and type */
-
-       result = cm_connect_netlogon(our_domain, &cli);
+       if (our_domain->internal) {
+               result = wb_open_internal_pipe(mem_ctx, &ndr_table_netlogon, &cli);
+       } else if (!connection_ok(our_domain)) {
+               DEBUG(3,("set_dc_type_and_flags_trustinfo: "
+                        "No connection to our domain!\n"));
+               TALLOC_FREE(mem_ctx);
+               return False;
+       } else {
+               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)));
+               TALLOC_FREE(mem_ctx);
                return False;
        }
-
        b = cli->binding_handle;
 
-       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;
-       }       
-
+       /* Use DsEnumerateDomainTrusts to get us the trust direction and type. */
        result = dcerpc_netr_DsrEnumerateDomainTrusts(b, mem_ctx,
                                                      cli->desthost,
                                                      flags,
@@ -1861,14 +2276,14 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                DEBUG(0,("set_dc_type_and_flags_trustinfo: "
                        "failed to query trusted domain list: %s\n",
                        nt_errstr(result)));
-               talloc_destroy(mem_ctx);
+               TALLOC_FREE(mem_ctx);
                return false;
        }
        if (!W_ERROR_IS_OK(werr)) {
                DEBUG(0,("set_dc_type_and_flags_trustinfo: "
                        "failed to query trusted domain list: %s\n",
                        win_errstr(werr)));
-               talloc_destroy(mem_ctx);
+               TALLOC_FREE(mem_ctx);
                return false;
        }
 
@@ -1880,7 +2295,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                        domain->domain_type           = trusts.array[i].trust_type;
                        domain->domain_trust_attribs  = trusts.array[i].trust_attributes;
 
-                       if ( domain->domain_type == NETR_TRUST_TYPE_UPLEVEL )
+                       if ( domain->domain_type == LSA_TRUST_TYPE_UPLEVEL )
                                domain->active_directory = True;
 
                        /* This flag is only set if the domain is *our* 
@@ -1898,7 +2313,6 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                                 domain->active_directory ? "" : "NOT "));
 
                        domain->can_do_ncacn_ip_tcp = domain->active_directory;
-                       domain->can_do_validation6 = domain->active_directory;
 
                        domain->initialized = True;
 
@@ -1906,7 +2320,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                }               
        }
 
-       talloc_destroy( mem_ctx );
+       TALLOC_FREE(mem_ctx);
 
        return domain->initialized;     
 }
@@ -1929,7 +2343,7 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain )
        union dssetup_DsRoleInfo info;
        union lsa_PolicyInformation *lsa_info = NULL;
 
-       if (!connection_ok(domain)) {
+       if (!domain->internal && !connection_ok(domain)) {
                return;
        }
 
@@ -1942,9 +2356,15 @@ 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 ));
 
-       status = cli_rpc_pipe_open_noauth(domain->conn.cli,
-                                         &ndr_table_dssetup.syntax_id,
-                                         &cli);
+       if (domain->internal) {
+               status = wb_open_internal_pipe(mem_ctx,
+                                              &ndr_table_dssetup,
+                                              &cli);
+       } else {
+               status = cli_rpc_pipe_open_noauth(domain->conn.cli,
+                                                 &ndr_table_dssetup,
+                                                 &cli);
+       }
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to "
@@ -1993,9 +2413,14 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain )
        }
 
 no_dssetup:
-       status = cli_rpc_pipe_open_noauth(domain->conn.cli,
-                                         &ndr_table_lsarpc.syntax_id, &cli);
-
+       if (domain->internal) {
+               status = wb_open_internal_pipe(mem_ctx,
+                                              &ndr_table_lsarpc,
+                                              &cli);
+       } else {
+               status = cli_rpc_pipe_open_noauth(domain->conn.cli,
+                                                 &ndr_table_lsarpc, &cli);
+       }
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to "
                          "PI_LSARPC on domain %s: (%s)\n",
@@ -2022,20 +2447,61 @@ no_dssetup:
                domain->active_directory = True;
 
                if (lsa_info->dns.name.string) {
-                       fstrcpy(domain->name, lsa_info->dns.name.string);
+                       if (!strequal(domain->name, lsa_info->dns.name.string))
+                       {
+                               DEBUG(1, ("set_dc_type_and_flags_connect: DC "
+                                         "for domain %s claimed it was a DC "
+                                         "for domain %s, refusing to "
+                                         "initialize\n",
+                                         domain->name,
+                                         lsa_info->dns.name.string));
+                               TALLOC_FREE(cli);
+                               TALLOC_FREE(mem_ctx);
+                               return;
+                       }
+                       talloc_free(domain->name);
+                       domain->name = talloc_strdup(domain,
+                                                    lsa_info->dns.name.string);
+                       if (domain->name == NULL) {
+                               goto done;
+                       }
                }
 
                if (lsa_info->dns.dns_domain.string) {
-                       fstrcpy(domain->alt_name,
-                               lsa_info->dns.dns_domain.string);
+                       if (domain->alt_name != NULL &&
+                           !strequal(domain->alt_name,
+                                     lsa_info->dns.dns_domain.string))
+                       {
+                               DEBUG(1, ("set_dc_type_and_flags_connect: DC "
+                                         "for domain %s (%s) claimed it was "
+                                         "a DC for domain %s, refusing to "
+                                         "initialize\n",
+                                         domain->alt_name, domain->name,
+                                         lsa_info->dns.dns_domain.string));
+                               TALLOC_FREE(cli);
+                               TALLOC_FREE(mem_ctx);
+                               return;
+                       }
+                       talloc_free(domain->alt_name);
+                       domain->alt_name =
+                               talloc_strdup(domain,
+                                             lsa_info->dns.dns_domain.string);
+                       if (domain->alt_name == NULL) {
+                               goto done;
+                       }
                }
 
                /* See if we can set some domain trust flags about
                   ourself */
 
                if (lsa_info->dns.dns_forest.string) {
-                       fstrcpy(domain->forest_name,
-                               lsa_info->dns.dns_forest.string);
+                       talloc_free(domain->forest_name);
+                       domain->forest_name =
+                               talloc_strdup(domain,
+                                             lsa_info->dns.dns_forest.string);
+                       if (domain->forest_name == NULL) {
+                               goto done;
+                       }
 
                        if (strequal(domain->forest_name, domain->alt_name)) {
                                domain->domain_flags |= NETR_TRUST_FLAG_TREEROOT;
@@ -2043,6 +2509,23 @@ no_dssetup:
                }
 
                if (lsa_info->dns.sid) {
+                       if (!is_null_sid(&domain->sid) &&
+                           !dom_sid_equal(&domain->sid,
+                                          lsa_info->dns.sid))
+                       {
+                               DEBUG(1, ("set_dc_type_and_flags_connect: DC "
+                                         "for domain %s (%s) claimed it was "
+                                         "a DC for domain %s, refusing to "
+                                         "initialize\n",
+                                         dom_sid_string(talloc_tos(),
+                                                        &domain->sid),
+                                         domain->name,
+                                         dom_sid_string(talloc_tos(),
+                                                        lsa_info->dns.sid)));
+                               TALLOC_FREE(cli);
+                               TALLOC_FREE(mem_ctx);
+                               return;
+                       }
                        sid_copy(&domain->sid, lsa_info->dns.sid);
                }
        } else {
@@ -2064,11 +2547,45 @@ no_dssetup:
                if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
 
                        if (lsa_info->account_domain.name.string) {
-                               fstrcpy(domain->name,
-                                       lsa_info->account_domain.name.string);
+                               if (!strequal(domain->name,
+                                       lsa_info->account_domain.name.string))
+                               {
+                                       DEBUG(1,
+                                             ("set_dc_type_and_flags_connect: "
+                                              "DC for domain %s claimed it was"
+                                              " a DC for domain %s, refusing "
+                                              "to initialize\n", domain->name,
+                                              lsa_info->
+                                               account_domain.name.string));
+                                       TALLOC_FREE(cli);
+                                       TALLOC_FREE(mem_ctx);
+                                       return;
+                               }
+                               talloc_free(domain->name);
+                               domain->name =
+                                       talloc_strdup(domain,
+                                                     lsa_info->account_domain.name.string);
                        }
 
                        if (lsa_info->account_domain.sid) {
+                               if (!is_null_sid(&domain->sid) &&
+                                   !dom_sid_equal(&domain->sid,
+                                               lsa_info->account_domain.sid))
+                               {
+                                       DEBUG(1,
+                                             ("set_dc_type_and_flags_connect: "
+                                              "DC for domain %s (%s) claimed "
+                                              "it was a DC for domain %s, "
+                                              "refusing to initialize\n",
+                                              dom_sid_string(talloc_tos(),
+                                                             &domain->sid),
+                                              domain->name,
+                                              dom_sid_string(talloc_tos(),
+                                               lsa_info->account_domain.sid)));
+                                       TALLOC_FREE(cli);
+                                       TALLOC_FREE(mem_ctx);
+                                       return;
+                               }
                                sid_copy(&domain->sid, lsa_info->account_domain.sid);
                        }
                }
@@ -2082,7 +2599,6 @@ done:
                  domain->name, domain->active_directory ? "" : "NOT "));
 
        domain->can_do_ncacn_ip_tcp = domain->active_directory;
-       domain->can_do_validation6 = domain->active_directory;
 
        TALLOC_FREE(cli);
 
@@ -2099,9 +2615,9 @@ static void set_dc_type_and_flags( struct winbindd_domain *domain )
 {
        /* we always have to contact our primary domain */
 
-       if ( domain->primary ) {
+       if ( domain->primary || domain->internal) {
                DEBUG(10,("set_dc_type_and_flags: setting up flags for "
-                         "primary domain\n"));
+                         "primary or internal domain\n"));
                set_dc_type_and_flags_connect( domain );
                return;         
        }
@@ -2123,13 +2639,23 @@ static void set_dc_type_and_flags( struct winbindd_domain *domain )
 ***********************************************************************/
 
 static NTSTATUS cm_get_schannel_creds(struct winbindd_domain *domain,
-                                  struct netlogon_creds_CredentialState **ppdc)
+                                  struct netlogon_creds_cli_context **ppdc)
 {
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
        struct rpc_pipe_client *netlogon_pipe;
 
-       if (lp_client_schannel() == False) {
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       *ppdc = NULL;
+
+       if ((!IS_DC) && (!domain->primary)) {
+               return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+       }
+
+       if (domain->conn.netlogon_creds != NULL) {
+               if (!(domain->conn.netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
+                       return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+               }
+               *ppdc = domain->conn.netlogon_creds;
+               return NT_STATUS_OK;
        }
 
        result = cm_connect_netlogon(domain, &netlogon_pipe);
@@ -2137,32 +2663,36 @@ static NTSTATUS cm_get_schannel_creds(struct winbindd_domain *domain,
                return result;
        }
 
-       /* Return a pointer to the struct netlogon_creds_CredentialState from the
-          netlogon pipe. */
+       if (domain->conn.netlogon_creds == NULL) {
+               return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+       }
 
-       if (!domain->conn.netlogon_pipe->dc) {
-               return NT_STATUS_INTERNAL_ERROR; /* This shouldn't happen. */
+       if (!(domain->conn.netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
+               return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
        }
 
-       *ppdc = domain->conn.netlogon_pipe->dc;
+       *ppdc = domain->conn.netlogon_creds;
        return NT_STATUS_OK;
 }
 
 NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
+                       bool need_rw_dc,
                        struct rpc_pipe_client **cli, struct policy_handle *sam_handle)
 {
        struct winbindd_cm_conn *conn;
        NTSTATUS status, result;
-       struct netlogon_creds_CredentialState *p_creds;
-       char *machine_password = NULL;
-       char *machine_account = NULL;
-       char *domain_name = NULL;
+       struct netlogon_creds_cli_context *p_creds;
+       struct cli_credentials *creds = NULL;
+       bool retry = false; /* allow one retry attempt for expired session */
 
-       if (sid_check_is_domain(&domain->sid)) {
-               return open_internal_samr_conn(mem_ctx, domain, cli, sam_handle);
+       if (sid_check_is_our_sam(&domain->sid)) {
+               if (domain->rodc == false || need_rw_dc == false) {
+                       return open_internal_samr_conn(mem_ctx, domain, cli, sam_handle);
+               }
        }
 
-       status = init_dc_connection_rpc(domain);
+retry:
+       status = init_dc_connection_rpc(domain, need_rw_dc);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -2182,61 +2712,65 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
         * anonymous.
         */
 
-       if ((conn->cli->user_name[0] == '\0') ||
-           (conn->cli->domain[0] == '\0') || 
-           (conn->cli->password == NULL || conn->cli->password[0] == '\0'))
-       {
-               status = get_trust_creds(domain, &machine_password,
-                                        &machine_account, NULL);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(10, ("cm_connect_sam: No no user available for "
-                                  "domain %s, trying schannel\n", conn->cli->domain));
-                       goto schannel;
-               }
-               domain_name = domain->name;
-       } else {
-               machine_password = SMB_STRDUP(conn->cli->password);
-               machine_account = SMB_STRDUP(conn->cli->user_name);
-               domain_name = conn->cli->domain;
+       result = get_trust_credentials(domain, talloc_tos(), false, &creds);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(10, ("cm_connect_sam: No user available for "
+                          "domain %s, trying schannel\n", domain->name));
+               goto schannel;
        }
 
-       if (!machine_password || !machine_account) {
-               status = NT_STATUS_NO_MEMORY;
-               goto done;
+       if (cli_credentials_is_anonymous(creds)) {
+               goto anonymous;
        }
 
-       /* We have an authenticated connection. Use a NTLMSSP SPNEGO
-          authenticated SAMR pipe with sign & seal. */
-       status = cli_rpc_pipe_open_spnego(conn->cli,
-                                         &ndr_table_samr,
-                                         NCACN_NP,
-                                         GENSEC_OID_NTLMSSP,
-                                         DCERPC_AUTH_LEVEL_PRIVACY,
-                                         smbXcli_conn_remote_name(conn->cli->conn),
-                                         domain_name,
-                                         machine_account,
-                                         machine_password,
-                                         &conn->samr_pipe);
+       /*
+        * We have an authenticated connection. Use a SPNEGO
+        * authenticated SAMR pipe with sign & seal.
+        */
+       status = cli_rpc_pipe_open_with_creds(conn->cli,
+                                             &ndr_table_samr,
+                                             NCACN_NP,
+                                             DCERPC_AUTH_TYPE_SPNEGO,
+                                             conn->auth_level,
+                                             smbXcli_conn_remote_name(conn->cli->conn),
+                                             creds,
+                                             &conn->samr_pipe);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+           && !retry) {
+               invalidate_cm_connection(domain);
+               retry = true;
+               goto retry;
+       }
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: failed to connect to SAMR "
                          "pipe for domain %s using NTLMSSP "
-                         "authenticated pipe: user %s\\%s. Error was "
-                         "%s\n", domain->name, domain_name,
-                         machine_account, nt_errstr(status)));
+                         "authenticated pipe: user %s. Error was "
+                         "%s\n", domain->name,
+                         cli_credentials_get_unparsed_name(creds, talloc_tos()),
+                         nt_errstr(status)));
                goto schannel;
        }
 
        DEBUG(10,("cm_connect_sam: connected to SAMR pipe for "
                  "domain %s using NTLMSSP authenticated "
-                 "pipe: user %s\\%s\n", domain->name,
-                 domain_name, machine_account));
+                 "pipe: user %s\n", domain->name,
+                 cli_credentials_get_unparsed_name(creds, talloc_tos())));
 
        status = dcerpc_samr_Connect2(conn->samr_pipe->binding_handle, mem_ctx,
                                      conn->samr_pipe->desthost,
                                      SEC_FLAG_MAXIMUM_ALLOWED,
                                      &conn->sam_connect_handle,
                                      &result);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->samr_pipe);
+               retry = true;
+               goto retry;
+       }
+
        if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
                goto open_domain;
        }
@@ -2262,10 +2796,24 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                        nt_errstr(status) ));
                goto anonymous;
        }
-       status = cli_rpc_pipe_open_schannel_with_key
-               (conn->cli, &ndr_table_samr.syntax_id, NCACN_NP,
-                DCERPC_AUTH_LEVEL_PRIVACY,
-                domain->name, &p_creds, &conn->samr_pipe);
+       TALLOC_FREE(creds);
+       result = get_trust_credentials(domain, talloc_tos(), true, &creds);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(10, ("cm_connect_sam: No user available for "
+                          "domain %s (error %s), trying anon\n", domain->name,
+                          nt_errstr(result)));
+               goto anonymous;
+       }
+       status = cli_rpc_pipe_open_schannel_with_creds
+               (conn->cli, &ndr_table_samr, NCACN_NP,
+                creds, p_creds, &conn->samr_pipe);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+           && !retry) {
+               invalidate_cm_connection(domain);
+               retry = true;
+               goto retry;
+       }
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for "
@@ -2281,6 +2829,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                                      SEC_FLAG_MAXIMUM_ALLOWED,
                                      &conn->sam_connect_handle,
                                      &result);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->samr_pipe);
+               retry = true;
+               goto retry;
+       }
+
        if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
                goto open_domain;
        }
@@ -2295,9 +2851,25 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
  anonymous:
 
        /* Finally fall back to anonymous. */
-       status = cli_rpc_pipe_open_noauth(conn->cli, &ndr_table_samr.syntax_id,
+       if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
+               status = NT_STATUS_DOWNGRADE_DETECTED;
+               DEBUG(1, ("Unwilling to make SAMR connection to domain %s "
+                         "without connection level security, "
+                         "must set 'winbind sealed pipes = false' and "
+                         "'require strong key = false' to proceed: %s\n",
+                         domain->name, nt_errstr(status)));
+               goto done;
+       }
+       status = cli_rpc_pipe_open_noauth(conn->cli, &ndr_table_samr,
                                          &conn->samr_pipe);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+           && !retry) {
+               invalidate_cm_connection(domain);
+               retry = true;
+               goto retry;
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                goto done;
        }
@@ -2307,6 +2879,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                                      SEC_FLAG_MAXIMUM_ALLOWED,
                                      &conn->sam_connect_handle,
                                      &result);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->samr_pipe);
+               retry = true;
+               goto retry;
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: rpccli_samr_Connect2 failed "
                          "for domain %s Error was %s\n",
@@ -2348,14 +2928,12 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                ZERO_STRUCT(conn->sam_domain_handle);
                return status;
        } else if (!NT_STATUS_IS_OK(status)) {
-               invalidate_cm_connection(conn);
+               invalidate_cm_connection(domain);
                return status;
        }
 
        *cli = conn->samr_pipe;
        *sam_handle = conn->sam_domain_handle;
-       SAFE_FREE(machine_password);
-       SAFE_FREE(machine_account);
        return status;
 }
 
@@ -2363,17 +2941,18 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
  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)
+static NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
+                                  TALLOC_CTX *mem_ctx,
+                                  struct rpc_pipe_client **cli)
 {
        struct winbindd_cm_conn *conn;
-       struct netlogon_creds_CredentialState *creds;
+       struct netlogon_creds_cli_context *p_creds = NULL;
+       struct cli_credentials *creds = NULL;
        NTSTATUS status;
 
        DEBUG(10,("cm_connect_lsa_tcp\n"));
 
-       status = init_dc_connection_rpc(domain);
+       status = init_dc_connection_rpc(domain, false);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -2382,25 +2961,29 @@ NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
 
        if (conn->lsa_pipe_tcp &&
            conn->lsa_pipe_tcp->transport->transport == NCACN_IP_TCP &&
-           conn->lsa_pipe_tcp->auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY &&
+           conn->lsa_pipe_tcp->auth->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY &&
            rpccli_is_connected(conn->lsa_pipe_tcp)) {
                goto done;
        }
 
        TALLOC_FREE(conn->lsa_pipe_tcp);
 
-       status = cm_get_schannel_creds(domain, &creds);
+       status = cm_get_schannel_creds(domain, &p_creds);
        if (!NT_STATUS_IS_OK(status)) {
                goto done;
        }
 
-       status = cli_rpc_pipe_open_schannel_with_key(conn->cli,
-                                                    &ndr_table_lsarpc.syntax_id,
-                                                    NCACN_IP_TCP,
-                                                    DCERPC_AUTH_LEVEL_PRIVACY,
-                                                    domain->name,
-                                                    &creds,
-                                                    &conn->lsa_pipe_tcp);
+       status = get_trust_credentials(domain, talloc_tos(), true, &creds);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       status = cli_rpc_pipe_open_schannel_with_creds(conn->cli,
+                                                      &ndr_table_lsarpc,
+                                                      NCACN_IP_TCP,
+                                                      creds,
+                                                      p_creds,
+                                                      &conn->lsa_pipe_tcp);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cli_rpc_pipe_open_schannel_with_key failed: %s\n",
                        nt_errstr(status)));
@@ -2423,9 +3006,12 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 {
        struct winbindd_cm_conn *conn;
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
-       struct netlogon_creds_CredentialState *p_creds;
+       struct netlogon_creds_cli_context *p_creds;
+       struct cli_credentials *creds = NULL;
+       bool retry = false; /* allow one retry attempt for expired session */
 
-       result = init_dc_connection_rpc(domain);
+retry:
+       result = init_dc_connection_rpc(domain, false);
        if (!NT_STATUS_IS_OK(result))
                return result;
 
@@ -2437,40 +3023,60 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 
        TALLOC_FREE(conn->lsa_pipe);
 
-       if ((conn->cli->user_name[0] == '\0') ||
-           (conn->cli->domain[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));
+       result = get_trust_credentials(domain, talloc_tos(), false, &creds);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(10, ("cm_connect_lsa: No user available for "
+                          "domain %s, trying schannel\n", domain->name));
                goto schannel;
        }
 
-       /* We have an authenticated connection. Use a NTLMSSP SPNEGO
-        * authenticated LSA pipe with sign & seal. */
-       result = cli_rpc_pipe_open_spnego
+       if (cli_credentials_is_anonymous(creds)) {
+               goto anonymous;
+       }
+
+       /*
+        * We have an authenticated connection. Use a SPNEGO
+        * authenticated LSA pipe with sign & seal.
+        */
+       result = cli_rpc_pipe_open_with_creds
                (conn->cli, &ndr_table_lsarpc, NCACN_NP,
-                GENSEC_OID_NTLMSSP,
-                DCERPC_AUTH_LEVEL_PRIVACY,
+                DCERPC_AUTH_TYPE_SPNEGO,
+                conn->auth_level,
                 smbXcli_conn_remote_name(conn->cli->conn),
-                conn->cli->domain, conn->cli->user_name, conn->cli->password,
+                creds,
                 &conn->lsa_pipe);
 
+       if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+           && !retry) {
+               invalidate_cm_connection(domain);
+               retry = true;
+               goto retry;
+       }
+
        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",
-                         domain->name, conn->cli->domain,
-                         conn->cli->user_name, nt_errstr(result)));
+                         "%s. Error was %s. Trying schannel.\n",
+                         domain->name,
+                         cli_credentials_get_unparsed_name(creds, talloc_tos()),
+                         nt_errstr(result)));
                goto schannel;
        }
 
        DEBUG(10,("cm_connect_lsa: connected to LSA pipe for domain %s using "
-                 "NTLMSSP authenticated pipe: user %s\\%s\n",
-                 domain->name, conn->cli->domain, conn->cli->user_name ));
+                 "NTLMSSP authenticated pipe: user %s\n",
+                 domain->name, cli_credentials_get_unparsed_name(creds, talloc_tos())));
 
        result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
                                        SEC_FLAG_MAXIMUM_ALLOWED,
                                        &conn->lsa_policy);
+       if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->lsa_pipe);
+               retry = true;
+               goto retry;
+       }
+
        if (NT_STATUS_IS_OK(result)) {
                goto done;
        }
@@ -2493,10 +3099,25 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                        nt_errstr(result) ));
                goto anonymous;
        }
-       result = cli_rpc_pipe_open_schannel_with_key
-               (conn->cli, &ndr_table_lsarpc.syntax_id, NCACN_NP,
-                DCERPC_AUTH_LEVEL_PRIVACY,
-                domain->name, &p_creds, &conn->lsa_pipe);
+
+       TALLOC_FREE(creds);
+       result = get_trust_credentials(domain, talloc_tos(), true, &creds);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(10, ("cm_connect_lsa: No user available for "
+                          "domain %s (error %s), trying anon\n", domain->name,
+                          nt_errstr(result)));
+               goto anonymous;
+       }
+       result = cli_rpc_pipe_open_schannel_with_creds
+               (conn->cli, &ndr_table_lsarpc, NCACN_NP,
+                creds, p_creds, &conn->lsa_pipe);
+
+       if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+           && !retry) {
+               invalidate_cm_connection(domain);
+               retry = true;
+               goto retry;
+       }
 
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
@@ -2510,6 +3131,14 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
                                        SEC_FLAG_MAXIMUM_ALLOWED,
                                        &conn->lsa_policy);
+
+       if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->lsa_pipe);
+               retry = true;
+               goto retry;
+       }
+
        if (NT_STATUS_IS_OK(result)) {
                goto done;
        }
@@ -2521,20 +3150,45 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 
  anonymous:
 
+       if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
+               result = NT_STATUS_DOWNGRADE_DETECTED;
+               DEBUG(1, ("Unwilling to make LSA connection to domain %s "
+                         "without connection level security, "
+                         "must set 'winbind sealed pipes = false' and "
+                         "'require strong key = false' to proceed: %s\n",
+                         domain->name, nt_errstr(result)));
+               goto done;
+       }
+
        result = cli_rpc_pipe_open_noauth(conn->cli,
-                                         &ndr_table_lsarpc.syntax_id,
+                                         &ndr_table_lsarpc,
                                          &conn->lsa_pipe);
+
+       if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+           && !retry) {
+               invalidate_cm_connection(domain);
+               retry = true;
+               goto retry;
+       }
+
        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_FLAG_MAXIMUM_ALLOWED,
                                        &conn->lsa_policy);
+
+       if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->lsa_pipe);
+               retry = true;
+               goto retry;
+       }
+
  done:
        if (!NT_STATUS_IS_OK(result)) {
-               invalidate_cm_connection(conn);
+               invalidate_cm_connection(domain);
                return result;
        }
 
@@ -2543,26 +3197,63 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        return result;
 }
 
+/****************************************************************************
+Open a LSA connection to a DC, suiteable for LSA lookup calls.
+****************************************************************************/
+
+NTSTATUS cm_connect_lsat(struct winbindd_domain *domain,
+                        TALLOC_CTX *mem_ctx,
+                        struct rpc_pipe_client **cli,
+                        struct policy_handle *lsa_policy)
+{
+       NTSTATUS status;
+
+       if (domain->can_do_ncacn_ip_tcp) {
+               status = cm_connect_lsa_tcp(domain, mem_ctx, cli);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+                   NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR) ||
+                   NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
+                       invalidate_cm_connection(domain);
+                       status = cm_connect_lsa_tcp(domain, mem_ctx, cli);
+               }
+               if (NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               /*
+                * we tried twice to connect via ncan_ip_tcp and schannel and
+                * failed - maybe it is a trusted domain we can't connect to ?
+                * do not try tcp next time - gd
+                *
+                * This also prevents NETLOGON over TCP
+                */
+               domain->can_do_ncacn_ip_tcp = false;
+       }
+
+       status = cm_connect_lsa(domain, mem_ctx, cli, lsa_policy);
+
+       return status;
+}
+
 /****************************************************************************
  Open the netlogon pipe to this DC. Use schannel if specified in client conf.
  session key stored in conn->netlogon_pipe->dc->sess_key.
 ****************************************************************************/
 
-NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
-                            struct rpc_pipe_client **cli)
+static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
+                                             enum dcerpc_transport_t transport,
+                                             struct rpc_pipe_client **cli)
 {
+       struct messaging_context *msg_ctx = winbind_messaging_context();
        struct winbindd_cm_conn *conn;
        NTSTATUS result;
-
-       uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
-       uint8  mach_pwd[16];
        enum netr_SchannelType sec_chan_type;
-       const char *account_name;
-       struct rpc_pipe_client *netlogon_pipe = NULL;
+       struct netlogon_creds_CredentialState *netlogon_creds = NULL;
+       struct cli_credentials *creds = NULL;
 
        *cli = NULL;
 
-       result = init_dc_connection_rpc(domain);
+       result = init_dc_connection_rpc(domain, domain->rodc);
        if (!NT_STATUS_IS_OK(result)) {
                return result;
        }
@@ -2575,64 +3266,83 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
        }
 
        TALLOC_FREE(conn->netlogon_pipe);
+       conn->netlogon_flags = 0;
+       TALLOC_FREE(conn->netlogon_creds);
 
-       result = cli_rpc_pipe_open_noauth(conn->cli,
-                                         &ndr_table_netlogon.syntax_id,
-                                         &netlogon_pipe);
+       result = get_trust_credentials(domain, talloc_tos(), true, &creds);
        if (!NT_STATUS_IS_OK(result)) {
-               return result;
-       }
-
-       if ((!IS_DC) && (!domain->primary)) {
-               /* Clear the schannel request bit and drop down */
-               neg_flags &= ~NETLOGON_NEG_SCHANNEL;            
-               goto no_schannel;
+               DEBUG(10, ("cm_connect_sam: No user available for "
+                          "domain %s when trying schannel\n", domain->name));
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
-       if (lp_client_schannel() != False) {
-               neg_flags |= NETLOGON_NEG_SCHANNEL;
+       if (cli_credentials_is_anonymous(creds)) {
+               DEBUG(1, ("get_trust_credential only gave anonymous for %s, unable to make get NETLOGON credentials\n",
+                         domain->name));
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
-       if (!get_trust_pw_hash(domain->name, mach_pwd, &account_name,
-                              &sec_chan_type))
-       {
-               TALLOC_FREE(netlogon_pipe);
+       sec_chan_type = cli_credentials_get_secure_channel_type(creds);
+       if (sec_chan_type == SEC_CHAN_NULL) {
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
-       result = rpccli_netlogon_setup_creds(
-                netlogon_pipe,
-                domain->dcname, /* server name. */
-                domain->name,   /* domain name */
-                lp_netbios_name(), /* client name */
-                account_name,   /* machine account */
-                mach_pwd,       /* machine password */
-                sec_chan_type,  /* from get_trust_pw */
-                &neg_flags);
-
+       result = rpccli_create_netlogon_creds_with_creds(creds,
+                                                        domain->dcname,
+                                                        msg_ctx,
+                                                        domain,
+                                                        &conn->netlogon_creds);
        if (!NT_STATUS_IS_OK(result)) {
-               TALLOC_FREE(netlogon_pipe);
+               DEBUG(1, ("rpccli_create_netlogon_creds failed for %s, "
+                         "unable to create NETLOGON credentials: %s\n",
+                         domain->name, nt_errstr(result)));
                return result;
        }
 
-       if ((lp_client_schannel() == True) &&
-                       ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
-               DEBUG(3, ("Server did not offer schannel\n"));
-               TALLOC_FREE(netlogon_pipe);
-               return NT_STATUS_ACCESS_DENIED;
+       result = rpccli_setup_netlogon_creds_with_creds(conn->cli, transport,
+                                               conn->netlogon_creds,
+                                               conn->netlogon_force_reauth,
+                                               creds);
+       conn->netlogon_force_reauth = false;
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(1, ("rpccli_setup_netlogon_creds failed for %s, "
+                         "unable to setup NETLOGON credentials: %s\n",
+                         domain->name, nt_errstr(result)));
+               return result;
        }
 
- no_schannel:
-       if ((lp_client_schannel() == False) ||
-                       ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
-               /*
-                * NetSamLogonEx only works for schannel
-                */
-               domain->can_do_samlogon_ex = False;
+       result = netlogon_creds_cli_get(conn->netlogon_creds,
+                                       talloc_tos(),
+                                       &netlogon_creds);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(1, ("netlogon_creds_cli_get failed for %s, "
+                         "unable to get NETLOGON credentials: %s\n",
+                         domain->name, nt_errstr(result)));
+               return result;
+       }
+       conn->netlogon_flags = netlogon_creds->negotiate_flags;
+       TALLOC_FREE(netlogon_creds);
+
+       if (!(conn->netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
+               if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
+                       result = NT_STATUS_DOWNGRADE_DETECTED;
+                       DEBUG(1, ("Unwilling to make connection to domain %s"
+                                 "without connection level security, "
+                                 "must set 'winbind sealed pipes = false' and "
+                                 "'require strong key = false' to proceed: %s\n",
+                                 domain->name, nt_errstr(result)));
+                       invalidate_cm_connection(domain);
+                       return result;
+               }
+               result = cli_rpc_pipe_open_noauth_transport(conn->cli,
+                                                           transport,
+                                                           &ndr_table_netlogon,
+                                                           &conn->netlogon_pipe);
+               if (!NT_STATUS_IS_OK(result)) {
+                       invalidate_cm_connection(domain);
+                       return result;
+               }
 
-               /* We're done - just keep the existing connection to NETLOGON
-                * open */
-               conn->netlogon_pipe = netlogon_pipe;
                *cli = conn->netlogon_pipe;
                return NT_STATUS_OK;
        }
@@ -2642,35 +3352,72 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
           part of the new pipe auth struct.
        */
 
-       result = cli_rpc_pipe_open_schannel_with_key(
-               conn->cli, &ndr_table_netlogon.syntax_id, NCACN_NP,
-               DCERPC_AUTH_LEVEL_PRIVACY, domain->name, &netlogon_pipe->dc,
+       result = cli_rpc_pipe_open_schannel_with_creds(
+               conn->cli, &ndr_table_netlogon, transport,
+               creds,
+               conn->netlogon_creds,
                &conn->netlogon_pipe);
-
-       /* We can now close the initial netlogon pipe. */
-       TALLOC_FREE(netlogon_pipe);
-
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(3, ("Could not open schannel'ed NETLOGON pipe. Error "
                          "was %s\n", nt_errstr(result)));
 
-               invalidate_cm_connection(conn);
+               invalidate_cm_connection(domain);
                return result;
        }
 
-       /*
-        * 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 = true;
-
        *cli = conn->netlogon_pipe;
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+Open a LSA connection to a DC, suiteable for LSA lookup calls.
+****************************************************************************/
+
+NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
+                            struct rpc_pipe_client **cli)
+{
+       NTSTATUS status;
+
+       status = init_dc_connection_rpc(domain, domain->rodc);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (domain->active_directory && domain->can_do_ncacn_ip_tcp) {
+               status = cm_connect_netlogon_transport(domain, NCACN_IP_TCP, cli);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+                   NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR) ||
+                   NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
+                       invalidate_cm_connection(domain);
+                       status = cm_connect_netlogon_transport(domain, NCACN_IP_TCP, cli);
+               }
+               if (NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               /*
+                * we tried twice to connect via ncan_ip_tcp and schannel and
+                * failed - maybe it is a trusted domain we can't connect to ?
+                * do not try tcp next time - gd
+                *
+                * This also prevents LSA over TCP
+                */
+               domain->can_do_ncacn_ip_tcp = false;
+       }
+
+       status = cm_connect_netlogon_transport(domain, NCACN_NP, cli);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
+               /*
+                * SMB2 session expired, needs reauthentication. Drop
+                * connection and retry.
+                */
+               invalidate_cm_connection(domain);
+               status = cm_connect_netlogon_transport(domain, NCACN_NP, cli);
+       }
+
+       return status;
+}
+
 void winbind_msg_ip_dropped(struct messaging_context *msg_ctx,
                            void *private_data,
                            uint32_t msg_type,
@@ -2725,7 +3472,7 @@ void winbind_msg_ip_dropped(struct messaging_context *msg_ctx,
                               smbXcli_conn_local_sockaddr(domain->conn.cli->conn));
 
                if (strequal(sockaddr, addr)) {
-                       cli_state_disconnect(domain->conn.cli);
+                       smbXcli_conn_disconnect(domain->conn.cli->conn, NT_STATUS_OK);
                }
        }
        TALLOC_FREE(freeit);