>From 7e2e08c6190056198028e99c77902a4d216f6b05 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Wed, 20 May 2015 14:01:44 +0200 Subject: [PATCH] In CCM and GCM mode we can't reuse nonces 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. Signed-off-by: Simo Sorce --- libcli/smb/smb2_constants.h | 1 + source3/librpc/idl/smbXsrv.idl | 2 ++ source3/smbd/smb2_server.c | 65 ++++++++++++++++++++++++++---------------- source3/smbd/smb2_sesssetup.c | 26 ++++++++++++++++- 4 files changed, 69 insertions(+), 25 deletions(-) diff --git a/libcli/smb/smb2_constants.h b/libcli/smb/smb2_constants.h index 2bda4e9c6b52d74bde5408e6fab998384f283487..6064da76d25e15dfab074c09c2790910875b923f 100644 --- a/libcli/smb/smb2_constants.h +++ b/libcli/smb/smb2_constants.h @@ -138,6 +138,7 @@ /* Values for the SMB2_ENCRYPTION_CAPABILITIES Context (>= 0x310) */ #define SMB2_ENCRYPTION_AES128_CCM 0x0001 /* only in dialect >= 0x224 */ #define SMB2_ENCRYPTION_AES128_GCM 0x0002 /* only in dialect >= 0x310 */ +#define SMB2_NONCE_HIGH_MAX(noncelenbytes) ((1 << ((noncelenbytes) - 8)) - 1) /* SMB2 session (request) flags */ #define SMB2_SESSION_FLAG_BINDING 0x01 diff --git a/source3/librpc/idl/smbXsrv.idl b/source3/librpc/idl/smbXsrv.idl index b3a24a55a1e667e6968d5cb4369e64e9e8fbf1cb..4367d724075b7baee1dfd64a8f049fe4037e3fa2 100644 --- a/source3/librpc/idl/smbXsrv.idl +++ b/source3/librpc/idl/smbXsrv.idl @@ -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; diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 9e5eff7cb6007e16dadb29954ec30da95ecc05de..7734bcb7c803649275b9ce5954c5478b6b5e1a73 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -1458,6 +1458,36 @@ 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; + + nonce_high = session->nonce_high_random; + 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; + } + /* CCM and GCM algorythms 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; + } + *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 +1554,11 @@ 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(NT_STATUS_ENCRYPTION_FAILED)); + return; } } @@ -2374,17 +2400,12 @@ 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; } /* @@ -2821,13 +2842,9 @@ 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; } } diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 28707ffae3831d04605a8a0c052fe0ad27f84ae9..44ba58127f0822d5751845dfde9d78a0414afe41 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -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; + int nonce_size; x->global->encryption_key = data_blob_talloc(x->global, session_key, @@ -349,7 +353,27 @@ 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)); + /* CCM and GCM algorythms 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 = 1; } -- 2.4.0