diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp | 168 | ||||
-rw-r--r-- | src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h | 19 | ||||
-rw-r--r-- | src/tls/tls_session.cpp | 12 | ||||
-rw-r--r-- | src/tls/tls_session.h | 18 |
4 files changed, 163 insertions, 54 deletions
diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp index 3fea68a74..04201efe3 100644 --- a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp +++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp @@ -7,8 +7,12 @@ #include <botan/tls_sqlite_sess_mgr.h> #include <botan/internal/assert.h> +#include <botan/lookup.h> #include <botan/hex.h> #include <botan/time.h> +#include <botan/loadstor.h> +#include <memory> + #include <sqlite3.h> namespace Botan { @@ -93,17 +97,61 @@ class sqlite3_statement sqlite3_stmt* m_stmt; }; +size_t row_count(sqlite3* db, const std::string& table_name) + { + sqlite3_statement stmt(db, "select count(*) from " + table_name); + + if(stmt.step() == SQLITE_ROW) + return stmt.get_size_t(0); + else + throw std::runtime_error("Querying size of table " + table_name + " failed"); + } + +void create_table(sqlite3* db, const char* table_schema) + { + char* errmsg = 0; + int rc = sqlite3_exec(db, table_schema, 0, 0, &errmsg); + + if(rc != SQLITE_OK) + { + const std::string err_msg = errmsg; + sqlite3_free(errmsg); + sqlite3_close(db); + throw std::runtime_error("sqlite3_exec for table failed - " + err_msg); + } + } + + +SymmetricKey derive_key(const std::string& passphrase, + const byte salt[], + size_t salt_len, + size_t iterations, + size_t& check_val) + { + std::auto_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)")); + + SecureVector<byte> x = pbkdf->derive_key(32 + 3, + passphrase, + salt, salt_len, + iterations).bits_of(); + + check_val = make_u32bit(0, x[0], x[1], x[2]); + return SymmetricKey(&x[3], x.size() - 3); + } + } -Session_Manager_SQLite::Session_Manager_SQLite(const std::string& db_filename, - const std::string& table_name, +Session_Manager_SQLite::Session_Manager_SQLite(const std::string& passphrase, + RandomNumberGenerator& rng, + const std::string& db_filename, size_t max_sessions, size_t session_lifetime) : - m_table_name(table_name), + m_rng(rng), m_max_sessions(max_sessions), m_session_lifetime(session_lifetime) { int rc = sqlite3_open(db_filename.c_str(), &m_db); + if(rc) { const std::string err_msg = sqlite3_errmsg(m_db); @@ -111,25 +159,72 @@ Session_Manager_SQLite::Session_Manager_SQLite(const std::string& db_filename, throw std::runtime_error("sqlite3_open failed - " + err_msg); } - const std::string table_sql = - "create table if not exists " + m_table_name + - "(" - "session_id TEXT PRIMARY KEY, " - "session_start INTEGER, " - "hostname TEXT, " - "hostport INTEGER, " - "session BLOB" - ")"; - - char* errmsg = 0; - rc = sqlite3_exec(m_db, table_sql.c_str(), 0, 0, &errmsg); + create_table(m_db, + "create table if not exists tls_sessions " + "(" + "session_id TEXT PRIMARY KEY, " + "session_start INTEGER, " + "hostname TEXT, " + "hostport INTEGER, " + "session BLOB" + ")"); + + create_table(m_db, + "create table if not exists tls_sessions_metadata " + "(" + "passphrase_salt BLOB, " + "passphrase_iterations INTEGER, " + "passphrase_check INTEGER " + ")"); + + const size_t salts = row_count(m_db, "tls_sessions_metadata"); + + if(salts == 1) + { + // existing db + sqlite3_statement stmt(m_db, "select * from tls_sessions_metadata"); - if(rc != SQLITE_OK) + int rc = stmt.step(); + if(rc == SQLITE_ROW) + { + std::pair<const byte*, size_t> salt = stmt.get_blob(0); + const size_t iterations = stmt.get_size_t(1); + const size_t check_val_db = stmt.get_size_t(2); + + size_t check_val_created; + m_session_key = derive_key(passphrase, + salt.first, + salt.second, + iterations, + check_val_created); + + if(check_val_created != check_val_db) + throw std::runtime_error("Session database password not valid"); + } + } + else { - const std::string err_msg = errmsg; - sqlite3_free(errmsg); - sqlite3_close(m_db); - throw std::runtime_error("sqlite3_exec for table failed - " + err_msg); + // maybe just zap the salts + sessions tables in this case? + if(salts != 0) + throw std::runtime_error("Seemingly corrupted database, multiple salts found"); + + // new database case + + MemoryVector<byte> salt = rng.random_vec(16); + const size_t iterations = 32000; + size_t check_val = 0; + + m_session_key = derive_key(passphrase, &salt[0], salt.size(), + iterations, check_val); + + sqlite3_statement stmt(m_db, "insert into tls_sessions_metadata" + " values(?1, ?2, ?3)"); + + stmt.bind(1, salt); + stmt.bind(2, iterations); + stmt.bind(3, check_val); + + stmt.spin(); } } @@ -141,7 +236,7 @@ Session_Manager_SQLite::~Session_Manager_SQLite() bool Session_Manager_SQLite::load_from_session_id(const MemoryRegion<byte>& session_id, Session& session) { - sqlite3_statement stmt(m_db, "select session from " + m_table_name + " where session_id = ?1"); + sqlite3_statement stmt(m_db, "select session from tls_sessions where session_id = ?1"); stmt.bind(1, hex_encode(session_id)); @@ -153,7 +248,7 @@ bool Session_Manager_SQLite::load_from_session_id(const MemoryRegion<byte>& sess try { - session = Session(blob.first, blob.second); + session = Session::decrypt(blob.first, blob.second, m_session_key); return true; } catch(...) @@ -170,7 +265,7 @@ bool Session_Manager_SQLite::load_from_host_info(const std::string& hostname, u16bit port, Session& session) { - sqlite3_statement stmt(m_db, "select session from " + m_table_name + + sqlite3_statement stmt(m_db, "select session from tls_sessions" " where hostname = ?1 and hostport = ?2" " order by session_start desc"); @@ -185,7 +280,7 @@ bool Session_Manager_SQLite::load_from_host_info(const std::string& hostname, try { - session = Session(blob.first, blob.second); + session = Session::decrypt(blob.first, blob.second, m_session_key); return true; } catch(...) @@ -200,7 +295,7 @@ bool Session_Manager_SQLite::load_from_host_info(const std::string& hostname, void Session_Manager_SQLite::remove_entry(const MemoryRegion<byte>& session_id) { - sqlite3_statement stmt(m_db, "delete from " + m_table_name + " where session_id = ?1"); + sqlite3_statement stmt(m_db, "delete from tls_sessions where session_id = ?1"); stmt.bind(1, hex_encode(session_id)); @@ -209,14 +304,14 @@ void Session_Manager_SQLite::remove_entry(const MemoryRegion<byte>& session_id) void Session_Manager_SQLite::save(const Session& session) { - sqlite3_statement stmt(m_db, "insert or replace into " + m_table_name + + sqlite3_statement stmt(m_db, "insert or replace into tls_sessions" " values(?1, ?2, ?3, ?4, ?5)"); stmt.bind(1, hex_encode(session.session_id())); stmt.bind(2, session.start_time()); stmt.bind(3, session.sni_hostname()); stmt.bind(4, 0); - stmt.bind(5, session.DER_encode()); + stmt.bind(5, session.encrypt(m_session_key, m_rng)); stmt.spin(); @@ -225,26 +320,21 @@ void Session_Manager_SQLite::save(const Session& session) void Session_Manager_SQLite::prune_session_cache() { - sqlite3_statement remove_expired(m_db, "delete from " + m_table_name + " where session_start <= ?1"); + sqlite3_statement remove_expired(m_db, "delete from tls_sessions where session_start <= ?1"); remove_expired.bind(1, system_time() - m_session_lifetime); remove_expired.spin(); - sqlite3_statement row_count(m_db, "select count(*) from " + m_table_name); + const size_t sessions = row_count(m_db, "tls_sessions"); - if(row_count.step() == SQLITE_ROW) + if(sessions > m_max_sessions) { - const size_t sessions = row_count.get_size_t(0); + sqlite3_statement remove_some(m_db, "delete from tls_sessions where session_id in " + "(select session_id from tls_sessions limit ?1)"); - if(sessions > m_max_sessions) - { - sqlite3_statement remove_some(m_db, "delete from " + m_table_name + " where session_id in " - "(select session_id from " + m_table_name + " limit ?1)"); - - remove_some.bind(1, sessions - m_max_sessions); - remove_some.spin(); - } + remove_some.bind(1, sessions - m_max_sessions); + remove_some.spin(); } } diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h index 35a37d206..424db24e5 100644 --- a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h +++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h @@ -9,6 +9,7 @@ #define TLS_SQLITE_SESSION_MANAGER_H__ #include <botan/tls_session_manager.h> +#include <botan/rng.h> class sqlite3; @@ -22,15 +23,18 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager { public: /** - * @param db_filename filename of the SQLite database file - * @param table_name names the table to store sessions in + * @param passphrase used to encrypt the session data + * @param db_filename filename of the SQLite database file. + The table names tls_sessions and tls_sessions_metadata + will be used * @param max_sessions a hint on the maximum number of sessions * to keep in memory at any one time. (If zero, don't cap) * @param session_lifetime sessions are expired after this many * seconds have elapsed from initial handshake. */ - Session_Manager_SQLite(const std::string& db_filename, - const std::string& table_name = "tls_sessions", + Session_Manager_SQLite(const std::string& passphrase, + RandomNumberGenerator& rng, + const std::string& db_filename, size_t max_sessions = 1000, size_t session_lifetime = 7200); @@ -46,12 +50,13 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager void save(const Session& session_data); private: - Session_Manager_SQLite(const Session_Manager_SQLite&) {} - Session_Manager_SQLite& operator=(const Session_Manager_SQLite&) { return (*this); } + Session_Manager_SQLite(const Session_Manager_SQLite&); + Session_Manager_SQLite& operator=(const Session_Manager_SQLite&); void prune_session_cache(); - std::string m_table_name; + SymmetricKey m_session_key; + RandomNumberGenerator& m_rng; size_t m_max_sessions, m_session_lifetime; class sqlite3* m_db; }; diff --git a/src/tls/tls_session.cpp b/src/tls/tls_session.cpp index a41112bf4..c01590000 100644 --- a/src/tls/tls_session.cpp +++ b/src/tls/tls_session.cpp @@ -139,7 +139,7 @@ const u64bit ENCRYPTED_SESSION_MAGIC = 0xACE4480800000000; MemoryVector<byte> Session::encrypt(const SymmetricKey& master_key, - RandomNumberGenerator& rng) + RandomNumberGenerator& rng) const { std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)")); @@ -169,7 +169,7 @@ Session::encrypt(const SymmetricKey& master_key, return out; } -Session Session::decrypt(const MemoryRegion<byte>& buf, +Session Session::decrypt(const byte buf[], size_t buf_len, const SymmetricKey& master_key) { try @@ -181,7 +181,7 @@ Session Session::decrypt(const MemoryRegion<byte>& buf, 16 bytes per AES block * 4 blocks (absolute min amount due to 48 bytes master secret) */ - if(buf.size() < (8 + 16 + 32 + 4*16)) + if(buf_len < (8 + 16 + 32 + 4*16)) throw Decoding_Error("Encrypted TLS session too short to be valid"); std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)")); @@ -192,10 +192,10 @@ Session Session::decrypt(const MemoryRegion<byte>& buf, std::auto_ptr<MessageAuthenticationCode> mac(get_mac("HMAC(SHA-256)")); mac->set_key(hmac_key); - mac->update(&buf[0], buf.size() - 32); + mac->update(&buf[0], buf_len - 32); MemoryVector<byte> computed_mac = mac->final(); - if(!same_mem(&buf[buf.size() - 32], &computed_mac[0], computed_mac.size())) + if(!same_mem(&buf[buf_len - 32], &computed_mac[0], computed_mac.size())) throw Decoding_Error("MAC verification failed for encrypted session"); const u64bit header = load_be<u64bit>(buf, 0); @@ -209,7 +209,7 @@ Session Session::decrypt(const MemoryRegion<byte>& buf, InitializationVector aes_iv(&buf[8], 16); Pipe pipe(get_cipher("AES-256/CBC", aes_key, aes_iv, DECRYPTION)); - pipe.process_msg(&buf[8+16], buf.size() - (32 + 8 + 16)); + pipe.process_msg(&buf[8+16], buf_len - (32 + 8 + 16)); SecureVector<byte> ber = pipe.read_all(); return Session(&ber[0], ber.size()); diff --git a/src/tls/tls_session.h b/src/tls/tls_session.h index ab11154c9..0c57201a4 100644 --- a/src/tls/tls_session.h +++ b/src/tls/tls_session.h @@ -76,17 +76,31 @@ class BOTAN_DLL Session * Encrypt a session (useful for serialization or session tickets) */ MemoryVector<byte> encrypt(const SymmetricKey& key, - RandomNumberGenerator& rng); + RandomNumberGenerator& rng) const; + /** * Decrypt a session created by encrypt * @param ctext the ciphertext returned by encrypt + * @param ctext_size the size of ctext in bytes * @param key the same key used by the encrypting side */ - static Session decrypt(const MemoryRegion<byte>& ctext, + static Session decrypt(const byte ctext[], + size_t ctext_size, const SymmetricKey& key); /** + * Decrypt a session created by encrypt + * @param ctext the ciphertext returned by encrypt + * @param key the same key used by the encrypting side + */ + static inline Session decrypt(const MemoryRegion<byte>& ctext, + const SymmetricKey& key) + { + return Session::decrypt(&ctext[0], ctext.size(), key); + } + + /** * Encode this session data for storage * @warning if the master secret is compromised so is the * session traffic |