diff options
author | lloyd <[email protected]> | 2014-12-20 13:45:23 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2014-12-20 13:45:23 +0000 |
commit | 4083193089f91ec11584ae729ecc3b4cc3b4b86a (patch) | |
tree | d6edc4e416f3eb34aeb91b00d2bee6386962f316 /src/lib/tls | |
parent | 4562cd4366c81c905dc8957837c6128b193a28bd (diff) |
Add abstract database interface so applications can easily store info
in places other than sqlite3, though sqlite3 remains the only
implementation. The interface is currently limited to precisely the
functionality the TLS session manager needs and will likely expand.
Diffstat (limited to 'src/lib/tls')
-rw-r--r-- | src/lib/tls/sessions_sql/info.txt | 5 | ||||
-rw-r--r-- | src/lib/tls/sessions_sql/tls_session_manager_sql.cpp | 215 | ||||
-rw-r--r-- | src/lib/tls/sessions_sql/tls_session_manager_sql.h (renamed from src/lib/tls/sessions_sqlite/tls_session_manager_sqlite.h) | 40 | ||||
-rw-r--r-- | src/lib/tls/sessions_sqlite/tls_session_manager_sqlite.cpp | 222 | ||||
-rw-r--r-- | src/lib/tls/sessions_sqlite3/info.txt (renamed from src/lib/tls/sessions_sqlite/info.txt) | 2 | ||||
-rw-r--r-- | src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp | 29 | ||||
-rw-r--r-- | src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h | 52 |
7 files changed, 321 insertions, 244 deletions
diff --git a/src/lib/tls/sessions_sql/info.txt b/src/lib/tls/sessions_sql/info.txt new file mode 100644 index 000000000..7016a3d42 --- /dev/null +++ b/src/lib/tls/sessions_sql/info.txt @@ -0,0 +1,5 @@ +define TLS_SESSION_MANAGER_SQL_DB 20141219 + +<requires> +pbkdf2 +</requires> diff --git a/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp b/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp new file mode 100644 index 000000000..561939def --- /dev/null +++ b/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp @@ -0,0 +1,215 @@ +/* +* SQL TLS Session Manager +* (C) 2012,2014 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_session_manager_sql.h> +#include <botan/database.h> +#include <botan/lookup.h> +#include <botan/hex.h> +#include <botan/loadstor.h> +#include <chrono> + +namespace Botan { + +namespace TLS { + +namespace { + +SymmetricKey derive_key(const std::string& passphrase, + const byte salt[], + size_t salt_len, + size_t iterations, + size_t& check_val) + { + std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)")); + + secure_vector<byte> x = pbkdf->derive_key(32 + 2, + passphrase, + salt, salt_len, + iterations).bits_of(); + + check_val = make_u16bit(x[0], x[1]); + return SymmetricKey(&x[2], x.size() - 2); + } + +} + +Session_Manager_SQL::Session_Manager_SQL(std::shared_ptr<SQL_Database> db, + const std::string& passphrase, + RandomNumberGenerator& rng, + size_t max_sessions, + std::chrono::seconds session_lifetime) : + m_db(db), + m_rng(rng), + m_max_sessions(max_sessions), + m_session_lifetime(session_lifetime) + { + m_db->create_table( + "create table if not exists tls_sessions " + "(" + "session_id TEXT PRIMARY KEY, " + "session_start INTEGER, " + "hostname TEXT, " + "hostport INTEGER, " + "session BLOB" + ")"); + + m_db->create_table( + "create table if not exists tls_sessions_metadata " + "(" + "passphrase_salt BLOB, " + "passphrase_iterations INTEGER, " + "passphrase_check INTEGER " + ")"); + + const size_t salts = m_db->row_count("tls_sessions_metadata"); + + if(salts == 1) + { + // existing db + auto stmt = m_db->new_statement("select * from tls_sessions_metadata"); + + if(stmt->step()) + { + 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 + { + // 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 + + std::vector<byte> salt = unlock(rng.random_vec(16)); + const size_t iterations = 256 * 1024; + size_t check_val = 0; + + m_session_key = derive_key(passphrase, &salt[0], salt.size(), + iterations, check_val); + + auto stmt = m_db->new_statement("insert into tls_sessions_metadata values(?1, ?2, ?3)"); + + stmt->bind(1, salt); + stmt->bind(2, iterations); + stmt->bind(3, check_val); + + stmt->spin(); + } + } + +bool Session_Manager_SQL::load_from_session_id(const std::vector<byte>& session_id, + Session& session) + { + auto stmt = m_db->new_statement("select session from tls_sessions where session_id = ?1"); + + stmt->bind(1, hex_encode(session_id)); + + while(stmt->step()) + { + std::pair<const byte*, size_t> blob = stmt->get_blob(0); + + try + { + session = Session::decrypt(blob.first, blob.second, m_session_key); + return true; + } + catch(...) + { + } + } + + return false; + } + +bool Session_Manager_SQL::load_from_server_info(const Server_Information& server, + Session& session) + { + auto stmt = m_db->new_statement("select session from tls_sessions" + " where hostname = ?1 and hostport = ?2" + " order by session_start desc"); + + stmt->bind(1, server.hostname()); + stmt->bind(2, server.port()); + + while(stmt->step()) + { + std::pair<const byte*, size_t> blob = stmt->get_blob(0); + + try + { + session = Session::decrypt(blob.first, blob.second, m_session_key); + return true; + } + catch(...) + { + } + } + + return false; + } + +void Session_Manager_SQL::remove_entry(const std::vector<byte>& session_id) + { + auto stmt = m_db->new_statement("delete from tls_sessions where session_id = ?1"); + + stmt->bind(1, hex_encode(session_id)); + + stmt->spin(); + } + +void Session_Manager_SQL::save(const Session& session) + { + auto stmt = m_db->new_statement("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.server_info().hostname()); + stmt->bind(4, session.server_info().port()); + stmt->bind(5, session.encrypt(m_session_key, m_rng)); + + stmt->spin(); + + prune_session_cache(); + } + +void Session_Manager_SQL::prune_session_cache() + { + // First expire old sessions + auto remove_expired = m_db->new_statement("delete from tls_sessions where session_start <= ?1"); + remove_expired->bind(1, std::chrono::system_clock::now() - m_session_lifetime); + remove_expired->spin(); + + const size_t sessions = m_db->row_count("tls_sessions"); + + // Then if needed expire some more sessions at random + if(sessions > m_max_sessions) + { + auto remove_some = m_db->new_statement("delete from tls_sessions where session_id in " + "(select session_id from tls_sessions limit ?1)"); + + remove_some->bind(1, sessions - m_max_sessions); + remove_some->spin(); + } + } + +} + +} diff --git a/src/lib/tls/sessions_sqlite/tls_session_manager_sqlite.h b/src/lib/tls/sessions_sql/tls_session_manager_sql.h index 7892ccd6a..0935b73ac 100644 --- a/src/lib/tls/sessions_sqlite/tls_session_manager_sqlite.h +++ b/src/lib/tls/sessions_sql/tls_session_manager_sql.h @@ -1,51 +1,52 @@ /* -* SQLite3 TLS Session Manager -* (C) 2012 Jack Lloyd +* TLS Session Manager storing to encrypted SQL db table +* (C) 2012,2014 Jack Lloyd * * Released under the terms of the Botan license */ -#ifndef BOTAN_TLS_SQLITE3_SESSION_MANAGER_H__ -#define BOTAN_TLS_SQLITE3_SESSION_MANAGER_H__ +#ifndef BOTAN_TLS_SQL_SESSION_MANAGER_H__ +#define BOTAN_TLS_SQL_SESSION_MANAGER_H__ #include <botan/tls_session_manager.h> +#include <botan/database.h> #include <botan/rng.h> namespace Botan { -class sqlite3_database; - namespace TLS { /** -* An implementation of Session_Manager that saves values in a SQLite3 +* An implementation of Session_Manager that saves values in a SQL * database file, with the session data encrypted using a passphrase. * * @warning For clients, the hostnames associated with the saved * sessions are stored in the database in plaintext. This may be a * serious privacy risk in some situations. */ -class BOTAN_DLL Session_Manager_SQLite : public Session_Manager +class BOTAN_DLL Session_Manager_SQL : public Session_Manager { public: /** + * @param db A connection to the database to use + The table names botan_tls_sessions and + botan_tls_sessions_metadata will be used * @param passphrase used to encrypt the session data * @param rng a random number generator - * @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& passphrase, - RandomNumberGenerator& rng, - const std::string& db_filename, - size_t max_sessions = 1000, - std::chrono::seconds session_lifetime = std::chrono::seconds(7200)); + Session_Manager_SQL(std::shared_ptr<SQL_Database> db, + const std::string& passphrase, + RandomNumberGenerator& rng, + size_t max_sessions = 1000, + std::chrono::seconds session_lifetime = std::chrono::seconds(7200)); + + Session_Manager_SQL(const Session_Manager_SQL&) = delete; - ~Session_Manager_SQLite(); + Session_Manager_SQL& operator=(const Session_Manager_SQL&) = delete; bool load_from_session_id(const std::vector<byte>& session_id, Session& session) override; @@ -61,16 +62,13 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager { return m_session_lifetime; } private: - Session_Manager_SQLite(const Session_Manager_SQLite&); - Session_Manager_SQLite& operator=(const Session_Manager_SQLite&); - void prune_session_cache(); + std::shared_ptr<SQL_Database> m_db; SymmetricKey m_session_key; RandomNumberGenerator& m_rng; size_t m_max_sessions; std::chrono::seconds m_session_lifetime; - sqlite3_database* m_db; }; } diff --git a/src/lib/tls/sessions_sqlite/tls_session_manager_sqlite.cpp b/src/lib/tls/sessions_sqlite/tls_session_manager_sqlite.cpp deleted file mode 100644 index 21483067f..000000000 --- a/src/lib/tls/sessions_sqlite/tls_session_manager_sqlite.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* -* SQLite TLS Session Manager -* (C) 2012 Jack Lloyd -* -* Released under the terms of the Botan license -*/ - -#include <botan/tls_session_manager_sqlite.h> -#include <botan/internal/sqlite3.h> -#include <botan/lookup.h> -#include <botan/hex.h> -#include <botan/loadstor.h> -#include <chrono> - -namespace Botan { - -namespace TLS { - -namespace { - -SymmetricKey derive_key(const std::string& passphrase, - const byte salt[], - size_t salt_len, - size_t iterations, - size_t& check_val) - { - std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)")); - - secure_vector<byte> x = pbkdf->derive_key(32 + 2, - passphrase, - salt, salt_len, - iterations).bits_of(); - - check_val = make_u16bit(x[0], x[1]); - return SymmetricKey(&x[2], x.size() - 2); - } - -} - -Session_Manager_SQLite::Session_Manager_SQLite(const std::string& passphrase, - RandomNumberGenerator& rng, - const std::string& db_filename, - size_t max_sessions, - std::chrono::seconds session_lifetime) : - m_rng(rng), - m_max_sessions(max_sessions), - m_session_lifetime(session_lifetime) - { - m_db = new sqlite3_database(db_filename); - - m_db->create_table( - "create table if not exists tls_sessions " - "(" - "session_id TEXT PRIMARY KEY, " - "session_start INTEGER, " - "hostname TEXT, " - "hostport INTEGER, " - "session BLOB" - ")"); - - m_db->create_table( - "create table if not exists tls_sessions_metadata " - "(" - "passphrase_salt BLOB, " - "passphrase_iterations INTEGER, " - "passphrase_check INTEGER " - ")"); - - const size_t salts = m_db->row_count("tls_sessions_metadata"); - - if(salts == 1) - { - // existing db - sqlite3_statement stmt(m_db, "select * from tls_sessions_metadata"); - - if(stmt.step()) - { - 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 - { - // 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 - - std::vector<byte> salt = unlock(rng.random_vec(16)); - const size_t iterations = 256 * 1024; - 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(); - } - } - -Session_Manager_SQLite::~Session_Manager_SQLite() - { - delete m_db; - } - -bool Session_Manager_SQLite::load_from_session_id(const std::vector<byte>& session_id, - Session& session) - { - sqlite3_statement stmt(m_db, "select session from tls_sessions where session_id = ?1"); - - stmt.bind(1, hex_encode(session_id)); - - while(stmt.step()) - { - std::pair<const byte*, size_t> blob = stmt.get_blob(0); - - try - { - session = Session::decrypt(blob.first, blob.second, m_session_key); - return true; - } - catch(...) - { - } - } - - return false; - } - -bool Session_Manager_SQLite::load_from_server_info(const Server_Information& server, - Session& session) - { - sqlite3_statement stmt(m_db, "select session from tls_sessions" - " where hostname = ?1 and hostport = ?2" - " order by session_start desc"); - - stmt.bind(1, server.hostname()); - stmt.bind(2, server.port()); - - while(stmt.step()) - { - std::pair<const byte*, size_t> blob = stmt.get_blob(0); - - try - { - session = Session::decrypt(blob.first, blob.second, m_session_key); - return true; - } - catch(...) - { - } - } - - return false; - } - -void Session_Manager_SQLite::remove_entry(const std::vector<byte>& session_id) - { - sqlite3_statement stmt(m_db, "delete from tls_sessions where session_id = ?1"); - - stmt.bind(1, hex_encode(session_id)); - - stmt.spin(); - } - -void Session_Manager_SQLite::save(const Session& session) - { - 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.server_info().hostname()); - stmt.bind(4, session.server_info().port()); - stmt.bind(5, session.encrypt(m_session_key, m_rng)); - - stmt.spin(); - - prune_session_cache(); - } - -void Session_Manager_SQLite::prune_session_cache() - { - sqlite3_statement remove_expired(m_db, "delete from tls_sessions where session_start <= ?1"); - - remove_expired.bind(1, std::chrono::system_clock::now() - m_session_lifetime); - - remove_expired.spin(); - - const size_t sessions = m_db->row_count("tls_sessions"); - - if(sessions > m_max_sessions) - { - sqlite3_statement remove_some(m_db, "delete from tls_sessions where session_id in " - "(select session_id from tls_sessions limit ?1)"); - - remove_some.bind(1, sessions - m_max_sessions); - remove_some.spin(); - } - } - -} - -} diff --git a/src/lib/tls/sessions_sqlite/info.txt b/src/lib/tls/sessions_sqlite3/info.txt index 76d53f995..b04b6a9d6 100644 --- a/src/lib/tls/sessions_sqlite/info.txt +++ b/src/lib/tls/sessions_sqlite3/info.txt @@ -1,6 +1,6 @@ define TLS_SQLITE3_SESSION_MANAGER 20131128 <requires> -pbkdf2 +sessions_sql sqlite3 </requires> diff --git a/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp b/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp new file mode 100644 index 000000000..30af3699f --- /dev/null +++ b/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp @@ -0,0 +1,29 @@ +/* +* SQLite TLS Session Manager +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_session_manager_sqlite.h> +#include <botan/sqlite3.h> + +namespace Botan { + +namespace TLS { + +Session_Manager_SQLite::Session_Manager_SQLite(const std::string& passphrase, + RandomNumberGenerator& rng, + const std::string& db_filename, + size_t max_sessions, + std::chrono::seconds session_lifetime) : + Session_Manager_SQL(std::make_shared<Sqlite3_Database>(db_filename), + passphrase, + rng, + max_sessions, + session_lifetime) + {} + +} + +} diff --git a/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h b/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h new file mode 100644 index 000000000..67c1c9e53 --- /dev/null +++ b/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h @@ -0,0 +1,52 @@ +/* +* SQLite3 TLS Session Manager +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_SQLITE3_SESSION_MANAGER_H__ +#define BOTAN_TLS_SQLITE3_SESSION_MANAGER_H__ + +#include <botan/tls_session_manager_sql.h> +#include <botan/rng.h> + +namespace Botan { + +namespace TLS { + +/** +* An implementation of Session_Manager that saves values in a SQLite3 +* database file, with the session data encrypted using a passphrase. +* +* @warning For clients, the hostnames associated with the saved +* sessions are stored in the database in plaintext. This may be a +* serious privacy risk in some situations. +*/ +class BOTAN_DLL +Session_Manager_SQLite : public Session_Manager_SQL + { + public: + /** + * @param passphrase used to encrypt the session data + * @param rng a random number generator + * @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& passphrase, + RandomNumberGenerator& rng, + const std::string& db_filename, + size_t max_sessions = 1000, + std::chrono::seconds session_lifetime = std::chrono::seconds(7200)); +}; + +} + +} + +#endif |