Botan  1.11.15
src/lib/tls/sessions_sql/tls_session_manager_sql.cpp
Go to the documentation of this file.
00001 /*
00002 * SQL TLS Session Manager
00003 * (C) 2012,2014 Jack Lloyd
00004 *
00005 * Botan is released under the Simplified BSD License (see license.txt)
00006 */
00007 
00008 #include <botan/tls_session_manager_sql.h>
00009 #include <botan/database.h>
00010 #include <botan/pbkdf.h>
00011 #include <botan/lookup.h>
00012 #include <botan/hex.h>
00013 #include <botan/loadstor.h>
00014 #include <chrono>
00015 
00016 namespace Botan {
00017 
00018 namespace TLS {
00019 
00020 namespace {
00021 
00022 SymmetricKey derive_key(const std::string& passphrase,
00023                         const byte salt[],
00024                         size_t salt_len,
00025                         size_t iterations,
00026                         size_t& check_val)
00027    {
00028    std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)"));
00029 
00030    secure_vector<byte> x = pbkdf->derive_key(32 + 2,
00031                                              passphrase,
00032                                              salt, salt_len,
00033                                              iterations).bits_of();
00034 
00035    check_val = make_u16bit(x[0], x[1]);
00036    return SymmetricKey(&x[2], x.size() - 2);
00037    }
00038 
00039 }
00040 
00041 Session_Manager_SQL::Session_Manager_SQL(std::shared_ptr<SQL_Database> db,
00042                                          const std::string& passphrase,
00043                                          RandomNumberGenerator& rng,
00044                                          size_t max_sessions,
00045                                          std::chrono::seconds session_lifetime) :
00046    m_db(db),
00047    m_rng(rng),
00048    m_max_sessions(max_sessions),
00049    m_session_lifetime(session_lifetime)
00050    {
00051    m_db->create_table(
00052       "create table if not exists tls_sessions "
00053       "("
00054       "session_id TEXT PRIMARY KEY, "
00055       "session_start INTEGER, "
00056       "hostname TEXT, "
00057       "hostport INTEGER, "
00058       "session BLOB"
00059       ")");
00060 
00061    m_db->create_table(
00062       "create table if not exists tls_sessions_metadata "
00063       "("
00064       "passphrase_salt BLOB, "
00065       "passphrase_iterations INTEGER, "
00066       "passphrase_check INTEGER "
00067       ")");
00068 
00069    const size_t salts = m_db->row_count("tls_sessions_metadata");
00070 
00071    if(salts == 1)
00072       {
00073       // existing db
00074       auto stmt = m_db->new_statement("select * from tls_sessions_metadata");
00075 
00076       if(stmt->step())
00077          {
00078          std::pair<const byte*, size_t> salt = stmt->get_blob(0);
00079          const size_t iterations = stmt->get_size_t(1);
00080          const size_t check_val_db = stmt->get_size_t(2);
00081 
00082          size_t check_val_created;
00083          m_session_key = derive_key(passphrase,
00084                                     salt.first,
00085                                     salt.second,
00086                                     iterations,
00087                                     check_val_created);
00088 
00089          if(check_val_created != check_val_db)
00090             throw std::runtime_error("Session database password not valid");
00091          }
00092       }
00093    else
00094       {
00095       // maybe just zap the salts + sessions tables in this case?
00096       if(salts != 0)
00097          throw std::runtime_error("Seemingly corrupted database, multiple salts found");
00098 
00099       // new database case
00100 
00101       std::vector<byte> salt = unlock(rng.random_vec(16));
00102       const size_t iterations = 256 * 1024;
00103       size_t check_val = 0;
00104 
00105       m_session_key = derive_key(passphrase, &salt[0], salt.size(),
00106                                  iterations, check_val);
00107 
00108       auto stmt = m_db->new_statement("insert into tls_sessions_metadata values(?1, ?2, ?3)");
00109 
00110       stmt->bind(1, salt);
00111       stmt->bind(2, iterations);
00112       stmt->bind(3, check_val);
00113 
00114       stmt->spin();
00115       }
00116    }
00117 
00118 bool Session_Manager_SQL::load_from_session_id(const std::vector<byte>& session_id,
00119                                                Session& session)
00120    {
00121    auto stmt = m_db->new_statement("select session from tls_sessions where session_id = ?1");
00122 
00123    stmt->bind(1, hex_encode(session_id));
00124 
00125    while(stmt->step())
00126       {
00127       std::pair<const byte*, size_t> blob = stmt->get_blob(0);
00128 
00129       try
00130          {
00131          session = Session::decrypt(blob.first, blob.second, m_session_key);
00132          return true;
00133          }
00134       catch(...)
00135          {
00136          }
00137       }
00138 
00139    return false;
00140    }
00141 
00142 bool Session_Manager_SQL::load_from_server_info(const Server_Information& server,
00143                                                 Session& session)
00144    {
00145    auto stmt = m_db->new_statement("select session from tls_sessions"
00146                                    " where hostname = ?1 and hostport = ?2"
00147                                    " order by session_start desc");
00148 
00149    stmt->bind(1, server.hostname());
00150    stmt->bind(2, server.port());
00151 
00152    while(stmt->step())
00153       {
00154       std::pair<const byte*, size_t> blob = stmt->get_blob(0);
00155 
00156       try
00157          {
00158          session = Session::decrypt(blob.first, blob.second, m_session_key);
00159          return true;
00160          }
00161       catch(...)
00162          {
00163          }
00164       }
00165 
00166    return false;
00167    }
00168 
00169 void Session_Manager_SQL::remove_entry(const std::vector<byte>& session_id)
00170    {
00171    auto stmt = m_db->new_statement("delete from tls_sessions where session_id = ?1");
00172 
00173    stmt->bind(1, hex_encode(session_id));
00174 
00175    stmt->spin();
00176    }
00177 
00178 void Session_Manager_SQL::save(const Session& session)
00179    {
00180    auto stmt = m_db->new_statement("insert or replace into tls_sessions"
00181                                    " values(?1, ?2, ?3, ?4, ?5)");
00182 
00183    stmt->bind(1, hex_encode(session.session_id()));
00184    stmt->bind(2, session.start_time());
00185    stmt->bind(3, session.server_info().hostname());
00186    stmt->bind(4, session.server_info().port());
00187    stmt->bind(5, session.encrypt(m_session_key, m_rng));
00188 
00189    stmt->spin();
00190 
00191    prune_session_cache();
00192    }
00193 
00194 void Session_Manager_SQL::prune_session_cache()
00195    {
00196    // First expire old sessions
00197    auto remove_expired = m_db->new_statement("delete from tls_sessions where session_start <= ?1");
00198    remove_expired->bind(1, std::chrono::system_clock::now() - m_session_lifetime);
00199    remove_expired->spin();
00200 
00201    const size_t sessions = m_db->row_count("tls_sessions");
00202 
00203    // Then if needed expire some more sessions at random
00204    if(sessions > m_max_sessions)
00205       {
00206       auto remove_some = m_db->new_statement("delete from tls_sessions where session_id in "
00207                                              "(select session_id from tls_sessions limit ?1)");
00208 
00209       remove_some->bind(1, sessions - m_max_sessions);
00210       remove_some->spin();
00211       }
00212    }
00213 
00214 }
00215 
00216 }