winbindd: Retry on expired session in cm_connect_netlogon
[obnox/samba/samba-obnox.git] / source3 / winbindd / winbindd_cm.c
index cb5bc113528a3bb48cad75df0edb3a889863f124..f593d24619b7d999aeb44ddbdec11181fad6a459 100644 (file)
@@ -446,7 +446,7 @@ void set_domain_offline(struct winbindd_domain *domain)
                messaging_send_buf(winbind_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_DOMAIN_OFFLINE,
-                                  (uint8 *)domain->name,
+                                  (uint8_t *)domain->name,
                                   strlen(domain->name) + 1);
        }
 
@@ -532,7 +532,7 @@ static void set_domain_online(struct winbindd_domain *domain)
                messaging_send_buf(winbind_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_DOMAIN_ONLINE,
-                                  (uint8 *)domain->name,
+                                  (uint8_t *)domain->name,
                                   strlen(domain->name) + 1);
        }
 
@@ -1162,6 +1162,7 @@ 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)) {
@@ -1225,6 +1226,8 @@ 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;
@@ -1350,7 +1353,7 @@ static bool add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name,
 }
 
 static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
-                                 struct sockaddr_storage *pss, uint16 port,
+                                 struct sockaddr_storage *pss, uint16_t port,
                                  struct sockaddr_storage **addrs, int *num)
 {
        *addrs = talloc_realloc(mem_ctx, *addrs, struct sockaddr_storage, (*num)+1);
@@ -1381,7 +1384,9 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
        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;
 
@@ -1390,6 +1395,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];
@@ -2201,7 +2212,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
        WERROR werr;
        struct netr_DomainTrustList trusts;
        int i;
-       uint32 flags = (NETR_TRUST_FLAG_IN_FOREST |
+       uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST |
                        NETR_TRUST_FLAG_OUTBOUND |
                        NETR_TRUST_FLAG_INBOUND);
        struct rpc_pipe_client *cli;
@@ -2672,6 +2683,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) {
@@ -2679,6 +2691,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                }
        }
 
+retry:
        status = init_dc_connection_rpc(domain, need_rw_dc);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -2701,7 +2714,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;
        }
@@ -2722,6 +2735,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 "
@@ -2742,6 +2763,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;
        }
@@ -2767,9 +2796,24 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                        nt_errstr(status) ));
                goto anonymous;
        }
-       status = cli_rpc_pipe_open_schannel_with_key
+       TALLOC_FREE(creds);
+       result = get_trust_credentials(domain, talloc_tos(), true, &creds);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(10, ("cm_connect_sam: No user available for "
+                          "domain %s (error %s), trying anon\n", domain->name,
+                          nt_errstr(result)));
+               goto anonymous;
+       }
+       status = cli_rpc_pipe_open_schannel_with_creds
                (conn->cli, &ndr_table_samr, NCACN_NP,
-                domain->name, p_creds, &conn->samr_pipe);
+                creds, p_creds, &conn->samr_pipe);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+           && !retry) {
+               invalidate_cm_connection(domain);
+               retry = true;
+               goto retry;
+       }
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for "
@@ -2785,6 +2829,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                                      SEC_FLAG_MAXIMUM_ALLOWED,
                                      &conn->sam_connect_handle,
                                      &result);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->samr_pipe);
+               retry = true;
+               goto retry;
+       }
+
        if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
                goto open_domain;
        }
@@ -2811,6 +2863,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;
        }
@@ -2820,6 +2879,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                                      SEC_FLAG_MAXIMUM_ALLOWED,
                                      &conn->sam_connect_handle,
                                      &result);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->samr_pipe);
+               retry = true;
+               goto retry;
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: rpccli_samr_Connect2 failed "
                          "for domain %s Error was %s\n",
@@ -2879,7 +2946,8 @@ 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;
+       struct cli_credentials *creds = NULL;
        NTSTATUS status;
 
        DEBUG(10,("cm_connect_lsa_tcp\n"));
