diff options
Diffstat (limited to 'indra/newview')
-rw-r--r-- | indra/newview/CMakeLists.txt | 10 | ||||
-rw-r--r-- | indra/newview/llsecapi.cpp | 69 | ||||
-rw-r--r-- | indra/newview/llsecapi.h | 232 | ||||
-rw-r--r-- | indra/newview/llsechandler_basic.cpp | 490 | ||||
-rw-r--r-- | indra/newview/llsechandler_basic.h | 116 |
5 files changed, 917 insertions, 0 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 62cb8380c0..9430bb1d56 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -7,6 +7,7 @@ include(Boost) include(BuildVersion) include(DBusGlib) include(DirectX) +include(OpenSSL) include(ELFIO) include(FMOD) include(OPENAL) @@ -372,6 +373,8 @@ set(viewer_SOURCE_FILES llscrollingpanelparam.cpp llsearchcombobox.cpp llsearchhistory.cpp + llsecapi.cpp + llsechandler_basic.cpp llselectmgr.cpp llsidepanelappearance.cpp llsidepanelinventory.cpp @@ -875,6 +878,8 @@ set(viewer_HEADER_FILES llscrollingpanelparam.h llsearchcombobox.h llsearchhistory.h + llsecapi.h + llsechandler_basic.h llselectmgr.h llsidepanelappearance.h llsidepanelinventory.h @@ -1618,6 +1623,8 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${WINDOWS_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${ELFIO_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} ${LLLOGIN_LIBRARIES} ${GOOGLE_PERFTOOLS_LIBRARIES} ) @@ -1803,6 +1810,9 @@ if (LL_TESTS) #ADD_VIEWER_BUILD_TEST(lltextureinfo viewer) #ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer) #ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer) + #ADD_COMM_BUILD_TEST(llsecapi viewer "" llviewerprecompiledheaders.cpp llsechandler_basic.cpp) + #ADD_VIEWER_COMM_BUILD_TEST(llsechandler_basic viewer "") + endif (LL_TESTS) diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp new file mode 100644 index 0000000000..c2cfde0dc7 --- /dev/null +++ b/indra/newview/llsecapi.cpp @@ -0,0 +1,69 @@ +/** + * @file llsecapi.cpp + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, 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 <map> + + +std::map<std::string, LLPointer<LLSecAPIHandler> > gHandlerMap; + + +void initializeSecHandler() +{ + gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler(); +} +// start using a given security api handler. If the string is empty +// the default is used +LLPointer<LLSecAPIHandler> getSecHandler(const std::string& handler_type) +{ + if (gHandlerMap.find(handler_type) != gHandlerMap.end()) + { + return gHandlerMap[handler_type]; + } + else + { + return LLPointer<LLSecAPIHandler>(NULL); + } +} +// register a handler +void registerSecHandler(const std::string& handler_type, + LLPointer<LLSecAPIHandler>& handler) +{ + gHandlerMap[handler_type] = handler; +} + + + + diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h new file mode 100644 index 0000000000..743d3d6770 --- /dev/null +++ b/indra/newview/llsecapi.h @@ -0,0 +1,232 @@ +/** + * @file llsecapi.h + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, 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$ + */ + +#ifndef LLSECAPI_H +#define LLSECAPI_H +#include <vector> +#include <openssl/x509.h> + +// All error handling is via exceptions. + + +#define CERT_SUBJECT_NAME "subject_name" +#define CERT_ISSUER_NAME "issuer_name" + +#define CERT_SUBJECT_NAME_STRING "subject_name_string" +#define CERT_ISSUER_NAME_STRING "issuer_name_string" + +#define CERT_SERIAL_NUMBER "serial_number" + +#define CERT_VALID_FROM "valid_from" +#define CERT_VALID_TO "valid_to" +#define CERT_SHA1_DIGEST "sha1_digest" +#define CERT_MD5_DIGEST "md5_digest" + +#define BASIC_SECHANDLER "BASIC_SECHANDLER" + + +// All error handling is via exceptions. + +class LLCertException +{ +public: + LLCertException(const char* msg) + { + llerrs << "Certificate Error: " << msg << llendl; + mMsg = std::string(msg); + } +protected: + std::string mMsg; +}; + +class LLProtectedDataException +{ +public: + LLProtectedDataException(const char *msg) + { + llerrs << "Certificate Error: " << msg << llendl; + mMsg = std::string(msg); + } +protected: + std::string mMsg; +}; + +// class LLCertificate +// parent class providing an interface for certifiate. +// LLCertificates are considered unmodifiable +// Certificates are pulled out of stores, or created via +// factory calls +class LLCertificate : public LLRefCount +{ + LOG_CLASS(LLCertificate); +public: + LLCertificate() {} + + virtual ~LLCertificate() {} + + // return a PEM encoded certificate. The encoding + // includes the -----BEGIN CERTIFICATE----- and end certificate elements + virtual std::string getPem()=0; + + // return a DER encoded certificate + virtual std::vector<U8> getBinary()=0; + + // return an LLSD object containing information about the certificate + // such as its name, signature, expiry time, serial number + virtual LLSD getLLSD()=0; + + // return an openSSL X509 struct for the certificate + virtual X509* getOpenSSLX509()=0; + +}; + + +// class LLCertificateChain +// Class representing a chain of certificates in order, with the +// 0th element being the CA +class LLCertificateChain : public LLRefCount +{ + LOG_CLASS(LLCertificateChain); + static const int VT_SSL = 0; + static const int VT_AGENT_DOMAIN = 1; + static const int VT_GRID_DOMAIN = 1; + +public: + LLCertificateChain() {} + + virtual ~LLCertificateChain() {} + + virtual X509_STORE getOpenSSLX509Store()=0; // return an openssl X509_STORE + // for this store + + virtual void appendCert(const LLCertificate& cert)=0; // append a cert to the end + //of the chain + + virtual LLPointer<LLCertificate>& operator [](int index)=0; // retrieve a certificate + // from the chain by index + // -1 == end of chain + + virtual int len() const =0; // return number of certificates in the chain + + // validate a certificate chain given the params. + // validation type indicates whether it's simply an SSL cert, or + // something more specific + virtual bool validate(int validation_type, + const LLSD& validation_params) const =0; +}; + + +// 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 LLCertificateStore : public LLRefCount +{ +public: + LLCertificateStore() {} + virtual ~LLCertificateStore() {} + + virtual X509_STORE getOpenSSLX509Store()=0; // return an openssl X509_STORE + // for this store + + // add a copy of a cert to the store + virtual void append(const LLCertificate& cert)=0; + + // add a copy of a cert to the store + virtual void insert(const int index, const LLCertificate& cert)=0; + + // remove a certificate from the store + virtual void remove(int index)=0; + + // return a certificate at the index + virtual LLPointer<LLCertificate>& operator[](int index)=0; + // return the number of certs in the store + virtual int len() const =0; + + // load the store from a persisted location + virtual void load(const std::string& store_id)=0; + + // persist the store + virtual void save()=0; + + // return the store id + virtual std::string storeId()=0; + + // validate a cert chain + virtual bool validate(const LLCertificateChain& cert_chain) const=0; +}; + + +// LLSecAPIHandler Class +// Interface handler class for the various security storage handlers. +class LLSecAPIHandler : public LLRefCount +{ +public: + + LLSecAPIHandler() {} + virtual ~LLSecAPIHandler() {} + + // instantiate a certificate from a pem string + virtual LLPointer<LLCertificate> getCertificate(const std::string& pem_cert)=0; + + + // instiate a certificate from an openssl X509 structure + virtual LLPointer<LLCertificate> getCertificate(X509* openssl_cert)=0; + + // instantiate a chain from an X509_STORE_CTX + virtual LLPointer<LLCertificateChain> getCertificateChain(const X509_STORE_CTX* chain)=0; + + // 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) + virtual LLPointer<LLCertificateStore> getCertificateStore(const std::string& store_id)=0; + + // persist data in a protected store + virtual void setProtectedData(const std::string& data_type, + const std::string& data_id, + const LLSD& data)=0; + + // retrieve protected data + virtual LLSD getProtectedData(const std::string& data_type, + const std::string& data_id)=0; +}; + +void secHandlerInitialize(); + +// retrieve a security api depending on the api type +LLPointer<LLSecAPIHandler> getSecHandler(const std::string& handler_type); + +void registerSecHandler(const std::string& handler_type, + LLPointer<LLSecAPIHandler>& handler); + +#endif // LL_SECAPI_H 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 <vector> +#include <ios> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/asn1.h> +#include <openssl/rand.h> +#include <iostream> +#include <iomanip> +#include <time.h> + +// 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<U8> 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<U8> 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<LLSDParser> 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<LLCertificate> LLSecAPIBasicHandler::getCertificate(const std::string& pem_cert) +{ + LLPointer<LLCertificate> result = new LLBasicCertificate(pem_cert); + return result; +} + + + +// instiate a certificate from an openssl X509 structure +LLPointer<LLCertificate> LLSecAPIBasicHandler::getCertificate(X509* openssl_cert) +{ + LLPointer<LLCertificate> result = new LLBasicCertificate(openssl_cert); + return result; +} + +// instantiate a chain from an X509_STORE_CTX +LLPointer<LLCertificateChain> LLSecAPIBasicHandler::getCertificateChain(const X509_STORE_CTX* chain) +{ + LLPointer<LLCertificateChain> 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<LLCertificateStore> LLSecAPIBasicHandler::getCertificateStore(const std::string& store_id) +{ + LLPointer<LLCertificateStore> 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 diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h new file mode 100644 index 0000000000..0ec6938583 --- /dev/null +++ b/indra/newview/llsechandler_basic.h @@ -0,0 +1,116 @@ +/** + * @file llsechandler_basic.h + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, 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$ + */ + +#ifndef LLSECHANDLER_BASIC +#define LLSECHANDLER_BASIC + +#include "llsecapi.h" +#include <vector> +#include <openssl/x509.h> + +// helpers +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 +// +class LLBasicCertificate : public LLCertificate +{ +public: + LOG_CLASS(LLBasicCertificate); + + LLBasicCertificate(const std::string& pem_cert); + LLBasicCertificate(X509* openSSLX509); + + virtual ~LLBasicCertificate(); + + virtual std::string getPem(); + virtual std::vector<U8> getBinary(); + virtual LLSD getLLSD(); + + virtual X509* getOpenSSLX509(); +protected: + // certificates are stored as X509 objects, as validation and + // other functionality is via openssl + X509* mCert; +}; + +// LLSecAPIBasicHandler Class +// Interface handler class for the various security storage handlers. +class LLSecAPIBasicHandler : public LLSecAPIHandler +{ +public: + + LLSecAPIBasicHandler(const std::string& protected_data_filename); + LLSecAPIBasicHandler(); + + virtual ~LLSecAPIBasicHandler(); + + // instantiate a certificate from a pem string + virtual LLPointer<LLCertificate> getCertificate(const std::string& pem_cert); + + + // instiate a certificate from an openssl X509 structure + virtual LLPointer<LLCertificate> getCertificate(X509* openssl_cert); + + // instantiate a chain from an X509_STORE_CTX + virtual LLPointer<LLCertificateChain> getCertificateChain(const X509_STORE_CTX* chain); + + // 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) + virtual LLPointer<LLCertificateStore> getCertificateStore(const std::string& store_id); + + // persist data in a protected store + virtual void setProtectedData(const std::string& data_type, + const std::string& data_id, + const LLSD& data); + + // retrieve protected data + virtual LLSD getProtectedData(const std::string& data_type, + const std::string& data_id); +protected: + void _readProtectedData(); + void _writeProtectedData(); + + std::string mProtectedDataFilename; + LLSD mProtectedDataMap; +}; + +#endif // LLSECHANDLER_BASIC + + + |