diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp')
-rw-r--r-- | comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp b/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp new file mode 100644 index 0000000000..09cfbc4e99 --- /dev/null +++ b/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp @@ -0,0 +1,213 @@ +/* +* SQL TLS Session Manager +* (C) 2012,2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/tls_session_manager_sql.h> +#include <botan/database.h> +#include <botan/pbkdf.h> +#include <botan/hex.h> +#include <botan/rng.h> +#include <botan/loadstor.h> +#include <chrono> + +namespace Botan { + +namespace TLS { + +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"); + + std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)")); + + if(salts == 1) + { + // existing db + auto stmt = m_db->new_statement("select * from tls_sessions_metadata"); + + if(stmt->step()) + { + std::pair<const uint8_t*, 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); + + secure_vector<uint8_t> x = pbkdf->pbkdf_iterations(32 + 2, + passphrase, + salt.first, salt.second, + iterations); + + const size_t check_val_created = make_uint16(x[0], x[1]); + m_session_key.assign(x.begin() + 2, x.end()); + + if(check_val_created != check_val_db) + throw Invalid_Argument("Session database password not valid"); + } + } + else + { + // maybe just zap the salts + sessions tables in this case? + if(salts != 0) + throw Internal_Error("Seemingly corrupted TLS session db, multiple salts found"); + + // new database case + + std::vector<uint8_t> salt; + rng.random_vec(salt, 16); + size_t iterations = 0; + + secure_vector<uint8_t> x = pbkdf->pbkdf_timed(32 + 2, + passphrase, + salt.data(), salt.size(), + std::chrono::milliseconds(100), + iterations); + + size_t check_val = make_uint16(x[0], x[1]); + m_session_key.assign(x.begin() + 2, x.end()); + + 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<uint8_t>& 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 uint8_t*, 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 uint8_t*, 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<uint8_t>& 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(); + } + +size_t Session_Manager_SQL::remove_all() + { + auto stmt = m_db->new_statement("delete from tls_sessions"); + return stmt->spin(); + } + +void Session_Manager_SQL::save(const Session& session) + { + if(session.server_info().hostname().empty()) + return; + + 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(); + } + } + +} + +} |