1524bc
From 94b8f3071fafea18ceb59098f8611a0f2cb5a655 Mon Sep 17 00:00:00 2001
1524bc
From: Andreas Schneider <asn@samba.org>
1524bc
Date: Tue, 26 Feb 2019 16:43:36 +0100
1524bc
Subject: [PATCH 125/187] libcli:smb: Support GnuTLS AES CCM and GCM in
1524bc
 smb2_signing_decrypt_pdu()
1524bc
1524bc
This requires GnuTLS >= 3.4.0.
1524bc
1524bc
Signed-off-by: Andreas Schneider <asn@samba.org>
1524bc
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
1524bc
1524bc
Adapted to remove Samba AES support
1524bc
1524bc
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
1524bc
(cherry picked from commit 3d2de36d9a08354fb775a5d93a9b40012bf6966f)
1524bc
---
1524bc
 libcli/smb/smb2_signing.c | 170 ++++++++++++++++++++++++++++----------
1524bc
 1 file changed, 125 insertions(+), 45 deletions(-)
1524bc
1524bc
diff --git a/libcli/smb/smb2_signing.c b/libcli/smb/smb2_signing.c
1524bc
index 52209d9553b..1d9c99337d8 100644
1524bc
--- a/libcli/smb/smb2_signing.c
1524bc
+++ b/libcli/smb/smb2_signing.c
1524bc
@@ -558,7 +558,6 @@ out:
1524bc
 	return status;
1524bc
 }
1524bc
 
