smb2_server: call smbXsrv_connection_disconnect_transport() early on network errors
[gd/samba-autobuild/.git] / source3 / smbd / smb2_sesssetup.c
index 52462882771df7135498ff2f86388641a1a3b900..f9c8819327c054ca1814698a20327c8553c1000e 100644 (file)
 #include "../lib/tsocket/tsocket.h"
 #include "../libcli/security/security.h"
 #include "../lib/util/tevent_ntstatus.h"
-#include "lib/crypto/sha512.h"
-#include "lib/crypto/aes.h"
-#include "lib/crypto/aes_ccm_128.h"
-#include "lib/crypto/aes_gcm_128.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SMB2
 
 static struct tevent_req *smbd_smb2_session_setup_wrap_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
@@ -104,7 +107,16 @@ NTSTATUS smbd_smb2_request_process_sesssetup(struct smbd_smb2_request *smb2req)
        }
        tevent_req_set_callback(subreq, smbd_smb2_request_sesssetup_done, smb2req);
 
-       return smbd_smb2_request_pending_queue(smb2req, subreq, 500);
+       /*
+        * Avoid sending a STATUS_PENDING message, which
+        * matches a Windows Server and avoids problems with
+        * MacOS clients.
+        *
+        * Even after 90 seconds a Windows Server doesn't return
+        * STATUS_PENDING if using NTLMSSP against a non reachable
+        * trusted domain.
+        */
+       return smbd_smb2_request_pending_queue(smb2req, subreq, 0);
 }
 
 static void smbd_smb2_request_sesssetup_done(struct tevent_req *subreq)
@@ -208,19 +220,32 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
                struct smbXsrv_preauth *preauth;
                struct _derivation *d;
                DATA_BLOB p;
-               struct hc_sha512state sctx;
+               gnutls_hash_hd_t hash_hnd;
+               int rc;
 
                preauth = talloc_move(smb2req, &auth->preauth);
 
-               samba_SHA512_Init(&sctx);
-               samba_SHA512_Update(&sctx, preauth->sha512_value,
-                                   sizeof(preauth->sha512_value));
+               rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_SHA512);
+               if (rc < 0) {
+                       return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+               }
+               rc = gnutls_hash(hash_hnd,
+                                preauth->sha512_value,
+                                sizeof(preauth->sha512_value));
+               if (rc < 0) {
+                       gnutls_hash_deinit(hash_hnd, NULL);
+                       return NT_STATUS_ACCESS_DENIED;
+               }
                for (i = 1; i < smb2req->in.vector_count; i++) {
-                       samba_SHA512_Update(&sctx,
-                                           smb2req->in.vector[i].iov_base,
-                                           smb2req->in.vector[i].iov_len);
+                       rc = gnutls_hash(hash_hnd,
+                                        smb2req->in.vector[i].iov_base,
+                                        smb2req->in.vector[i].iov_len);
+                       if (rc < 0) {
+                               gnutls_hash_deinit(hash_hnd, NULL);
+                               return NT_STATUS_ACCESS_DENIED;
+                       }
                }
-               samba_SHA512_Final(preauth->sha512_value, &sctx);
+               gnutls_hash_deinit(hash_hnd, preauth->sha512_value);
 
                p = data_blob_const(preauth->sha512_value,
                                    sizeof(preauth->sha512_value));
@@ -311,10 +336,21 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        memcpy(session_key, session_info->session_key.data,
               MIN(session_info->session_key.length, sizeof(session_key)));
 
