From 9da06f93519f198b13dd5255fc8baff43e9af0bd Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 7 Apr 2021 02:54:28 -0400
Subject: [PATCH] Change cert verification to sha256 digest

---
 indra/newview/llsecapi.h             |  1 +
 indra/newview/llsechandler_basic.cpp | 40 +++++++++++++++++++++++-----
 indra/newview/llsechandler_basic.h   |  2 +-
 3 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h
index 14059f828a4..93fdd774c73 100644
--- a/indra/newview/llsecapi.h
+++ b/indra/newview/llsecapi.h
@@ -53,6 +53,7 @@
 #define CERT_VALID_FROM "valid_from"
 #define CERT_VALID_TO "valid_to"
 #define CERT_SHA1_DIGEST "sha1_digest"
+#define CERT_SHA256_DIGEST "sha256_digest"
 #define CERT_MD5_DIGEST "md5_digest"
 #define CERT_HOSTNAME "hostname"
 #define CERT_BASIC_CONSTRAINTS "basicConstraints"
diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp
index 5a8a00a70dc..2ff5f937998 100644
--- a/indra/newview/llsechandler_basic.cpp
+++ b/indra/newview/llsechandler_basic.cpp
@@ -64,6 +64,7 @@ LLSD _basic_constraints_ext(X509* cert);
 LLSD _key_usage_ext(X509* cert);
 LLSD _ext_key_usage_ext(X509* cert);
 std::string _subject_key_identifier(X509 *cert);
+std::string _cert_sha256_digest(X509* cert);
 LLSD _authority_key_identifier(X509* cert);
 void _validateCert(int validation_policy,
                    LLPointer<LLCertificate> cert,
@@ -188,6 +189,7 @@ LLSD& LLBasicCertificate::_initLLSD()
 	mLLSDInfo[CERT_EXTENDED_KEY_USAGE] = _ext_key_usage_ext(mCert);
 	mLLSDInfo[CERT_SUBJECT_KEY_IDENTFIER] = _subject_key_identifier(mCert);
 	mLLSDInfo[CERT_AUTHORITY_KEY_IDENTIFIER] = _authority_key_identifier(mCert);
+	mLLSDInfo[CERT_SHA256_DIGEST] = _cert_sha256_digest(mCert);
 	return mLLSDInfo; 
 }
 
@@ -1005,6 +1007,24 @@ bool _verify_signature(LLPointer<LLCertificate> parent,
 	return verify_result;
 }
 
+std::string _cert_sha256_digest(X509* cert)
+{
+	unsigned char digest_data[BUFFER_READ_SIZE];
+	unsigned int len = sizeof(digest_data);
+	std::stringstream result;
+	const EVP_MD* digest = EVP_sha256();
+	X509_digest(cert, digest, digest_data, &len);
+	result << std::hex << std::setprecision(2);
+	for (unsigned int i = 0; i < len; i++)
+	{
+		if (i != 0)
+		{
+			result << ":";
+		}
+		result << std::setfill('0') << std::setw(2) << (int)digest_data[i];
+	}
+	return result.str();
+}
 
 // validate the certificate chain against a store.
 // There are many aspects of cert validatioin policy involved in
