idl: Merge NETR_TRUST and LSA_TRUST definitions into one set only in lsa.idl
[samba.git] / source3 / winbindd / winbindd_cm.c
index 4de91663631ec0373d42841f90a19bc8f2df6f33..1e639b7cfa67a5aacaa8ce708e30fb564e4a954a 100644 (file)
@@ -79,6 +79,9 @@
 #include "auth/gensec/gensec.h"
 #include "../libcli/smb/smbXcli_base.h"
 #include "lib/param/loadparm.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+#include "auth.h"
+#include "rpc_server/rpc_ncacn_np.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -91,7 +94,7 @@ 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 get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
                    struct dc_name_ip **dcs, int *num_dcs);
@@ -173,7 +176,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;
                }
        }
@@ -340,6 +343,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.
@@ -347,6 +390,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 ));
 
@@ -394,6 +439,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 *)domain->name,
+                                  strlen(domain->name) + 1);
+       }
+
        /* Send an offline message to the idmap child when our
           primary domain goes offline */
 
@@ -418,6 +472,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 ));
 
@@ -469,6 +525,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 *)domain->name,
+                                  strlen(domain->name) + 1);
+       }
+
        /* Send an online message to the idmap child when our
           primary domain comes online */
 
@@ -771,6 +836,10 @@ static NTSTATUS get_trust_creds(const struct winbindd_domain *domain,
                        return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;                       
                }
 
+               if (our_domain->alt_name == NULL) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
                if (asprintf(machine_krb5_principal, "%s$@%s",
                             account_name, our_domain->alt_name) == -1)
                {
@@ -842,8 +911,8 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
        cli_set_timeout(*cli, 10000); /* 10 seconds */
 
        result = smbXcli_negprot((*cli)->conn, (*cli)->timeout,
-                                lp_cli_minprotocol(),
-                                lp_cli_maxprotocol());
+                                lp_client_min_protocol(),
+                                lp_client_max_protocol());
 
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(1, ("cli_negprot failed: %s\n", nt_errstr(result)));
@@ -1137,7 +1206,7 @@ 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)) {
                ADS_STRUCT *ads;
                ADS_STATUS ads_status;
                char addr[INET6_ADDRSTRLEN];
@@ -1161,7 +1230,7 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
 
                        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
@@ -1170,17 +1239,15 @@ 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);
 
@@ -1265,7 +1332,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
@@ -1279,7 +1346,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. */
@@ -1303,7 +1370,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;
                }
 
@@ -1503,7 +1570,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;
@@ -1512,7 +1580,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, ' ');
@@ -1541,23 +1609,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 */
 
@@ -1577,13 +1688,14 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
 
                        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, &dcname)) {
                                domain->dcname = talloc_strdup(domain,
                                                               dcname);
                                if (domain->dcname == NULL) {
-                                       SAFE_FREE(saf_servername);
+                                       TALLOC_FREE(mem_ctx);
                                        return NT_STATUS_NO_MEMORY;
                                }
                        } else {
@@ -1594,12 +1706,10 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                } else {
                        domain->dcname = talloc_strdup(domain, saf_servername);
                        if (domain->dcname == NULL) {
-                               SAFE_FREE(saf_servername);
+                               TALLOC_FREE(mem_ctx);
                                return NT_STATUS_NO_MEMORY;
                        }
                }
-
-               SAFE_FREE( saf_servername );
        }
 
        for (retries = 0; retries < 3; retries++) {
@@ -1660,6 +1770,7 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
        }
 
        if (NT_STATUS_IS_OK(result)) {
+               bool seal_pipes = true;
 
                winbindd_set_locator_kdc_envs(domain);
 
@@ -1679,6 +1790,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);
@@ -1751,6 +1873,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);
        }
@@ -1808,17 +1935,21 @@ 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;
+       bool skip_connection = domain->internal;
+       if (need_rw_dc && domain->rodc) {
+               skip_connection = false;
+       }
 
        /* Internal connections never use the network. */
