From c69a481c6777b156165fe1226b3af7c4be365be4 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@samba.org>
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 <asn@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Adapted to remove Samba AES support
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
(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