Blob Blame History Raw
From 1511f1959b2a48cb2d41550441df366276e3c5cb Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 7 Nov 2017 14:57:12 +0100
Subject: [PATCH] FIPS mode support

---
 libfreerdp-core/certificate.c | 17 +++++++-----
 libfreerdp-core/connection.c  |  3 +++
 libfreerdp-core/crypto.c      | 61 ++++++++++++++++++++++++++++++++++++++-----
 libfreerdp-core/crypto.h      |  6 +++--
 libfreerdp-core/license.c     | 15 ++++++++---
 libfreerdp-core/nego.c        |  5 +++-
 libfreerdp-core/security.c    | 33 +++++++++++++++++++----
 libfreerdp-utils/args.c       |  3 ---
 8 files changed, 116 insertions(+), 27 deletions(-)

diff --git a/libfreerdp-core/certificate.c b/libfreerdp-core/certificate.c
index 801ed3609..1fd2cd216 100644
--- a/libfreerdp-core/certificate.c
+++ b/libfreerdp-core/certificate.c
@@ -276,15 +276,19 @@ static boolean certificate_process_server_public_key(rdpCertificate* certificate
 
 static boolean certificate_process_server_public_signature(rdpCertificate* certificate, uint8* sigdata, int sigdatalen, STREAM* s, uint32 siglen)
 {
-	uint8 md5hash[CRYPTO_MD5_DIGEST_LENGTH];
+	/*uint8 md5hash[CRYPTO_MD5_DIGEST_LENGTH];*/
 	uint8 encsig[TSSK_KEY_LENGTH + 8];
 	uint8 sig[TSSK_KEY_LENGTH];
-	CryptoMd5 md5ctx;
+	/*CryptoMd5 md5ctx;*/
 	int i, sum;
 
-	md5ctx = crypto_md5_init();
+	/* Do not bother with validation of server proprietary certificate. The use of MD5 here is not allowed under FIPS. */
+	/* Since the validation is not protecting against anything since the private/public keys are well known and documented in */
+	/* MS-RDPBCGR section 5.3.3.1, we are not gaining any security by using MD5 for signature comparison. Rather then use MD5 */
+	/* here we just dont do the validation to avoid its use. Historically, freerdp has been ignoring a failed validation anyways. */
+	/*md5ctx = crypto_md5_init();
 	crypto_md5_update(md5ctx, sigdata, sigdatalen);
-	crypto_md5_final(md5ctx, md5hash);
+	crypto_md5_final(md5ctx, md5hash);*/
 
 	stream_read(s, encsig, siglen);
 
@@ -304,11 +308,12 @@ static boolean certificate_process_server_public_signature(rdpCertificate* certi
 	crypto_rsa_public_decrypt(encsig, siglen, TSSK_KEY_LENGTH, tssk_modulus, tssk_exponent, sig);
 
 	/* Verify signature. */
-	if (memcmp(md5hash, sig, sizeof(md5hash)) != 0)
+	/* Do not bother with validation of server proprietary certificate as described above. */
+	/*if (memcmp(md5hash, sig, sizeof(md5hash)) != 0)
 	{
 		printf("certificate_process_server_public_signature: invalid signature\n");
 		//return false;
-	}
+	}*/
 
 	/*
 	 * Verify rest of decrypted data:
diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c
index 7e1769ee4..7853c86bd 100644
--- a/libfreerdp-core/connection.c
+++ b/libfreerdp-core/connection.c
@@ -65,6 +65,9 @@ boolean rdp_client_connect(rdpRdp* rdp)
 	uint32 selectedProtocol;
 	rdpSettings* settings = rdp->settings;
 
+	if (FIPS_mode() == 1)
+		settings->nla_security = false;
+
 	nego_init(rdp->nego);
 	nego_set_target(rdp->nego, settings->hostname, settings->port);
 	nego_set_cookie(rdp->nego, settings->username);
diff --git a/libfreerdp-core/crypto.c b/libfreerdp-core/crypto.c
index a0e2ccb9d..e6115b867 100644
--- a/libfreerdp-core/crypto.c
+++ b/libfreerdp-core/crypto.c
@@ -37,34 +37,81 @@ void crypto_sha1_final(CryptoSha1 sha1, uint8* out_data)
 	xfree(sha1);
 }
 
-CryptoMd5 crypto_md5_init(void)
+static CryptoMd5 crypto_md5_init_internal(boolean override_fips)
 {
 	CryptoMd5 md5 = xmalloc(sizeof(*md5));
-	MD5_Init(&md5->md5_ctx);
+
+	EVP_MD_CTX_init(&md5->md5_ctx);
+	if (override_fips)
+		EVP_MD_CTX_set_flags(&md5->md5_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+	if (EVP_DigestInit_ex(&md5->md5_ctx, EVP_md5(), NULL) != 1)
+	{
+		printf("EVP_DigestInit_ex failed\n");
+		abort();
+	}
+
 	return md5;
 }
 
+CryptoMd5 crypto_md5_init(void)
+{
+	return crypto_md5_init_internal(false);
+}
+
+CryptoMd5 crypto_md5_init_allow_fips(void)
+{
+	return crypto_md5_init_internal(true);
+}
+
 void crypto_md5_update(CryptoMd5 md5, const uint8* data, uint32 length)
 {
-	MD5_Update(&md5->md5_ctx, data, length);
+	EVP_DigestUpdate(&md5->md5_ctx, data, length);
 }
 
 void crypto_md5_final(CryptoMd5 md5, uint8* out_data)
 {
-	MD5_Final(out_data, &md5->md5_ctx);
+	EVP_DigestFinal_ex(&md5->md5_ctx, out_data, NULL);
 	xfree(md5);
 }
 
-CryptoRc4 crypto_rc4_init(const uint8* key, uint32 length)
+static CryptoRc4 crypto_rc4_init_internal(const uint8* key, uint32 length, boolean override_fips)
 {
 	CryptoRc4 rc4 = xmalloc(sizeof(*rc4));
-	RC4_set_key(&rc4->rc4_key, length, key);
+
+	EVP_CIPHER_CTX_init(&rc4->rc4_ctx);
+	if (EVP_EncryptInit_ex(&rc4->rc4_ctx, EVP_rc4(), NULL, NULL, NULL) != 1)
+	{
+		printf("EVP_EncryptInit_ex failed\n");
+		abort();
+	}
+
+	if (override_fips)
+		EVP_CIPHER_CTX_set_flags(&rc4->rc4_ctx, EVP_CIPH_FLAG_NON_FIPS_ALLOW);
+
+	EVP_CIPHER_CTX_set_key_length(&rc4->rc4_ctx, length);
+	if (EVP_EncryptInit_ex(&rc4->rc4_ctx, NULL, NULL, key, NULL) != 1)
+	{
+		printf("EVP_EncryptInit_ex failed\n");
+		abort();
+	}
+
 	return rc4;
 }
 
+CryptoRc4 crypto_rc4_init(const uint8* key, uint32 length)
+{
+	return crypto_rc4_init_internal(key, length, false);
+}
+
+CryptoRc4 crypto_rc4_init_allow_fips(const uint8* key, uint32 length)
+{
+	return crypto_rc4_init_internal(key, length, true);
+}
+
 void crypto_rc4(CryptoRc4 rc4, uint32 length, const uint8* in_data, uint8* out_data)
 {
-	RC4(&rc4->rc4_key, length, in_data, out_data);
+	int outputLength;
+	EVP_CipherUpdate(&rc4->rc4_ctx, out_data, &outputLength, in_data, length);
 }
 
 void crypto_rc4_free(CryptoRc4 rc4)
diff --git a/libfreerdp-core/crypto.h b/libfreerdp-core/crypto.h
index 15afca8a4..a595c3831 100644
--- a/libfreerdp-core/crypto.h
+++ b/libfreerdp-core/crypto.h
@@ -54,12 +54,12 @@ struct crypto_sha1_struct
 
 struct crypto_md5_struct
 {
-	MD5_CTX md5_ctx;
+	EVP_MD_CTX md5_ctx;
 };
 
 struct crypto_rc4_struct
 {
-	RC4_KEY rc4_key;
+	EVP_CIPHER_CTX rc4_ctx;
 };
 
 struct crypto_des3_struct
@@ -86,11 +86,13 @@ void crypto_sha1_final(CryptoSha1 sha1, uint8* out_data);
 #define	CRYPTO_MD5_DIGEST_LENGTH	MD5_DIGEST_LENGTH
 typedef struct crypto_md5_struct* CryptoMd5;
 CryptoMd5 crypto_md5_init(void);
+CryptoMd5 crypto_md5_init_allow_fips(void);
 void crypto_md5_update(CryptoMd5 md5, const uint8* data, uint32 length);
 void crypto_md5_final(CryptoMd5 md5, uint8* out_data);
 
 typedef struct crypto_rc4_struct* CryptoRc4;
 CryptoRc4 crypto_rc4_init(const uint8* key, uint32 length);
+CryptoRc4 crypto_rc4_init_allow_fips(const uint8* key, uint32 length);
 void crypto_rc4(CryptoRc4 rc4, uint32 length, const uint8* in_data, uint8* out_data);
 void crypto_rc4_free(CryptoRc4 rc4);
 
diff --git a/libfreerdp-core/license.c b/libfreerdp-core/license.c
index 60b9f9366..7e5734069 100644
--- a/libfreerdp-core/license.c
+++ b/libfreerdp-core/license.c
@@ -295,7 +295,10 @@ void license_generate_hwid(rdpLicense* license)
 	memset(license->hwid, 0, HWID_LENGTH);
 	mac_address = license->rdp->transport->tcp->mac_address;
 
-	md5 = crypto_md5_init();
+	/* Allow FIPS override for use of MD5 here, really this does not have to be MD5 as we are just taking a MD5 hash of the 6 bytes of 0's(macAddress) */
+	/* and filling in the Data1-Data4 fields of the CLIENT_HARDWARE_ID structure(from MS-RDPELE section 2.2.2.3.1). This is for RDP licensing packets */
+	/* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data protection. */
+	md5 = crypto_md5_init_allow_fips();
 	crypto_md5_update(md5, mac_address, 6);
 	crypto_md5_final(md5, &license->hwid[HWID_PLATFORM_ID_LENGTH]);
 }
@@ -354,7 +357,10 @@ void license_decrypt_platform_challenge(rdpLicense* license)
 	license->platform_challenge->length =
 			license->encrypted_platform_challenge->length;
 
-	rc4 = crypto_rc4_init(license->licensing_encryption_key, LICENSING_ENCRYPTION_KEY_LENGTH);
+	/* Allow FIPS override for use of RC4 here, this is only used for decrypting the MACData field of the */
+	/* Server Platform Challenge packet (from MS-RDPELE section 2.2.2.4). This is for RDP licensing packets */
+	/* which will already be encrypted under FIPS, so the use of RC4 here is not for sensitive data protection. */
+	rc4 = crypto_rc4_init_allow_fips(license->licensing_encryption_key, LICENSING_ENCRYPTION_KEY_LENGTH);
 
 	crypto_rc4(rc4, license->encrypted_platform_challenge->length,
 			license->encrypted_platform_challenge->data,
@@ -829,8 +835,11 @@ void license_send_platform_challenge_response_packet(rdpLicense* license)
 	security_mac_data(license->mac_salt_key, buffer, length, mac_data);
 	xfree(buffer);
 
+	/* Allow FIPS override for use of RC4 here, this is only used for encrypting the EncryptedHWID field of the */
+	/* Client Platform Challenge Response packet (from MS-RDPELE section 2.2.2.5). This is for RDP licensing packets */
+	/* which will already be encrypted under FIPS, so the use of RC4 here is not for sensitive data protection. */
 	buffer = (uint8*) xmalloc(HWID_LENGTH);
-	rc4 = crypto_rc4_init(license->licensing_encryption_key, LICENSING_ENCRYPTION_KEY_LENGTH);
+	rc4 = crypto_rc4_init_allow_fips(license->licensing_encryption_key, LICENSING_ENCRYPTION_KEY_LENGTH);
 	crypto_rc4(rc4, HWID_LENGTH, license->hwid, buffer);
 	crypto_rc4_free(rc4);
 
diff --git a/libfreerdp-core/nego.c b/libfreerdp-core/nego.c
index 7eb810bba..199870408 100644
--- a/libfreerdp-core/nego.c
+++ b/libfreerdp-core/nego.c
@@ -89,7 +89,10 @@ boolean nego_connect(rdpNego* nego)
 	if(nego->selected_protocol == PROTOCOL_RDP)
 	{
 		nego->transport->settings->encryption = true;
-		nego->transport->settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS;
+		if (FIPS_mode() != 1)
+			nego->transport->settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS;
+		else
+			nego->transport->settings->encryption_method = ENCRYPTION_METHOD_FIPS;
 		nego->transport->settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
 	}
 
diff --git a/libfreerdp-core/security.c b/libfreerdp-core/security.c
index d93c3b9a8..d93390de2 100644
--- a/libfreerdp-core/security.c
+++ b/libfreerdp-core/security.c
@@ -131,7 +131,10 @@ static void security_salted_hash(uint8* salt, uint8* input, int length, uint8* s
 	crypto_sha1_final(sha1, sha1_digest);
 
 	/* SaltedHash(Salt, Input, Salt1, Salt2) = MD5(S + SHA1_Digest) */
-	md5 = crypto_md5_init();
+	/* Allow FIPS override for use of MD5 here, this is used for creating hashes of the premaster_secret and master_secret */
+	/* used for RDP licensing as described in MS-RDPELE. This is for RDP licensing packets */
+	/* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data protection. */
+	md5 = crypto_md5_init_allow_fips();
 	crypto_md5_update(md5, salt, 48); /* Salt (48 bytes) */
 	crypto_md5_update(md5, sha1_digest, sizeof(sha1_digest)); /* SHA1_Digest */
 	crypto_md5_final(md5, output);
@@ -182,10 +185,24 @@ void security_md5_16_32_32(uint8* in0, uint8* in1, uint8* in2, uint8* output)
 	crypto_md5_final(md5, output);
 }
 
+void security_md5_16_32_32_allow_fips(uint8* in0, uint8* in1, uint8* in2, uint8* output)
+{
+	CryptoMd5 md5;
+
+	md5 = crypto_md5_init_allow_fips();
+	crypto_md5_update(md5, in0, 16);
+	crypto_md5_update(md5, in1, 32);
+	crypto_md5_update(md5, in2, 32);
+	crypto_md5_final(md5, output);
+}
+
 void security_licensing_encryption_key(uint8* session_key_blob, uint8* client_random, uint8* server_random, uint8* output)
 {
 	/* LicensingEncryptionKey = MD5(Second128Bits(SessionKeyBlob) + ClientRandom + ServerRandom)) */
-	security_md5_16_32_32(&session_key_blob[16], client_random, server_random, output);
+	/* Allow FIPS use of MD5 here, this is just used for creating the licensing encryption key as described in MS-RDPELE. */
+	/* This is for RDP licensing packets which will already be encrypted under FIPS, so the use of MD5 here is not for */
+	/* sensitive data protection. */
+	security_md5_16_32_32_allow_fips(&session_key_blob[16], client_random, server_random, output);
 }
 
 void security_uint32_le(uint8* output, uint32 value)
@@ -216,7 +233,10 @@ void security_mac_data(uint8* mac_salt_key, uint8* data, uint32 length, uint8* o
 	crypto_sha1_final(sha1, sha1_digest);
 
 	/* MacData = MD5(MacSaltKey + pad2 + SHA1_Digest) */
-	md5 = crypto_md5_init();
+	/* Allow FIPS override for use of MD5 here, this is only used for creating the MACData field of the */
+	/* Client Platform Challenge Response packet (from MS-RDPELE section 2.2.2.5). This is for RDP licensing packets */
+	/* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data protection. */
+	md5 = crypto_md5_init_allow_fips();
 	crypto_md5_update(md5, mac_salt_key, 16); /* MacSaltKey */
 	crypto_md5_update(md5, pad2, sizeof(pad2)); /* pad2 */
 	crypto_md5_update(md5, sha1_digest, sizeof(sha1_digest)); /* SHA1_Digest */
@@ -400,9 +420,12 @@ boolean security_establish_keys(uint8* client_random, rdpRdp* rdp)
 		security_md5_16_32_32(&session_key_blob[32], client_random,
 		    server_random, rdp->decrypt_key);
 	} else {
-		security_md5_16_32_32(&session_key_blob[16], client_random,
+		/* Allow FIPS use of MD5 here, this is just used for generation of the SessionKeyBlob as described in MS-RDPELE. */
+		/* This is for RDP licensing packets which will already be encrypted under FIPS, so the use of MD5 here is not */
+		/* for sensitive data protection. */
+		security_md5_16_32_32_allow_fips(&session_key_blob[16], client_random,
 		    server_random, rdp->decrypt_key);
-		security_md5_16_32_32(&session_key_blob[32], client_random,
+		security_md5_16_32_32_allow_fips(&session_key_blob[32], client_random,
 		    server_random, rdp->encrypt_key);
 	}
 
diff --git a/libfreerdp-utils/args.c b/libfreerdp-utils/args.c
index d9b08c41f..333742536 100644
--- a/libfreerdp-utils/args.c
+++ b/libfreerdp-utils/args.c
@@ -550,9 +550,6 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv,
 				settings->rdp_security = true;
 				settings->tls_security = false;
 				settings->nla_security = false;
-				settings->encryption = true;
-				settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS;
-				settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
 			}
 			else if (strncmp("tls", argv[index], 1) == 0) /* TLS */
 			{
-- 
2.15.0