1524bc
-
1524bc
 NTSTATUS smb2_signing_decrypt_pdu(DATA_BLOB decryption_key,
1524bc
 				  uint16_t cipher_id,
1524bc
 				  struct iovec *vector,
1524bc
@@ -566,17 +565,20 @@ NTSTATUS smb2_signing_decrypt_pdu(DATA_BLOB decryption_key,
1524bc
 {
1524bc
 	uint8_t *tf;
1524bc
 	uint16_t flags;
1524bc
-	uint8_t *sig_ptr = NULL;
1524bc
-	uint8_t sig[16];
1524bc
 	int i;
1524bc
 	size_t a_total;
1524bc
 	ssize_t m_total;
1524bc
 	uint32_t msg_size = 0;
1524bc
-	union {
1524bc
-		struct aes_ccm_128_context ccm;
1524bc
-		struct aes_gcm_128_context gcm;
1524bc
-	} c;
1524bc
-	uint8_t key[AES_BLOCK_SIZE];
1524bc
+	uint32_t iv_size = 0;
1524bc
+	uint32_t key_size = 0;
1524bc
+	uint32_t tag_size = 0;
1524bc
+	uint8_t _key[16] = {0};
1524bc
+	gnutls_cipher_algorithm_t algo = 0;
1524bc
+	gnutls_aead_cipher_hd_t cipher_hnd = NULL;
1524bc
+	gnutls_datum_t key;
1524bc
+	gnutls_datum_t iv;
1524bc
+	NTSTATUS status;
1524bc
+	int rc;
1524bc
 
1524bc
 	if (count < 1) {
1524bc
 		return NT_STATUS_INVALID_PARAMETER;
1524bc
@@ -612,53 +614,131 @@ NTSTATUS smb2_signing_decrypt_pdu(DATA_BLOB decryption_key,
1524bc
 		return NT_STATUS_INTERNAL_ERROR;
1524bc
 	}
1524bc
 
1524bc
-	ZERO_STRUCT(key);
1524bc
-	memcpy(key, decryption_key.data,
1524bc
-	       MIN(decryption_key.length, AES_BLOCK_SIZE));
1524bc
-
1524bc
 	switch (cipher_id) {
1524bc
 	case SMB2_ENCRYPTION_AES128_CCM:
1524bc
-		aes_ccm_128_init(&c.ccm, key,
1524bc
-				 tf + SMB2_TF_NONCE,
1524bc
-				 a_total, m_total);
1524bc
-		aes_ccm_128_update(&c.ccm, tf + SMB2_TF_NONCE, a_total);
1524bc
-		for (i=1; i < count; i++) {
1524bc
-			aes_ccm_128_crypt(&c.ccm,
1524bc
-					(uint8_t *)vector[i].iov_base,
1524bc
-					vector[i].iov_len);
1524bc
-			aes_ccm_128_update(&c.ccm,
1524bc
-					( uint8_t *)vector[i].iov_base,
1524bc
-					vector[i].iov_len);
1524bc
-		}
1524bc
-		aes_ccm_128_digest(&c.ccm, sig);
1524bc
+		algo = GNUTLS_CIPHER_AES_128_CCM;
1524bc
+		iv_size = SMB2_AES_128_CCM_NONCE_SIZE;
1524bc
 		break;
1524bc
-
1524bc
 	case SMB2_ENCRYPTION_AES128_GCM:
1524bc
-		aes_gcm_128_init(&c.gcm, key, tf + SMB2_TF_NONCE);
1524bc
-		aes_gcm_128_updateA(&c.gcm, tf + SMB2_TF_NONCE, a_total);
1524bc
-		for (i=1; i < count; i++) {
1524bc
-			aes_gcm_128_updateC(&c.gcm,
1524bc
-					(const uint8_t *)vector[i].iov_base,
1524bc
-					vector[i].iov_len);
1524bc
-			aes_gcm_128_crypt(&c.gcm,
1524bc
-					(uint8_t *)vector[i].iov_base,
1524bc
-					vector[i].iov_len);
1524bc
-		}
1524bc
-		aes_gcm_128_digest(&c.gcm, sig);
1524bc
+		algo = GNUTLS_CIPHER_AES_128_GCM;
1524bc
+		iv_size = gnutls_cipher_get_iv_size(algo);
1524bc
 		break;
1524bc
-
1524bc
 	default:
1524bc
-		ZERO_STRUCT(key);
1524bc
 		return NT_STATUS_INVALID_PARAMETER;
1524bc
 	}
1524bc
-	ZERO_STRUCT(key);
1524bc
 
1524bc
-	sig_ptr = tf + SMB2_TF_SIGNATURE;
1524bc
-	if (memcmp(sig_ptr, sig, 16) != 0) {
1524bc
-		return NT_STATUS_ACCESS_DENIED;
1524bc
+	key_size = gnutls_cipher_get_key_size(algo);
1524bc
+	tag_size = gnutls_cipher_get_tag_size(algo);
1524bc
+
1524bc
+	if (key_size > sizeof(_key)) {
1524bc
+		return NT_STATUS_BUFFER_TOO_SMALL;
1524bc
 	}
1524bc
 
1524bc
-	DEBUG(5,("decrypt SMB2 message\n"));
1524bc
+	key = (gnutls_datum_t) {
1524bc
+		.data = _key,
1524bc
+		.size = key_size,
1524bc
+	};
1524bc
 
1524bc
-	return NT_STATUS_OK;
1524bc
+	memcpy(key.data,
1524bc
+	       decryption_key.data,
1524bc
+	       MIN(decryption_key.length, key.size));
1524bc
+
1524bc
+	iv = (gnutls_datum_t) {
1524bc
+		.data = tf + SMB2_TF_NONCE,
1524bc
+		.size = iv_size,
1524bc
+	};
1524bc
+
1524bc
+	rc = gnutls_aead_cipher_init(&cipher_hnd,
1524bc
+				     algo,
1524bc
+				     &key);
1524bc
+	if (rc < 0) {
1524bc
+		status = NT_STATUS_NO_MEMORY;
1524bc
+		goto out;
1524bc
+	}
1524bc
+
1524bc
+	{
1524bc
+		size_t ctext_size = m_total + tag_size;
1524bc
+		uint8_t *ctext = NULL;
1524bc
+		size_t ptext_size = m_total;
1524bc
+		uint8_t *ptext = NULL;
1524bc
+		size_t len = 0;
1524bc
+
1524bc
+		/* GnuTLS doesn't have a iovec API for decryption yet */
1524bc
+
1524bc
+		ptext = talloc_size(talloc_tos(), ptext_size);
1524bc
+		if (ptext == NULL) {
1524bc
+			gnutls_aead_cipher_deinit(cipher_hnd);
1524bc
+			status = NT_STATUS_NO_MEMORY;
1524bc
+			goto out;
1524bc
+		}
1524bc
+
1524bc
+		ctext = talloc_size(talloc_tos(), ctext_size);
1524bc
+		if (ctext == NULL) {
1524bc
+			TALLOC_FREE(ptext);
1524bc
+			gnutls_aead_cipher_deinit(cipher_hnd);
1524bc
+			status = NT_STATUS_NO_MEMORY;
1524bc
+			goto out;
1524bc
+		}
1524bc
+
1524bc
+
1524bc
+		for (i = 1; i < count; i++) {
1524bc
+			memcpy(ctext + len,
1524bc
+			       vector[i].iov_base,
1524bc
+			       vector[i].iov_len);
1524bc
+
1524bc
+			len += vector[i].iov_len;
1524bc
+		}
1524bc
+		if (len != m_total) {
1524bc
+			TALLOC_FREE(ptext);
1524bc
+			TALLOC_FREE(ctext);
1524bc
+			gnutls_aead_cipher_deinit(cipher_hnd);
1524bc
+			status = NT_STATUS_INTERNAL_ERROR;
1524bc
+			goto out;
1524bc
+		}
1524bc
+
1524bc
+		memcpy(ctext + len,
1524bc
+		       tf + SMB2_TF_SIGNATURE,
1524bc
+		       tag_size);
1524bc
+
1524bc
+		/* This function will verify the tag */
1524bc
+		rc = gnutls_aead_cipher_decrypt(cipher_hnd,
1524bc
+						iv.data,
1524bc
+						iv.size,
1524bc
+						tf + SMB2_TF_NONCE,
1524bc
+						a_total,
1524bc
+						tag_size,
1524bc
+						ctext,
1524bc
+						ctext_size,
1524bc
+						ptext,
1524bc
+						&ptext_size);
1524bc
+		if (rc < 0 || ptext_size != m_total) {
1524bc
+			DBG_ERR("ERROR: %s\n", gnutls_strerror(rc));
1524bc
+			TALLOC_FREE(ptext);
1524bc
+			TALLOC_FREE(ctext);
1524bc
+			gnutls_aead_cipher_deinit(cipher_hnd);
1524bc
+			status = NT_STATUS_INTERNAL_ERROR;
1524bc
+			goto out;
1524bc
+		}
1524bc
+
1524bc
+		len = 0;
1524bc
+		for (i = 1; i < count; i++) {
1524bc
+			memcpy(vector[i].iov_base,
1524bc
+			       ptext + len,
1524bc
+			       vector[i].iov_len);
1524bc
+
1524bc
+			len += vector[i].iov_len;
1524bc
+		}
1524bc
+
1524bc
+		TALLOC_FREE(ptext);
1524bc
+		TALLOC_FREE(ctext);
1524bc
+	}
1524bc
+	gnutls_aead_cipher_deinit(cipher_hnd);
1524bc
+
1524bc
+	DBG_INFO("Decrypted SMB2 message\n");
1524bc
+
1524bc
+	status = NT_STATUS_OK;
1524bc
+out:
1524bc
+	ZERO_ARRAY(_key);
1524bc
+
1524bc
+	return status;
1524bc
 }
1524bc
-- 
1524bc
2.23.0
1524bc