diff options
author | James Cook <james@lindenlab.com> | 2007-02-15 20:24:08 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-02-15 20:24:08 +0000 |
commit | 12ac04231b8d358e70c830f7958f7efbc0f7c0d1 (patch) | |
tree | 189b9bc4e3bfa63275cc41be8dc42a28b0f54ffe /indra/llmessage | |
parent | 4feabe4a9caec380cad405d5410bc762c862113d (diff) |
merge -r 57761:57967 im-email-session-3, removes database load from IM to email session tracking, introduces new format for email return addresses
Diffstat (limited to 'indra/llmessage')
-rw-r--r-- | indra/llmessage/llblowfishcipher.cpp | 135 | ||||
-rw-r--r-- | indra/llmessage/llblowfishcipher.h | 39 | ||||
-rw-r--r-- | indra/llmessage/llcipher.h | 38 | ||||
-rw-r--r-- | indra/llmessage/llfiltersd2xmlrpc.cpp | 1 | ||||
-rw-r--r-- | indra/llmessage/llmail.cpp | 80 | ||||
-rw-r--r-- | indra/llmessage/llmail.h | 119 | ||||
-rw-r--r-- | indra/llmessage/llnullcipher.cpp | 16 | ||||
-rw-r--r-- | indra/llmessage/llnullcipher.h | 30 | ||||
-rw-r--r-- | indra/llmessage/llxorcipher.cpp | 16 | ||||
-rw-r--r-- | indra/llmessage/llxorcipher.h | 49 |
10 files changed, 454 insertions, 69 deletions
diff --git a/indra/llmessage/llblowfishcipher.cpp b/indra/llmessage/llblowfishcipher.cpp new file mode 100644 index 0000000000..0255a654df --- /dev/null +++ b/indra/llmessage/llblowfishcipher.cpp @@ -0,0 +1,135 @@ +/** + * @file llblowcipher.cpp + * @brief Wrapper around OpenSSL Blowfish encryption algorithm. + * + * We do not have OpenSSL headers or libraries on Windows, so this + * class only works on Linux. + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "llblowfishcipher.h" + + +LLBlowfishCipher::LLBlowfishCipher(const U8* secret, size_t secret_size) +: LLCipher() +{ + llassert(secret); + + mSecretSize = secret_size; + mSecret = new U8[mSecretSize]; + memcpy(mSecret, secret, mSecretSize); +} + +LLBlowfishCipher::~LLBlowfishCipher() +{ + delete [] mSecret; + mSecret = NULL; +} + + +#if LL_LINUX + +#include <openssl/evp.h> + +// virtual +U32 LLBlowfishCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +{ + if (!src || !src_len || !dst || !dst_len) return 0; + if (src_len > dst_len) return 0; + + // OpenSSL uses "cipher contexts" to hold encryption parameters. + EVP_CIPHER_CTX context; + EVP_CIPHER_CTX_init(&context); + + // We want a blowfish cyclic block chain cipher, but need to set + // the key length before we pass in a key, so call EncryptInit + // first with NULLs. + EVP_EncryptInit_ex(&context, EVP_bf_cbc(), NULL, NULL, NULL); + EVP_CIPHER_CTX_set_key_length(&context, (int)mSecretSize); + + // Complete initialization. Per EVP_EncryptInit man page, the + // cipher pointer must be NULL. Apparently initial_vector must + // be 8 bytes for blowfish, as this is the block size. + unsigned char initial_vector[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + EVP_EncryptInit_ex(&context, NULL, NULL, mSecret, initial_vector); + + int blocksize = EVP_CIPHER_CTX_block_size(&context); + int keylen = EVP_CIPHER_CTX_key_length(&context); + int iv_length = EVP_CIPHER_CTX_iv_length(&context); + lldebugs << "LLBlowfishCipher blocksize " << blocksize + << " keylen " << keylen + << " iv_len " << iv_length + << llendl; + + int output_len = 0; + if (!EVP_EncryptUpdate(&context, + dst, + &output_len, + src, + src_len)) + { + llwarns << "LLBlowfishCipher::encrypt EVP_EncryptUpdate failure" << llendl; + return 0; + } + + // There may be some final data left to encrypt if the input is + // not an exact multiple of the block size. + int temp_len = 0; + if (!EVP_EncryptFinal_ex(&context, (unsigned char*)(dst + output_len), &temp_len)) + { + llwarns << "LLBlowfishCipher::encrypt EVP_EncryptFinal failure" << llendl; + return 0; + } + output_len += temp_len; + return output_len; +} + +// virtual +U32 LLBlowfishCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +{ + llerrs << "LLBlowfishCipher decrypt unsupported" << llendl; + return 0; +} + +// virtual +U32 LLBlowfishCipher::requiredEncryptionSpace(U32 len) const +{ + // *HACK: We know blowfish uses an 8 byte block size. + // Oddly, sometimes EVP_Encrypt produces an extra block + // if the input is an exact multiple of the block size. + // So round up. + const U32 BLOCK_SIZE = 8; + len += BLOCK_SIZE; + len -= (len % BLOCK_SIZE); + return len; +} + +#else // !LL_LINUX + +// virtual +U32 LLBlowfishCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +{ + llerrs << "LLBlowfishCipher only supported on Linux" << llendl; + return 0; +} + +// virtual +U32 LLBlowfishCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +{ + llerrs << "LLBlowfishCipher only supported on Linux" << llendl; + return 0; +} + +// virtual +U32 LLBlowfishCipher::requiredEncryptionSpace(U32 len) const +{ + llerrs << "LLBlowfishCipher only supported on Linux" << llendl; + return 0; +} + +#endif + diff --git a/indra/llmessage/llblowfishcipher.h b/indra/llmessage/llblowfishcipher.h new file mode 100644 index 0000000000..3c5ee1a626 --- /dev/null +++ b/indra/llmessage/llblowfishcipher.h @@ -0,0 +1,39 @@ +/** + * @file llblowfishcipher.h + * @brief A symmetric block cipher, designed in 1993 by Bruce Schneier. + * We use it because it has an 8 byte block size, allowing encryption of + * two UUIDs and a timestamp (16x2 + 4 = 36 bytes) with only 40 bytes of + * output. AES has a block size of 32 bytes, so this would require 64 bytes. + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LLBLOWFISHCIPHER_H +#define LLBLOWFISHCIPHER_H + +#include "llcipher.h" + + +class LLBlowfishCipher : public LLCipher +{ +public: + // Secret may be up to 56 bytes in length per Blowfish spec. + LLBlowfishCipher(const U8* secret, size_t secret_size); + virtual ~LLBlowfishCipher(); + + // See llcipher.h for documentation. + /*virtual*/ U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len); + /*virtual*/ U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len); + /*virtual*/ U32 requiredEncryptionSpace(U32 src_len) const; + +#ifdef _DEBUG + static BOOL testHarness(); +#endif + +private: + U8* mSecret; + size_t mSecretSize; +}; + +#endif // LL_LLCRYPTO_H diff --git a/indra/llmessage/llcipher.h b/indra/llmessage/llcipher.h new file mode 100644 index 0000000000..3dce949c2e --- /dev/null +++ b/indra/llmessage/llcipher.h @@ -0,0 +1,38 @@ +/** + * @file llcipher.h + * @brief Abstract base class for encryption ciphers. + * + * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LLCIPHER_H +#define LLCIPHER_H + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLCipher +// +// Abstract base class for a cipher object. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLCipher +{ +public: + virtual ~LLCipher() {} + + // encrypt src and place result into dst. returns TRUE if + // Returns number of bytes written into dst, or 0 on error. + virtual U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) = 0; + + // decrypt src and place result into dst. + // Returns number of bytes written into dst, or 0 on error. + virtual U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) = 0; + + // returns the minimum amount of space required to encrypt for a + // unencrypted source buffer of length len. + // *NOTE: This is estimated space and you should check the return value + // of the encrypt function. + virtual U32 requiredEncryptionSpace(U32 src_len) const = 0 ; +}; + +#endif diff --git a/indra/llmessage/llfiltersd2xmlrpc.cpp b/indra/llmessage/llfiltersd2xmlrpc.cpp index 6d5f92e983..5f81471348 100644 --- a/indra/llmessage/llfiltersd2xmlrpc.cpp +++ b/indra/llmessage/llfiltersd2xmlrpc.cpp @@ -248,6 +248,7 @@ void LLFilterSD2XMLRPC::streamOut(std::ostream& ostr, const LLSD& sd) LLSD::Binary buffer = sd.asBinary(); if(!buffer.empty()) { + // *TODO: convert to LLBase64 int b64_buffer_length = apr_base64_encode_len(buffer.size()); char* b64_buffer = new char[b64_buffer_length]; b64_buffer_length = apr_base64_encode_binary( diff --git a/indra/llmessage/llmail.cpp b/indra/llmessage/llmail.cpp index 4dd550901e..5417976006 100644 --- a/indra/llmessage/llmail.cpp +++ b/indra/llmessage/llmail.cpp @@ -8,6 +8,8 @@ #include "linden_common.h" +#include "llmail.h" + // APR on Windows needs full windows headers #ifdef LL_WINDOWS # undef WIN32_LEAN_AND_MEAN @@ -19,14 +21,16 @@ #include <sstream> #include <boost/regex.hpp> -#include "llmail.h" - #include "apr-1/apr_pools.h" #include "apr-1/apr_network_io.h" #include "llapr.h" +#include "llbase64.h" // IM-to-email address +#include "llblowfishcipher.h" #include "llerror.h" #include "llhost.h" +#include "llstring.h" +#include "lluuid.h" #include "net.h" // @@ -86,11 +90,12 @@ void disconnect_smtp() // Returns TRUE on success. // message should NOT be SMTP escaped. -BOOL send_mail(const char* from_name, const char* from_address, +// static +BOOL LLMail::send(const char* from_name, const char* from_address, const char* to_name, const char* to_address, const char* subject, const char* message) { - std::string header = build_smtp_transaction( + std::string header = buildSMTPTransaction( from_name, from_address, to_name, @@ -106,12 +111,13 @@ BOOL send_mail(const char* from_name, const char* from_address, { message_str = message; } - bool rv = send_mail(header, message_str, to_address, from_address); + bool rv = send(header, message_str, to_address, from_address); if(rv) return TRUE; return FALSE; } -void init_mail(const std::string& hostname, apr_pool_t* pool) +// static +void LLMail::init(const std::string& hostname, apr_pool_t* pool) { gMailSocket = NULL; if(hostname.empty() || !pool) @@ -138,12 +144,14 @@ void init_mail(const std::string& hostname, apr_pool_t* pool) } } -void enable_mail(bool mail_enabled) +// static +void LLMail::enable(bool mail_enabled) { gMailEnabled = mail_enabled; } -std::string build_smtp_transaction( +// static +std::string LLMail::buildSMTPTransaction( const char* from_name, const char* from_address, const char* to_name, @@ -197,7 +205,8 @@ std::string build_smtp_transaction( return header.str(); } -bool send_mail( +// static +bool LLMail::send( const std::string& header, const std::string& message, const char* from_address, @@ -290,3 +299,56 @@ bool send_mail( #endif return true; } + + +// static +std::string LLMail::encryptIMEmailAddress(const LLUUID& from_agent_id, + const LLUUID& to_agent_id, + U32 time, + const U8* secret, + size_t secret_size) +{ + size_t data_size = 4 + UUID_BYTES + UUID_BYTES; + // Convert input data into a binary blob + std::vector<U8> data; + data.resize(data_size); + // *NOTE: This may suffer from endian issues. Could be htonmemcpy. + memcpy(&data[0], &time, 4); + memcpy(&data[4], &from_agent_id.mData[0], UUID_BYTES); + memcpy(&data[4 + UUID_BYTES], &to_agent_id.mData[0], UUID_BYTES); + + // Encrypt the blob + LLBlowfishCipher cipher(secret, secret_size); + size_t encrypted_size = cipher.requiredEncryptionSpace(data.size()); + U8* encrypted = new U8[encrypted_size]; + cipher.encrypt(&data[0], data_size, encrypted, encrypted_size); + + // Base64 encoded and replace the pieces of base64 that are less compatible + // with e-mail local-parts. + // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" + std::string address = LLBase64::encode(encrypted, encrypted_size); + LLString::replaceChar(address, '+', '-'); + LLString::replaceChar(address, '/', '_'); + + // Strip padding = signs, see RFC + size_t extra_bytes = encrypted_size % 3; + size_t padding_size = 0; + if (extra_bytes == 0) + { + padding_size = 0; + } + else if (extra_bytes == 1) + { + padding_size = 2; + } + else if (extra_bytes == 2) + { + padding_size = 1; + } + + address.resize(address.size() - padding_size); + + delete [] encrypted; + + return address; +} diff --git a/indra/llmessage/llmail.h b/indra/llmessage/llmail.h index af02264776..dc642b96d5 100644 --- a/indra/llmessage/llmail.h +++ b/indra/llmessage/llmail.h @@ -11,55 +11,84 @@ typedef struct apr_pool_t apr_pool_t; -// if hostname is NULL, then the host is resolved as 'mail' -void init_mail(const std::string& hostname, apr_pool_t* pool); +class LLUUID; -// Allow all email transmission to be disabled/enabled. -void enable_mail(bool mail_enabled); +class LLMail +{ +public: + // if hostname is NULL, then the host is resolved as 'mail' + static void init(const std::string& hostname, apr_pool_t* pool); -// returns TRUE if the call succeeds, FALSE otherwise. -// -// Results in: -// From: "from_name" <from_address> -// To: "to_name" <to_address> -// Subject: subject -// message -BOOL send_mail(const char* from_name, const char* from_address, - const char* to_name, const char* to_address, - const char* subject, const char* message); + // Allow all email transmission to be disabled/enabled. + static void enable(bool mail_enabled); -/** - * @brief build the complete smtp transaction & header for use in an - * mail. - * - * @param from_name The name of the email sender - * @param from_address The email address for the sender - * @param to_name The name of the email recipient - * @param to_name The email recipient address - * @param subject The subject of the email - * @return Returns the complete SMTP transaction mail header. - */ -std::string build_smtp_transaction( - const char* from_name, - const char* from_address, - const char* to_name, - const char* to_address, - const char* subject); + // returns TRUE if the call succeeds, FALSE otherwise. + // + // Results in: + // From: "from_name" <from_address> + // To: "to_name" <to_address> + // Subject: subject + // message + static BOOL send(const char* from_name, const char* from_address, + const char* to_name, const char* to_address, + const char* subject, const char* message); -/** - * @brief send an email with header and body. - * - * @param header The email header. Use build_mail_header(). - * @param message The unescaped email message. - * @param from_address Used for debugging - * @param to_address Used for debugging - * @return Returns true if the message could be sent. - */ -bool send_mail( - const std::string& header, - const std::string& message, - const char* from_address, - const char* to_address); + /** + * @brief build the complete smtp transaction & header for use in an + * mail. + * + * @param from_name The name of the email sender + * @param from_address The email address for the sender + * @param to_name The name of the email recipient + * @param to_name The email recipient address + * @param subject The subject of the email + * @return Returns the complete SMTP transaction mail header. + */ + static std::string buildSMTPTransaction( + const char* from_name, + const char* from_address, + const char* to_name, + const char* to_address, + const char* subject); + + /** + * @brief send an email with header and body. + * + * @param header The email header. Use build_mail_header(). + * @param message The unescaped email message. + * @param from_address Used for debugging + * @param to_address Used for debugging + * @return Returns true if the message could be sent. + */ + static bool send( + const std::string& header, + const std::string& message, + const char* from_address, + const char* to_address); + + // IM-to-email sessions use a "session id" based on an encrypted + // combination of from agent_id, to agent_id, and timestamp. When + // a user replies to an email we use the from_id to determine the + // sender's name and the to_id to route the message. The address + // is encrypted to prevent users from building addresses to spoof + // IMs from other users. The timestamps allow the "sessions" to + // expire, in case one of the sessions is stolen/hijacked. + // + // indra/tools/mailglue is responsible for parsing the inbound mail. + // + // secret: binary blob passed to blowfish, max length 56 bytes + // secret_size: length of blob, in bytes + // + // Returns: "base64" encoded email local-part, with _ and - as the + // non-alphanumeric characters. This allows better compatibility + // with email systems than the default / and + extra chars. JC + static std::string encryptIMEmailAddress( + const LLUUID& from_agent_id, + const LLUUID& to_agent_id, + U32 time, + const U8* secret, + size_t secret_size); +}; extern const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE; diff --git a/indra/llmessage/llnullcipher.cpp b/indra/llmessage/llnullcipher.cpp index 53bb748415..0d28b5c393 100644 --- a/indra/llmessage/llnullcipher.cpp +++ b/indra/llmessage/llnullcipher.cpp @@ -8,33 +8,33 @@ #include "linden_common.h" -#include "llcrypto.h" +#include "llnullcipher.h" ///---------------------------------------------------------------------------- /// Class LLNullCipher ///---------------------------------------------------------------------------- -BOOL LLNullCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +U32 LLNullCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) { if((src_len == dst_len) && src && dst) { memmove(dst, src, src_len); - return TRUE; + return src_len; } - return FALSE; + return 0; } -BOOL LLNullCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +U32 LLNullCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) { if((src_len == dst_len) && src && dst) { memmove(dst, src, src_len); - return TRUE; + return src_len; } - return FALSE; + return 0; } -U32 LLNullCipher::requiredEncryptionSpace(U32 len) +U32 LLNullCipher::requiredEncryptionSpace(U32 len) const { return len; } diff --git a/indra/llmessage/llnullcipher.h b/indra/llmessage/llnullcipher.h new file mode 100644 index 0000000000..bc8c2f2fa3 --- /dev/null +++ b/indra/llmessage/llnullcipher.h @@ -0,0 +1,30 @@ +/** + * @file llnullcipher.h + * + * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LLNULLCIPHER_H +#define LLNULLCIPHER_H + +#include "llcipher.h" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLNullCipher +// +// A class which implements LLCipher, but does not transform src +// during encryption. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLNullCipher : public LLCipher +{ +public: + LLNullCipher() {} + virtual ~LLNullCipher() {} + virtual U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len); + virtual U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len); + virtual U32 requiredEncryptionSpace(U32 src_len) const; +}; + +#endif diff --git a/indra/llmessage/llxorcipher.cpp b/indra/llmessage/llxorcipher.cpp index 1fbbfec9e0..9ddcf091b2 100644 --- a/indra/llmessage/llxorcipher.cpp +++ b/indra/llmessage/llxorcipher.cpp @@ -8,7 +8,8 @@ #include "linden_common.h" -#include "llcrypto.h" +#include "llxorcipher.h" + #include "llerror.h" ///---------------------------------------------------------------------------- @@ -44,25 +45,26 @@ LLXORCipher& LLXORCipher::operator=(const LLXORCipher& cipher) return *this; } -BOOL LLXORCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +U32 LLXORCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) { - if(!src || !src_len || !dst || !dst_len || !mPad) return FALSE; + if(!src || !src_len || !dst || !dst_len || !mPad) return 0; U8* pad_end = mPad + mPadLen; - while(src_len--) + U32 count = src_len; + while(count--) { *dst++ = *src++ ^ *mHead++; if(mHead >= pad_end) mHead = mPad; } - return TRUE; + return src_len; } -BOOL LLXORCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) +U32 LLXORCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) { // xor is a symetric cipher, thus, just call the other function. return encrypt(src, src_len, dst, dst_len); } -U32 LLXORCipher::requiredEncryptionSpace(U32 len) +U32 LLXORCipher::requiredEncryptionSpace(U32 len) const { return len; } diff --git a/indra/llmessage/llxorcipher.h b/indra/llmessage/llxorcipher.h new file mode 100644 index 0000000000..1f0f58f62f --- /dev/null +++ b/indra/llmessage/llxorcipher.h @@ -0,0 +1,49 @@ +/** + * @file llxorcipher.h + * + * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LLXORCIPHER_H +#define LLXORCIPHER_H + +#include "llcipher.h" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLXORCipher +// +// Implementation of LLCipher which encrypts using a XOR pad. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLXORCipher : public LLCipher +{ +public: + LLXORCipher(const U8* pad, U32 pad_len); + LLXORCipher(const LLXORCipher& cipher); + virtual ~LLXORCipher(); + LLXORCipher& operator=(const LLXORCipher& cipher); + + // Cipher functions + /*virtual*/ U32 encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len); + /*virtual*/ U32 decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len); + /*virtual*/ U32 requiredEncryptionSpace(U32 src_len) const; + + // special syntactic-sugar since xor can be performed in place. + BOOL encrypt(U8* buf, U32 len) { return encrypt((const U8*)buf, len, buf, len); } + BOOL decrypt(U8* buf, U32 len) { return decrypt((const U8*)buf, len, buf, len); } + +#ifdef _DEBUG + // This function runs tests to make sure the crc is + // working. Returns TRUE if it is. + static BOOL testHarness(); +#endif + +protected: + void init(const U8* pad, U32 pad_len); + U8* mPad; + U8* mHead; + U32 mPadLen; +}; + +#endif |