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