From 4bf583aaa23700ec92df0fcf1b9d8978e29199f7 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Thu, 21 Jul 2016 17:14:07 -0400 Subject: SL-429: Use a new certificate authority bundle based on data from Mozilla --- indra/newview/llsechandler_basic.cpp | 98 +++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 40 deletions(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 40516f9bbb..6f5e1cf8bb 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -586,8 +586,7 @@ LLPointer LLBasicCertificateVector::erase(iterator _iter) // // LLBasicCertificateStore // This class represents a store of CA certificates. The basic implementation -// uses a pem file such as the legacy CA.pem stored in the existing -// SL implementation. +// uses a crt file such as the ca-bundle.crt in the existing SL implementation. LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) { mFilename = filename; @@ -596,39 +595,51 @@ LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) void LLBasicCertificateStore::load_from_file(const std::string& filename) { + int loaded = 0; + // scan the PEM file extracting each certificate - if (!LLFile::isfile(filename)) - { - return; - } - - BIO* file_bio = BIO_new(BIO_s_file()); - if(file_bio) - { - if (BIO_read_filename(file_bio, filename.c_str()) > 0) - { - X509 *cert_x509 = NULL; - while((PEM_read_bio_X509(file_bio, &cert_x509, 0, NULL)) && - (cert_x509 != NULL)) - { - try - { - add(new LLBasicCertificate(cert_x509)); - } - catch (...) - { - LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL; - } - X509_free(cert_x509); - cert_x509 = NULL; - } - BIO_free(file_bio); - } - } - else + if (LLFile::isfile(filename)) { - LL_WARNS("SECAPI") << "Could not allocate a file BIO" << LL_ENDL; - } + BIO* file_bio = BIO_new(BIO_s_file()); + if(file_bio) + { + if (BIO_read_filename(file_bio, filename.c_str()) > 0) + { + X509 *cert_x509 = NULL; + while((PEM_read_bio_X509(file_bio, &cert_x509, 0, NULL)) && + (cert_x509 != NULL)) + { + try + { + add(new LLBasicCertificate(cert_x509)); + loaded++; + } + catch (...) + { + LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL; + } + X509_free(cert_x509); + cert_x509 = NULL; + } + BIO_free(file_bio); + } + else + { + LL_WARNS("SECAPI") << "BIO read failed for " << filename << LL_ENDL; + } + + LL_INFOS("SECAPI") << "loaded " << loaded << " certificates from " << filename << LL_ENDL; + } + else + { + LL_WARNS("SECAPI") << "Could not allocate a file BIO" << LL_ENDL; + } + } + else + { + // since the user certificate store may not be there, this is not a warning + LL_INFOS("SECAPI") << "Certificate store not found at " << filename << LL_ENDL; + } } @@ -664,7 +675,7 @@ void LLBasicCertificateStore::save() // return the store id std::string LLBasicCertificateStore::storeId() const { - // this is the basic handler which uses the CA.pem store, + // this is the basic handler which uses the ca-bundle.crt store, // so we ignore this. return std::string(""); } @@ -1014,7 +1025,11 @@ void LLBasicCertificateStore::validate(int validation_policy, const LLSD& validation_params) { // If --no-verify-ssl-cert was passed on the command line, stop right now. - if (gSavedSettings.getBOOL("NoVerifySSLCert")) return; + if (gSavedSettings.getBOOL("NoVerifySSLCert")) + { + LL_WARNS_ONCE("SECAPI") << "All Certificate validation disabled; viewer operation is insecure" << LL_ENDL; + return; + } if(cert_chain->size() < 1) { @@ -1062,7 +1077,6 @@ void LLBasicCertificateStore::validate(int validation_policy, t_cert_cache::iterator cache_entry = mTrustedCertCache.find(sha1_hash); if(cache_entry != mTrustedCertCache.end()) { - LL_DEBUGS("SECAPI") << "Found cert in cache" << LL_ENDL; // this cert is in the cache, so validate the time. if (validation_policy & VALIDATION_POLICY_TIME) { @@ -1079,6 +1093,7 @@ void LLBasicCertificateStore::validate(int validation_policy, } } // successfully found in cache + LL_DEBUGS("SECAPI") << "Valid cert for " << validation_params[CERT_HOSTNAME].asString() << " found in cache" << LL_ENDL; return; } if(current_cert_info.isUndefined()) @@ -1123,6 +1138,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if(found_store_cert != end()) { mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); + LL_DEBUGS("SECAPI") << "Valid cert for " << validation_params[CERT_HOSTNAME].asString() << " found in cert store" << LL_ENDL; return; } @@ -1160,6 +1176,7 @@ void LLBasicCertificateStore::validate(int validation_policy, } // successfully validated. mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); + LL_DEBUGS("SECAPI") << "Valid CA cert for " << validation_params[CERT_HOSTNAME].asString() << " found in cert store" << LL_ENDL; return; } previous_cert = (*current_cert); @@ -1176,6 +1193,7 @@ void LLBasicCertificateStore::validate(int validation_policy, throw LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1]); } + LL_DEBUGS("SECAPI") << "Valid ? cert for " << validation_params[CERT_HOSTNAME].asString() << " found in cert store" << LL_ENDL; mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); } @@ -1214,13 +1232,13 @@ void LLSecAPIBasicHandler::init() "CA.pem"); - LL_DEBUGS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; + LL_INFOS("SECAPI") << "Loading user certificate store from " << store_file << LL_ENDL; mStore = new LLBasicCertificateStore(store_file); - // grab the application CA.pem file that contains the well-known certs shipped + // grab the application ca-bundle.crt file that contains the well-known certs shipped // with the product - std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); - LL_INFOS() << "app path " << ca_file_path << LL_ENDL; + std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "ca-bundle.crt"); + LL_INFOS("SECAPI") << "Loading application certificate store from " << ca_file_path << LL_ENDL; LLPointer app_ca_store = new LLBasicCertificateStore(ca_file_path); // push the applicate CA files into the store, therefore adding any new CA certs that -- cgit v1.2.3 From fd3628ef45a8160f2434e0d8b747d31d65685340 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Fri, 14 Apr 2017 16:05:59 -0400 Subject: Change certificate store infrastructure to key off of the Subject Key Id rather than sha1 hash, since that is rarely used in modern certs. The previous form was storing trusted certs using an empty sha1 hash value as the key, which meant most certificates matched... not good. Modify the LLCertException to pass certificate information back as LLSD rather than an LLPointer, because when the exception is being thown from the certificate constructor that results in one of a couple of other exceptions (even refcounting won't save you when the problem is that the thing you're pointing to never finished coming into being properly). Update the certificates in the llsechandler_basic_test to modern conventions, and extend the classes to allow for an optional validation date so that the test can use a fixed date. Also make all the certificates include the plain text form for ease of reference. --- indra/newview/llsechandler_basic.cpp | 273 ++++++++++++++++++++--------------- 1 file changed, 153 insertions(+), 120 deletions(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index bf7faff13a..ffb9aa1585 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -62,37 +62,45 @@ std::string cert_string_from_octet_string(ASN1_OCTET_STRING* value); LLSD _basic_constraints_ext(X509* cert); LLSD _key_usage_ext(X509* cert); LLSD _ext_key_usage_ext(X509* cert); -LLSD _subject_key_identifier_ext(X509 *cert); -LLSD _authority_key_identifier_ext(X509* cert); +std::string _subject_key_identifier(X509 *cert); +LLSD _authority_key_identifier(X509* cert); +void _validateCert(int validation_policy, + LLPointer cert, + const LLSD& validation_params, + int depth); -LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) +LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert, + const LLSD* validation_params) { - // BIO_new_mem_buf returns a read only bio, but takes a void* which isn't const // so we need to cast it. BIO * pem_bio = BIO_new_mem_buf((void*)pem_cert.c_str(), pem_cert.length()); if(pem_bio == NULL) { LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; - LLTHROW(LLInvalidCertificate(this)); + LLTHROW(LLInvalidCertificate(LLSD::emptyMap())); } mCert = NULL; PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); BIO_free(pem_bio); if (!mCert) { - LLTHROW(LLInvalidCertificate(this)); + LL_WARNS("SECAPI") << "Could not decode certificate to x509." << LL_ENDL; + LLTHROW(LLInvalidCertificate(LLSD::emptyMap())); } } -LLBasicCertificate::LLBasicCertificate(X509* pCert) +LLBasicCertificate::LLBasicCertificate(X509* pCert, + const LLSD* validation_params) { if (!pCert || !pCert->cert_info) { - LLTHROW(LLInvalidCertificate(this)); + LLTHROW(LLInvalidCertificate(LLSD::emptyMap())); } mCert = X509_dup(pCert); + // it is tempting to run _validateCert here, but doing so causes problems + // the trick is figuring out which aspects to validate. TBD } LLBasicCertificate::~LLBasicCertificate() @@ -100,6 +108,7 @@ LLBasicCertificate::~LLBasicCertificate() if(mCert) { X509_free(mCert); + mCert = NULL; } } @@ -172,14 +181,12 @@ LLSD& LLBasicCertificate::_initLLSD() mLLSDInfo[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); mLLSDInfo[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); - mLLSDInfo[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); - mLLSDInfo[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); // add the known extensions mLLSDInfo[CERT_BASIC_CONSTRAINTS] = _basic_constraints_ext(mCert); mLLSDInfo[CERT_KEY_USAGE] = _key_usage_ext(mCert); mLLSDInfo[CERT_EXTENDED_KEY_USAGE] = _ext_key_usage_ext(mCert); - mLLSDInfo[CERT_SUBJECT_KEY_IDENTFIER] = _subject_key_identifier_ext(mCert); - mLLSDInfo[CERT_AUTHORITY_KEY_IDENTIFIER] = _authority_key_identifier_ext(mCert); + mLLSDInfo[CERT_SUBJECT_KEY_IDENTFIER] = _subject_key_identifier(mCert); + mLLSDInfo[CERT_AUTHORITY_KEY_IDENTIFIER] = _authority_key_identifier(mCert); return mLLSDInfo; } @@ -278,21 +285,20 @@ LLSD _ext_key_usage_ext(X509* cert) } // retrieve the subject key identifier of the cert -LLSD _subject_key_identifier_ext(X509 *cert) +std::string _subject_key_identifier(X509 *cert) { - LLSD result; + std::string result; ASN1_OCTET_STRING *skeyid = (ASN1_OCTET_STRING *)X509_get_ext_d2i(cert, NID_subject_key_identifier, NULL, NULL); if(skeyid) { result = cert_string_from_octet_string(skeyid); - ASN1_OCTET_STRING_free( skeyid ); } return result; } // retrieve the authority key identifier of the cert -LLSD _authority_key_identifier_ext(X509* cert) +LLSD _authority_key_identifier(X509* cert) { LLSD result; AUTHORITY_KEYID *akeyid = (AUTHORITY_KEYID *)X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); @@ -308,14 +314,10 @@ LLSD _authority_key_identifier_ext(X509* cert) result[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL] = cert_string_from_asn1_integer(akeyid->serial); } - AUTHORITY_KEYID_free( akeyid ); } - // we ignore the issuer name in the authority key identifier, we check the issue name via // the the issuer name entry in the cert. - - return result; } @@ -466,44 +468,6 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) #endif // LL_WINDOWS } - -// Generate a string containing a digest. The digest time is 'ssh1' or -// 'md5', and the resulting string is of the form "aa:12:5c:' and so on -std::string cert_get_digest(const std::string& digest_type, X509 *cert) -{ - unsigned char digest_data[BUFFER_READ_SIZE]; - unsigned int len = sizeof(digest_data); - std::stringstream result; - const EVP_MD* digest = NULL; - // we could use EVP_get_digestbyname, but that requires initializer code which - // would require us to complicate things by plumbing it into the system. - if (digest_type == "md5") - { - digest = EVP_md5(); - } - else if (digest_type == "sha1") - { - digest = EVP_sha1(); - } - else - { - return std::string(); - } - - 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(); -} - - // class LLBasicCertificateVector // This class represents a list of certificates, implemented by a vector of certificate pointers. // it contains implementations of the virtual functions for iterators, search, add, remove, etc. @@ -513,38 +477,33 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert) // It will find a cert that has minimally the params listed, with the values being the same LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& params) { - BOOL found = FALSE; // loop through the entire vector comparing the values in the certs // against those passed in via the params. // params should be a map. Only the items specified in the map will be // checked, but they must match exactly, even if they're maps or arrays. - - for(iterator cert = begin(); - cert != end(); - cert++) + bool found = false; + iterator cert = begin(); + while ( !found && cert != end() ) { - - found= TRUE; + found = true; LLSD cert_info; (*cert)->getLLSD(cert_info); - for (LLSD::map_const_iterator param = params.beginMap(); - param != params.endMap(); + for (LLSD::map_const_iterator param = params.beginMap(); + found && param != params.endMap(); param++) { - - if (!cert_info.has((std::string)param->first) || - (!valueCompareLLSD(cert_info[(std::string)param->first], param->second))) + if ( !cert_info.has((std::string)param->first) + || !valueCompareLLSD(cert_info[(std::string)param->first], param->second)) { - found = FALSE; - break; + found = false; } } - if (found) - { - return (cert); - } + if (!found) + { + cert++; + } } - return end(); + return cert; } // Insert a certificate into the store. If the certificate already @@ -554,20 +513,37 @@ void LLBasicCertificateVector::insert(iterator _iter, { LLSD cert_info; cert->getLLSD(cert_info); - if (cert_info.isMap() && cert_info.has(CERT_SHA1_DIGEST)) + if (cert_info.isMap() && cert_info.has(CERT_SUBJECT_KEY_IDENTFIER)) { LLSD existing_cert_info = LLSD::emptyMap(); - existing_cert_info[CERT_MD5_DIGEST] = cert_info[CERT_MD5_DIGEST]; + existing_cert_info[CERT_SUBJECT_KEY_IDENTFIER] = cert_info[CERT_SUBJECT_KEY_IDENTFIER]; if(find(existing_cert_info) == end()) { BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); - llassert(basic_iter); if (basic_iter) { mCerts.insert(basic_iter->mIter, cert); } + else + { + LL_WARNS("SECAPI") << "Invalid certificate postion vector" + << LL_ENDL; + } } + else + { + LL_DEBUGS("SECAPI") << "Certificate already in vector: " + << "'" << cert_info << "'" + << LL_ENDL; + } + } + else + { + LL_WARNS("SECAPI") << "Certificate does not have Subject Key Identifier; not inserted: " + << "'" << cert_info << "'" + << LL_ENDL; + } } // remove a certificate from the store @@ -613,12 +589,28 @@ void LLBasicCertificateStore::load_from_file(const std::string& filename) { try { - add(new LLBasicCertificate(cert_x509)); + LLPointer new_cert(new LLBasicCertificate(cert_x509)); + LLSD validation_params; + _validateCert(VALIDATION_POLICY_TIME, + new_cert, + validation_params, + 0); + add(new_cert); + LL_DEBUGS("SECAPI") << "Loaded valid cert for " + << "Name '" << cert_string_name_from_X509_NAME(X509_get_subject_name(cert_x509)) << "'"; + std::string skeyid(_subject_key_identifier(cert_x509)); + LL_CONT << " Id '" << skeyid << "'" + << LL_ENDL; loaded++; } + catch (LLCertException& cert_exception) + { + LLSD cert_info(cert_exception.getCertData()); + LL_WARNS("SECAPI") << "invalid certificate (" << cert_exception.what() << "): " << cert_info << LL_ENDL; + } catch (...) { - LOG_UNHANDLED_EXCEPTION("creating certificate from the certificate store file"); + LOG_UNHANDLED_EXCEPTION("creating certificate from the certificate store file"); } X509_free(cert_x509); cert_x509 = NULL; @@ -880,33 +872,31 @@ void _validateCert(int validation_policy, const LLSD& validation_params, int depth) { - LLSD current_cert_info; cert->getLLSD(current_cert_info); // check basic properties exist in the cert if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING)) { - LLTHROW(LLCertException(cert, "Cert doesn't have a Subject Name")); + LLTHROW(LLCertException(current_cert_info, "Cert doesn't have a Subject Name")); } if(!current_cert_info.has(CERT_ISSUER_NAME_STRING)) { - LLTHROW(LLCertException(cert, "Cert doesn't have an Issuer Name")); + LLTHROW(LLCertException(current_cert_info, "Cert doesn't have an Issuer Name")); } // check basic properties exist in the cert if(!current_cert_info.has(CERT_VALID_FROM) || !current_cert_info.has(CERT_VALID_TO)) { - LLTHROW(LLCertException(cert, "Cert doesn't have an expiration period")); + LLTHROW(LLCertException(current_cert_info, "Cert doesn't have an expiration period")); } - if (!current_cert_info.has(CERT_SHA1_DIGEST)) + if (!current_cert_info.has(CERT_SUBJECT_KEY_IDENTFIER)) { - LLTHROW(LLCertException(cert, "No SHA1 digest")); + LLTHROW(LLCertException(current_cert_info, "Cert doesn't have a Subject Key Id")); } if (validation_policy & VALIDATION_POLICY_TIME) { - LLDate validation_date(time(NULL)); if(validation_params.has(CERT_VALIDATION_DATE)) { @@ -916,7 +906,7 @@ void _validateCert(int validation_policy, if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) || (validation_date > current_cert_info[CERT_VALID_TO].asDate())) { - LLTHROW(LLCertValidationExpirationException(cert, validation_date)); + LLTHROW(LLCertValidationExpirationException(current_cert_info, validation_date)); } } if (validation_policy & VALIDATION_POLICY_SSL_KU) @@ -927,14 +917,14 @@ void _validateCert(int validation_policy, !(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT))))) { - LLTHROW(LLCertKeyUsageValidationException(cert)); + LLTHROW(LLCertKeyUsageValidationException(current_cert_info)); } // only validate EKU if the cert has it if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() && (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE], LLSD((std::string)CERT_EKU_SERVER_AUTH)))) { - LLTHROW(LLCertKeyUsageValidationException(cert)); + LLTHROW(LLCertKeyUsageValidationException(current_cert_info)); } } if (validation_policy & VALIDATION_POLICY_CA_KU) @@ -943,7 +933,7 @@ void _validateCert(int validation_policy, (!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], (std::string)CERT_KU_CERT_SIGN))) { - LLTHROW(LLCertKeyUsageValidationException(cert)); + LLTHROW(LLCertKeyUsageValidationException(current_cert_info)); } } @@ -955,13 +945,13 @@ void _validateCert(int validation_policy, if(!current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_CA) || !current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_CA]) { - LLTHROW(LLCertBasicConstraintsValidationException(cert)); + LLTHROW(LLCertBasicConstraintsValidationException(current_cert_info)); } if (current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_PATHLEN) && ((current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger() != 0) && (depth > current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger()))) { - LLTHROW(LLCertBasicConstraintsValidationException(cert)); + LLTHROW(LLCertBasicConstraintsValidationException(current_cert_info)); } } } @@ -1035,26 +1025,28 @@ void LLBasicCertificateStore::validate(int validation_policy, if(cert_chain->size() < 1) { - LLTHROW(LLCertException(NULL, "No certs in chain")); + LLTHROW(LLCertException(LLSD::emptyMap(), "No certs in chain")); } iterator current_cert = cert_chain->begin(); - LLSD current_cert_info; LLSD validation_date; if (validation_params.has(CERT_VALIDATION_DATE)) { validation_date = validation_params[CERT_VALIDATION_DATE]; } + // get LLSD info from the cert to throw in any exception + LLSD current_cert_info; + (*current_cert)->getLLSD(current_cert_info); + if (validation_policy & VALIDATION_POLICY_HOSTNAME) { - (*current_cert)->getLLSD(current_cert_info); if(!validation_params.has(CERT_HOSTNAME)) { - LLTHROW(LLCertException((*current_cert), "No hostname passed in for validation")); + LLTHROW(LLCertException(current_cert_info, "No hostname passed in for validation")); } if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info[CERT_SUBJECT_NAME].has(CERT_NAME_CN)) { - LLTHROW(LLInvalidCertificate((*current_cert))); + LLTHROW(LLInvalidCertificate(current_cert_info)); } LL_DEBUGS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() << @@ -1063,39 +1055,60 @@ void LLBasicCertificateStore::validate(int validation_policy, current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString())) { throw LLCertValidationHostnameException(validation_params[CERT_HOSTNAME].asString(), - (*current_cert)); + current_cert_info); } } - + // check the cache of already validated certs X509* cert_x509 = (*current_cert)->getOpenSSLX509(); if(!cert_x509) { - LLTHROW(LLInvalidCertificate((*current_cert))); + LLTHROW(LLInvalidCertificate(current_cert_info)); } - std::string sha1_hash((const char *)cert_x509->sha1_hash, SHA_DIGEST_LENGTH); + + std::string subject_name(cert_string_name_from_X509_NAME(X509_get_subject_name(cert_x509))); + std::string skeyid(_subject_key_identifier(cert_x509)); + + LL_DEBUGS("SECAPI") << "attempting to validate cert " + << " for '" << (validation_params.has(CERT_HOSTNAME) ? validation_params[CERT_HOSTNAME].asString() : "(unknown hostname)") << "'" + << " as subject name '" << subject_name << "'" + << " subject key id '" << skeyid << "'" + << LL_ENDL; + X509_free( cert_x509 ); cert_x509 = NULL; - t_cert_cache::iterator cache_entry = mTrustedCertCache.find(sha1_hash); + if (skeyid.empty()) + { + LLTHROW(LLCertException(current_cert_info, "No Subject Key Id")); + } + + t_cert_cache::iterator cache_entry = mTrustedCertCache.find(skeyid); if(cache_entry != mTrustedCertCache.end()) { // this cert is in the cache, so validate the time. if (validation_policy & VALIDATION_POLICY_TIME) { - LLDate validation_date(time(NULL)); + LLDate validation_date; if(validation_params.has(CERT_VALIDATION_DATE)) { validation_date = validation_params[CERT_VALIDATION_DATE]; } + else + { + validation_date = LLDate(time(NULL)); // current time + } if((validation_date < cache_entry->second.first) || (validation_date > cache_entry->second.second)) { - LLTHROW(LLCertValidationExpirationException((*current_cert), validation_date)); + LLTHROW(LLCertValidationExpirationException(current_cert_info, validation_date)); } } // successfully found in cache - LL_DEBUGS("SECAPI") << "Valid cert for " << validation_params[CERT_HOSTNAME].asString() << " found in cache" << LL_ENDL; + LL_DEBUGS("SECAPI") << "Valid cert for '" << validation_params[CERT_HOSTNAME].asString() << "'" + << " skeyid '" << skeyid << "'" + << " found in cache" + << LL_ENDL; return; } if(current_cert_info.isUndefined()) @@ -1109,7 +1122,6 @@ void LLBasicCertificateStore::validate(int validation_policy, // loop through the cert chain, validating the current cert against the next one. while(current_cert != cert_chain->end()) { - int local_validation_policy = validation_policy; if(current_cert == cert_chain->begin()) { @@ -1124,7 +1136,9 @@ void LLBasicCertificateStore::validate(int validation_policy, if(!_verify_signature((*current_cert), previous_cert)) { - LLTHROW(LLCertValidationInvalidSignatureException(previous_cert)); + LLSD previous_cert_info; + previous_cert->getLLSD(previous_cert_info); + LLTHROW(LLCertValidationInvalidSignatureException(previous_cert_info)); } } _validateCert(local_validation_policy, @@ -1135,12 +1149,20 @@ 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_SHA1_DIGEST] = current_cert_info[CERT_SHA1_DIGEST]; + cert_search_params[CERT_SUBJECT_KEY_IDENTFIER] = current_cert_info[CERT_SUBJECT_KEY_IDENTFIER]; LLCertificateStore::iterator found_store_cert = find(cert_search_params); if(found_store_cert != end()) { - mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); - LL_DEBUGS("SECAPI") << "Valid cert for " << validation_params[CERT_HOSTNAME].asString() << " found in cert store" << LL_ENDL; + mTrustedCertCache[skeyid] = std::pair(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(); + std::string found_cert_subject_name(cert_string_name_from_X509_NAME(X509_get_subject_name(cert_x509))); + X509_free(cert_x509); + LL_CONT << " as '" << found_cert_subject_name << "'" + << " skeyid '" << current_cert_info[CERT_SUBJECT_KEY_IDENTFIER].asString() << "'" + << " found in cert store" + << LL_ENDL; return; } @@ -1174,11 +1196,16 @@ void LLBasicCertificateStore::validate(int validation_policy, if(!_verify_signature((*found_store_cert), (*current_cert))) { - LLTHROW(LLCertValidationInvalidSignatureException(*current_cert)); + LLTHROW(LLCertValidationInvalidSignatureException(current_cert_info)); } // successfully validated. - mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); - LL_DEBUGS("SECAPI") << "Valid CA cert for " << validation_params[CERT_HOSTNAME].asString() << " found in cert store" << LL_ENDL; + mTrustedCertCache[skeyid] = std::pair(from_time, to_time); + LL_DEBUGS("SECAPI") << "Verified and cached cert for '" << validation_params[CERT_HOSTNAME].asString() << "'" + << " as '" << subject_name << "'" + << " id '" << skeyid << "'" + << " 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; return; } previous_cert = (*current_cert); @@ -1192,11 +1219,17 @@ void LLBasicCertificateStore::validate(int validation_policy, if (validation_policy & VALIDATION_POLICY_TRUSTED) { // we reached the end without finding a trusted cert. - LLTHROW(LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1])); - + LLSD last_cert_info; + ((*cert_chain)[cert_chain->size()-1])->getLLSD(last_cert_info); + LLTHROW(LLCertValidationTrustException(last_cert_info)); } - LL_DEBUGS("SECAPI") << "Valid ? cert for " << validation_params[CERT_HOSTNAME].asString() << " found in cert store" << LL_ENDL; - mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); + else + { + LL_DEBUGS("SECAPI") << "! Caching untrusted cert for '" << subject_name << "'" + << " skeyid '" << skeyid << "' in cert store because ! VALIDATION_POLICY_TRUSTED" + << LL_ENDL; + mTrustedCertCache[skeyid] = std::pair(from_time, to_time); + } } -- cgit v1.2.3 From 47257bb010ef91ddd43a822738d529d5437c83df Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Fri, 12 Jan 2018 12:34:19 -0500 Subject: remove warnings for bad certificates (too scary), add info for the rejected count --- indra/newview/llsechandler_basic.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index ffb9aa1585..814cfde75d 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -574,6 +574,7 @@ LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) void LLBasicCertificateStore::load_from_file(const std::string& filename) { int loaded = 0; + int rejected = 0; // scan the PEM file extracting each certificate if (LLFile::isfile(filename)) @@ -606,11 +607,13 @@ void LLBasicCertificateStore::load_from_file(const std::string& filename) catch (LLCertException& cert_exception) { LLSD cert_info(cert_exception.getCertData()); - LL_WARNS("SECAPI") << "invalid certificate (" << cert_exception.what() << "): " << cert_info << LL_ENDL; + LL_DEBUGS("SECAPI_BADCERT","SECAPI") << "invalid certificate (" << cert_exception.what() << "): " << cert_info << LL_ENDL; + rejected++; } catch (...) { LOG_UNHANDLED_EXCEPTION("creating certificate from the certificate store file"); + rejected++; } X509_free(cert_x509); cert_x509 = NULL; @@ -622,7 +625,7 @@ void LLBasicCertificateStore::load_from_file(const std::string& filename) LL_WARNS("SECAPI") << "BIO read failed for " << filename << LL_ENDL; } - LL_INFOS("SECAPI") << "loaded " << loaded << " certificates from " << filename << LL_ENDL; + LL_INFOS("SECAPI") << "loaded " << loaded << " good certificates (rejected " << rejected << ") from " << filename << LL_ENDL; } else { -- cgit v1.2.3