diff --git a/source3/librpc/idl/smbXsrv.idl b/source3/librpc/idl/smbXsrv.idl index b3a24a55a1e..4367d724075 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 38590a9ace5..a8d54cbd019 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -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; } } diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 28707ffae38..3e80da81993 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; + 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,