-       if (domain->internal) {
-               domain->initialized = True;
-               return NT_STATUS_OK;
+       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);
                }
@@ -1836,9 +1967,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;
        }
 
@@ -1847,14 +1978,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;
        }
@@ -1893,38 +2024,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,
@@ -1934,14 +2073,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;
        }
 
@@ -1953,7 +2092,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* 
@@ -1971,7 +2110,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;
 
@@ -1979,7 +2117,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                }               
        }
 
-       talloc_destroy( mem_ctx );
+       TALLOC_FREE(mem_ctx);
 
        return domain->initialized;     
 }
@@ -2002,7 +2140,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;
        }
 
@@ -2015,9 +2153,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,
-                                         &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 "
@@ -2066,9 +2210,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, &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",
@@ -2172,7 +2321,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);
 
@@ -2189,9 +2337,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;         
        }
@@ -2213,13 +2361,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);
@@ -2227,32 +2385,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;
+       struct netlogon_creds_cli_context *p_creds;
        char *machine_password = NULL;
        char *machine_account = NULL;
        const char *domain_name = NULL;
 
        if (sid_check_is_our_sam(&domain->sid)) {
-               return open_internal_samr_conn(mem_ctx, domain, cli, sam_handle);
+               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);
+       status = init_dc_connection_rpc(domain, need_rw_dc);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -2301,7 +2463,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                                          &ndr_table_samr,
                                          NCACN_NP,
                                          GENSEC_OID_NTLMSSP,
-                                         DCERPC_AUTH_LEVEL_PRIVACY,
+                                         conn->auth_level,
                                          smbXcli_conn_remote_name(conn->cli->conn),
                                          domain_name,
                                          machine_account,
@@ -2354,8 +2516,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        }
        status = cli_rpc_pipe_open_schannel_with_key
                (conn->cli, &ndr_table_samr, NCACN_NP,
-                DCERPC_AUTH_LEVEL_PRIVACY,
-                domain->name, &p_creds, &conn->samr_pipe);
+                domain->name, p_creds, &conn->samr_pipe);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for "
@@ -2458,12 +2619,12 @@ NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
                            struct rpc_pipe_client **cli)
 {
        struct winbindd_cm_conn *conn;
-       struct netlogon_creds_CredentialState *creds;
+       struct netlogon_creds_cli_context *creds;
        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;
        }
@@ -2472,7 +2633,7 @@ 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;
        }
@@ -2487,9 +2648,8 @@ NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
        status = cli_rpc_pipe_open_schannel_with_key(conn->cli,
                                                     &ndr_table_lsarpc,
                                                     NCACN_IP_TCP,
-                                                    DCERPC_AUTH_LEVEL_PRIVACY,
                                                     domain->name,
-                                                    &creds,
+                                                    creds,
                                                     &conn->lsa_pipe_tcp);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cli_rpc_pipe_open_schannel_with_key failed: %s\n",
@@ -2513,9 +2673,9 @@ 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;
 
-       result = init_dc_connection_rpc(domain);
+       result = init_dc_connection_rpc(domain, false);
        if (!NT_STATUS_IS_OK(result))
                return result;
 
@@ -2540,7 +2700,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        result = cli_rpc_pipe_open_spnego
                (conn->cli, &ndr_table_lsarpc, NCACN_NP,
                 GENSEC_OID_NTLMSSP,
-                DCERPC_AUTH_LEVEL_PRIVACY,
+                conn->auth_level,
                 smbXcli_conn_remote_name(conn->cli->conn),
                 conn->cli->domain, conn->cli->user_name, conn->cli->password,
                 &conn->lsa_pipe);
@@ -2585,8 +2745,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        }
        result = cli_rpc_pipe_open_schannel_with_key
                (conn->cli, &ndr_table_lsarpc, NCACN_NP,
-                DCERPC_AUTH_LEVEL_PRIVACY,
-                domain->name, &p_creds, &conn->lsa_pipe);
+                domain->name, p_creds, &conn->lsa_pipe);
 
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
@@ -2615,7 +2774,6 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                                          &ndr_table_lsarpc,
                                          &conn->lsa_pipe);
        if (!NT_STATUS_IS_OK(result)) {
-               result = NT_STATUS_PIPE_NOT_AVAILABLE;
                goto done;
        }
 
