From 1a9d19d95527d717b89d4ebf45af81824fcbdf44 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Fri, 3 Jul 2009 01:05:27 +0000 Subject: Initial secapi merge svn merge -c112450 svn+ssh://svn.lindenlab.com/svn/linden/branches/giab-viewer/giab-viewer-1 giab-viewer-1-23 svn merge -c112913 svn+ssh://svn.lindenlab.com/svn/linden/branches/giab-viewer/giab-viewer-1 giab-viewer-1-23 --- indra/newview/llsechandler_basic.cpp | 490 +++++++++++++++++++++++++++++++++++ 1 file changed, 490 insertions(+) create mode 100644 indra/newview/llsechandler_basic.cpp (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp new file mode 100644 index 0000000000..ab6bf034f1 --- /dev/null +++ b/indra/newview/llsechandler_basic.cpp @@ -0,0 +1,490 @@ +/** + * @file llsechandler_basic.cpp + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2000, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llsecapi.h" +#include "llsechandler_basic.h" +#include "llsdserialize.h" +#include "llfile.h" +#include "lldir.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 128 bits of salt data... +#define STORE_SALT_SIZE 16 +#define BUFFER_READ_SIZE 256 + + +LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) +{ + + // 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()); + + mCert = NULL; + PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); + BIO_free(pem_bio); + if (!mCert) + { + throw LLCertException("Error parsing certificate"); + } +} + + +LLBasicCertificate::LLBasicCertificate(X509* pCert) +{ + if (!pCert || !pCert->cert_info) + { + throw LLCertException("Invalid certificate"); + } + mCert = X509_dup(pCert); +} + +LLBasicCertificate::~LLBasicCertificate() +{ + + X509_free(mCert); +} + +// +// retrieve the pem using the openssl functionality +std::string LLBasicCertificate::getPem() +{ + char * pem_bio_chars = NULL; + // a BIO is the equivalent of a 'std::stream', and + // can be a file, mem stream, whatever. Grab a memory based + // BIO for the result + BIO *pem_bio = BIO_new(BIO_s_mem()); + if (!pem_bio) + { + throw LLCertException("couldn't allocate memory buffer"); + } + PEM_write_bio_X509(pem_bio, mCert); + int length = BIO_get_mem_data(pem_bio, &pem_bio_chars); + std::string result = std::string(pem_bio_chars, length); + BIO_free(pem_bio); + return result; +} + +// get the DER encoding for the cert +// DER is a binary encoding format for certs... +std::vector LLBasicCertificate::getBinary() +{ + U8 * der_bio_data = NULL; + // get a memory bio + BIO *der_bio = BIO_new(BIO_s_mem()); + if (!der_bio) + { + throw LLCertException("couldn't allocate memory buffer"); + } + i2d_X509_bio(der_bio, mCert); + int length = BIO_get_mem_data(der_bio, &der_bio_data); + std::vector result(length); + // vectors are guranteed to be a contiguous chunk of memory. + memcpy(&result[0], der_bio_data, length); + BIO_free(der_bio); + return result; +} + + +LLSD LLBasicCertificate::getLLSD() +{ + LLSD result; + + // call the various helpers to build the LLSD + result[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); + result[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); + result[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); + result[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert)); + ASN1_INTEGER *sn = X509_get_serialNumber(mCert); + if (sn != NULL) + { + result[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn); + } + + result[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); + result[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); + result[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); + result[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); + + + + return result; +} + +X509* LLBasicCertificate::getOpenSSLX509() +{ + return X509_dup(mCert); +} + +// generate a single string containing the subject or issuer +// name of the cert. +std::string cert_string_name_from_X509_NAME(X509_NAME* name) +{ + char * name_bio_chars = NULL; + // get a memory bio + BIO *name_bio = BIO_new(BIO_s_mem()); + // stream the name into the bio. The name will be in the 'short name' format + X509_NAME_print_ex(name_bio, name, 0, XN_FLAG_RFC2253); + int length = BIO_get_mem_data(name_bio, &name_bio_chars); + std::string result = std::string(name_bio_chars, length); + BIO_free(name_bio); + return result; +} + +// generate an LLSD from a certificate name (issuer or subject name). +// the name will be strings indexed by the 'long form' +LLSD cert_name_from_X509_NAME(X509_NAME* name) +{ + LLSD result = LLSD::emptyMap(); + int name_entries = X509_NAME_entry_count(name); + for (int entry_index=0; entry_index < name_entries; entry_index++) + { + char buffer[32]; + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, entry_index); + + std::string name_value = std::string((const char*)M_ASN1_STRING_data(X509_NAME_ENTRY_get_data(entry)), + M_ASN1_STRING_length(X509_NAME_ENTRY_get_data(entry))); + + ASN1_OBJECT* name_obj = X509_NAME_ENTRY_get_object(entry); + OBJ_obj2txt(buffer, sizeof(buffer), name_obj, 0); + std::string obj_buffer_str = std::string(buffer); + result[obj_buffer_str] = name_value; + } + + return result; +} + +// Generate a string from an ASN1 integer. ASN1 Integers are +// bignums, so they can be 'infinitely' long, therefore we +// cannot simply use a conversion to U64 or something. +// We retrieve as a readable string for UI + +std::string cert_string_from_asn1_integer(ASN1_INTEGER* value) +{ + BIGNUM *bn = ASN1_INTEGER_to_BN(value, NULL); + char * ascii_bn = BN_bn2hex(bn); + + BN_free(bn); + + std::string result(ascii_bn); + OPENSSL_free(ascii_bn); + return result; +} + +// retrieve a date structure from an ASN1 time, for +// validity checking. +LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) +{ + + struct tm timestruct = {0}; + int i = asn1_time->length; + + if (i < 10) + throw LLCertException("invalid certificate time value"); + + // convert the date from the ASN1 time (which is a string in ZULU time), to + // a timeval. + timestruct.tm_year = (asn1_time->data[0]-'0') * 10 + (asn1_time->data[1]-'0'); + + /* Deal with Year 2000 */ + if (timestruct.tm_year < 70) + timestruct.tm_year += 100; + + timestruct.tm_mon = (asn1_time->data[2]-'0') * 10 + (asn1_time->data[3]-'0') - 1; + timestruct.tm_mday = (asn1_time->data[4]-'0') * 10 + (asn1_time->data[5]-'0'); + timestruct.tm_hour = (asn1_time->data[6]-'0') * 10 + (asn1_time->data[7]-'0'); + timestruct.tm_min = (asn1_time->data[8]-'0') * 10 + (asn1_time->data[9]-'0'); + timestruct.tm_sec = (asn1_time->data[10]-'0') * 10 + (asn1_time->data[11]-'0'); + + return LLDate((F64)mktime(×truct)); +} + +// 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[1024]; + unsigned int len = 1024; + std::stringstream result; + const EVP_MD* digest = NULL; + OpenSSL_add_all_digests(); + // 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 + { + throw LLCertException("Invalid digest"); + } + + 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(); +} + + +// LLSecAPIBasicHandler Class +// Interface handler class for the various security storage handlers. + +// We read the file on construction, and write it on destruction. This +// means multiple processes cannot modify the datastore. +LLSecAPIBasicHandler::LLSecAPIBasicHandler(const std::string& protected_data_file) +{ + mProtectedDataFilename = protected_data_file; + mProtectedDataMap = LLSD::emptyMap(); + _readProtectedData(); +} + +LLSecAPIBasicHandler::LLSecAPIBasicHandler() +{ + std::string mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "bin_conf.dat"); + + mProtectedDataMap = LLSD::emptyMap(); + _readProtectedData(); +} + +LLSecAPIBasicHandler::~LLSecAPIBasicHandler() +{ + _writeProtectedData(); +} + +void LLSecAPIBasicHandler::_readProtectedData() +{ + // attempt to load the file into our map + LLPointer parser = new LLSDXMLParser(); + + llifstream protected_data_stream(mProtectedDataFilename.c_str(), + llifstream::binary); + if (!protected_data_stream.fail()) { + int offset; + U8 salt[STORE_SALT_SIZE]; + U8 buffer[BUFFER_READ_SIZE]; + U8 decrypted_buffer[BUFFER_READ_SIZE]; + int decrypted_length; + + + // read in the salt and key + protected_data_stream.read((char *)salt, STORE_SALT_SIZE); + offset = 0; + if (protected_data_stream.gcount() < STORE_SALT_SIZE) + { + throw LLProtectedDataException("Corrupt Protected Data Store"); + } + + // totally lame. As we're not using the OS level protected data, we need to + // at least obfuscate the data. We do this by using a salt stored at the head of the file + // to encrypt the data, therefore obfuscating it from someone using simple existing tools. + // It would be better to use the password, but as this store + // will be used to store the SL password when the user decides to have SL remember it, + // so we can't use that. OS-dependent store implementations will use the OS password/storage + // mechanisms and are considered to be more secure. + // We've a strong intent to move to OS dependent protected data stores. + + + // read in the rest of the file. + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_DecryptInit(&ctx, EVP_rc4(), salt, NULL); + // allocate memory: + std::string decrypted_data; + + while(protected_data_stream.good()) { + // read data as a block: + protected_data_stream.read((char *)buffer, BUFFER_READ_SIZE); + + EVP_DecryptUpdate(&ctx, decrypted_buffer, &decrypted_length, + buffer, protected_data_stream.gcount()); + decrypted_data.append((const char *)decrypted_buffer, protected_data_stream.gcount()); + } + + // RC4 is a stream cipher, so we don't bother to EVP_DecryptFinal, as there is + // no block padding. + EVP_CIPHER_CTX_cleanup(&ctx); + std::istringstream parse_stream(decrypted_data); + if (parser->parse(parse_stream, mProtectedDataMap, + LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) + { + throw LLProtectedDataException("Corrupt Protected Data Store"); + } + } +} + +void LLSecAPIBasicHandler::_writeProtectedData() +{ + std::ostringstream formatted_data_ostream; + U8 salt[STORE_SALT_SIZE]; + U8 buffer[BUFFER_READ_SIZE]; + U8 encrypted_buffer[BUFFER_READ_SIZE]; + + if(mProtectedDataMap.isUndefined()) + { + LLFile::remove(mProtectedDataFilename); + return; + } + // create a string with the formatted data. + LLSDSerialize::toXML(mProtectedDataMap, formatted_data_ostream); + + std::istringstream formatted_data_istream(formatted_data_ostream.str()); + // generate the seed + RAND_bytes(salt, STORE_SALT_SIZE); + + // write to a temp file so we don't clobber the initial file if there is + // an error. + std::string tmp_filename = mProtectedDataFilename + ".tmp"; + + llofstream protected_data_stream(tmp_filename.c_str(), + llofstream::binary); + try + { + + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_EncryptInit(&ctx, EVP_rc4(), salt, NULL); + protected_data_stream.write((const char *)salt, STORE_SALT_SIZE); + while (formatted_data_istream.good()) + { + formatted_data_istream.read((char *)buffer, BUFFER_READ_SIZE); + if(formatted_data_istream.gcount() == 0) + { + break; + } + int encrypted_length; + EVP_EncryptUpdate(&ctx, encrypted_buffer, &encrypted_length, + buffer, formatted_data_istream.gcount()); + protected_data_stream.write((const char *)encrypted_buffer, encrypted_length); + } + + // no EVP_EncrypteFinal, as this is a stream cipher + EVP_CIPHER_CTX_cleanup(&ctx); + + protected_data_stream.close(); + } + catch (...) + { + // it's good practice to clean up any secure information on error + // (even though this file isn't really secure. Perhaps in the future + // it may be, however. + LLFile::remove(tmp_filename); + throw LLProtectedDataException("Error writing Protected Data Store"); + } + + // move the temporary file to the specified file location. + if((LLFile::remove(mProtectedDataFilename) != 0) || + (LLFile::rename(tmp_filename, mProtectedDataFilename))) + { + LLFile::remove(tmp_filename); + throw LLProtectedDataException("Could not overwrite protected data store"); + } +} + +// instantiate a certificate from a pem string +LLPointer LLSecAPIBasicHandler::getCertificate(const std::string& pem_cert) +{ + LLPointer result = new LLBasicCertificate(pem_cert); + return result; +} + + + +// instiate a certificate from an openssl X509 structure +LLPointer LLSecAPIBasicHandler::getCertificate(X509* openssl_cert) +{ + LLPointer result = new LLBasicCertificate(openssl_cert); + return result; +} + +// instantiate a chain from an X509_STORE_CTX +LLPointer LLSecAPIBasicHandler::getCertificateChain(const X509_STORE_CTX* chain) +{ + LLPointer result = NULL; + return result; +} + +// instantiate a cert store given it's id. if a persisted version +// exists, it'll be loaded. If not, one will be created (but not +// persisted) +LLPointer LLSecAPIBasicHandler::getCertificateStore(const std::string& store_id) +{ + LLPointer result; + return result; +} + +// retrieve protected data +LLSD LLSecAPIBasicHandler::getProtectedData(const std::string& data_type, + const std::string& data_id) +{ + if (mProtectedDataMap.has(data_type) && + mProtectedDataMap[data_type].isMap() && + mProtectedDataMap[data_type].has(data_id)) + { + return mProtectedDataMap[data_type][data_id]; + } + + return LLSD(); +} + +// persist data in a protected store +void LLSecAPIBasicHandler::setProtectedData(const std::string& data_type, + const std::string& data_id, + const LLSD& data) +{ + if (!mProtectedDataMap.has(data_type) || !mProtectedDataMap[data_type].isMap()) { + mProtectedDataMap[data_type] = LLSD::emptyMap(); + } + + mProtectedDataMap[data_type][data_id] = data; +} \ No newline at end of file -- cgit v1.2.3 From fe71dd340ab396b93bde45df438041af5d85fd47 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 1 Feb 2010 15:10:19 -0800 Subject: Merge giab-viewer-trunk 2497, general merge of more secapi stuff as well as certificate handling stuff. Grid manager as well --- indra/newview/llsechandler_basic.cpp | 284 +++++++++++++++++++++++++++++++++-- 1 file changed, 269 insertions(+), 15 deletions(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index ab6bf034f1..4180f578b9 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -35,8 +35,11 @@ #include "llsecapi.h" #include "llsechandler_basic.h" #include "llsdserialize.h" +#include "llviewernetwork.h" +#include "llxorcipher.h" #include "llfile.h" #include "lldir.h" +#include "llviewercontrol.h" #include #include #include @@ -276,22 +279,94 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert) } +// +// LLBasicCertificateStore +// +LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) +{ +} +LLBasicCertificateStore::LLBasicCertificateStore(const X509_STORE* store) +{ +} + +LLBasicCertificateStore::~LLBasicCertificateStore() +{ +} + + +X509_STORE* LLBasicCertificateStore::getOpenSSLX509Store() +{ + return NULL; +} + + // add a copy of a cert to the store +void LLBasicCertificateStore::append(const LLCertificate& cert) +{ +} + + // add a copy of a cert to the store +void LLBasicCertificateStore::insert(const int index, const LLCertificate& cert) +{ +} + + // remove a certificate from the store +void LLBasicCertificateStore::remove(int index) +{ +} + + // return a certificate at the index +LLPointer LLBasicCertificateStore::operator[](int index) +{ + LLPointer result = NULL; + return result; +} + // return the number of certs in the store +int LLBasicCertificateStore::len() const +{ + return 0; +} + + // load the store from a persisted location +void LLBasicCertificateStore::load(const std::string& store_id) +{ +} + + // persist the store +void LLBasicCertificateStore::save() +{ +} + + // return the store id +std::string LLBasicCertificateStore::storeId() +{ + return std::string(""); +} + + // validate a cert chain +bool LLBasicCertificateStore::validate(const LLCertificateChain& cert_chain) const +{ + return FALSE; +} + // LLSecAPIBasicHandler Class // Interface handler class for the various security storage handlers. // We read the file on construction, and write it on destruction. This // means multiple processes cannot modify the datastore. -LLSecAPIBasicHandler::LLSecAPIBasicHandler(const std::string& protected_data_file) +LLSecAPIBasicHandler::LLSecAPIBasicHandler(const std::string& protected_data_file, + const std::string& legacy_password_path) { mProtectedDataFilename = protected_data_file; mProtectedDataMap = LLSD::emptyMap(); + mLegacyPasswordPath = legacy_password_path; _readProtectedData(); } LLSecAPIBasicHandler::LLSecAPIBasicHandler() { - std::string 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"); mProtectedDataMap = LLSD::emptyMap(); _readProtectedData(); @@ -306,7 +381,6 @@ void LLSecAPIBasicHandler::_readProtectedData() { // attempt to load the file into our map LLPointer parser = new LLSDXMLParser(); - llifstream protected_data_stream(mProtectedDataFilename.c_str(), llifstream::binary); if (!protected_data_stream.fail()) { @@ -314,21 +388,27 @@ void LLSecAPIBasicHandler::_readProtectedData() U8 salt[STORE_SALT_SIZE]; U8 buffer[BUFFER_READ_SIZE]; U8 decrypted_buffer[BUFFER_READ_SIZE]; - int decrypted_length; - + int decrypted_length; + unsigned char MACAddress[MAC_ADDRESS_BYTES]; + LLUUID::getNodeID(MACAddress); + LLXORCipher cipher(MACAddress, MAC_ADDRESS_BYTES); // read in the salt and key protected_data_stream.read((char *)salt, STORE_SALT_SIZE); offset = 0; if (protected_data_stream.gcount() < STORE_SALT_SIZE) { - throw LLProtectedDataException("Corrupt Protected Data Store"); + throw LLProtectedDataException("Corrupt Protected Data Store1"); } - + + cipher.decrypt(salt, STORE_SALT_SIZE); + // totally lame. As we're not using the OS level protected data, we need to // at least obfuscate the data. We do this by using a salt stored at the head of the file // to encrypt the data, therefore obfuscating it from someone using simple existing tools. - // It would be better to use the password, but as this store + // We do include the MAC address as part of the obfuscation, which would require an + // attacker to get the MAC address as well as the protected store, which improves things + // somewhat. It would be better to use the password, but as this store // will be used to store the SL password when the user decides to have SL remember it, // so we can't use that. OS-dependent store implementations will use the OS password/storage // mechanisms and are considered to be more secure. @@ -369,6 +449,7 @@ void LLSecAPIBasicHandler::_writeProtectedData() U8 salt[STORE_SALT_SIZE]; U8 buffer[BUFFER_READ_SIZE]; U8 encrypted_buffer[BUFFER_READ_SIZE]; + if(mProtectedDataMap.isUndefined()) { @@ -377,10 +458,10 @@ void LLSecAPIBasicHandler::_writeProtectedData() } // create a string with the formatted data. LLSDSerialize::toXML(mProtectedDataMap, formatted_data_ostream); - std::istringstream formatted_data_istream(formatted_data_ostream.str()); // generate the seed RAND_bytes(salt, STORE_SALT_SIZE); + // write to a temp file so we don't clobber the initial file if there is // an error. @@ -394,7 +475,12 @@ void LLSecAPIBasicHandler::_writeProtectedData() EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit(&ctx, EVP_rc4(), salt, NULL); - protected_data_stream.write((const char *)salt, STORE_SALT_SIZE); + unsigned char MACAddress[MAC_ADDRESS_BYTES]; + LLUUID::getNodeID(MACAddress); + LLXORCipher cipher(MACAddress, MAC_ADDRESS_BYTES); + cipher.encrypt(salt, STORE_SALT_SIZE); + protected_data_stream.write((const char *)salt, STORE_SALT_SIZE); + while (formatted_data_istream.good()) { formatted_data_istream.read((char *)buffer, BUFFER_READ_SIZE); @@ -423,7 +509,8 @@ void LLSecAPIBasicHandler::_writeProtectedData() } // move the temporary file to the specified file location. - if((LLFile::remove(mProtectedDataFilename) != 0) || + if((((LLFile::isfile(mProtectedDataFilename) != 0) && + (LLFile::remove(mProtectedDataFilename) != 0))) || (LLFile::rename(tmp_filename, mProtectedDataFilename))) { LLFile::remove(tmp_filename); @@ -477,14 +564,181 @@ LLSD LLSecAPIBasicHandler::getProtectedData(const std::string& data_type, return LLSD(); } +void LLSecAPIBasicHandler::deleteProtectedData(const std::string& data_type, + const std::string& data_id) +{ + if (mProtectedDataMap.has(data_type) && + mProtectedDataMap[data_type].isMap() && + mProtectedDataMap[data_type].has(data_id)) + { + mProtectedDataMap[data_type].erase(data_id); + } +} + + +// // persist data in a protected store +// void LLSecAPIBasicHandler::setProtectedData(const std::string& data_type, - const std::string& data_id, - const LLSD& data) + const std::string& data_id, + const LLSD& data) { if (!mProtectedDataMap.has(data_type) || !mProtectedDataMap[data_type].isMap()) { mProtectedDataMap[data_type] = LLSD::emptyMap(); } mProtectedDataMap[data_type][data_id] = data; -} \ No newline at end of file +} + +// +// Create a credential object from an identifier and authenticator. credentials are +// per grid. +LLPointer LLSecAPIBasicHandler::createCredential(const std::string& grid, + const LLSD& identifier, + const LLSD& authenticator) +{ + LLPointer result = new LLSecAPIBasicCredential(grid); + result->setCredentialData(identifier, authenticator); + return result; +} + +// Load a credential from the credential store, given the grid +LLPointer LLSecAPIBasicHandler::loadCredential(const std::string& grid) +{ + LLSD credential = getProtectedData("credential", grid); + LLPointer result = new LLSecAPIBasicCredential(grid); + if(credential.isMap() && + credential.has("identifier")) + { + + LLSD identifier = credential["identifier"]; + LLSD authenticator; + if (credential.has("authenticator")) + { + authenticator = credential["authenticator"]; + } + result->setCredentialData(identifier, authenticator); + } + else + { + // credential was not in protected storage, so pull the credential + // from the legacy store. + std::string first_name = gSavedSettings.getString("FirstName"); + std::string last_name = gSavedSettings.getString("LastName"); + + if ((first_name != "") && + (last_name != "")) + { + LLSD identifier = LLSD::emptyMap(); + LLSD authenticator; + identifier["type"] = "agent"; + identifier["first_name"] = first_name; + identifier["last_name"] = last_name; + + std::string legacy_password = _legacyLoadPassword(); + if (legacy_password.length() > 0) + { + authenticator = LLSD::emptyMap(); + authenticator["type"] = "hash"; + authenticator["algorithm"] = "md5"; + authenticator["secret"] = legacy_password; + } + result->setCredentialData(identifier, authenticator); + } + } + return result; +} + +// Save the credential to the credential store. Save the authenticator also if requested. +// That feature is used to implement the 'remember password' functionality. +void LLSecAPIBasicHandler::saveCredential(LLPointer cred, bool save_authenticator) +{ + LLSD credential = LLSD::emptyMap(); + credential["identifier"] = cred->getIdentifier(); + if (save_authenticator) + { + credential["authenticator"] = cred->getAuthenticator(); + } + LL_INFOS("Credentials") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; + setProtectedData("credential", cred->getGrid(), credential); + //*TODO: If we're saving Agni credentials, should we write the + // credentials to the legacy password.dat/etc? +} + +// Remove a credential from the credential store. +void LLSecAPIBasicHandler::deleteCredential(LLPointer cred) +{ + LLSD undefVal; + deleteProtectedData("credential", cred->getGrid()); + cred->setCredentialData(undefVal, undefVal); +} + +// load the legacy hash for agni, and decrypt it given the +// mac address +std::string LLSecAPIBasicHandler::_legacyLoadPassword() +{ + const S32 HASHED_LENGTH = 32; + std::vector buffer(HASHED_LENGTH); + llifstream password_file(mLegacyPasswordPath, llifstream::binary); + + if(password_file.fail()) + { + return std::string(""); + } + + password_file.read((char*)&buffer[0], buffer.size()); + if(password_file.gcount() != buffer.size()) + { + return std::string(""); + } + + // Decipher with MAC address + unsigned char MACAddress[MAC_ADDRESS_BYTES]; + LLUUID::getNodeID(MACAddress); + LLXORCipher cipher(MACAddress, 6); + cipher.decrypt(&buffer[0], buffer.size()); + + return std::string((const char*)&buffer[0], buffer.size()); +} + + +// return an identifier for the user +std::string LLSecAPIBasicCredential::userID() const +{ + if (!mIdentifier.isMap()) + { + return mGrid + "(null)"; + } + else if ((std::string)mIdentifier["type"] == "agent") + { + return (std::string)mIdentifier["first_name"] + "_" + (std::string)mIdentifier["last_name"]; + } + else if ((std::string)mIdentifier["type"] == "account") + { + return (std::string)mIdentifier["account_name"]; + } + + return "unknown"; + +} + +// return a printable user identifier +std::string LLSecAPIBasicCredential::asString() const +{ + if (!mIdentifier.isMap()) + { + return mGrid + ":(null)"; + } + else if ((std::string)mIdentifier["type"] == "agent") + { + return mGrid + ":" + (std::string)mIdentifier["first_name"] + " " + (std::string)mIdentifier["last_name"]; + } + else if ((std::string)mIdentifier["type"] == "account") + { + return mGrid + ":" + (std::string)mIdentifier["account_name"]; + } + + return mGrid + ":(unknown type)"; +} + + -- cgit v1.2.3 From 9e89819d55a3b6ee7fc56f3efb36f273e4e05c83 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 8 Jul 2009 00:45:17 +0000 Subject: DEV-34822 - merge with 1.23 certificate notification code -r 118191 ignore-dead-branch --- indra/newview/llsechandler_basic.cpp | 913 ++++++++++++++++++++++++++++++++--- 1 file changed, 837 insertions(+), 76 deletions(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 4180f578b9..097cc395d7 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -20,7 +20,7 @@ * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlife.com/developers/opensource/flossexception * - * By copying, modifying or distributing this software, you acknowledge +LLS * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * @@ -42,17 +42,26 @@ #include "llviewercontrol.h" #include #include +#include #include +#include #include #include #include +#include #include #include #include + + // 128 bits of salt data... #define STORE_SALT_SIZE 16 #define BUFFER_READ_SIZE 256 +std::string cert_string_from_asn1_string(ASN1_STRING* value); +LLSD _basic_constraints_ext(X509* cert); +LLSD _key_usage_ext(X509* cert); +LLSD _ext_key_usage_ext(X509* cert); LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) @@ -61,14 +70,19 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) // 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; + throw LLInvalidCertificate(this); + } mCert = NULL; PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); BIO_free(pem_bio); if (!mCert) { - throw LLCertException("Error parsing certificate"); + throw LLInvalidCertificate(this); } + _initLLSD(); } @@ -76,20 +90,23 @@ LLBasicCertificate::LLBasicCertificate(X509* pCert) { if (!pCert || !pCert->cert_info) { - throw LLCertException("Invalid certificate"); + throw LLInvalidCertificate(this); } mCert = X509_dup(pCert); + _initLLSD(); } LLBasicCertificate::~LLBasicCertificate() { - - X509_free(mCert); + if(mCert) + { + X509_free(mCert); + } } // // retrieve the pem using the openssl functionality -std::string LLBasicCertificate::getPem() +std::string LLBasicCertificate::getPem() const { char * pem_bio_chars = NULL; // a BIO is the equivalent of a 'std::stream', and @@ -98,7 +115,8 @@ std::string LLBasicCertificate::getPem() BIO *pem_bio = BIO_new(BIO_s_mem()); if (!pem_bio) { - throw LLCertException("couldn't allocate memory buffer"); + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + return std::string(); } PEM_write_bio_X509(pem_bio, mCert); int length = BIO_get_mem_data(pem_bio, &pem_bio_chars); @@ -109,14 +127,15 @@ std::string LLBasicCertificate::getPem() // get the DER encoding for the cert // DER is a binary encoding format for certs... -std::vector LLBasicCertificate::getBinary() +std::vector LLBasicCertificate::getBinary() const { U8 * der_bio_data = NULL; // get a memory bio BIO *der_bio = BIO_new(BIO_s_mem()); if (!der_bio) { - throw LLCertException("couldn't allocate memory buffer"); + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + return std::vector(); } i2d_X509_bio(der_bio, mCert); int length = BIO_get_mem_data(der_bio, &der_bio_data); @@ -128,32 +147,131 @@ std::vector LLBasicCertificate::getBinary() } -LLSD LLBasicCertificate::getLLSD() +LLSD LLBasicCertificate::getLLSD() const +{ + return mLLSDInfo; +} + +// Initialize the LLSD info for the certificate +LLSD& LLBasicCertificate::_initLLSD() { - LLSD result; // call the various helpers to build the LLSD - result[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); - result[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); - result[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); - result[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert)); + mLLSDInfo[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); + mLLSDInfo[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); + mLLSDInfo[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); + mLLSDInfo[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert)); ASN1_INTEGER *sn = X509_get_serialNumber(mCert); if (sn != NULL) { - result[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn); + mLLSDInfo[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn); } - result[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); - result[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); - result[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); - result[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); + 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); + return mLLSDInfo; +} + +// Retrieve the basic constraints info +LLSD _basic_constraints_ext(X509* cert) +{ + LLSD result; + BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL); + if(bs) + { + result = LLSD::emptyMap(); + // Determines whether the cert can be used as a CA + result[CERT_BASIC_CONSTRAINTS_CA] = (bool)bs->ca; + + if(bs->pathlen) + { + // the pathlen determines how deep a certificate chain can be from + // this CA + if((bs->pathlen->type == V_ASN1_NEG_INTEGER) + || !bs->ca) + { + result[CERT_BASIC_CONSTRAINTS_PATHLEN] = 0; + } + else + { + result[CERT_BASIC_CONSTRAINTS_PATHLEN] = (int)ASN1_INTEGER_get(bs->pathlen); + } + } + } + return result; +} +// retrieve the key usage, which specifies how the cert can be used. +// +LLSD _key_usage_ext(X509* cert) +{ + LLSD result; + ASN1_STRING *usage_str = (ASN1_STRING *)X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL); + if(usage_str) + { + result = LLSD::emptyArray(); + long usage = 0; + if(usage_str->length > 0) + { + usage = usage_str->data[0]; + if(usage_str->length > 1) + { + usage |= usage_str->data[1] << 8; + } + } + ASN1_STRING_free(usage_str); + if(usage) + { + if(usage & KU_DIGITAL_SIGNATURE) result.append(LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE)); + if(usage & KU_NON_REPUDIATION) result.append(LLSD((std::string)CERT_KU_NON_REPUDIATION)); + if(usage & KU_KEY_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT)); + if(usage & KU_DATA_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_DATA_ENCIPHERMENT)); + if(usage & KU_KEY_AGREEMENT) result.append(LLSD((std::string)CERT_KU_KEY_AGREEMENT)); + if(usage & KU_KEY_CERT_SIGN) result.append(LLSD((std::string)CERT_KU_CERT_SIGN)); + if(usage & KU_CRL_SIGN) result.append(LLSD((std::string)CERT_KU_CRL_SIGN)); + if(usage & KU_ENCIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_ENCIPHER_ONLY)); + if(usage & KU_DECIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_DECIPHER_ONLY)); + } + } + return result; +} - return result; +// retrieve the extended key usage for the cert +LLSD _ext_key_usage_ext(X509* cert) +{ + LLSD result; + EXTENDED_KEY_USAGE *eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL); + if(eku) + { + result = LLSD::emptyArray(); + while(sk_ASN1_OBJECT_num(eku)) + { + ASN1_OBJECT *usage = sk_ASN1_OBJECT_pop(eku); + if(usage) + { + int nid = OBJ_obj2nid(usage); + if (nid) + { + std::string sn = OBJ_nid2sn(nid); + result.append(sn); + } + ASN1_OBJECT_free(usage); + } + } + } + return result; } -X509* LLBasicCertificate::getOpenSSLX509() +// retrieve an openssl x509 object, +// which must be freed by X509_free +X509* LLBasicCertificate::getOpenSSLX509() const { return X509_dup(mCert); } @@ -203,13 +321,46 @@ LLSD cert_name_from_X509_NAME(X509_NAME* name) std::string cert_string_from_asn1_integer(ASN1_INTEGER* value) { + std::string result; BIGNUM *bn = ASN1_INTEGER_to_BN(value, NULL); - char * ascii_bn = BN_bn2hex(bn); + if(bn) + { + char * ascii_bn = BN_bn2hex(bn); - BN_free(bn); + if(ascii_bn) + { + result = ascii_bn; + OPENSSL_free(ascii_bn); + } + BN_free(bn); + } + return result; +} + +// Generate a string from an ASN1 integer. ASN1 Integers are +// bignums, so they can be 'infinitely' long, therefore we +// cannot simply use a conversion to U64 or something. +// We retrieve as a readable string for UI - std::string result(ascii_bn); - OPENSSL_free(ascii_bn); +std::string cert_string_from_asn1_string(ASN1_STRING* value) +{ + char * string_bio_chars = NULL; + std::string result; + // get a memory bio + BIO *string_bio = BIO_new(BIO_s_mem()); + if(!string_bio) + { + // stream the name into the bio. The name will be in the 'short name' format + ASN1_STRING_print_ex(string_bio, value, ASN1_STRFLGS_RFC2253); + int length = BIO_get_mem_data(string_bio, &string_bio_chars); + result = std::string(string_bio_chars, length); + BIO_free(string_bio); + } + else + { + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + } + return result; } @@ -222,8 +373,9 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) int i = asn1_time->length; if (i < 10) - throw LLCertException("invalid certificate time value"); - + { + return LLDate(); + } // convert the date from the ASN1 time (which is a string in ZULU time), to // a timeval. timestruct.tm_year = (asn1_time->data[0]-'0') * 10 + (asn1_time->data[1]-'0'); @@ -237,19 +389,23 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) timestruct.tm_hour = (asn1_time->data[6]-'0') * 10 + (asn1_time->data[7]-'0'); timestruct.tm_min = (asn1_time->data[8]-'0') * 10 + (asn1_time->data[9]-'0'); timestruct.tm_sec = (asn1_time->data[10]-'0') * 10 + (asn1_time->data[11]-'0'); - - return LLDate((F64)mktime(×truct)); + +#if LL_WINDOWS + return LLDate((F64)_mkgmtime(×truct)); +#else // LL_WINDOWS + return LLDate((F64)timegm(×truct)); +#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[1024]; - unsigned int len = 1024; + unsigned char digest_data[BUFFER_READ_SIZE]; + unsigned int len = sizeof(digest_data); std::stringstream result; const EVP_MD* digest = NULL; - OpenSSL_add_all_digests(); // 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") @@ -262,7 +418,7 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert) } else { - throw LLCertException("Invalid digest"); + return std::string(); } X509_digest(cert, digest, digest_data, &len); @@ -279,75 +435,588 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert) } +// 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. +// + +// 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) +{ + 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++) + { + + found= TRUE; + LLSD cert_info = (*cert)->getLLSD(); + for (LLSD::map_const_iterator param = params.beginMap(); + param != params.endMap(); + param++) + { + + if (!cert_info.has((std::string)param->first) || + (!valueCompareLLSD(cert_info[(std::string)param->first], param->second))) + { + found = FALSE; + break; + } + } + if (found) + { + return (cert); + } + } + return end(); +} + +// Insert a certificate into the store. If the certificate already +// exists in the store, nothing is done. +void LLBasicCertificateVector::insert(iterator _iter, + LLPointer cert) +{ + LLSD cert_info = cert->getLLSD(); + if (cert_info.isMap() && cert_info.has(CERT_SHA1_DIGEST)) + { + LLSD existing_cert_info = LLSD::emptyMap(); + existing_cert_info[CERT_MD5_DIGEST] = cert_info[CERT_MD5_DIGEST]; + if(find(existing_cert_info) == end()) + { + BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); + mCerts.insert(basic_iter->mIter, cert); + } + } +} + +// remove a certificate from the store +LLPointer LLBasicCertificateVector::erase(iterator _iter) +{ + + if (_iter != end()) + { + BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); + LLPointer result = (*_iter); + mCerts.erase(basic_iter->mIter); + return result; + } + return NULL; +} + + // // 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. LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) { + mFilename = filename; + load_from_file(filename); } -LLBasicCertificateStore::LLBasicCertificateStore(const X509_STORE* store) + +void LLBasicCertificateStore::load_from_file(const std::string& filename) { + // scan the PEM file extracting each certificate + 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 + { + LL_WARNS("SECAPI") << "Could not allocate a file BIO" << LL_ENDL; + } } + LLBasicCertificateStore::~LLBasicCertificateStore() { } - -X509_STORE* LLBasicCertificateStore::getOpenSSLX509Store() + +// persist the store +void LLBasicCertificateStore::save() { - return NULL; + llofstream file_store(mFilename, llofstream::binary); + if(!file_store.fail()) + { + for(iterator cert = begin(); + cert != end(); + cert++) + { + std::string pem = (*cert)->getPem(); + if(!pem.empty()) + { + file_store << (*cert)->getPem() << std::endl; + } + } + file_store.close(); + } + else + { + LL_WARNS("SECAPI") << "Could not open certificate store " << mFilename << "for save" << LL_ENDL; + } } - - // add a copy of a cert to the store -void LLBasicCertificateStore::append(const LLCertificate& cert) + +// return the store id +std::string LLBasicCertificateStore::storeId() const { + // this is the basic handler which uses the CA.pem store, + // so we ignore this. + return std::string(""); } - - // add a copy of a cert to the store -void LLBasicCertificateStore::insert(const int index, const LLCertificate& cert) + + +// +// LLBasicCertificateChain +// This class represents a chain of certs, each cert being signed by the next cert +// in the chain. Certs must be properly signed by the parent +LLBasicCertificateChain::LLBasicCertificateChain(const X509_STORE_CTX* store) { + + // we're passed in a context, which contains a cert, and a blob of untrusted + // certificates which compose the chain. + if((store == NULL) || (store->cert == NULL)) + { + LL_WARNS("SECAPI") << "An invalid store context was passed in when trying to create a certificate chain" << LL_ENDL; + return; + } + // grab the child cert + LLPointer current = new LLBasicCertificate(store->cert); + + add(current); + if(store->untrusted != NULL) + { + // if there are other certs in the chain, we build up a vector + // of untrusted certs so we can search for the parents of each + // consecutive cert. + LLBasicCertificateVector untrusted_certs; + for(int i = 0; i < sk_X509_num(store->untrusted); i++) + { + LLPointer cert = new LLBasicCertificate(sk_X509_value(store->untrusted, i)); + untrusted_certs.add(cert); + + } + while(untrusted_certs.size() > 0) + { + LLSD find_data = LLSD::emptyMap(); + LLSD cert_data = current->getLLSD(); + // we simply build the chain via subject/issuer name as the + // client should not have passed in multiple CA's with the same + // subject name. If they did, it'll come out in the wash during + // validation. + find_data[CERT_SUBJECT_NAME_STRING] = cert_data[CERT_ISSUER_NAME_STRING]; + LLBasicCertificateVector::iterator issuer = untrusted_certs.find(find_data); + if (issuer != untrusted_certs.end()) + { + current = untrusted_certs.erase(issuer); + add(current); + } + else + { + break; + } + } + } } - - // remove a certificate from the store -void LLBasicCertificateStore::remove(int index) + + +// subdomain wildcard specifiers can be divided into 3 parts +// the part before the first *, the part after the first * but before +// the second *, and the part after the second *. +// It then iterates over the second for each place in the string +// that it matches. ie if the subdomain was testfoofoobar, and +// the wildcard was test*foo*bar, it would match test, then +// recursively match foofoobar and foobar + +bool _cert_subdomain_wildcard_match(const std::string& subdomain, + const std::string& wildcard) { + // split wildcard into the portion before the *, and the portion after + + int wildcard_pos = wildcard.find_first_of('*'); + // check the case where there is no wildcard. + if(wildcard_pos == wildcard.npos) + { + return (subdomain == wildcard); + } + + // we need to match the first part of the subdomain string up to the wildcard + // position + if(subdomain.substr(0, wildcard_pos) != wildcard.substr(0, wildcard_pos)) + { + // the first portions of the strings didn't match + return FALSE; + } + + // as the portion of the wildcard string before the * matched, we need to check the + // portion afterwards. Grab that portion. + std::string new_wildcard_string = wildcard.substr( wildcard_pos+1, wildcard.npos); + if(new_wildcard_string.empty()) + { + // we had nothing after the *, so it's an automatic match + return TRUE; + } + + // grab the portion of the remaining wildcard string before the next '*'. We need to find this + // within the remaining subdomain string. and then recursively check. + std::string new_wildcard_match_string = new_wildcard_string.substr(0, new_wildcard_string.find_first_of('*')); + + // grab the portion of the subdomain after the part that matched the initial wildcard portion + std::string new_subdomain = subdomain.substr(wildcard_pos, subdomain.npos); + + // iterate through the current subdomain, finding instances of the match string. + int sub_pos = new_subdomain.find_first_of(new_wildcard_match_string); + while(sub_pos != std::string::npos) + { + new_subdomain = new_subdomain.substr(sub_pos, std::string::npos); + if(_cert_subdomain_wildcard_match(new_subdomain, new_wildcard_string)) + { + return TRUE; + } + sub_pos = new_subdomain.find_first_of(new_wildcard_match_string, 1); + + + } + // didn't find any instances of the match string that worked in the subdomain, so fail. + return FALSE; } - - // return a certificate at the index -LLPointer LLBasicCertificateStore::operator[](int index) + + +// RFC2459 does not address wildcards as part of it's name matching +// specification, and there is no RFC specifying wildcard matching, +// RFC2818 does a few statements about wildcard matching, but is very +// general. Generally, wildcard matching is per implementation, although +// it's pretty similar. +// in our case, we use the '*' wildcard character only, within each +// subdomain. The hostname and the CN specification should have the +// same number of subdomains. +// We then iterate that algorithm over each subdomain. +bool _cert_hostname_wildcard_match(const std::string& hostname, const std::string& common_name) { - LLPointer result = NULL; - return result; + std::string new_hostname = hostname; + std::string new_cn = common_name; + int subdomain_pos = new_hostname.find_first_of('.'); + int subcn_pos = new_cn.find_first_of('.'); + + while((subcn_pos != std::string::npos) && (subdomain_pos != std::string::npos)) + { + // snip out the first subdomain and cn element + + if(!_cert_subdomain_wildcard_match(new_hostname.substr(0, subdomain_pos), + new_cn.substr(0, subcn_pos))) + { + return FALSE; + } + new_hostname = new_hostname.substr(subdomain_pos+1, std::string::npos); + new_cn = new_cn.substr(subcn_pos+1, std::string::npos); + subdomain_pos = new_hostname.find_first_of('.'); + subcn_pos = new_cn.find_first_of('.'); + } + return _cert_subdomain_wildcard_match(new_hostname, new_cn); + } - // return the number of certs in the store -int LLBasicCertificateStore::len() const + +// validate that the LLSD array in llsd_set contains the llsd_value +bool _LLSDArrayIncludesValue(const LLSD& llsd_set, LLSD llsd_value) { - return 0; + for(LLSD::array_const_iterator set_value = llsd_set.beginArray(); + set_value != llsd_set.endArray(); + set_value++) + { + if(valueCompareLLSD((*set_value), llsd_value)) + { + return TRUE; + } + } + return FALSE; } - - // load the store from a persisted location -void LLBasicCertificateStore::load(const std::string& store_id) + +void _validateCert(int validation_policy, + const LLPointer cert, + const LLSD& validation_params, + int depth) { -} + + LLSD current_cert_info = cert->getLLSD(); + // check basic properties exist in the cert + if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING)) + { + throw LLCertException(cert, "Cert doesn't have a Subject Name"); + } + + if(!current_cert_info.has(CERT_ISSUER_NAME_STRING)) + { + throw LLCertException(cert, "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)) + { + throw LLCertException(cert, "Cert doesn't have an expiration period"); + } + if (!current_cert_info.has(CERT_SHA1_DIGEST)) + { + throw LLCertException(cert, "No SHA1 digest"); + } + + if (validation_policy & VALIDATION_POLICY_TIME) + { + + LLDate validation_date(time(NULL)); + if(validation_params.has(CERT_VALIDATION_DATE)) + { + validation_date = validation_params[CERT_VALIDATION_DATE]; + } - // persist the store -void LLBasicCertificateStore::save() -{ + if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) || + (validation_date > current_cert_info[CERT_VALID_TO].asDate())) + { + throw LLCertValidationExpirationException(cert, validation_date); + } + } + if (validation_policy & VALIDATION_POLICY_SSL_KU) + { + if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && + (!(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE))) || + !(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT))))) + { + throw LLCertKeyUsageValidationException(cert); + } + // 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)))) + { + throw LLCertKeyUsageValidationException(cert); + } + } + if (validation_policy & VALIDATION_POLICY_CA_KU) + { + if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && + (!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + (std::string)CERT_KU_CERT_SIGN))) + { + throw LLCertKeyUsageValidationException(cert); + } + } + + // validate basic constraints + if ((validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS) && + current_cert_info.has(CERT_BASIC_CONSTRAINTS) && + current_cert_info[CERT_BASIC_CONSTRAINTS].isMap()) + { + if(!current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_CA) || + !current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_CA]) + { + throw LLCertBasicConstraintsValidationException(cert); + } + 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()))) + { + throw LLCertBasicConstraintsValidationException(cert); + } + } } - - // return the store id -std::string LLBasicCertificateStore::storeId() + +bool _verify_signature(LLPointer parent, + LLPointer child) { - return std::string(""); -} + bool verify_result = FALSE; + LLSD cert1 = parent->getLLSD(); + LLSD cert2 = child->getLLSD(); + X509 *signing_cert = parent->getOpenSSLX509(); + X509 *child_cert = child->getOpenSSLX509(); + if((signing_cert != NULL) && (child_cert != NULL)) + { + EVP_PKEY *pkey = X509_get_pubkey(signing_cert); + - // validate a cert chain -bool LLBasicCertificateStore::validate(const LLCertificateChain& cert_chain) const + if(pkey) + { + int verify_code = X509_verify(child_cert, pkey); + verify_result = ( verify_code > 0); + EVP_PKEY_free(pkey); + } + else + { + LL_WARNS("SECAPI") << "Could not validate the cert chain signature, as the public key of the signing cert could not be retrieved" << LL_ENDL; + } + + } + else + { + LL_WARNS("SECAPI") << "Signature verification failed as there are no certs in the chain" << LL_ENDL; + } + if(child_cert) + { + X509_free(child_cert); + } + if(signing_cert) + { + X509_free(signing_cert); + } + return verify_result; +} + +// validate the certificate chain against a store. +// There are many aspects of cert validatioin policy involved in +// trust validation. The policies in this validation algorithm include +// * Hostname matching for SSL certs +// * Expiration time matching +// * Signature validation +// * Chain trust (is the cert chain trusted against the store) +// * Basic constraints +// * key usage and extended key usage +// TODO: We should add 'authority key identifier' for chaining. +// This algorithm doesn't simply validate the chain by itself +// and verify the last cert is in the certificate store, or points +// to a cert in the store. It validates whether any cert in the chain +// is trusted in the store, even if it's not the last one. +void LLBasicCertificateChain::validate(int validation_policy, + LLPointer ca_store, + const LLSD& validation_params) { - return FALSE; + + if(size() < 1) + { + throw LLCertException(NULL, "No certs in chain"); + } + iterator current_cert = begin(); + LLSD current_cert_info = (*current_cert)->getLLSD(); + LLSD validation_date; + if (validation_params.has(CERT_VALIDATION_DATE)) + { + validation_date = validation_params[CERT_VALIDATION_DATE]; + } + + if (validation_policy & VALIDATION_POLICY_HOSTNAME) + { + if(!validation_params.has(CERT_HOSTNAME)) + { + throw LLCertException((*current_cert), "No hostname passed in for validation"); + } + if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info[CERT_SUBJECT_NAME].has(CERT_NAME_CN)) + { + throw LLInvalidCertificate((*current_cert)); + } + + LL_INFOS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() << + "against the cert CN " << current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString() << LL_ENDL; + if(!_cert_hostname_wildcard_match(validation_params[CERT_HOSTNAME].asString(), + current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString())) + { + throw LLCertValidationHostnameException(validation_params[CERT_HOSTNAME].asString(), + (*current_cert)); + } + } + + + int depth = 0; + LLPointer previous_cert; + // loop through the cert chain, validating the current cert against the next one. + while(current_cert != end()) + { + + int local_validation_policy = validation_policy; + if(current_cert == begin()) + { + // for the child cert, we don't validate CA stuff + local_validation_policy &= ~(VALIDATION_POLICY_CA_KU | + VALIDATION_POLICY_CA_BASIC_CONSTRAINTS); + } + else + { + // for non-child certs, we don't validate SSL Key usage + local_validation_policy &= ~VALIDATION_POLICY_SSL_KU; + if(!_verify_signature((*current_cert), + previous_cert)) + { + throw LLCertValidationInvalidSignatureException(previous_cert); + } + } + _validateCert(local_validation_policy, + (*current_cert), + validation_params, + depth); + + // look for a CA in the CA store that may belong to this chain. + LLSD cert_llsd = (*current_cert)->getLLSD(); + LLSD cert_search_params = LLSD::emptyMap(); + // is the cert itself in the store? + cert_search_params[CERT_SHA1_DIGEST] = cert_llsd[CERT_SHA1_DIGEST]; + LLCertificateStore::iterator found_store_cert = ca_store->find(cert_search_params); + if(found_store_cert != ca_store->end()) + { + return; + } + + // is the parent in the cert store? + + cert_search_params = LLSD::emptyMap(); + cert_search_params[CERT_SUBJECT_NAME_STRING] = cert_llsd[CERT_ISSUER_NAME_STRING]; + found_store_cert = ca_store->find(cert_search_params); + + if(found_store_cert != ca_store->end()) + { + LLSD foo = (*found_store_cert)->getLLSD(); + // validate the store cert against the depth + _validateCert(validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS, + (*found_store_cert), + LLSD(), + depth); + + // verify the signature of the CA + if(!_verify_signature((*found_store_cert), + (*current_cert))) + { + throw LLCertValidationInvalidSignatureException(*current_cert); + } + // successfully validated. + return; + } + previous_cert = (*current_cert); + current_cert++; + depth++; + } + if (validation_policy & VALIDATION_POLICY_TRUSTED) + { + LLPointer untrusted_ca_cert = (*this)[size()-1]; + // we reached the end without finding a trusted cert. + throw LLCertValidationTrustException((*this)[size()-1]); + + } } + // LLSecAPIBasicHandler Class // Interface handler class for the various security storage handlers. @@ -369,7 +1038,39 @@ LLSecAPIBasicHandler::LLSecAPIBasicHandler() mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); mProtectedDataMap = LLSD::emptyMap(); + + mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "bin_conf.dat"); _readProtectedData(); + + std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "CA.pem"); + // copy the CA file to a user writable location so we can manipulate it. + // for this provider, by using a user writable file, there is a risk that + // an attacking program can modify the file, but OS dependent providers + // will reduce that risk. + // by using a user file, modifications will be limited to one user if + // we read-only the main file + + + if (!LLFile::isfile(store_file)) + { + + std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); + llifstream ca_file(ca_file_path.c_str(), llifstream::binary | llifstream::in); + llofstream copied_store_file(store_file.c_str(), llofstream::binary | llofstream::out); + + while(!ca_file.fail()) + { + char buffer[BUFFER_READ_SIZE]; + ca_file.read(buffer, sizeof(buffer)); + copied_store_file.write(buffer, ca_file.gcount()); + } + ca_file.close(); + copied_store_file.close(); + } + LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; + mStore = new LLBasicCertificateStore(store_file); } LLSecAPIBasicHandler::~LLSecAPIBasicHandler() @@ -537,7 +1238,7 @@ LLPointer LLSecAPIBasicHandler::getCertificate(X509* openssl_cert // instantiate a chain from an X509_STORE_CTX LLPointer LLSecAPIBasicHandler::getCertificateChain(const X509_STORE_CTX* chain) { - LLPointer result = NULL; + LLPointer result = new LLBasicCertificateChain(chain); return result; } @@ -546,8 +1247,7 @@ LLPointer LLSecAPIBasicHandler::getCertificateChain(const X5 // persisted) LLPointer LLSecAPIBasicHandler::getCertificateStore(const std::string& store_id) { - LLPointer result; - return result; + return mStore; } // retrieve protected data @@ -659,7 +1359,7 @@ void LLSecAPIBasicHandler::saveCredential(LLPointer cred, bool sav { credential["authenticator"] = cred->getAuthenticator(); } - LL_INFOS("Credentials") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; + LL_INFOS("SECAPI") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; setProtectedData("credential", cred->getGrid(), credential); //*TODO: If we're saving Agni credentials, should we write the // credentials to the legacy password.dat/etc? @@ -742,3 +1442,64 @@ std::string LLSecAPIBasicCredential::asString() const } +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs) +{ + if (lhs.type() != rhs.type()) + { + return FALSE; + } + if (lhs.isMap()) + { + // iterate through the map, verifying the right hand side has all of the + // values that the left hand side has. + for (LLSD::map_const_iterator litt = lhs.beginMap(); + litt != lhs.endMap(); + litt++) + { + if (!rhs.has(litt->first)) + { + return FALSE; + } + } + + // Now validate that the left hand side has everything the + // right hand side has, and that the values are equal. + for (LLSD::map_const_iterator ritt = rhs.beginMap(); + ritt != rhs.endMap(); + ritt++) + { + if (!lhs.has(ritt->first)) + { + return FALSE; + } + if (!valueCompareLLSD(lhs[ritt->first], ritt->second)) + { + return FALSE; + } + } + return TRUE; + } + else if (lhs.isArray()) + { + LLSD::array_const_iterator ritt = rhs.beginArray(); + // iterate through the array, comparing + for (LLSD::array_const_iterator litt = lhs.beginArray(); + litt != lhs.endArray(); + litt++) + { + if (!valueCompareLLSD(*ritt, *litt)) + { + return FALSE; + } + ritt++; + } + + return (ritt == rhs.endArray()); + } + else + { + // simple type, compare as string + return (lhs.asString() == rhs.asString()); + } + +} -- cgit v1.2.3 From 73e86b0bed548b2aaba8d92837e562d6d753808a Mon Sep 17 00:00:00 2001 From: Karen Lahey Date: Thu, 15 Oct 2009 16:52:03 -0700 Subject: MAC Address Change no longer causes viewer to die cr:Roxie --- indra/newview/llsechandler_basic.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 097cc395d7..1453506b0d 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -1028,20 +1028,29 @@ LLSecAPIBasicHandler::LLSecAPIBasicHandler(const std::string& protected_data_fil mProtectedDataFilename = protected_data_file; mProtectedDataMap = LLSD::emptyMap(); mLegacyPasswordPath = legacy_password_path; - _readProtectedData(); + } LLSecAPIBasicHandler::LLSecAPIBasicHandler() { - mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "bin_conf.dat"); - mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); +} + +void LLSecAPIBasicHandler::init() +{ + if (mProtectedDataFilename.length() == 0) + { + mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "bin_conf.dat"); + } + if (mLegacyPasswordPath.length() == 0) + { + mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); + } mProtectedDataMap = LLSD::emptyMap(); mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "bin_conf.dat"); - _readProtectedData(); std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "CA.pem"); @@ -1051,8 +1060,6 @@ LLSecAPIBasicHandler::LLSecAPIBasicHandler() // will reduce that risk. // by using a user file, modifications will be limited to one user if // we read-only the main file - - if (!LLFile::isfile(store_file)) { @@ -1071,8 +1078,9 @@ LLSecAPIBasicHandler::LLSecAPIBasicHandler() } LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; mStore = new LLBasicCertificateStore(store_file); + _readProtectedData(); // initialize mProtectedDataMap + // may throw LLProtectedDataException if saved datamap is not decryptable } - LLSecAPIBasicHandler::~LLSecAPIBasicHandler() { _writeProtectedData(); @@ -1084,6 +1092,7 @@ void LLSecAPIBasicHandler::_readProtectedData() LLPointer parser = new LLSDXMLParser(); llifstream protected_data_stream(mProtectedDataFilename.c_str(), llifstream::binary); + if (!protected_data_stream.fail()) { int offset; U8 salt[STORE_SALT_SIZE]; @@ -1099,7 +1108,7 @@ void LLSecAPIBasicHandler::_readProtectedData() offset = 0; if (protected_data_stream.gcount() < STORE_SALT_SIZE) { - throw LLProtectedDataException("Corrupt Protected Data Store1"); + throw LLProtectedDataException("Config file too short."); } cipher.decrypt(salt, STORE_SALT_SIZE); @@ -1139,7 +1148,7 @@ void LLSecAPIBasicHandler::_readProtectedData() if (parser->parse(parse_stream, mProtectedDataMap, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) { - throw LLProtectedDataException("Corrupt Protected Data Store"); + throw LLProtectedDataException("Config file cannot be decrypted."); } } } @@ -1254,6 +1263,7 @@ LLPointer LLSecAPIBasicHandler::getCertificateStore(const st LLSD LLSecAPIBasicHandler::getProtectedData(const std::string& data_type, const std::string& data_id) { + if (mProtectedDataMap.has(data_type) && mProtectedDataMap[data_type].isMap() && mProtectedDataMap[data_type].has(data_id)) -- cgit v1.2.3 From 8f2df75f3d506fd0c8db57faee4aa0cff0e711aa Mon Sep 17 00:00:00 2001 From: Karen Lahey Date: Fri, 16 Oct 2009 19:26:46 -0700 Subject: Fix build. --- indra/newview/llsechandler_basic.cpp | 54 +++++++++++++++++------------------- 1 file changed, 25 insertions(+), 29 deletions(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 1453506b0d..9827dc605a 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -1038,46 +1038,42 @@ LLSecAPIBasicHandler::LLSecAPIBasicHandler() void LLSecAPIBasicHandler::init() { + mProtectedDataMap = LLSD::emptyMap(); if (mProtectedDataFilename.length() == 0) { mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "bin_conf.dat"); - } - if (mLegacyPasswordPath.length() == 0) - { mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); - } - mProtectedDataMap = LLSD::emptyMap(); - mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "bin_conf.dat"); + std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "CA.pem"); + // copy the CA file to a user writable location so we can manipulate it. + // for this provider, by using a user writable file, there is a risk that + // an attacking program can modify the file, but OS dependent providers + // will reduce that risk. + // by using a user file, modifications will be limited to one user if + // we read-only the main file + if (!LLFile::isfile(store_file)) + { - std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "CA.pem"); - // copy the CA file to a user writable location so we can manipulate it. - // for this provider, by using a user writable file, there is a risk that - // an attacking program can modify the file, but OS dependent providers - // will reduce that risk. - // by using a user file, modifications will be limited to one user if - // we read-only the main file - if (!LLFile::isfile(store_file)) - { - - std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); - llifstream ca_file(ca_file_path.c_str(), llifstream::binary | llifstream::in); - llofstream copied_store_file(store_file.c_str(), llofstream::binary | llofstream::out); + std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); + llifstream ca_file(ca_file_path.c_str(), llifstream::binary | llifstream::in); + llofstream copied_store_file(store_file.c_str(), llofstream::binary | llofstream::out); - while(!ca_file.fail()) - { - char buffer[BUFFER_READ_SIZE]; - ca_file.read(buffer, sizeof(buffer)); - copied_store_file.write(buffer, ca_file.gcount()); + while(!ca_file.fail()) + { + char buffer[BUFFER_READ_SIZE]; + ca_file.read(buffer, sizeof(buffer)); + copied_store_file.write(buffer, ca_file.gcount()); + } + ca_file.close(); + copied_store_file.close(); } - ca_file.close(); - copied_store_file.close(); + LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; + mStore = new LLBasicCertificateStore(store_file); } - LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; - mStore = new LLBasicCertificateStore(store_file); _readProtectedData(); // initialize mProtectedDataMap // may throw LLProtectedDataException if saved datamap is not decryptable } -- cgit v1.2.3 From c70d0f0ee280f5b417426154290a752489cded13 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 8 Dec 2009 00:00:51 -0800 Subject: DEV-42996 GIAB: configuring via CLI tools corrupts viewer certs Added authority key identifier/subject key identifier checking. Whenever a new cert was created, a new private key was also created. Typically you get a new key identifier with that private key which is written to the child cert. The child cert can then find the appropriate parent cert for validation via subject key identifier. --- indra/newview/llsechandler_basic.cpp | 75 +++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index be5c7b3c61..d41ec96ab6 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -59,10 +59,13 @@ LLS * By copying, modifying or distributing this software, you acknowledge #define STORE_SALT_SIZE 16 #define BUFFER_READ_SIZE 256 std::string cert_string_from_asn1_string(ASN1_STRING* value); +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); LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) { @@ -175,6 +178,8 @@ LLSD& LLBasicCertificate::_initLLSD() 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); return mLLSDInfo; } @@ -269,6 +274,43 @@ LLSD _ext_key_usage_ext(X509* cert) return result; } +// retrieve the subject key identifier of the cert +LLSD _subject_key_identifier_ext(X509 *cert) +{ + LLSD 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); + } + return result; +} + +// retrieve the authority key identifier of the cert +LLSD _authority_key_identifier_ext(X509* cert) +{ + LLSD result; + AUTHORITY_KEYID *akeyid = (AUTHORITY_KEYID *)X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); + if(akeyid) + { + result = LLSD::emptyMap(); + if(akeyid->keyid) + { + result[CERT_AUTHORITY_KEY_IDENTIFIER_ID] = cert_string_from_octet_string(akeyid->keyid); + } + if(akeyid->serial) + { + result[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL] = cert_string_from_asn1_integer(akeyid->serial); + } + } + + // 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; +} + // retrieve an openssl x509 object, // which must be freed by X509_free X509* LLBasicCertificate::getOpenSSLX509() const @@ -337,6 +379,25 @@ std::string cert_string_from_asn1_integer(ASN1_INTEGER* value) return result; } +// Generate a string from an OCTET string. +// we retrieve as a + +std::string cert_string_from_octet_string(ASN1_OCTET_STRING* value) +{ + + std::stringstream result; + result << std::hex << std::setprecision(2); + for (unsigned int i=0; i < value->length; i++) + { + if (i != 0) + { + result << ":"; + } + result << std::setfill('0') << std::setw(2) << (int)value->data[i]; + } + return result.str(); +} + // Generate a string from an ASN1 integer. ASN1 Integers are // bignums, so they can be 'infinitely' long, therefore we // cannot simply use a conversion to U64 or something. @@ -983,6 +1044,18 @@ void LLBasicCertificateChain::validate(int validation_policy, cert_search_params = LLSD::emptyMap(); cert_search_params[CERT_SUBJECT_NAME_STRING] = cert_llsd[CERT_ISSUER_NAME_STRING]; + if (cert_llsd.has(CERT_AUTHORITY_KEY_IDENTIFIER)) + { + LLSD cert_aki = cert_llsd[CERT_AUTHORITY_KEY_IDENTIFIER]; + if(cert_aki.has(CERT_AUTHORITY_KEY_IDENTIFIER_ID)) + { + cert_search_params[CERT_SUBJECT_KEY_IDENTFIER] = cert_aki[CERT_AUTHORITY_KEY_IDENTIFIER_ID]; + } + if(cert_aki.has(CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL)) + { + cert_search_params[CERT_SERIAL_NUMBER] = cert_aki[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL]; + } + } found_store_cert = ca_store->find(cert_search_params); if(found_store_cert != ca_store->end()) -- cgit v1.2.3 From d79af12e04e182bab639e64e9cd1181d5c799582 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 2 Mar 2010 13:42:17 -0800 Subject: Fix windows build break, segfault on exit and bad library link location --- indra/newview/llsechandler_basic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llsechandler_basic.cpp') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index d41ec96ab6..51e250ffc6 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -387,7 +387,7 @@ std::string cert_string_from_octet_string(ASN1_OCTET_STRING* value) std::stringstream result; result << std::hex << std::setprecision(2); - for (unsigned int i=0; i < value->length; i++) + for (int i=0; i < value->length; i++) { if (i != 0) { -- cgit v1.2.3