void initializeSecHandler() { ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler(); // Currently, we only have the Basic handler, so we can point the main sechandler // pointer to the basic handler. Later, we'll create a wrapper handler that // selects the appropriate sechandler as needed, for instance choosing the // mac keyring handler, with fallback to the basic sechandler gSecAPIHandler = gHandlerMap[BASIC_SECHANDLER]; // initialize all SecAPIHandlers std::string exception_msg; std::map<std::string, LLPointer<LLSecAPIHandler> >::const_iterator itr; for(itr = gHandlerMap.begin(); itr != gHandlerMap.end(); ++itr) { LLPointer<LLSecAPIHandler> handler = (*itr).second; try { handler->init(); } catch (LLProtectedDataException e) { exception_msg = e.getMessage(); } } if (!exception_msg.empty()) // an exception was thrown. { throw LLProtectedDataException(exception_msg.c_str()); } }
void initializeSecHandler() { ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); // <FS:ND If we can, enabled the rdrand engine. It is available as a CPU instruction on newer Intel CPUs ENGINE_load_builtin_engines(); ENGINE* engRdRand = ENGINE_by_id("rdrand"); if( engRdRand ) ENGINE_set_default(engRdRand, ENGINE_METHOD_RAND); unsigned long lErr ( ERR_get_error() ); while( lErr ) { char aError[128]; ERR_error_string_n( lErr, aError, sizeof( aError ) ); LL_WARNS() << aError << LL_ENDL; lErr = ERR_get_error(); } // </FS:ND> gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler(); // Currently, we only have the Basic handler, so we can point the main sechandler // pointer to the basic handler. Later, we'll create a wrapper handler that // selects the appropriate sechandler as needed, for instance choosing the // mac keyring handler, with fallback to the basic sechandler gSecAPIHandler = gHandlerMap[BASIC_SECHANDLER]; // initialize all SecAPIHandlers std::string exception_msg; std::map<std::string, LLPointer<LLSecAPIHandler> >::const_iterator itr; for(itr = gHandlerMap.begin(); itr != gHandlerMap.end(); ++itr) { LLPointer<LLSecAPIHandler> handler = (*itr).second; try { handler->init(); } catch (LLProtectedDataException e) { exception_msg = e.getMessage(); } } if (!exception_msg.empty()) // an exception was thrown. { throw LLProtectedDataException(exception_msg.c_str()); } }
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); unsigned char unique_id[MAC_ADDRESS_BYTES]; LLMachineID::getUniqueID(unique_id, sizeof(unique_id)); LLXORCipher cipher(unique_id, sizeof(unique_id)); 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); 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::isfile(mProtectedDataFilename) != 0) && (LLFile::remove(mProtectedDataFilename) != 0))) || (LLFile::rename(tmp_filename, mProtectedDataFilename))) { LLFile::remove(tmp_filename); throw LLProtectedDataException("Could not overwrite protected data store"); } }
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; unsigned char unique_id[MAC_ADDRESS_BYTES]; LLMachineID::getUniqueID(unique_id, sizeof(unique_id)); LLXORCipher cipher(unique_id, sizeof(unique_id)); // 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("Config file too short."); } 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. // 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. // 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("Config file cannot be decrypted."); } } }