From c69a481c6777b156165fe1226b3af7c4be365be4 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 25 Feb 2019 18:05:16 +0100 Subject: [PATCH 124/187] libcli:smb: Support GnuTLS AES CCM and GCM in smb2_signing_encrypt_pdu() This requires GnuTLS >= 3.4.0. Signed-off-by: Andreas Schneider Reviewed-by: Andrew Bartlett Adapted to remove Samba AES support Signed-off-by: Andrew Bartlett (cherry picked from commit f43da2adf64a8ff20ce6478f656927e531bc42af) --- libcli/smb/smb2_signing.c | 169 ++++++++++++++++++++++++++++---------- 1 file changed, 124 insertions(+), 45 deletions(-) diff --git a/libcli/smb/smb2_signing.c b/libcli/smb/smb2_signing.c index 1ec60a4f9a5..52209d9553b 100644 --- a/libcli/smb/smb2_signing.c +++ b/libcli/smb/smb2_signing.c @@ -392,15 +392,19 @@ NTSTATUS smb2_signing_encrypt_pdu(DATA_BLOB encryption_key, int count) { uint8_t *tf; - uint8_t sig[16]; int i; size_t a_total; ssize_t m_total; - union { - struct aes_ccm_128_context ccm; - struct aes_gcm_128_context gcm; - } c; - uint8_t key[AES_BLOCK_SIZE]; + uint32_t iv_size = 0; + uint32_t key_size = 0; + uint32_t tag_size = 0; + uint8_t _key[16] = {0}; + gnutls_cipher_algorithm_t algo = 0; + gnutls_aead_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t key; + gnutls_datum_t iv; + NTSTATUS status; + int rc; if (count < 1) { return NT_STATUS_INVALID_PARAMETER; @@ -428,58 +432,133 @@ NTSTATUS smb2_signing_encrypt_pdu(DATA_BLOB encryption_key, SSVAL(tf, SMB2_TF_FLAGS, SMB2_TF_FLAGS_ENCRYPTED); SIVAL(tf, SMB2_TF_MSG_SIZE, m_total); - ZERO_STRUCT(key); - memcpy(key, encryption_key.data, - MIN(encryption_key.length, AES_BLOCK_SIZE)); - switch (cipher_id) { case SMB2_ENCRYPTION_AES128_CCM: - aes_ccm_128_init(&c.ccm, key, - tf + SMB2_TF_NONCE, - a_total, m_total); - memset(tf + SMB2_TF_NONCE + AES_CCM_128_NONCE_SIZE, 0, - 16 - AES_CCM_128_NONCE_SIZE); - aes_ccm_128_update(&c.ccm, tf + SMB2_TF_NONCE, a_total); - for (i=1; i < count; i++) { - aes_ccm_128_update(&c.ccm, - (const uint8_t *)vector[i].iov_base, - vector[i].iov_len); - aes_ccm_128_crypt(&c.ccm, - (uint8_t *)vector[i].iov_base, - vector[i].iov_len); - } - aes_ccm_128_digest(&c.ccm, sig); + algo = GNUTLS_CIPHER_AES_128_CCM; + iv_size = SMB2_AES_128_CCM_NONCE_SIZE; break; - case SMB2_ENCRYPTION_AES128_GCM: - aes_gcm_128_init(&c.gcm, key, tf + SMB2_TF_NONCE); - memset(tf + SMB2_TF_NONCE + AES_GCM_128_IV_SIZE, 0, - 16 - AES_GCM_128_IV_SIZE); - aes_gcm_128_updateA(&c.gcm, tf + SMB2_TF_NONCE, a_total); - for (i=1; i < count; i++) { - aes_gcm_128_crypt(&c.gcm, - (uint8_t *)vector[i].iov_base, - vector[i].iov_len); - aes_gcm_128_updateC(&c.gcm, - (const uint8_t *)vector[i].iov_base, - vector[i].iov_len); - } - aes_gcm_128_digest(&c.gcm, sig); + algo = GNUTLS_CIPHER_AES_128_GCM; + iv_size = gnutls_cipher_get_iv_size(algo); break; - default: - ZERO_STRUCT(key); return NT_STATUS_INVALID_PARAMETER; } - ZERO_STRUCT(key); - memcpy(tf + SMB2_TF_SIGNATURE, sig, 16); + key_size = gnutls_cipher_get_key_size(algo); + tag_size = gnutls_cipher_get_tag_size(algo); - DEBUG(5,("encrypt SMB2 message\n")); + if (key_size > sizeof(_key)) { + return NT_STATUS_BUFFER_TOO_SMALL; + } - return NT_STATUS_OK; + key = (gnutls_datum_t) { + .data = _key, + .size = key_size, + }; + + memcpy(key.data, + encryption_key.data, + MIN(encryption_key.length, key.size)); + + iv = (gnutls_datum_t) { + .data = tf + SMB2_TF_NONCE, + .size = iv_size, + }; + + rc = gnutls_aead_cipher_init(&cipher_hnd, + algo, + &key); + if (rc < 0) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + memset(tf + SMB2_TF_NONCE + iv_size, + 0, + 16 - iv_size); + + { + size_t ptext_size = m_total; + uint8_t *ptext = NULL; + size_t ctext_size = m_total + tag_size; + uint8_t *ctext = NULL; + size_t len = 0; + + ptext = talloc_size(talloc_tos(), ptext_size); + if (ptext == NULL) { + gnutls_aead_cipher_deinit(cipher_hnd); + status = NT_STATUS_NO_MEMORY; + goto out; + } + + ctext = talloc_size(talloc_tos(), ctext_size); + if (ctext == NULL) { + gnutls_aead_cipher_deinit(cipher_hnd); + status = NT_STATUS_NO_MEMORY; + goto out; + } + + for (i = 1; i < count; i++) { + memcpy(ptext + len, + vector[i].iov_base, + vector[i].iov_len); + + len += vector[i].iov_len; + if (len > ptext_size) { + TALLOC_FREE(ptext); + TALLOC_FREE(ctext); + gnutls_aead_cipher_deinit(cipher_hnd); + status = NT_STATUS_INTERNAL_ERROR; + goto out; + } + } + + rc = gnutls_aead_cipher_encrypt(cipher_hnd, + iv.data, + iv.size, + tf + SMB2_TF_NONCE, + a_total, + tag_size, + ptext, + ptext_size, + ctext, + &ctext_size); + if (rc < 0 || ctext_size != m_total + tag_size) { + DBG_ERR("ERROR: %s\n", gnutls_strerror(rc)); + TALLOC_FREE(ptext); + TALLOC_FREE(ctext); + gnutls_aead_cipher_deinit(cipher_hnd); + status = NT_STATUS_INTERNAL_ERROR; + goto out; + } + + len = 0; + for (i = 1; i < count; i++) { + memcpy(vector[i].iov_base, + ctext + len, + vector[i].iov_len); + + len += vector[i].iov_len; + } + + memcpy(tf + SMB2_TF_SIGNATURE, ctext + m_total, tag_size); + + TALLOC_FREE(ptext); + TALLOC_FREE(ctext); + } + gnutls_aead_cipher_deinit(cipher_hnd); + + DBG_INFO("Enencrypted SMB2 message\n"); + + status = NT_STATUS_OK; +out: + ZERO_ARRAY(_key); + + return status; } + NTSTATUS smb2_signing_decrypt_pdu(DATA_BLOB decryption_key, uint16_t cipher_id, struct iovec *vector, -- 2.23.0