s4:torture/vfs/fruit: enable AAPL extensions in a bunch of tests
[samba.git] / source3 / winbindd / winbindd_cm.c
index 5071e020e0c12207cc684a2812a37b04238c38f9..f3f8d547c7e95ee6de00e991b2ceb504b5661bce 100644 (file)
@@ -60,6 +60,7 @@
 
 #include "includes.h"
 #include "winbindd.h"
+#include "libsmb/namequery.h"
 #include "../libcli/auth/libcli_auth.h"
 #include "../librpc/gen_ndr/ndr_netlogon_c.h"
 #include "rpc_client/cli_pipe.h"
@@ -83,6 +84,7 @@
 #include "rpc_server/rpc_ncacn_np.h"
 #include "auth/credentials/credentials.h"
 #include "lib/param/param.h"
+#include "lib/gencache.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -99,7 +101,8 @@ static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain, bool
 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);
+                   struct dc_name_ip **dcs, int *num_dcs,
+                   uint32_t request_flags);
 
 /****************************************************************
  Child failed to find DC's. Reschedule check.
@@ -198,6 +201,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
        pid_t parent_pid = getpid();
        char *lfile = NULL;
        NTSTATUS status;
+       bool ok;
 
        if (domain->dc_probe_pid != (pid_t)-1) {
                /*
@@ -222,10 +226,10 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
 
        if (domain->dc_probe_pid != (pid_t)0) {
                /* Parent */