@@ -2900,17 +2968,22 @@ static NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
 
        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 = get_trust_credentials(domain, talloc_tos(), true, &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,
+                                                      creds,
+                                                      p_creds,
+                                                      &conn->lsa_pipe_tcp);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cli_rpc_pipe_open_schannel_with_key failed: %s\n",
                        nt_errstr(status)));
@@ -2935,7 +3008,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;
@@ -2950,7 +3025,7 @@ NTSTATUS cm_connect_lsa(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_lsa: No user available for "
                           "domain %s, trying schannel\n", domain->name));
                goto schannel;
        }
@@ -2970,6 +3045,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 "
@@ -2987,6 +3070,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;
        }
@@ -3009,9 +3099,25 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                        nt_errstr(result) ));
                goto anonymous;
        }
-       result = cli_rpc_pipe_open_schannel_with_key
+
+       TALLOC_FREE(creds);
+       result = get_trust_credentials(domain, talloc_tos(), true, &creds);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(10, ("cm_connect_lsa: No user available for "
+                          "domain %s (error %s), trying anon\n", domain->name,
+                          nt_errstr(result)));
+               goto anonymous;
+       }
+       result = cli_rpc_pipe_open_schannel_with_creds
                (conn->cli, &ndr_table_lsarpc, NCACN_NP,
-                domain->name, p_creds, &conn->lsa_pipe);
+                creds, p_creds, &conn->lsa_pipe);
+
+       if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+           && !retry) {
+               invalidate_cm_connection(domain);
+               retry = true;
+               goto retry;
+       }
 
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
@@ -3025,6 +3131,14 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
                                        SEC_FLAG_MAXIMUM_ALLOWED,
                                        &conn->lsa_policy);
+
+       if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+               invalidate_cm_connection(domain);
+               TALLOC_FREE(conn->lsa_pipe);
+               retry = true;
+               goto retry;
+       }
+
        if (NT_STATUS_IS_OK(result)) {
                goto done;
        }
@@ -3038,7 +3152,7 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 
        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",
@@ -3049,6 +3163,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;
        }
@@ -3056,6 +3178,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);
@@ -3118,16 +3248,12 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
        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);
+       result = init_dc_connection_rpc(domain, domain->rodc);
        if (!NT_STATUS_IS_OK(result)) {
                return result;
        }
@@ -3145,7 +3271,7 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
 
        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 "
+               DEBUG(10, ("cm_connect_sam: No user available for "
                           "domain %s when trying schannel\n", domain->name));
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
@@ -3159,23 +3285,13 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
        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);
+       result = rpccli_create_netlogon_creds_with_creds(creds,
+                                                        domain->dcname,
+                                                        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",
@@ -3183,11 +3299,10 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
                return result;
        }
 
-       result = rpccli_setup_netlogon_creds(conn->cli, transport,
-                                            conn->netlogon_creds,
-                                            conn->netlogon_force_reauth,
-                                            *current_nt_hash,
-                                            previous_nt_hash);
+       result = rpccli_setup_netlogon_creds_with_creds(conn->cli, transport,
+                                               conn->netlogon_creds,
+                                               conn->netlogon_force_reauth,
+                                               creds);
        conn->netlogon_force_reauth = false;
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(1, ("rpccli_setup_netlogon_creds failed for %s, "
@@ -3208,7 +3323,6 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
        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;
@@ -3238,9 +3352,9 @@ static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
           part of the new pipe auth struct.
        */
 
-       result = cli_rpc_pipe_open_schannel_with_key(
+       result = cli_rpc_pipe_open_schannel_with_creds(
                conn->cli, &ndr_table_netlogon, transport,
-               domain->name,
+               creds,
                conn->netlogon_creds,
                &conn->netlogon_pipe);
        if (!NT_STATUS_IS_OK(result)) {
@@ -3264,7 +3378,7 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
 {
        NTSTATUS status;
 
-       status = init_dc_connection_rpc(domain, true);
+       status = init_dc_connection_rpc(domain, domain->rodc);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -3292,6 +3406,14 @@ 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;
 }