-       x->global->signing_key = data_blob_talloc(x->global,
-                                                 session_key,
-                                                 sizeof(session_key));
-       if (x->global->signing_key.data == NULL) {
+       x->global->signing_key = talloc_zero(x->global,
+                                            struct smb2_signing_key);
+       if (x->global->signing_key == NULL) {
+               ZERO_STRUCT(session_key);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_set_destructor(x->global->signing_key,
+                             smb2_signing_key_destructor);
+
+       x->global->signing_key->blob =
+               x->global->signing_key_blob =
+                       data_blob_talloc(x->global,
+                                        session_key,
+                                        sizeof(session_key));
+       if (!smb2_signing_key_valid(x->global->signing_key)) {
                ZERO_STRUCT(session_key);
                return NT_STATUS_NO_MEMORY;
        }
@@ -322,45 +358,74 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        if (xconn->protocol >= PROTOCOL_SMB2_24) {
                struct _derivation *d = &derivation.signing;
 
-               smb2_key_derivation(session_key, sizeof(session_key),
-                                   d->label.data, d->label.length,
-                                   d->context.data, d->context.length,
-                                   x->global->signing_key.data);
+               status = smb2_key_derivation(session_key, sizeof(session_key),
+                                            d->label.data, d->label.length,
+                                            d->context.data, d->context.length,
+                                            x->global->signing_key->blob.data);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
 
        if (xconn->protocol >= PROTOCOL_SMB2_24) {
                struct _derivation *d = &derivation.decryption;
 
-               x->global->decryption_key = data_blob_talloc(x->global,
-                                                            session_key,
-                                                            sizeof(session_key));
-               if (x->global->decryption_key.data == NULL) {
+               x->global->decryption_key =
+                       talloc_zero(x->global, struct smb2_signing_key);
+               if (x->global->decryption_key == NULL) {
                        ZERO_STRUCT(session_key);
                        return NT_STATUS_NO_MEMORY;
                }
 
-               smb2_key_derivation(session_key, sizeof(session_key),
-                                   d->label.data, d->label.length,
-                                   d->context.data, d->context.length,
-                                   x->global->decryption_key.data);
+               x->global->decryption_key->blob =
+                       x->global->decryption_key_blob =
+                               data_blob_talloc(x->global->decryption_key,
+                                                session_key,
+                                                sizeof(session_key));
+               if (!smb2_signing_key_valid(x->global->decryption_key)) {
+                       ZERO_STRUCT(session_key);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               talloc_keep_secret(x->global->decryption_key->blob.data);
+
+               status = smb2_key_derivation(session_key, sizeof(session_key),
+                                            d->label.data, d->label.length,
+                                            d->context.data, d->context.length,
+                                            x->global->decryption_key->blob.data);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
 
        if (xconn->protocol >= PROTOCOL_SMB2_24) {
                struct _derivation *d = &derivation.encryption;
                size_t nonce_size;
 
-               x->global->encryption_key = data_blob_talloc(x->global,
-                                                            session_key,
-                                                            sizeof(session_key));
-               if (x->global->encryption_key.data == NULL) {
+               x->global->encryption_key =
+                       talloc_zero(x->global, struct smb2_signing_key);
+               if (x->global->encryption_key == NULL) {
                        ZERO_STRUCT(session_key);
                        return NT_STATUS_NO_MEMORY;
                }
 
-               smb2_key_derivation(session_key, sizeof(session_key),
-                                   d->label.data, d->label.length,
-                                   d->context.data, d->context.length,
-                                   x->global->encryption_key.data);
+               x->global->encryption_key->blob =
+                       x->global->encryption_key_blob =
+                               data_blob_talloc(x->global->encryption_key,
+                                                session_key,
+                                                sizeof(session_key));
+               if (!smb2_signing_key_valid(x->global->encryption_key)) {
+                       ZERO_STRUCT(session_key);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               talloc_keep_secret(x->global->encryption_key->blob.data);
+
+               status = smb2_key_derivation(session_key, sizeof(session_key),
+                                            d->label.data, d->label.length,
+                                            d->context.data, d->context.length,
+                                            x->global->encryption_key->blob.data);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
 
                /*
                 * CCM and GCM algorithms must never have their
@@ -371,14 +436,14 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
                 *
                 * NOTE: We assume nonces greater than 8 bytes.
                 */
-               generate_random_buffer((uint8_t *)&x->nonce_high_random,
-                                      sizeof(x->nonce_high_random));
+               generate_nonce_buffer((uint8_t *)&x->nonce_high_random,
+                                     sizeof(x->nonce_high_random));
                switch (xconn->smb2.server.cipher) {
                case SMB2_ENCRYPTION_AES128_CCM:
-                       nonce_size = AES_CCM_128_NONCE_SIZE;
+                       nonce_size = SMB2_AES_128_CCM_NONCE_SIZE;
                        break;
                case SMB2_ENCRYPTION_AES128_GCM:
-                       nonce_size = AES_GCM_128_IV_SIZE;
+                       nonce_size = gnutls_cipher_get_iv_size(GNUTLS_CIPHER_AES_128_GCM);
                        break;
                default:
                        nonce_size = 0;
@@ -389,28 +454,68 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
                x->nonce_low = 0;
        }
 
-       x->global->application_key = data_blob_dup_talloc(x->global,
-                                               x->global->signing_key);
+       x->global->application_key =
+               data_blob_dup_talloc(x->global, x->global->signing_key->blob);
        if (x->global->application_key.data == NULL) {
                ZERO_STRUCT(session_key);
                return NT_STATUS_NO_MEMORY;
        }
+       talloc_keep_secret(x->global->application_key.data);
 
        if (xconn->protocol >= PROTOCOL_SMB2_24) {
                struct _derivation *d = &derivation.application;
 
-               smb2_key_derivation(session_key, sizeof(session_key),
-                                   d->label.data, d->label.length,
-                                   d->context.data, d->context.length,
-                                   x->global->application_key.data);
+               status = smb2_key_derivation(session_key, sizeof(session_key),
+                                            d->label.data, d->label.length,
+                                            d->context.data, d->context.length,
+                                            x->global->application_key.data);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
+
+       if (xconn->protocol >= PROTOCOL_SMB3_00 && lp_debug_encryption()) {
+               DEBUG(0, ("debug encryption: dumping generated session keys\n"));
+               DEBUGADD(0, ("Session Id    "));
+               dump_data(0, (uint8_t*)&session->global->session_wire_id,
+                         sizeof(session->global->session_wire_id));
+               DEBUGADD(0, ("Session Key   "));
+               dump_data(0, session_key, sizeof(session_key));
+               DEBUGADD(0, ("Signing Key   "));
+               dump_data(0, x->global->signing_key->blob.data,
+                         x->global->signing_key->blob.length);
+               DEBUGADD(0, ("App Key       "));
+               dump_data(0, x->global->application_key.data,
+                         x->global->application_key.length);
+
+               /* In server code, ServerIn is the decryption key */
+
+               DEBUGADD(0, ("ServerIn Key  "));
+               dump_data(0, x->global->decryption_key->blob.data,
+                         x->global->decryption_key->blob.length);
+               DEBUGADD(0, ("ServerOut Key "));
+               dump_data(0, x->global->encryption_key->blob.data,
+                         x->global->encryption_key->blob.length);
+       }
+
        ZERO_STRUCT(session_key);
 
-       x->global->channels[0].signing_key = data_blob_dup_talloc(x->global->channels,
-                                               x->global->signing_key);
-       if (x->global->channels[0].signing_key.data == NULL) {
+       x->global->channels[0].signing_key =
+               talloc_zero(x->global->channels, struct smb2_signing_key);
+       if (x->global->channels[0].signing_key == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
+       talloc_set_destructor(x->global->channels[0].signing_key,
+                             smb2_signing_key_destructor);
+
+       x->global->channels[0].signing_key->blob =
+               x->global->channels[0].signing_key_blob =
+                       data_blob_dup_talloc(x->global->channels[0].signing_key,
+                                            x->global->signing_key->blob);
+       if (!smb2_signing_key_valid(x->global->channels[0].signing_key)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_keep_secret(x->global->channels[0].signing_key->blob.data);
 
        data_blob_clear_free(&session_info->session_key);
        session_info->session_key = data_blob_dup_talloc(session_info,
@@ -418,21 +523,12 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        if (session_info->session_key.data == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
+       talloc_keep_secret(session_info->session_key.data);
 
-       session->compat = talloc_zero(session, struct user_struct);
-       if (session->compat == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       session->compat->session = session;
-       session->compat->homes_snum = -1;
-       session->compat->session_info = session_info;
-       session->compat->session_keystr = NULL;
-       session->compat->vuid = session->global->session_wire_id;
-       DLIST_ADD(smb2req->sconn->users, session->compat);
        smb2req->sconn->num_users++;
 
        if (security_session_user_level(session_info, NULL) >= SECURITY_USER) {
-               session->compat->homes_snum =
+               session->homes_snum =
                        register_homes_share(session_info->unix_info->unix_name);
        }
 
@@ -459,7 +555,7 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        if (!session_claim(session)) {
                DEBUG(1, ("smb2: Failed to claim session "
                        "for vuid=%llu\n",
-                       (unsigned long long)session->compat->vuid));
+                       (unsigned long long)session->global->session_wire_id));
                return NT_STATUS_LOGON_FAILURE;
        }
 
@@ -467,7 +563,7 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        status = smbXsrv_session_update(session);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0, ("smb2: Failed to update session for vuid=%llu - %s\n",
-                         (unsigned long long)session->compat->vuid,
+                         (unsigned long long)session->global->session_wire_id,
                          nt_errstr(status)));
                return NT_STATUS_LOGON_FAILURE;
        }
@@ -483,6 +579,7 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
        global_client_caps |= (CAP_LEVEL_II_OPLOCKS|CAP_STATUS32);
 
        *out_session_id = session->global->session_wire_id;
+       smb2req->last_session_id = session->global->session_wire_id;
 
        return NT_STATUS_OK;
 }
@@ -508,11 +605,9 @@ static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session,
        if (session_info->session_key.data == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
+       talloc_keep_secret(session_info->session_key.data);
 
-       session->compat->session_info = session_info;
-       session->compat->vuid = session->global->session_wire_id;
-
-       session->compat->homes_snum =
+       session->homes_snum =
                        register_homes_share(session_info->unix_info->unix_name);
 
        set_current_user_info(session_info->unix_info->sanitized_username,
@@ -521,6 +616,10 @@ static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session,
 
        reload_services(smb2req->sconn, conn_snum_used, true);
 
+       if (security_session_user_level(session_info, NULL) >= SECURITY_USER) {
+               smb2req->do_signing = true;
+       }
+
        session->status = NT_STATUS_OK;
        TALLOC_FREE(session->global->auth_session_info);
        session->global->auth_session_info = talloc_move(session->global,
@@ -540,16 +639,13 @@ static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session,
        status = smbXsrv_session_update(session);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0, ("smb2: Failed to update session for vuid=%llu - %s\n",
-                         (unsigned long long)session->compat->vuid,
+                         (unsigned long long)session->global->session_wire_id,
                          nt_errstr(status)));
                return NT_STATUS_LOGON_FAILURE;
        }
 
-       conn_clear_vuid_caches(xconn->client->sconn, session->compat->vuid);
-
-       if (security_session_user_level(session_info, NULL) >= SECURITY_USER) {
-               smb2req->do_signing = true;
-       }
+       conn_clear_vuid_caches(xconn->client->sconn,
+                              session->global->session_wire_id);
 
        *out_session_id = session->global->session_wire_id;
 
@@ -585,19 +681,33 @@ static NTSTATUS smbd_smb2_bind_auth_return(struct smbXsrv_session *session,
                struct smbXsrv_preauth *preauth;
                struct _derivation *d;
                DATA_BLOB p;
-               struct hc_sha512state sctx;
+               gnutls_hash_hd_t hash_hnd = NULL;
+               int rc;
 
                preauth = talloc_move(smb2req, &auth->preauth);
 
-               samba_SHA512_Init(&sctx);
-               samba_SHA512_Update(&sctx, preauth->sha512_value,
-                                   sizeof(preauth->sha512_value));
+               rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_SHA512);
+               if (rc < 0) {
+                       return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+               }
+
+               rc = gnutls_hash(hash_hnd,
+                                preauth->sha512_value,
+                                sizeof(preauth->sha512_value));
+               if (rc < 0) {
+                       gnutls_hash_deinit(hash_hnd, NULL);
+                       return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+               }
                for (i = 1; i < smb2req->in.vector_count; i++) {
-                       samba_SHA512_Update(&sctx,
-                                           smb2req->in.vector[i].iov_base,
-                                           smb2req->in.vector[i].iov_len);
+                       rc = gnutls_hash(hash_hnd,
+                                        smb2req->in.vector[i].iov_base,
+                                        smb2req->in.vector[i].iov_len);
+                       if (rc < 0) {
+                               gnutls_hash_deinit(hash_hnd, NULL);
+                               return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+                       }
                }
-               samba_SHA512_Final(preauth->sha512_value, &sctx);
+               gnutls_hash_deinit(hash_hnd, preauth->sha512_value);
 
                p = data_blob_const(preauth->sha512_value,
                                    sizeof(preauth->sha512_value));
@@ -634,21 +744,35 @@ static NTSTATUS smbd_smb2_bind_auth_return(struct smbXsrv_session *session,
        memcpy(session_key, session_info->session_key.data,
               MIN(session_info->session_key.length, sizeof(session_key)));
 
-       c->signing_key = data_blob_talloc(x->global,
-                                         session_key,
-                                         sizeof(session_key));
-       if (c->signing_key.data == NULL) {
+       c->signing_key = talloc_zero(x->global, struct smb2_signing_key);
+       if (c->signing_key == NULL) {
                ZERO_STRUCT(session_key);
                return NT_STATUS_NO_MEMORY;
        }
+       talloc_set_destructor(c->signing_key,
+                             smb2_signing_key_destructor);
+
+       c->signing_key->blob =
+               c->signing_key_blob =
+                       data_blob_talloc(c->signing_key,
+                                        session_key,
+                                        sizeof(session_key));
+       if (!smb2_signing_key_valid(c->signing_key)) {
+               ZERO_STRUCT(session_key);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_keep_secret(c->signing_key->blob.data);
 
        if (xconn->protocol >= PROTOCOL_SMB2_24) {
                struct _derivation *d = &derivation.signing;
 
-               smb2_key_derivation(session_key, sizeof(session_key),
-                                   d->label.data, d->label.length,
-                                   d->context.data, d->context.length,
-                                   c->signing_key.data);
+               status = smb2_key_derivation(session_key, sizeof(session_key),
+                                            d->label.data, d->label.length,
+                                            d->context.data, d->context.length,
+                                            c->signing_key->blob.data);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
        ZERO_STRUCT(session_key);
 
@@ -656,7 +780,7 @@ static NTSTATUS smbd_smb2_bind_auth_return(struct smbXsrv_session *session,
        status = smbXsrv_session_update(session);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0, ("smb2: Failed to update session for vuid=%llu - %s\n",
-                         (unsigned long long)session->compat->vuid,
+                         (unsigned long long)session->global->session_wire_id,
                          nt_errstr(status)));
                return NT_STATUS_LOGON_FAILURE;
        }
@@ -746,7 +870,7 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx,
                                                      smb2req->xconn,
                                                      &c);
                if (NT_STATUS_IS_OK(status)) {
-                       if (c->signing_key.length == 0) {
+                       if (!smb2_signing_key_valid(c->signing_key)) {
                                goto auth;
                        }
                        tevent_req_nterror(req, NT_STATUS_REQUEST_NOT_ACCEPTED);
@@ -788,6 +912,7 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx,
 
                status = smbXsrv_session_add_channel(smb2req->session,
                                                     smb2req->xconn,
+                                                    now,
                                                     &c);
                if (!NT_STATUS_IS_OK(status)) {
                        tevent_req_nterror(req, status);
@@ -864,6 +989,7 @@ auth:
 
                gensec_want_feature(state->auth->gensec, GENSEC_FEATURE_SESSION_KEY);
                gensec_want_feature(state->auth->gensec, GENSEC_FEATURE_UNIX_TOKEN);
+               gensec_want_feature(state->auth->gensec, GENSEC_FEATURE_SMB_TRANSPORT);
 
                status = gensec_start_mech_by_oid(state->auth->gensec,
                                                  GENSEC_OID_SPNEGO);
@@ -1220,10 +1346,10 @@ NTSTATUS smbd_smb2_request_process_logoff(struct smbd_smb2_request *req)
        tevent_req_set_callback(subreq, smbd_smb2_request_logoff_done, req);
 
        /*
-        * Wait a long time before going async on this to allow
-        * requests we're waiting on to finish. Set timeout to 10 secs.
+        * Avoid sending a STATUS_PENDING message, it's very likely
+        * the client won't expect that.
         */
-       return smbd_smb2_request_pending_queue(req, subreq, 10000000);
+       return smbd_smb2_request_pending_queue(req, subreq, 0);
 }
 
 static void smbd_smb2_request_logoff_done(struct tevent_req *subreq)