-               messaging_register(winbind_messaging_context(), NULL,
+               messaging_register(global_messaging_context(), NULL,
                                   MSG_WINBIND_TRY_TO_GO_ONLINE,
                                   msg_try_to_go_online);
-               messaging_register(winbind_messaging_context(), NULL,
+               messaging_register(global_messaging_context(), NULL,
                                   MSG_WINBIND_FAILED_TO_GO_ONLINE,
                                   msg_failed_to_go_online);
                return True;
@@ -246,7 +250,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("winbindd_reinit_after_fork failed: %s\n",
                          nt_errstr(status)));
-               messaging_send_buf(winbind_messaging_context(),
+               messaging_send_buf(global_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_FAILED_TO_GO_ONLINE,
                                   (const uint8_t *)domain->name,
@@ -255,10 +259,12 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
        }
        SAFE_FREE(lfile);
 
+       setproctitle("dc-connect child");
+
        mem_ctx = talloc_init("fork_child_dc_connect");
        if (!mem_ctx) {
                DEBUG(0,("talloc_init failed.\n"));
-               messaging_send_buf(winbind_messaging_context(),
+               messaging_send_buf(global_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_FAILED_TO_GO_ONLINE,
                                   (const uint8_t *)domain->name,
@@ -266,9 +272,11 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
                _exit(1);
        }
 
-       if ((!get_dcs(mem_ctx, domain, &dcs, &num_dcs)) || (num_dcs == 0)) {
+       ok = get_dcs(mem_ctx, domain, &dcs, &num_dcs, 0);
+       TALLOC_FREE(mem_ctx);
+       if (!ok || (num_dcs == 0)) {
                /* Still offline ? Can't find DC's. */
-               messaging_send_buf(winbind_messaging_context(),
+               messaging_send_buf(global_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_FAILED_TO_GO_ONLINE,
                                   (const uint8_t *)domain->name,
@@ -279,7 +287,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
        /* We got a DC. Send a message to our parent to get it to
           try and do the same. */
 
-       messaging_send_buf(winbind_messaging_context(),
+       messaging_send_buf(global_messaging_context(),
                           pid_to_procid(parent_pid),
                           MSG_WINBIND_TRY_TO_GO_ONLINE,
                           (const uint8_t *)domain->name,
@@ -427,7 +435,7 @@ void set_domain_offline(struct winbindd_domain *domain)
 
        calc_new_online_timeout_check(domain);
 
-       domain->check_online_event = tevent_add_timer(winbind_event_context(),
+       domain->check_online_event = tevent_add_timer(global_event_context(),
                                                NULL,
                                                timeval_current_ofs(domain->check_online_timeout,0),
                                                check_domain_online_handler,
@@ -443,10 +451,10 @@ void set_domain_offline(struct winbindd_domain *domain)
 
        /* Send a message to the parent that the domain is offline. */
        if (parent_pid > 1 && !domain->internal) {
-               messaging_send_buf(winbind_messaging_context(),
+               messaging_send_buf(global_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_DOMAIN_OFFLINE,
-                                  (uint8 *)domain->name,
+                                  (uint8_t *)domain->name,
                                   strlen(domain->name) + 1);
        }
 
@@ -457,7 +465,7 @@ void set_domain_offline(struct winbindd_domain *domain)
                struct winbindd_child *idmap = idmap_child();
 
                if ( idmap->pid != 0 ) {
-                       messaging_send_buf(winbind_messaging_context(),
+                       messaging_send_buf(global_messaging_context(),
                                           pid_to_procid(idmap->pid), 
                                           MSG_WINBIND_OFFLINE, 
                                           (const uint8_t *)domain->name,
@@ -520,19 +528,19 @@ static void set_domain_online(struct winbindd_domain *domain)
        TALLOC_FREE(domain->check_online_event);
 
        /* Ensure we ignore any pending child messages. */
-       messaging_deregister(winbind_messaging_context(),
+       messaging_deregister(global_messaging_context(),
                             MSG_WINBIND_TRY_TO_GO_ONLINE, NULL);
-       messaging_deregister(winbind_messaging_context(),
+       messaging_deregister(global_messaging_context(),
                             MSG_WINBIND_FAILED_TO_GO_ONLINE, NULL);
 
        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(),
+               messaging_send_buf(global_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_DOMAIN_ONLINE,
-                                  (uint8 *)domain->name,
+                                  (uint8_t *)domain->name,
                                   strlen(domain->name) + 1);
        }
 
@@ -543,7 +551,7 @@ static void set_domain_online(struct winbindd_domain *domain)
                struct winbindd_child *idmap = idmap_child();
 
                if ( idmap->pid != 0 ) {
-                       messaging_send_buf(winbind_messaging_context(),
+                       messaging_send_buf(global_messaging_context(),
                                           pid_to_procid(idmap->pid), 
                                           MSG_WINBIND_ONLINE, 
                                           (const uint8_t *)domain->name,
@@ -603,7 +611,7 @@ void set_domain_online_request(struct winbindd_domain *domain)
 
        TALLOC_FREE(domain->check_online_event);
 
-       domain->check_online_event = tevent_add_timer(winbind_event_context(),
+       domain->check_online_event = tevent_add_timer(global_event_context(),
                                                     NULL,
                                                     tev,
                                                     check_domain_online_handler,
@@ -769,7 +777,8 @@ static bool cm_is_ipc_credentials(struct cli_credentials *creds)
 
 static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
                                     fstring dcname,
-                                    struct sockaddr_storage *dc_ss)
+                                    struct sockaddr_storage *dc_ss,
+                                    uint32_t request_flags)
 {
        struct winbindd_domain *our_domain = NULL;
        struct rpc_pipe_client *netlogon_pipe = NULL;
@@ -814,13 +823,17 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
        if (our_domain->active_directory) {
                struct netr_DsRGetDCNameInfo *domain_info = NULL;
 
+               /*
+                * TODO request flags are not respected in the server
+                * (and in some cases, like REQUIRE_PDC, causes an error)
+                */
                result = dcerpc_netr_DsRGetDCName(b,
                                                  mem_ctx,
                                                  our_domain->dcname,
                                                  domain->name,
                                                  NULL,
                                                  NULL,
-                                                 DS_RETURN_DNS_NAME,
+                                                 request_flags|DS_RETURN_DNS_NAME,
                                                  &domain_info,
                                                  &werr);
                if (NT_STATUS_IS_OK(result) && W_ERROR_IS_OK(werr)) {
@@ -902,10 +915,24 @@ static NTSTATUS get_trust_credentials(struct winbindd_domain *domain,
        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 && netlogon) {
+       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 (IS_DC && !force_machine_account) {
                creds_domain = domain;
        } else {
                creds_domain = find_our_domain();
@@ -922,19 +949,6 @@ static NTSTATUS get_trust_credentials(struct winbindd_domain *domain,
                goto ipc_fallback;
        }
 
-       if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
-               cli_credentials_set_kerberos_state(creds,
-                                                  CRED_MUST_USE_KERBEROS);
-       }
-
-       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_DONT_USE_KERBEROS);
-       }
-
        if (creds_domain != domain) {
                /*
                 * We can only use schannel against a direct trust
@@ -972,19 +986,45 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                                      bool *retry)
 {
        bool try_ipc_auth = false;
-       const char *machine_password = NULL;
-       const char *machine_krb5_principal = NULL;
+       const char *machine_principal = NULL;
+       const char *machine_realm = NULL;
        const char *machine_account = NULL;
        const char *machine_domain = NULL;
        int flags = 0;
        struct cli_credentials *creds = NULL;
-       enum credentials_use_kerberos krb5_state;
 
        struct named_mutex *mutex;
 
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       NTSTATUS tmp_status;
+       NTSTATUS tcon_status = NT_STATUS_NETWORK_NAME_DELETED;
+
+       enum smb_signing_setting smb_sign_client_connections = lp_client_ipc_signing();
 
-       enum smb_signing_setting smb_sign_client_connections = lp_client_signing();
+       if (IS_AD_DC) {
+               if (domain->secure_channel_type == SEC_CHAN_NULL) {
+                       /*
+                        * Make sure we don't even try to
+                        * connect to a foreign domain
+                        * without a direct outbound trust.
+                        */
+                       return NT_STATUS_NO_TRUST_LSA_SECRET;
+               }
+
+               /*
+                * As AD DC we only use netlogon and lsa
+                * using schannel over an anonymous transport
+                * (ncacn_ip_tcp or ncacn_np).
+                *
+                * Currently we always establish the SMB connection,
+                * even if we don't use it, because we later use ncacn_ip_tcp.
+                *
+                * As we won't use the SMB connection there's no
+                * need to try kerberos. And NT4 domains expect
+                * an anonymous IPC$ connection anyway.
+                */
+               smb_sign_client_connections = SMB_SIGNING_OFF;
+       }
 
        if (smb_sign_client_connections == SMB_SIGNING_DEFAULT) {
                /*
@@ -998,8 +1038,7 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                 * 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)
+               } else if ((lp_security() == SEC_ADS)
                           && domain->active_directory
                           && (domain->domain_trust_attribs
                               & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
@@ -1022,10 +1061,7 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                goto done;
        }
 
-       flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
-
-       *cli = cli_state_create(NULL, sockfd,
-                               controller, domain->alt_name,
+       *cli = cli_state_create(NULL, sockfd, controller,
                                smb_sign_client_connections, flags);
        if (*cli == NULL) {
                close(sockfd);
@@ -1036,9 +1072,11 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
 
        cli_set_timeout(*cli, 10000); /* 10 seconds */
 
+       set_socket_options(sockfd, lp_socket_options());
+
        result = smbXcli_negprot((*cli)->conn, (*cli)->timeout,
-                                lp_client_min_protocol(),
-                                lp_winbindd_max_protocol());
+                                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)));
@@ -1059,6 +1097,22 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                try_ipc_auth = true;
        }
 
+       if (IS_AD_DC) {
+               /*
+                * As AD DC we only use netlogon and lsa
+                * using schannel over an anonymous transport
+                * (ncacn_ip_tcp or ncacn_np).
+                *
+                * Currently we always establish the SMB connection,
+                * even if we don't use it, because we later use ncacn_ip_tcp.
+                *
+                * As we won't use the SMB connection there's no
+                * need to try kerberos. And NT4 domains expect
+                * an anonymous IPC$ connection anyway.
+                */
+               try_ipc_auth = false;
+       }
+
        if (try_ipc_auth) {
                result = get_trust_credentials(domain, talloc_tos(), false, &creds);
                if (!NT_STATUS_IS_OK(result)) {
@@ -1082,66 +1136,34 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                }
        }
 
-       krb5_state = cli_credentials_get_kerberos_state(creds);
-
-       machine_krb5_principal = cli_credentials_get_principal(creds,
+       machine_principal = cli_credentials_get_principal(creds,
                                                        talloc_tos());
-       if (machine_krb5_principal == NULL) {
-               krb5_state = CRED_DONT_USE_KERBEROS;
-       }
-
+       machine_realm = cli_credentials_get_realm(creds);
        machine_account = cli_credentials_get_username(creds);
-       machine_password = cli_credentials_get_password(creds);
        machine_domain = cli_credentials_get_domain(creds);
 
-       if (krb5_state != CRED_DONT_USE_KERBEROS) {
-
-               /* Try a krb5 session */
-
-               (*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));
-
-               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);
+       DEBUG(5, ("connecting to %s (%s, %s) with account [%s\\%s] principal "
+                 "[%s] and realm [%s]\n",
+                 controller, domain->name, domain->alt_name,
+                 machine_domain, machine_account,
+                 machine_principal, machine_realm));
 
-               if (NT_STATUS_IS_OK(result)) {
-                       goto session_setup_done;
-               }
-
-               DEBUG(4,("failed kerberos session setup with %s\n",
-                        nt_errstr(result)));
+       if (cli_credentials_is_anonymous(creds)) {
+               goto anon_fallback;
        }
 
-       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 using NTLMSSP with username "
-                         "[%s]\\[%s]\n",  controller, lp_netbios_name(),
-                         machine_domain, machine_account));
-
-               result = cli_session_setup(*cli,
-                                          machine_account,
-                                          machine_password,
-                                          strlen(machine_password)+1,
-                                          machine_password,
-                                          strlen(machine_password)+1,
-                                          machine_domain);
-       }
+       winbindd_set_locator_kdc_envs(domain);
 
+       result = cli_session_setup_creds(*cli, creds);
        if (NT_STATUS_IS_OK(result)) {
                goto session_setup_done;
        }
 
+       DEBUG(1, ("authenticated session setup to %s using %s failed with %s\n",
+                 controller,
+                 cli_credentials_get_unparsed_name(creds, talloc_tos()),
+                 nt_errstr(result)));
+
        /*
         * If we are not going to validiate the conneciton
         * with SMB signing, then allow us to fall back to
@@ -1149,12 +1171,10 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
         */
        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 (!cm_is_ipc_credentials(creds)) {
                        goto ipc_fallback;
                }
@@ -1166,45 +1186,37 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                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)) {
+       TALLOC_FREE(creds);
+       tmp_status = cm_get_ipc_credentials(talloc_tos(), &creds);
+       if (!NT_STATUS_IS_OK(tmp_status)) {
+               result = tmp_status;
                goto done;
        }
 
        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;
-
        DEBUG(5, ("connecting to %s from %s using NTLMSSP with username "
                  "[%s]\\[%s]\n",  controller, lp_netbios_name(),
                  machine_domain, machine_account));
 
-       result = cli_session_setup(*cli,
-                                  machine_account,
-                                  machine_password,
-                                  strlen(machine_password)+1,
-                                  machine_password,
-                                  strlen(machine_password)+1,
-                                  machine_domain);
-
+       result = cli_session_setup_creds(*cli, creds);
        if (NT_STATUS_IS_OK(result)) {
                goto session_setup_done;
        }
 
+       DEBUG(1, ("authenticated session setup to %s using %s failed with %s\n",
+                 controller,
+                 cli_credentials_get_unparsed_name(creds, talloc_tos()),
+                 nt_errstr(result)));
+
        /*
         * If we are not going to validiate the conneciton
         * with SMB signing, then allow us to fall back to
@@ -1212,56 +1224,41 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
         */
        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;
        }
 
-       DEBUG(4, ("authenticated session setup failed with %s\n",
-               nt_errstr(result)));
-
        goto done;
 
  anon_fallback:
+       TALLOC_FREE(creds);
 
        if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
                goto done;
        }
 
-       creds = cli_credentials_init_anon(talloc_tos());
-       if (creds == NULL) {
-               result = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
-
-       machine_account = cli_credentials_get_username(creds);
-       machine_password = cli_credentials_get_password(creds);
-       machine_domain = cli_credentials_get_domain(creds);
-
        /* Fall back to anonymous connection, this might fail later */
-       DEBUG(10,("cm_prepare_connection: falling back to anonymous "
+       DEBUG(5,("cm_prepare_connection: falling back to anonymous "
                "connection for DC %s\n",
                controller ));
 
-       (*cli)->use_kerberos = False;
-
-       result = cli_session_setup(*cli,
-                                  machine_account,
-                                  machine_password,
-                                  strlen(machine_password)+1,
-                                  machine_password,
-                                  strlen(machine_password)+1,
-                                  machine_domain);
-
+       result = cli_session_setup_anon(*cli);
        if (NT_STATUS_IS_OK(result)) {
                DEBUG(5, ("Connected anonymously\n"));
                goto session_setup_done;
        }
 
+       DEBUG(1, ("anonymous session setup to %s failed with %s\n",
+                 controller, nt_errstr(result)));
+
        /* We can't session setup */
        goto done;
 
  session_setup_done:
+       TALLOC_FREE(creds);
 
        /*
         * This should be a short term hack until
@@ -1274,17 +1271,17 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
                smbXcli_session_set_disconnect_expired((*cli)->smb2.session);
        }
 
-       result = cli_tree_connect(*cli, "IPC$", "IPC", "", 0);
-
+       result = cli_tree_connect(*cli, "IPC$", "IPC", NULL);
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
                goto done;
        }
+       tcon_status = result;
 
        /* cache the server name for later connections */
 
        saf_store(domain->name, controller);
-       if (domain->alt_name && (*cli)->use_kerberos) {
+       if (domain->alt_name) {
                saf_store(domain->alt_name, controller);
        }
 
@@ -1297,8 +1294,15 @@ static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
 
  done:
        TALLOC_FREE(mutex);
+       TALLOC_FREE(creds);
+
+       if (NT_STATUS_IS_OK(result)) {
+               result = tcon_status;
+       }
 
        if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(1, ("Failed to prepare SMB connection to %s: %s\n",
+                         controller, nt_errstr(result)));
                winbind_add_failed_connection_entry(domain, controller, result);
                if ((*cli) != NULL) {
                        cli_shutdown(*cli);
@@ -1354,7 +1358,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);
@@ -1373,19 +1377,22 @@ static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
 
 /*******************************************************************
  convert an ip to a name
+ For an AD Domain, it checks the requirements of the request flags.
 *******************************************************************/
 
-static bool dcip_to_name(TALLOC_CTX *mem_ctx,
-               const struct winbindd_domain *domain,
-               struct sockaddr_storage *pss,
-               char **name)
+static bool dcip_check_name(TALLOC_CTX *mem_ctx,
+                           const struct winbindd_domain *domain,
+                           struct sockaddr_storage *pss,
+                           char **name, uint32_t request_flags)
 {
        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;
 
@@ -1394,6 +1401,12 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
           None of these failures should be considered critical for now */
 
        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];
@@ -1402,6 +1415,8 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
 
                ads = ads_init(domain->alt_name, domain->name, addr);
                ads->auth.flags |= ADS_AUTH_NO_BIND;
+               ads->config.flags |= request_flags;
+               ads->server.no_fallback = true;
 
                ads_status = ads_connect(ads);
                if (ADS_ERR_OK(ads_status)) {
@@ -1413,7 +1428,7 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                        }
                        namecache_store(*name, 0x20, 1, &ip_list);
 
-                       DEBUG(10,("dcip_to_name: flags = 0x%x\n", (unsigned int)ads->config.flags));
+                       DEBUG(10,("dcip_check_name: flags = 0x%x\n", (unsigned int)ads->config.flags));
 
                        if (domain->primary && (ads->config.flags & NBT_SERVER_KDC)) {
                                if (ads_closest_dc(ads)) {
@@ -1452,9 +1467,21 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
        }
 #endif
 
-       status = nbt_getdc(winbind_messaging_context(), 10, pss, domain->name,
-                          &domain->sid, nt_version, mem_ctx, &nt_version,
-                          &dc_name, NULL);
+       {
+               size_t len = strlen(lp_netbios_name());
+               char my_acct_name[len+2];
+
+               snprintf(my_acct_name,
+                        sizeof(my_acct_name),
+                        "%s$",
+                        lp_netbios_name());
+
+               status = nbt_getdc(global_messaging_context(), 10, pss,
+                                  domain->name, &domain->sid,
+                                  my_acct_name, ACB_WSTRUST,
+                                  nt_version, mem_ctx, &nt_version,
+                                  &dc_name, NULL);
+       }
        if (NT_STATUS_IS_OK(status)) {
                *name = talloc_strdup(mem_ctx, dc_name);
                if (*name == NULL) {
@@ -1494,7 +1521,8 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
 *******************************************************************/
 
 static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
-                   struct dc_name_ip **dcs, int *num_dcs)
+                   struct dc_name_ip **dcs, int *num_dcs,
+                   uint32_t request_flags)
 {
        fstring dcname;
        struct  sockaddr_storage ss;
@@ -1508,7 +1536,7 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
 
        /* If not our domain, get the preferred DC, by asking our primary DC */
        if ( !is_our_domain
-               && get_dc_name_via_netlogon(domain, dcname, &ss)
+               && get_dc_name_via_netlogon(domain, dcname, &ss, request_flags)
                && add_one_dc_unique(mem_ctx, domain->name, dcname, &ss, dcs,
                       num_dcs) )
        {
@@ -1625,13 +1653,14 @@ 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,
-                       char **dcname, struct sockaddr_storage *pss, int *fd)
+                       char **dcname, struct sockaddr_storage *pss, int *fd,
+                       uint32_t request_flags)
 {
        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;
@@ -1644,7 +1673,7 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
        *fd = -1;
 
  again:
-       if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs) || (num_dcs == 0))
+       if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs, request_flags) || (num_dcs == 0))
                return False;
 
        for (i=0; i<num_dcs; i++) {
@@ -1692,7 +1721,7 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
        }
 
        /* Try to figure out the name */
-       if (dcip_to_name(mem_ctx, domain, pss, dcname)) {
+       if (dcip_check_name(mem_ctx, domain, pss, dcname, request_flags)) {
                return True;
        }
 
@@ -1710,8 +1739,10 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
        TALLOC_FREE(addrs);
        num_addrs = 0;
 
-       close(*fd);
-       *fd = -1;
+       if (*fd != -1) {
+               close(*fd);
+               *fd = -1;
+       }
 
        goto again;
 }
@@ -1818,14 +1849,16 @@ NTSTATUS wb_open_internal_pipe(TALLOC_CTX *mem_ctx,
                                                 table,
                                                 session_info,
                                                 NULL,
-                                                winbind_messaging_context(),
+                                                NULL,
+                                                global_messaging_context(),
                                                 &cli);
        } else {
                status = rpc_pipe_open_internal(mem_ctx,
-                                               &table->syntax_id,
+                                               table,
                                                session_info,
                                                NULL,
-                                               winbind_messaging_context(),
+                                               NULL,
+                                               global_messaging_context(),
                                                &cli);
        }
        if (!NT_STATUS_IS_OK(status)) {
@@ -1842,12 +1875,14 @@ NTSTATUS wb_open_internal_pipe(TALLOC_CTX *mem_ctx,
 }
 
 static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
-                                  struct winbindd_cm_conn *new_conn)
+                                  struct winbindd_cm_conn *new_conn,
+                                  bool need_rw_dc)
 {
        TALLOC_CTX *mem_ctx;
        NTSTATUS result;
        char *saf_servername;
        int retries;
+       uint32_t request_flags = need_rw_dc ? DS_WRITABLE_REQUIRED : 0;
 
        if ((mem_ctx = talloc_init("cm_open_connection")) == NULL) {
                set_domain_offline(domain);
@@ -1863,39 +1898,39 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
           before talking to it. It going down may have
           triggered the reconnection. */
 
-       if ( saf_servername && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, saf_servername))) {
+       if (saf_servername && NT_STATUS_IS_OK(check_negative_conn_cache(domain->name, saf_servername))) {
+               struct sockaddr_storage ss;
+               char *dcname = NULL;
+               bool resolved = true;
 
-               DEBUG(10,("cm_open_connection: saf_servername is '%s' for domain %s\n",
-                       saf_servername, domain->name ));
+               DEBUG(10, ("cm_open_connection: saf_servername is '%s' for domain %s\n",
+                          saf_servername, domain->name));
 
                /* convert an ip address to a name */
-               if (is_ipaddress( saf_servername ) ) {
-                       char *dcname = NULL;
-                       struct sockaddr_storage ss;
-
+               if (is_ipaddress(saf_servername)) {
                        if (!interpret_string_addr(&ss, saf_servername,
-                                               AI_NUMERICHOST)) {
+                                                  AI_NUMERICHOST)) {
                                TALLOC_FREE(mem_ctx);
                                return NT_STATUS_UNSUCCESSFUL;
                        }
-                       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 {
-                       domain->dcname = talloc_strdup(domain, saf_servername);
+                       if (!resolve_name(saf_servername, &ss, 0x20, true)) {
+                               resolved = false;
+                       }
+               }
+
+               if (resolved && dcip_check_name(mem_ctx, domain, &ss, &dcname, request_flags)) {
+                       domain->dcname = talloc_strdup(domain,
+                                                      dcname);
                        if (domain->dcname == NULL) {
                                TALLOC_FREE(mem_ctx);
                                return NT_STATUS_NO_MEMORY;
                        }
+
+                       domain->dcaddr = ss;
+               } else {
+                       winbind_add_failed_connection_entry(domain, saf_servername,
+                                                           NT_STATUS_UNSUCCESSFUL);
                }
        }
 
@@ -1906,12 +1941,12 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
 
                result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
 
-               DEBUG(10,("cm_open_connection: dcname is '%s' for domain %s\n",
-                       domain->dcname ? domain->dcname : "", domain->name ));
+               DEBUG(10, ("cm_open_connection: dcname is '%s' for domain %s\n",
+                          domain->dcname ? domain->dcname : "", domain->name));
 
-               if (domain->dcname != NULL
-                       && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname))
-                       && (resolve_name(domain->dcname, &domain->dcaddr, 0x20, true)))
+               if (domain->dcname != NULL &&
+                   NT_STATUS_IS_OK(check_negative_conn_cache(domain->name,
+                                                             domain->dcname)))
                {
                        NTSTATUS status;
 
@@ -1924,7 +1959,7 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                }
 
                if ((fd == -1) &&
-                   !find_new_dc(mem_ctx, domain, &dcname, &domain->dcaddr, &fd))
+                   !find_new_dc(mem_ctx, domain, &dcname, &domain->dcaddr, &fd, request_flags))
                {
                        /* This is the one place where we will
                           set the global winbindd offline state
@@ -1949,7 +1984,10 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                        &new_conn->cli, &retry);
                if (!NT_STATUS_IS_OK(result)) {
                        /* Don't leak the smb connection socket */
-                       close(fd);
+                       if (fd != -1) {
+                               close(fd);
+                               fd = -1;
+                       }
                }
 
                if (!retry)
@@ -2062,9 +2100,7 @@ void invalidate_cm_connection(struct winbindd_domain *domain)
        }
 
        conn->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
-       conn->netlogon_force_reauth = false;
-       conn->netlogon_flags = 0;
-       TALLOC_FREE(conn->netlogon_creds);
+       TALLOC_FREE(conn->netlogon_creds_ctx);
 
        if (conn->cli) {
                cli_shutdown(conn->cli);
@@ -2154,7 +2190,7 @@ static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain, bool
                set_dc_type_and_flags_trustinfo(domain);
        }
 
-       result = cm_open_connection(domain, &domain->conn);
+       result = cm_open_connection(domain, &domain->conn, need_rw_dc);
 
        if (NT_STATUS_IS_OK(result) && !domain->initialized) {
                set_dc_type_and_flags(domain);
@@ -2205,13 +2241,22 @@ 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;
        TALLOC_CTX *mem_ctx = NULL;
        struct dcerpc_binding_handle *b;
 
+       if (IS_DC) {
+               /*
+                * On a DC we loaded all trusts
+                * from configuration and never learn
+                * new domains.
+                */
+               return true;
+       }
+
        DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s\n", domain->name ));
 
        /* Our primary domain doesn't need to worry about trust flags.
@@ -2606,6 +2651,15 @@ done:
 
 static void set_dc_type_and_flags( struct winbindd_domain *domain )
 {
+       if (IS_DC) {
+               /*
+                * On a DC we loaded all trusts
+                * from configuration and never learn
+                * new domains.
+                */
+               return;
+       }
+
        /* we always have to contact our primary domain */
 
        if ( domain->primary || domain->internal) {
@@ -2643,28 +2697,16 @@ static NTSTATUS cm_get_schannel_creds(struct winbindd_domain *domain,
                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;
+       if (domain->conn.netlogon_creds_ctx != NULL) {
+               *ppdc = domain->conn.netlogon_creds_ctx;
                return NT_STATUS_OK;
        }
 
-       result = cm_connect_netlogon(domain, &netlogon_pipe);
+       result = cm_connect_netlogon_secure(domain, &netlogon_pipe, ppdc);
        if (!NT_STATUS_IS_OK(result)) {
                return result;
        }
 
-       if (domain->conn.netlogon_creds == NULL) {
-               return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
-       }
-
-       if (!(domain->conn.netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
-               return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
-       }
-
-       *ppdc = domain->conn.netlogon_creds;
        return NT_STATUS_OK;
 }
 
@@ -2676,6 +2718,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        NTSTATUS status, result;
        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_our_sam(&domain->sid)) {
                if (domain->rodc == false || need_rw_dc == false) {
@@ -2683,6 +2726,21 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                }
        }
 
+       if (IS_AD_DC) {
+               /*
+                * In theory we should not use SAMR within
+                * winbindd at all, but that's a larger task to
+                * remove this and avoid breaking existing
+                * setups.
+                *
+                * At least as AD DC we have the restriction
+                * to avoid SAMR against trusted domains,
+                * as there're no existing setups.
+                */
+               return NT_STATUS_REQUEST_NOT_ACCEPTED;
+       }
+
+retry:
        status = init_dc_connection_rpc(domain, need_rw_dc);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -2705,7 +2763,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 
        result = get_trust_credentials(domain, talloc_tos(), false, &creds);
        if (!NT_STATUS_IS_OK(result)) {
-               DEBUG(10, ("cm_connect_sam: No no user available for "
+               DEBUG(10, ("cm_connect_sam: No user available for "
                           "domain %s, trying schannel\n", domain->name));
                goto schannel;
        }
@@ -2726,6 +2784,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                                              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 "
@@ -2746,6 +2812,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;
        }
@@ -2771,9 +2845,17 @@ 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, NCACN_NP,
-                domain->name, p_creds, &conn->samr_pipe);
+       TALLOC_FREE(creds);
+       status = cli_rpc_pipe_open_schannel_with_creds(
+               conn->cli, &ndr_table_samr, NCACN_NP, 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 "
@@ -2789,6 +2871,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;
        }
@@ -2805,7 +2895,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        /* Finally fall back to anonymous. */
        if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
                status = NT_STATUS_DOWNGRADE_DETECTED;
-               DEBUG(1, ("Unwilling to make SAMR connection to domain %s"
+               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",
@@ -2815,6 +2905,13 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        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;
        }
@@ -2824,6 +2921,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",
@@ -2883,7 +2988,7 @@ static NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
                                   struct rpc_pipe_client **cli)
 {
        struct winbindd_cm_conn *conn;
-       struct netlogon_creds_cli_context *creds;
+       struct netlogon_creds_cli_context *p_creds = NULL;
        NTSTATUS status;
 
        DEBUG(10,("cm_connect_lsa_tcp\n"));
@@ -2895,26 +3000,27 @@ static NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
 
        conn = &domain->conn;
 
-       if (conn->lsa_pipe_tcp &&
+       /*
+        * rpccli_is_connected handles more error cases
+        */
+       if (rpccli_is_connected(conn->lsa_pipe_tcp) &&
            conn->lsa_pipe_tcp->transport->transport == NCACN_IP_TCP &&
-           conn->lsa_pipe_tcp->auth->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY &&
-           rpccli_is_connected(conn->lsa_pipe_tcp)) {
+           conn->lsa_pipe_tcp->auth->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
                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,
-                                                    NCACN_IP_TCP,
-                                                    domain->name,
-                                                    creds,
-                                                    &conn->lsa_pipe_tcp);
+       status = cli_rpc_pipe_open_schannel_with_creds(conn->cli,
+                                                      &ndr_table_lsarpc,
+                                                      NCACN_IP_TCP,
+                                                      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)));
@@ -2939,7 +3045,9 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
        struct netlogon_creds_cli_context *p_creds;
        struct cli_credentials *creds = NULL;
+       bool retry = false; /* allow one retry attempt for expired session */
 
+retry:
        result = init_dc_connection_rpc(domain, false);
        if (!NT_STATUS_IS_OK(result))
                return result;
@@ -2952,9 +3060,16 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 
        TALLOC_FREE(conn->lsa_pipe);
 
+       if (IS_AD_DC) {
+               /*
+                * Make sure we only use schannel as AD DC.
+                */
+               goto schannel;
+       }
+
        result = get_trust_credentials(domain, talloc_tos(), false, &creds);
        if (!NT_STATUS_IS_OK(result)) {
-               DEBUG(10, ("cm_connect_sam: No no user available for "
+               DEBUG(10, ("cm_connect_lsa: No user available for "
                           "domain %s, trying schannel\n", domain->name));
                goto schannel;
        }
@@ -2974,6 +3089,14 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                 smbXcli_conn_remote_name(conn->cli->conn),
                 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 "
@@ -2991,6 +3114,13 @@ 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;
        }
@@ -3013,9 +3143,18 @@ 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, NCACN_NP,
-                domain->name, p_creds, &conn->lsa_pipe);
+
+       TALLOC_FREE(creds);
+       result = cli_rpc_pipe_open_schannel_with_creds(
+               conn->cli, &ndr_table_lsarpc, NCACN_NP, 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 "
@@ -3029,10 +3168,25 @@ 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;
        }
 
+       if (IS_AD_DC) {
+               /*
+                * Make sure we only use schannel as AD DC.
+                */
+               goto done;
+       }
+
        DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying "
                  "anonymous\n"));
 
@@ -3040,9 +3194,16 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 
  anonymous:
 
+       if (IS_AD_DC) {
+               /*
+                * Make sure we only use schannel as AD DC.
+                */
+               goto done;
+       }
+
        if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
                result = NT_STATUS_DOWNGRADE_DETECTED;
-               DEBUG(1, ("Unwilling to make LSA connection to domain %s"
+               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",
@@ -3053,6 +3214,14 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        result = cli_rpc_pipe_open_noauth(conn->cli,
                                          &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)) {
                goto done;
        }
@@ -3060,6 +3229,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;
+       }
+
  done:
        if (!NT_STATUS_IS_OK(result)) {
                invalidate_cm_connection(domain);
@@ -3110,28 +3287,33 @@ NTSTATUS cm_connect_lsat(struct winbindd_domain *domain,
 }
 
 /****************************************************************************
- Open the netlogon pipe to this DC. Use schannel if specified in client conf.
- session key stored in conn->netlogon_pipe->dc->sess_key.
+ Open the netlogon pipe to this DC.
 ****************************************************************************/
 
 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 messaging_context *msg_ctx = global_messaging_context();
        struct winbindd_cm_conn *conn;
        NTSTATUS result;
        enum netr_SchannelType sec_chan_type;
-       const char *account_name;
-       const char *domain_name;
-       const struct samr_Password *current_nt_hash = NULL;
-       const struct samr_Password *previous_nt_hash = NULL;
-       struct netlogon_creds_CredentialState *netlogon_creds = NULL;
        struct cli_credentials *creds = NULL;
 
        *cli = NULL;
 
-       result = init_dc_connection_rpc(domain, true);
+       if (IS_AD_DC) {
+               if (domain->secure_channel_type == SEC_CHAN_NULL) {
+                       /*
+                        * Make sure we don't even try to
+                        * connect to a foreign domain
+                        * without a direct outbound trust.
+                        */
+                       return NT_STATUS_NO_TRUST_LSA_SECRET;
+               }
+       }
+
+       result = init_dc_connection_rpc(domain, domain->rodc);
        if (!NT_STATUS_IS_OK(result)) {
                return result;
        }
@@ -3144,90 +3326,41 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
        }
 
        TALLOC_FREE(conn->netlogon_pipe);
-       conn->netlogon_flags = 0;
-       TALLOC_FREE(conn->netlogon_creds);
+       TALLOC_FREE(conn->netlogon_creds_ctx);
 
        result = get_trust_credentials(domain, talloc_tos(), true, &creds);
        if (!NT_STATUS_IS_OK(result)) {
-               DEBUG(10, ("cm_connect_sam: No no user available for "
-                          "domain %s when trying schannel\n", domain->name));
+               DBG_DEBUG("No user available for domain %s when trying "
+                         "schannel\n", domain->name);
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
        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));
+               DBG_WARNING("get_trust_credential only gave anonymous for %s, "
+                           "unable to make get NETLOGON credentials\n",
+                           domain->name);
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
        sec_chan_type = cli_credentials_get_secure_channel_type(creds);
        if (sec_chan_type == SEC_CHAN_NULL) {
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
-               goto no_schannel;
-       }
-
-       account_name = cli_credentials_get_username(creds);
-       domain_name = cli_credentials_get_domain(creds);
-       current_nt_hash = cli_credentials_get_nt_hash(creds, talloc_tos());
-       if (current_nt_hash == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       result = rpccli_create_netlogon_creds(domain->dcname,
-                                             domain_name,
-                                             account_name,
-                                             sec_chan_type,
-                                             msg_ctx,
-                                             domain,
-                                             &conn->netlogon_creds);
-       if (!NT_STATUS_IS_OK(result)) {
-               DEBUG(1, ("rpccli_create_netlogon_creds failed for %s, "
-                         "unable to create NETLOGON credentials: %s\n",
-                         domain->name, nt_errstr(result)));
-               return result;
-       }
-
-       result = rpccli_setup_netlogon_creds(conn->cli, transport,
-                                            conn->netlogon_creds,
-                                            conn->netlogon_force_reauth,
-                                            *current_nt_hash,
-                                            previous_nt_hash);
-       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;
-       }
-
-       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);
-
- no_schannel:
-       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 (transport == NCACN_IP_TCP) {
+                       DBG_NOTICE("get_secure_channel_type gave SEC_CHAN_NULL "
+                                  "for %s, deny NCACN_IP_TCP and let the "
+                                  "caller fallback to NCACN_NP.\n",
+                                  domain->name);
+                       return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               }
+
+               DBG_NOTICE("get_secure_channel_type gave SEC_CHAN_NULL for %s, "
+                          "fallback to noauth on NCACN_NP.\n",
+                          domain->name);
+
+               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;
@@ -3237,21 +3370,26 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
                return NT_STATUS_OK;
        }
 
-       /* Using the credentials from the first pipe, open a signed and sealed
-          second netlogon pipe. The session key is stored in the schannel
-          part of the new pipe auth struct.
-       */
+       result = rpccli_create_netlogon_creds_ctx(creds,
+                                                 domain->dcname,
+                                                 msg_ctx,
+                                                 domain,
+                                                 &conn->netlogon_creds_ctx);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(1, ("rpccli_create_netlogon_creds failed for %s, "
+                         "unable to create NETLOGON credentials: %s\n",
+                         domain->name, nt_errstr(result)));
+               return result;
+       }
 
-       result = cli_rpc_pipe_open_schannel_with_key(
-               conn->cli, &ndr_table_netlogon, transport,
-               domain->name,
-               conn->netlogon_creds,
+       result = rpccli_connect_netlogon(
+               conn->cli, transport,
+               conn->netlogon_creds_ctx, conn->netlogon_force_reauth, creds,
                &conn->netlogon_pipe);
+       conn->netlogon_force_reauth = false;
        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(domain);
+               DBG_DEBUG("rpccli_connect_netlogon failed: %s\n",
+                         nt_errstr(result));
                return result;
        }
 
@@ -3260,7 +3398,7 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
 }
 
 /****************************************************************************
-Open a LSA connection to a DC, suiteable for LSA lookup calls.
+Open a NETLOGON connection to a DC, suiteable for SamLogon calls.
 ****************************************************************************/
 
 NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
@@ -3268,6 +3406,11 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
 {
        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) ||
@@ -3291,10 +3434,41 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
        }
 
        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;
 }
 
+NTSTATUS cm_connect_netlogon_secure(struct winbindd_domain *domain,
+                                   struct rpc_pipe_client **cli,
+                                   struct netlogon_creds_cli_context **ppdc)
+{
+       NTSTATUS status;
+
+       if (domain->secure_channel_type == SEC_CHAN_NULL) {
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       status = cm_connect_netlogon(domain, cli);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (domain->conn.netlogon_creds_ctx == NULL) {
+               return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+       }
+
+       *ppdc = domain->conn.netlogon_creds_ctx;
+       return NT_STATUS_OK;
+}
+
 void winbind_msg_ip_dropped(struct messaging_context *msg_ctx,
                            void *private_data,
                            uint32_t msg_type,
@@ -3354,3 +3528,19 @@ void winbind_msg_ip_dropped(struct messaging_context *msg_ctx,
        }
        TALLOC_FREE(freeit);
 }
+
+void winbind_msg_disconnect_dc(struct messaging_context *msg_ctx,
+                              void *private_data,
+                              uint32_t msg_type,
+                              struct server_id server_id,
+                              DATA_BLOB *data)
+{
+       struct winbindd_domain *domain;
+
+       for (domain = domain_list(); domain; domain = domain->next) {
+               if (domain->internal) {
+                       continue;
+               }
+               invalidate_cm_connection(domain);
+       }
+}