diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp index 85c7451d8a..a15d01dcf5 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -161,9 +161,16 @@ void LLCredential::authenticatorType(std::string &idType) } } -LLCertException::LLCertException(const LLSD& cert_data, const std::string& msg) +LLCertException::LLCertException(const LLSD& cert_data, const std::string& msg, bool suppress_warning) : LLException(msg), mCertData(cert_data) { - LL_WARNS("SECAPI") << "Certificate Error: " << msg << LL_ENDL; + if (!suppress_warning) + { + LL_WARNS("SECAPI") << "Certificate Error: " << msg << LL_ENDL; + } + else + { + LL_DEBUGS("SECAPI") << "Certificate Error: " << msg << LL_ENDL; + } } diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index 14dda20225..5eeb25901b 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -115,10 +115,6 @@ VALIDATION_POLICY_CA_KU) - - - - struct LLProtectedDataException: public LLException { LLProtectedDataException(const std::string& msg): @@ -217,10 +213,10 @@ class LLCertificateVector : public LLThreadSafeRefCount // return the number of certs in the store virtual int size() const = 0; - // append the cert to the store. if a copy of the cert already exists in the store, it is removed first + // append the cert to the store. If the certificate already exists in the store, nothing is done. virtual void add(LLPointer cert)=0; - // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + // insert the cert to the store. If the certificate already exists in the store, nothing is done. virtual void insert(iterator location, LLPointer cert)=0; // remove a certificate from the store @@ -338,7 +334,7 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred); class LLCertException: public LLException { public: - LLCertException(const LLSD& cert_data, const std::string& msg); + LLCertException(const LLSD& cert_data, const std::string& msg, bool suppress_warning = false); virtual ~LLCertException() throw() {} LLSD getCertData() const { return mCertData; } protected: @@ -392,8 +388,8 @@ class LLCertValidationHostnameException : public LLCertException class LLCertValidationExpirationException : public LLCertException { public: - LLCertValidationExpirationException(const LLSD& cert_data, - LLDate current_time) : LLCertException(cert_data, "CertExpired") + LLCertValidationExpirationException(const LLSD& cert_data, LLDate current_time, bool suppress_warning = false) : + LLCertException(cert_data, "CertExpired", suppress_warning) { mTime = current_time; } diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 1e50135e89..b90da37bc2 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -27,30 +27,39 @@ #include "llviewerprecompiledheaders.h" -#include "llsecapi.h" + #include "llsechandler_basic.h" +#include "llsecapi.h" #include "llsdserialize.h" #include "llviewernetwork.h" #include "llxorcipher.h" #include "llfile.h" -#include "lldir.h" #include "llviewercontrol.h" #include "llexception.h" #include "stringize.h" #include #include + #include #include #include #include #include #include -#include #include -#include -#include #include "llmachineid.h" +#if LL_WINDOWS +// This define renames X509_NAME to WINCRYPT_X509_NAME to avoid clashes with the openssl type +#define WINCRYPT_USE_SYMBOL_PREFIX +#include +#elif LL_DARWIN +#include +#include +#elif LL_LINUX +#include +#include +#endif static const std::string DEFAULT_CREDENTIAL_STORAGE = "credential"; @@ -68,7 +77,8 @@ LLSD _authority_key_identifier(X509* cert); void _validateCert(int validation_policy, LLPointer cert, const LLSD& validation_params, - int depth); + int depth, + bool suppress_expire_warning = false); LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert, const LLSD* validation_params) @@ -91,7 +101,6 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert, } } - LLBasicCertificate::LLBasicCertificate(X509* pCert, const LLSD* validation_params) { @@ -155,7 +164,6 @@ std::vector LLBasicCertificate::getBinary() const return result; } - void LLBasicCertificate::getLLSD(LLSD &llsd) { if (mLLSDInfo.isUndefined()) @@ -472,9 +480,19 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) // 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. -// +// and also a function to load certificates from files +LLBasicCertificateVector::LLBasicCertificateVector(const std::string& filename) : + mLoaded(0), + mRejected(0) +{ + if (load_from(filename)) + { + LL_INFOS("SECAPI") << "Loaded " << size() << " unique certificates successfully (good: " + << mLoaded << ", rejected: " << mRejected << ") from " << filename << LL_ENDL; + } +} -// Find a certificate in the list. +// Find a certificate in the list. // It will find a cert that has minimally the params listed, with the values being the same LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& params) { @@ -507,7 +525,7 @@ LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& pa return cert; } -// Insert a certificate into the store. If the certificate already +// Insert a certificate into the store. If the certificate already // exists in the store, nothing is done. void LLBasicCertificateVector::insert(iterator _iter, LLPointer cert) @@ -527,7 +545,7 @@ void LLBasicCertificateVector::insert(iterator _iter, } else { - LL_WARNS("SECAPI") << "Invalid certificate postion vector" + LL_WARNS("SECAPI") << "Invalid certificate position vector" << LL_ENDL; } } @@ -537,7 +555,6 @@ void LLBasicCertificateVector::insert(iterator _iter, << "'" << cert_info << "'" << LL_ENDL; } - } else { @@ -550,7 +567,6 @@ void LLBasicCertificateVector::insert(iterator _iter, // remove a certificate from the store LLPointer LLBasicCertificateVector::erase(iterator _iter) { - if (_iter != end()) { BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); @@ -561,90 +577,243 @@ LLPointer LLBasicCertificateVector::erase(iterator _iter) return NULL; } - -// -// LLBasicCertificateStore -// This class represents a store of CA certificates. The basic implementation -// uses a crt file such as the ca-bundle.crt in the existing SL implementation. -LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) +// load a certificate from a file or directory and add all found certificates to the store if valid +bool LLBasicCertificateVector::load_from(const std::string& filename, bool suppress_expire_warning) { - mFilename = filename; - load_from_file(filename); -} - -void LLBasicCertificateStore::load_from_file(const std::string& filename) -{ - int loaded = 0; - int rejected = 0; - + bool success = false; // scan the PEM file extracting each certificate if (LLFile::isfile(filename)) { - BIO* file_bio = BIO_new(BIO_s_file()); - if(file_bio) + BIO* file_bio = BIO_new_file(filename.c_str(), "rt"); + 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)) { - X509 *cert_x509 = NULL; - while((PEM_read_bio_X509(file_bio, &cert_x509, 0, NULL)) && - (cert_x509 != NULL)) - { - try - { - 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_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; - } - BIO_free(file_bio); + success |= verify_and_add(cert_x509, suppress_expire_warning); + X509_free(cert_x509); + cert_x509 = NULL; } - else - { - LL_WARNS("SECAPI") << "BIO read failed for " << filename << LL_ENDL; - } - - LL_INFOS("SECAPI") << "loaded " << loaded << " good certificates (rejected " << rejected << ") from " << filename << LL_ENDL; + BIO_free(file_bio); } else { LL_WARNS("SECAPI") << "Could not allocate a file BIO" << LL_ENDL; } } + else if (LLFile::isdir(filename)) + { + std::vector files = gDirUtilp->getFilesInDir(filename); + for (std::string& file : files) + { + success |= load_from(file, suppress_expire_warning); + } + } 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; } + return success; +} + +// verify the certificate to be valid and not expired and add it to the store. If the certificate +// already exists in the store (based on CERT_SUBJECT_KEY_IDENTFIER), it will be ignored +bool LLBasicCertificateVector::verify_and_add(const unsigned char* cert_bytes, long length, bool suppress_expire_warning) +{ + bool success = false; + X509* cert_x509 = d2i_X509(nullptr, &cert_bytes, length); + if (cert_x509) + { + success = verify_and_add(cert_x509, suppress_expire_warning); + X509_free(cert_x509); + } + return success; } +// verify the certificate to be valid and not expired and add it to the store. If the certificate +// already exists in the store (based on CERT_SUBJECT_KEY_IDENTFIER), it will be ignored +bool LLBasicCertificateVector::verify_and_add(X509* cert_x509, bool suppress_expire_warning) +{ + try + { + LLPointer new_cert(new LLBasicCertificate(cert_x509)); + LLSD validation_params; + _validateCert(VALIDATION_POLICY_TIME, new_cert, validation_params, 0, suppress_expire_warning); + 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; + mLoaded++; + return true; + } + catch (LLCertValidationExpirationException& cert_exception) + { + LLSD cert_info(cert_exception.getCertData()); + LL_DEBUGS("SECAPI_BADCERT", "SECAPI") << "invalid certificate (" << cert_exception.what() << "): " << cert_info << LL_ENDL; + } + catch (LLCertException& cert_exception) + { + LLSD cert_info(cert_exception.getCertData()); + LL_DEBUGS("SECAPI_BADCERT", "SECAPI") << "invalid certificate (" << cert_exception.what() << "): " << cert_info << LL_ENDL; + } + catch (...) + { + LOG_UNHANDLED_EXCEPTION("creating certificate from the certificate store"); + } + mRejected++; + return false; +} + +// constructor for the system certification store, which enumerates the certificates for the current +// operating system +LLSystemCertificateVector::LLSystemCertificateVector(const std::string& storename, bool suppress_expire_warning) +{ + // Try to load the system store through the platform API + if (load_from_system(storename, suppress_expire_warning)) + { + LL_INFOS("SECAPI") << "Loaded " << size() << " unique certificates successfully (good: " + << mLoaded << ", rejected: " << mRejected << ") from system store" << LL_ENDL; + } + else + { + // fall back grabbing instead the application ca-bundle.crt file shipped with the product + // that contains the well-known certs, so we have at least something + std::string filename = gDirUtilp->getCAFile(); + LL_INFOS("SECAPI") << "Loading application certificate store from " << filename << LL_ENDL; + if (load_from(filename)) + { + LL_INFOS("SECAPI") << "Loaded " << size() << " unique certificates successfully (good: " + << mLoaded << ", rejected: " << mRejected << ") from " << filename << LL_ENDL; + } + else + { + LL_WARNS("SECAPI") << "Failed to initialize the certificate store with valid CA certificates." + "It's probably not possible to connect to any internet service!" << LL_ENDL; + } + } +} -LLBasicCertificateStore::~LLBasicCertificateStore() +// Enumerate the certificates in the system CA store +bool LLSystemCertificateVector::load_from_system(const std::string& storename, bool suppress_expire_warning) { + bool success = false; +#if LL_WINDOWS + HCERTSTORE cert_store = CertOpenSystemStoreA(0, storename.empty() ? "ROOT" : storename.c_str()); + if (cert_store == nullptr) + { + LL_WARNS("SECAPI") << "System Certificate store could not be opened" << LL_ENDL; + return false; + } + + PCCERT_CONTEXT cert_ctxt = CertEnumCertificatesInStore(cert_store, nullptr); + for (; cert_ctxt; cert_ctxt = CertEnumCertificatesInStore(cert_store, cert_ctxt)) + { + const unsigned char* cert = reinterpret_cast(cert_ctxt->pbCertEncoded); + success |= verify_and_add(cert, cert_ctxt->cbCertEncoded, suppress_expire_warning); + } + CertCloseStore(cert_store, 0); +#elif LL_DARWIN + // FM: Quite some googling led eventually to some old MacOS Open Source repository that showed the + // implementation for SecTrustSettignsCopyCertificates() and that eventually led to this code + const static SecTrustSettingsDomain domains[] = + { + kSecTrustSettingsDomainUser, + kSecTrustSettingsDomainAdmin, + kSecTrustSettingsDomainSystem, + }; + + for (SecTrustSettingsDomain domain : domains) + { + CFArrayRef cfCerts; + OSStatus status = SecTrustSettingsCopyCertificates(domain, &cfCerts); + if (status == errSecSuccess) + { + CFIndex idx, count = CFArrayGetCount(cfCerts); + for (idx = 0; idx < count; idx++) + { + SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, idx); + CFDataRef der = SecCertificateCopyData(cfCert); + if (der) + { + const unsigned char* cert = reinterpret_cast(CFDataGetBytePtr(der)); + success |= verify_and_add(cert, CFDataGetLength(der), suppress_expire_warning); + CFRelease(der); + } + } + CFRelease(cfCerts); + } + else if (status != errSecNoTrustSettings) + { + LL_WARNS("SECAPI") << "Could not open certificate trust settings for '" << domain << "' error = " << status << LL_ENDL; + } + } +#elif LL_LINUX + // FM: These are from the Go certificate store implementation + // Possible certificate files; stop after finding one. + const static std::string certFiles[] = + { + "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. + "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6 + "/etc/ssl/ca-bundle.pem", // OpenSUSE + "/etc/pki/tls/cacert.pem", // OpenELEC + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7 + "/etc/ssl/cert.pem", // Alpine Linux + }; + + // Possible directories with certificate files; all will be read if they exist + const static std::string certDirs[] = + { + "/etc/ssl/certs", // SLES10/SLES11 + "/etc/pki/tls/certs", // Fedora/RHEL + }; + + const std::string certFile = getenv("SSL_CERT_FILE"); + if (!certFile.empty()) + { + success = load_from(certFile, suppress_expire_warning); + } + else + { + for (const std::string& certFile : certFiles) + { + success |= load_from(certFile, suppress_expire_warning); + if (success) + break; + } + } + if (!success) + { + const std::string dirs = getenv("SSL_CERT_DIR"); + if (!dirs.empty()) + { + // OpenSSL and BoringSSL both use ":" as the SSL_CERT_DIR separator. + // See: https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html + for (boost::split_iterator + ti(dirs, boost::first_finder(":")), tend; + ti != tend && !success; ++ti) + { + const std::string certDir = std::string(ti->begin(), ti->end()); + success |= load_from(certDir, suppress_expire_warning); + } + } + else + { + for (const std::string& certDir : certDirs) + { + success |= load_from(certDir, suppress_expire_warning); + if (success) + break; + } + } + } +#endif + return success; } +// +// LLBasicCertificateStore +// This class represents a store of CA certificates. // persist the store void LLBasicCertificateStore::save() @@ -678,7 +847,6 @@ std::string LLBasicCertificateStore::storeId() const return std::string(""); } - // // LLBasicCertificateChain // This class represents a chain of certs, each cert being signed by the next cert @@ -874,7 +1042,8 @@ bool _LLSDArrayIncludesValue(const LLSD& llsd_set, LLSD llsd_value) void _validateCert(int validation_policy, LLPointer cert, const LLSD& validation_params, - int depth) + int depth, + bool suppress_expire_warning) { LLSD current_cert_info; cert->getLLSD(current_cert_info); @@ -910,7 +1079,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(current_cert_info, validation_date)); + LLTHROW(LLCertValidationExpirationException(current_cert_info, validation_date, suppress_expire_warning)); } } if (validation_policy & VALIDATION_POLICY_SSL_KU) @@ -1273,29 +1442,21 @@ void LLSecAPIBasicHandler::init() mProtectedDataMap = LLSD::emptyMap(); if (mProtectedDataFilename.length() == 0) { - mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "bin_conf.dat"); + mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "bin_conf.dat"); mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); - mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "bin_conf.dat"); - std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "CA.pem"); - + std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "CA.pem"); LL_INFOS("SECAPI") << "Loading user certificate store from " << store_file << LL_ENDL; mStore = new LLBasicCertificateStore(store_file); - // grab the application ca-bundle.crt file that contains the well-known certs shipped - // with the product - std::string ca_file_path = gDirUtilp->getCAFile(); - LL_INFOS("SECAPI") << "Loading application certificate store from " << ca_file_path << LL_ENDL; - LLPointer app_ca_store = new LLBasicCertificateStore(ca_file_path); + // load the system certificate store and if that fails fall back to the application + // ca-bundle.crt file that contains the well-known certs shipped with the product + LLPointer ca_store = new LLSystemCertificateVector(); - // push the applicate CA files into the store, therefore adding any new CA certs that - // updated - for(LLCertificateVector::iterator i = app_ca_store->begin(); - i != app_ca_store->end(); + // push the new CA files into our store, therefore adding any new CA certs that updated + for (LLCertificateVector::iterator i = ca_store->begin(); + i != ca_store->end(); i++) { mStore->add(*i); diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h index 2dffe84775..e53bd785e3 100644 --- a/indra/newview/llsechandler_basic.h +++ b/indra/newview/llsechandler_basic.h @@ -42,8 +42,6 @@ extern LLSD cert_name_from_X509_NAME(X509_NAME* name); extern std::string cert_string_name_from_X509_NAME(X509_NAME* name); extern std::string cert_string_from_asn1_integer(ASN1_INTEGER* value); extern LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time); -extern std::string cert_get_digest(const std::string& digest_type, X509 *cert); - // class LLCertificate // @@ -77,15 +75,14 @@ class LLBasicCertificate : public LLCertificate LLSD mLLSDInfo; }; - // class LLBasicCertificateVector // Class representing a list of certificates // This implementation uses a stl vector of certificates. class LLBasicCertificateVector : virtual public LLCertificateVector { - public: - LLBasicCertificateVector() {} + LLBasicCertificateVector() : mLoaded(0), mRejected(0) {} + LLBasicCertificateVector(const std::string& path); virtual ~LLBasicCertificateVector() {} @@ -97,6 +94,7 @@ class LLBasicCertificateVector : virtual public LLCertificateVector public: BasicIteratorImpl(std::vector >::iterator _iter) { mIter = _iter;} virtual ~BasicIteratorImpl() {}; + // seek forward or back. Used by the operator++/operator-- implementations virtual void seek(bool incr) { @@ -145,31 +143,56 @@ class LLBasicCertificateVector : virtual public LLCertificateVector // return the number of certs in the store virtual int size() const { return static_cast(mCerts.size()); } - // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + // insert the cert to the store. If the certificate already exists in the store, nothing is done. virtual void add(LLPointer cert) { insert(end(), cert); } - // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + // insert the cert to the store. If the certificate already exists in the store, nothing is done. virtual void insert(iterator _iter, LLPointer cert); // remove a certificate from the store virtual LLPointer erase(iterator _iter); protected: - std::vector >mCerts; + // verify the certificate and if it is valid and not expired add it to this certificate vector + bool verify_and_add(const unsigned char* cert_bytes, long length, bool suppress_expire_warning = false); + bool verify_and_add(X509* cert, bool suppress_expire_warning = false); + // load certificates from a file or directory + bool load_from(const std::string& path, bool suppress_expire_warning = false); + + int mLoaded; + int mRejected; + +private: + std::vector> mCerts; +}; + +// load certificates from the system store +// by default suppress warnings about expired certificates since at least on Windows +// and Mac the store contains usually also several expired certificates +class LLSystemCertificateVector : public LLBasicCertificateVector +{ +public: + LLSystemCertificateVector(const std::string& store = "", bool suppress_expire_warning = true); + + virtual ~LLSystemCertificateVector() {} + +protected: + bool load_from_system(const std::string& store, bool suppress_expire_warning = true); }; // class LLCertificateStore // represents a store of certificates, typically a store of root CA // certificates. The store can be persisted, and can be used to validate // a cert chain -// class LLBasicCertificateStore : virtual public LLBasicCertificateVector, public LLCertificateStore { public: - LLBasicCertificateStore(const std::string& filename); - void load_from_file(const std::string& filename); + // Load the certificate bundle from the file + LLBasicCertificateStore(const std::string& filename) : + LLBasicCertificateVector(filename), + mFilename(filename) {} - virtual ~LLBasicCertificateStore(); + virtual ~LLBasicCertificateStore() {} // persist the store virtual void save(); @@ -183,12 +206,10 @@ class LLBasicCertificateStore : virtual public LLBasicCertificateVector, public LLPointer ca_chain, const LLSD& validation_params); - // Clears cache of certs validated agains store + // Clears cache of certs validated against store virtual void clearSertCache() { mTrustedCertCache.clear(); } -protected: - std::vector > mCerts; - +private: // cache of cert sha1 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. @@ -211,8 +232,6 @@ class LLBasicCertificateChain : virtual public LLBasicCertificateVector, public }; - - // LLSecAPIBasicCredential class class LLSecAPIBasicCredential : public LLCredential { diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index f4ee15319e..9658713e39 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -128,7 +128,7 @@ S32 LLMachineID::getLegacyID(unsigned char *unique_id, size_t len) S32 LLMachineID::init() { return 1; } -LLCertException::LLCertException(const LLSD& cert_data, const std::string& msg) +LLCertException::LLCertException(const LLSD& cert_data, const std::string& msg, bool suppress_warning) : LLException(msg), mCertData(cert_data) {