@@ -2677,18 +2835,20 @@ NTSTATUS cm_connect_lsat(struct winbindd_domain *domain,
 NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
                             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 | NETLOGON_NEG_SUPPORTS_AES;
-       uint8_t  mach_pwd[16];
        enum netr_SchannelType sec_chan_type;
+       const char *_account_name;
        const char *account_name;
-       struct rpc_pipe_client *netlogon_pipe = NULL;
+       struct samr_Password current_nt_hash;
+       struct samr_Password *previous_nt_hash = NULL;
+       struct netlogon_creds_CredentialState *creds = NULL;
+       bool ok;
 
        *cli = NULL;
 
-       result = init_dc_connection_rpc(domain);
+       result = init_dc_connection_rpc(domain, true);
        if (!NT_STATUS_IS_OK(result)) {
                return result;
        }
@@ -2701,64 +2861,80 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
        }
 
        TALLOC_FREE(conn->netlogon_pipe);
-
-       result = cli_rpc_pipe_open_noauth(conn->cli,
-                                         &ndr_table_netlogon,
-                                         &netlogon_pipe);
-       if (!NT_STATUS_IS_OK(result)) {
-               return result;
-       }
+       conn->netlogon_flags = 0;
+       TALLOC_FREE(conn->netlogon_creds);
 
        if ((!IS_DC) && (!domain->primary)) {
-               /* Clear the schannel request bit and drop down */
-               neg_flags &= ~NETLOGON_NEG_SCHANNEL;            
                goto no_schannel;
        }
 
-       if (lp_client_schannel() != False) {
-               neg_flags |= NETLOGON_NEG_SCHANNEL;
+       ok = get_trust_pw_hash(domain->name,
+                              current_nt_hash.hash,
+                              &_account_name,
+                              &sec_chan_type);
+       if (!ok) {
+               DEBUG(1, ("get_trust_pw_hash failed for %s, "
+                         "unable to 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);
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       account_name = talloc_asprintf(talloc_tos(), "%s$", _account_name);
+       if (account_name == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
-       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(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)));
+               SAFE_FREE(previous_nt_hash);
+               return result;
+       }
 
+       result = rpccli_setup_netlogon_creds(conn->cli,
+                                            conn->netlogon_creds,
+                                            conn->netlogon_force_reauth,
+                                            current_nt_hash,
+                                            previous_nt_hash);
+       conn->netlogon_force_reauth = false;
+       SAFE_FREE(previous_nt_hash);
        if (!NT_STATUS_IS_OK(result)) {
-               TALLOC_FREE(netlogon_pipe);
+               DEBUG(1, ("rpccli_setup_netlogon_creds failed for %s, "
+                         "unable to setup 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 = netlogon_creds_cli_get(conn->netlogon_creds,
+                                       talloc_tos(),
+                                       &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 = creds->negotiate_flags;
+       TALLOC_FREE(creds);
 
  no_schannel:
-       if ((lp_client_schannel() == False) ||
-                       ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
-               /*
-                * NetSamLogonEx only works for schannel
-                */
-               domain->can_do_samlogon_ex = False;
+       if (!(conn->netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
+               result = cli_rpc_pipe_open_noauth(conn->cli,
+                                       &ndr_table_netlogon,
+                                       &conn->netlogon_pipe);
+               if (!NT_STATUS_IS_OK(result)) {
+                       invalidate_cm_connection(conn);
+                       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;
        }
@@ -2770,12 +2946,9 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
 
        result = cli_rpc_pipe_open_schannel_with_key(
                conn->cli, &ndr_table_netlogon, NCACN_NP,
-               DCERPC_AUTH_LEVEL_PRIVACY, domain->name, &netlogon_pipe->dc,
+               domain->name,
+               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)));
@@ -2784,15 +2957,6 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *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;
 }