2
* SQL TLS Session Manager
3
* (C) 2012,2014 Jack Lloyd
5
* Botan is released under the Simplified BSD License (see license.txt)
8
#include <botan/tls_session_manager_sql.h>
9
#include <botan/database.h>
10
#include <botan/pbkdf.h>
11
#include <botan/hex.h>
12
#include <botan/rng.h>
13
#include <botan/loadstor.h>
20
Session_Manager_SQL::Session_Manager_SQL(std::shared_ptr<SQL_Database> db,
21
const std::string& passphrase,
22
RandomNumberGenerator& rng,
24
std::chrono::seconds session_lifetime) :
27
m_max_sessions(max_sessions),
28
m_session_lifetime(session_lifetime)
31
"create table if not exists tls_sessions "
33
"session_id TEXT PRIMARY KEY, "
34
"session_start INTEGER, "
41
"create table if not exists tls_sessions_metadata "
43
"passphrase_salt BLOB, "
44
"passphrase_iterations INTEGER, "
45
"passphrase_check INTEGER "
48
const size_t salts = m_db->row_count("tls_sessions_metadata");
50
std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)"));
55
auto stmt = m_db->new_statement("select * from tls_sessions_metadata");
59
std::pair<const uint8_t*, size_t> salt = stmt->get_blob(0);
60
const size_t iterations = stmt->get_size_t(1);
61
const size_t check_val_db = stmt->get_size_t(2);
63
secure_vector<uint8_t> x = pbkdf->pbkdf_iterations(32 + 2,
65
salt.first, salt.second,
68
const size_t check_val_created = make_uint16(x[0], x[1]);
69
m_session_key.assign(x.begin() + 2, x.end());
71
if(check_val_created != check_val_db)
72
throw Exception("Session database password not valid");
77
// maybe just zap the salts + sessions tables in this case?
79
throw Exception("Seemingly corrupted database, multiple salts found");
83
std::vector<uint8_t> salt = unlock(rng.random_vec(16));
84
size_t iterations = 0;
86
secure_vector<uint8_t> x = pbkdf->pbkdf_timed(32 + 2,
88
salt.data(), salt.size(),
89
std::chrono::milliseconds(100),
92
size_t check_val = make_uint16(x[0], x[1]);
93
m_session_key.assign(x.begin() + 2, x.end());
95
auto stmt = m_db->new_statement("insert into tls_sessions_metadata values(?1, ?2, ?3)");
98
stmt->bind(2, iterations);
99
stmt->bind(3, check_val);
105
bool Session_Manager_SQL::load_from_session_id(const std::vector<uint8_t>& session_id,
108
auto stmt = m_db->new_statement("select session from tls_sessions where session_id = ?1");
110
stmt->bind(1, hex_encode(session_id));
114
std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
118
session = Session::decrypt(blob.first, blob.second, m_session_key);
129
bool Session_Manager_SQL::load_from_server_info(const Server_Information& server,
132
auto stmt = m_db->new_statement("select session from tls_sessions"
133
" where hostname = ?1 and hostport = ?2"
134
" order by session_start desc");
136
stmt->bind(1, server.hostname());
137
stmt->bind(2, server.port());
141
std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
145
session = Session::decrypt(blob.first, blob.second, m_session_key);
156
void Session_Manager_SQL::remove_entry(const std::vector<uint8_t>& session_id)
158
auto stmt = m_db->new_statement("delete from tls_sessions where session_id = ?1");
160
stmt->bind(1, hex_encode(session_id));
165
size_t Session_Manager_SQL::remove_all()
167
auto stmt = m_db->new_statement("delete from tls_sessions");
171
void Session_Manager_SQL::save(const Session& session)
173
if(session.server_info().hostname().empty())
176
auto stmt = m_db->new_statement("insert or replace into tls_sessions"
177
" values(?1, ?2, ?3, ?4, ?5)");
179
stmt->bind(1, hex_encode(session.session_id()));
180
stmt->bind(2, session.start_time());
181
stmt->bind(3, session.server_info().hostname());
182
stmt->bind(4, session.server_info().port());
183
stmt->bind(5, session.encrypt(m_session_key, m_rng));
187
prune_session_cache();
190
void Session_Manager_SQL::prune_session_cache()
192
// First expire old sessions
193
auto remove_expired = m_db->new_statement("delete from tls_sessions where session_start <= ?1");
194
remove_expired->bind(1, std::chrono::system_clock::now() - m_session_lifetime);
195
remove_expired->spin();
197
const size_t sessions = m_db->row_count("tls_sessions");
199
// Then if needed expire some more sessions at random
200
if(sessions > m_max_sessions)
202
auto remove_some = m_db->new_statement("delete from tls_sessions where session_id in "
203
"(select session_id from tls_sessions limit ?1)");
205
remove_some->bind(1, sessions - m_max_sessions);