s3:smb2_server: In CCM and GCM mode we can't reuse nonces
authorSimo Sorce <idra@samba.org>
Wed, 20 May 2015 12:01:44 +0000 (14:01 +0200)
committerSimo Sorce <idra@samba.org>
Fri, 29 May 2015 20:38:50 +0000 (22:38 +0200)
Reuse of nonces with AES-CCM and AES-GCM leads to catastrophic failure,
so make sure the server drops the connection if that ever happens.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=11300

Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>

Signed-off-by: Simo Sorce <simo@redhat.com>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Autobuild-User(master): Simo Sorce <idra@samba.org>
Autobuild-Date(master): Fri May 29 22:38:50 CEST 2015 on sn-devel-104

source3/librpc/idl/smbXsrv.idl
source3/smbd/smb2_server.c
source3/smbd/smb2_sesssetup.c

index b3a24a55a1e667e6968d5cb4369e64e9e8fbf1cb..4367d724075b7baee1dfd64a8f049fe4037e3fa2 100644 (file)
@@ -185,6 +185,8 @@ interface smbXsrv
                [ref] smbXsrv_session_global0           *global;
                NTSTATUS                                status;
                NTTIME                                  idle_time;
+               hyper                                   nonce_high_random;
+               hyper                                   nonce_high_max;
                hyper                                   nonce_high;
                hyper                                   nonce_low;
                [ignore] gensec_security                *gensec;
index 38590a9ace5aaf782aef24848d30e6ecf46b2c48..a8d54cbd019e6958bb05a7cfcad49f38018f0860 100644 (file)
@@ -1458,6 +1458,41 @@ static DATA_BLOB smbd_smb2_signing_key(struct smbXsrv_session *session,
        return key;
 }
 
+static NTSTATUS smb2_get_new_nonce(struct smbXsrv_session *session,
+                                  uint64_t *new_nonce_high,
+                                  uint64_t *new_nonce_low)
+{
+       uint64_t nonce_high;
+       uint64_t nonce_low;
+
+       session->nonce_low += 1;
+       if (session->nonce_low == 0) {
+               session->nonce_low += 1;
+               session->nonce_high += 1;
+       }
+
+       /*
+        * CCM and GCM algorithms must never have their
+        * nonce wrap, or the security of the whole
+        * communication and the keys is destroyed.
+        * We must drop the connection once we have
+        * transfered too much data.
+        *
+        * NOTE: We assume nonces greater than 8 bytes.
+        */
+       if (session->nonce_high >= session->nonce_high_max) {
+               return NT_STATUS_ENCRYPTION_FAILED;
+       }
+
+       nonce_high = session->nonce_high_random;
+       nonce_high += session->nonce_high;
+       nonce_low = session->nonce_low;
+
+       *new_nonce_high = nonce_high;
+       *new_nonce_low = nonce_low;
+       return NT_STATUS_OK;
+}
+
 static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
                                            struct tevent_timer *te,
                                            struct timeval current_time,
@@ -1524,15 +1559,13 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
        dyn = body + 8;
 
        if (req->do_encryption) {
-               struct smbXsrv_session *x = req->session;
-
-               nonce_high = x->nonce_high;
-               nonce_low = x->nonce_low;
-
-               x->nonce_low += 1;
-               if (x->nonce_low == 0) {
-                       x->nonce_low += 1;
-                       x->nonce_high += 1;
+               status = smb2_get_new_nonce(req->session,
+                                           &nonce_high,
+                                           &nonce_low);
+               if (!NT_STATUS_IS_OK(status)) {
+                       smbd_server_connection_terminate(xconn,
+                                                        nt_errstr(status));
+                       return;
                }
        }
 
@@ -2374,17 +2407,14 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
                DATA_BLOB encryption_key = req->session->global->encryption_key;
                uint8_t *tf;
                uint64_t session_id = req->session->global->session_wire_id;
-               struct smbXsrv_session *x = req->session;
                uint64_t nonce_high;
                uint64_t nonce_low;
 
-               nonce_high = x->nonce_high;
-               nonce_low = x->nonce_low;
-
-               x->nonce_low += 1;
-               if (x->nonce_low == 0) {
-                       x->nonce_low += 1;
-                       x->nonce_high += 1;
+               status = smb2_get_new_nonce(req->session,
+                                           &nonce_high,
+                                           &nonce_low);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
                }
 
                /*
@@ -2829,13 +2859,11 @@ static NTSTATUS smbd_smb2_send_break(struct smbXsrv_connection *xconn,
        talloc_set_name_const(state, "struct smbd_smb2_send_break_state");
 
        if (do_encryption) {
-               nonce_high = session->nonce_high;
-               nonce_low = session->nonce_low;
-
-               session->nonce_low += 1;
-               if (session->nonce_low == 0) {
-                       session->nonce_low += 1;
-                       session->nonce_high += 1;
+               status = smb2_get_new_nonce(session,
+                                           &nonce_high,
+                                           &nonce_low);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
                }
        }
 
index 28707ffae3831d04605a8a0c052fe0ad27f84ae9..3e80da81993be7fe78461fa2f0f3057cdf951b5d 100644 (file)
@@ -29,6 +29,9 @@
 #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"
 
 static struct tevent_req *smbd_smb2_session_setup_wrap_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
@@ -335,6 +338,7 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
 
        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,
@@ -349,8 +353,31 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session,
                                    d->context.data, d->context.length,
                                    x->global->encryption_key.data);
 
-               generate_random_buffer((uint8_t *)&x->nonce_high, sizeof(x->nonce_high));
-               x->nonce_low = 1;
+               /*
+                * CCM and GCM algorithms must never have their
+                * nonce wrap, or the security of the whole
+                * communication and the keys is destroyed.
+                * We must drop the connection once we have
+                * transfered too much data.
+                *
+                * NOTE: We assume nonces greater than 8 bytes.
+                */
+               generate_random_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;
+                       break;
+               case SMB2_ENCRYPTION_AES128_GCM:
+                       nonce_size = AES_GCM_128_IV_SIZE;
+                       break;
+               default:
+                       ZERO_STRUCT(session_key);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               x->nonce_high_max = SMB2_NONCE_HIGH_MAX(nonce_size);
+               x->nonce_high = 0;
+               x->nonce_low = 0;
        }
 
        x->global->application_key = data_blob_dup_talloc(x->global,