@@ -1076,6 +1096,7 @@ void LLBasicCertificateStore::validate(int validation_policy,
 
     std::string subject_name(cert_string_name_from_X509_NAME(X509_get_subject_name(cert_x509)));
     std::string skeyid(_subject_key_identifier(cert_x509));
+	std::string sha256_digest(_cert_sha256_digest(cert_x509));
 
     LL_DEBUGS("SECAPI") << "attempting to validate cert "
                         << " for '" << (validation_params.has(CERT_HOSTNAME) ? validation_params[CERT_HOSTNAME].asString() : "(unknown hostname)") << "'"
@@ -1090,7 +1111,11 @@ void LLBasicCertificateStore::validate(int validation_policy,
         LLTHROW(LLCertException(current_cert_info, "No Subject Key Id"));
     }
     
-	t_cert_cache::iterator cache_entry = mTrustedCertCache.find(skeyid);
+	if (sha256_digest.empty())
+	{
+		LLTHROW(LLCertException(current_cert_info, "No SHA256 digest"));
+	}
+	t_cert_cache::iterator cache_entry = mTrustedCertCache.find(sha256_digest);
 	if(cache_entry != mTrustedCertCache.end())
 	{
 		// this cert is in the cache, so validate the time.
@@ -1157,11 +1182,11 @@ void LLBasicCertificateStore::validate(int validation_policy,
 		// look for a CA in the CA store that may belong to this chain.
 		LLSD cert_search_params = LLSD::emptyMap();		
 		// is the cert itself in the store?
-		cert_search_params[CERT_SUBJECT_KEY_IDENTFIER] = current_cert_info[CERT_SUBJECT_KEY_IDENTFIER];
+		cert_search_params[CERT_SHA256_DIGEST] = current_cert_info[CERT_SHA256_DIGEST];
 		LLCertificateStore::iterator found_store_cert = find(cert_search_params);
 		if(found_store_cert != end())
 		{
-			mTrustedCertCache[skeyid] = std::pair<LLDate, LLDate>(from_time, to_time);
+			mTrustedCertCache[sha256_digest] = std::pair<LLDate, LLDate>(from_time, to_time);
             LL_DEBUGS("SECAPI") << "Valid cert "
                                 << " for '" << (validation_params.has(CERT_HOSTNAME) ? validation_params[CERT_HOSTNAME].asString() : "(unknown hostname)") << "'";
             X509* cert_x509 = (*found_store_cert)->getOpenSSLX509();
@@ -1169,6 +1194,7 @@ void LLBasicCertificateStore::validate(int validation_policy,
             X509_free(cert_x509);
             LL_CONT << " as '" << found_cert_subject_name << "'"
                     << " skeyid '" << current_cert_info[CERT_SUBJECT_KEY_IDENTFIER].asString() << "'"
+                    << " sha256 digest '" << current_cert_info[CERT_SHA256_DIGEST].asString() << "'"
                     << " found in cert store"
                     << LL_ENDL;	
 			return;
@@ -1207,10 +1233,11 @@ void LLBasicCertificateStore::validate(int validation_policy,
 				LLTHROW(LLCertValidationInvalidSignatureException(current_cert_info));
 			}			
 			// successfully validated.
-			mTrustedCertCache[skeyid] = std::pair<LLDate, LLDate>(from_time, to_time);		
+			mTrustedCertCache[sha256_digest] = std::pair<LLDate, LLDate>(from_time, to_time);
             LL_DEBUGS("SECAPI") << "Verified and cached cert for '" << validation_params[CERT_HOSTNAME].asString() << "'"
                                 << " as '" << subject_name << "'"
                                 << " id '" << skeyid << "'"
+                                << " hash '" << sha256_digest << "'"
                                 << " using CA '" << cert_search_params[CERT_SUBJECT_NAME_STRING] << "'"
                                 << " with id '" <<  cert_search_params[CERT_SUBJECT_KEY_IDENTFIER].asString() << "' found in cert store"
                                 << LL_ENDL;	
@@ -1234,9 +1261,10 @@ void LLBasicCertificateStore::validate(int validation_policy,
     else
     {
         LL_DEBUGS("SECAPI") << "! Caching untrusted cert for '" << subject_name << "'"
-                            << " skeyid '" << skeyid << "' in cert store because ! VALIDATION_POLICY_TRUSTED"
+                            << " skeyid '" << skeyid << "'"
+                            << " sha256_digest '" << sha256_digest << "' in cert store because ! VALIDATION_POLICY_TRUSTED"
                             << LL_ENDL;	
-        mTrustedCertCache[skeyid] = std::pair<LLDate, LLDate>(from_time, to_time);	
+        mTrustedCertCache[sha256_digest] = std::pair<LLDate, LLDate>(from_time, to_time);
     }
 }
 
diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h
index 0bc7f5230f8..c81b071bfa5 100644
--- a/indra/newview/llsechandler_basic.h
+++ b/indra/newview/llsechandler_basic.h
@@ -181,7 +181,7 @@ class LLBasicCertificateStore : virtual public LLBasicCertificateVector, public
 protected:
 	std::vector<LLPointer<LLCertificate> >            mCerts;
 	
-	// cache of cert sha1 hashes to from/to date pairs, to improve
+	// cache of cert sha256 hashes to from/to date pairs, to improve
 	// performance of cert trust.  Note, these are not the CA certs,
 	// but the certs that have been validated against this store.
 	typedef std::map<std::string, std::pair<LLDate, LLDate> > t_cert_cache;
-- 
GitLab