Botan 2.19.3
Crypto and TLS for C&
tls_session_manager_sql.cpp
Go to the documentation of this file.
1/*
2* SQL TLS Session Manager
3* (C) 2012,2014 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
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>
14#include <chrono>
15
16namespace Botan {
17
18namespace TLS {
19
20Session_Manager_SQL::Session_Manager_SQL(std::shared_ptr<SQL_Database> db,
21 const std::string& passphrase,
23 size_t max_sessions,
24 std::chrono::seconds session_lifetime) :
25 m_db(db),
26 m_rng(rng),
27 m_max_sessions(max_sessions),
28 m_session_lifetime(session_lifetime)
29 {
30 m_db->create_table(
31 "create table if not exists tls_sessions "
32 "("
33 "session_id TEXT PRIMARY KEY, "
34 "session_start INTEGER, "
35 "hostname TEXT, "
36 "hostport INTEGER, "
37 "session BLOB"
38 ")");
39
40 m_db->create_table(
41 "create table if not exists tls_sessions_metadata "
42 "("
43 "passphrase_salt BLOB, "
44 "passphrase_iterations INTEGER, "
45 "passphrase_check INTEGER "
46 ")");
47
48 const size_t salts = m_db->row_count("tls_sessions_metadata");
49
50 std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)"));
51
52 if(salts == 1)
53 {
54 // existing db
55 auto stmt = m_db->new_statement("select * from tls_sessions_metadata");
56
57 if(stmt->step())
58 {
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);
62
63 secure_vector<uint8_t> x = pbkdf->pbkdf_iterations(32 + 2,
64 passphrase,
65 salt.first, salt.second,
66 iterations);
67
68 const size_t check_val_created = make_uint16(x[0], x[1]);
69 m_session_key.assign(x.begin() + 2, x.end());
70
71 if(check_val_created != check_val_db)
72 throw Invalid_Argument("Session database password not valid");
73 }
74 }
75 else
76 {
77 // maybe just zap the salts + sessions tables in this case?
78 if(salts != 0)
79 throw Internal_Error("Seemingly corrupted TLS session db, multiple salts found");
80
81 // new database case
82
83 std::vector<uint8_t> salt;
84 rng.random_vec(salt, 16);
85 size_t iterations = 0;
86
87 secure_vector<uint8_t> x = pbkdf->pbkdf_timed(32 + 2,
88 passphrase,
89 salt.data(), salt.size(),
90 std::chrono::milliseconds(100),
91 iterations);
92
93 size_t check_val = make_uint16(x[0], x[1]);
94 m_session_key.assign(x.begin() + 2, x.end());
95
96 auto stmt = m_db->new_statement("insert into tls_sessions_metadata values(?1, ?2, ?3)");
97
98 stmt->bind(1, salt);
99 stmt->bind(2, iterations);
100 stmt->bind(3, check_val);
101
102 stmt->spin();
103 }
104 }
105
106bool Session_Manager_SQL::load_from_session_id(const std::vector<uint8_t>& session_id,
107 Session& session)
108 {
109 auto stmt = m_db->new_statement("select session from tls_sessions where session_id = ?1");
110
111 stmt->bind(1, hex_encode(session_id));
112
113 while(stmt->step())
114 {
115 std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
116
117 try
118 {
119 session = Session::decrypt(blob.first, blob.second, m_session_key);
120 return true;
121 }
122 catch(...)
123 {
124 }
125 }
126
127 return false;
128 }
129
131 Session& session)
132 {
133 auto stmt = m_db->new_statement("select session from tls_sessions"
134 " where hostname = ?1 and hostport = ?2"
135 " order by session_start desc");
136
137 stmt->bind(1, server.hostname());
138 stmt->bind(2, server.port());
139
140 while(stmt->step())
141 {
142 std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
143
144 try
145 {
146 session = Session::decrypt(blob.first, blob.second, m_session_key);
147 return true;
148 }
149 catch(...)
150 {
151 }
152 }
153
154 return false;
155 }
156
157void Session_Manager_SQL::remove_entry(const std::vector<uint8_t>& session_id)
158 {
159 auto stmt = m_db->new_statement("delete from tls_sessions where session_id = ?1");
160
161 stmt->bind(1, hex_encode(session_id));
162
163 stmt->spin();
164 }
165
167 {
168 auto stmt = m_db->new_statement("delete from tls_sessions");
169 return stmt->spin();
170 }
171
173 {
174 if(session.server_info().hostname().empty())
175 return;
176
177 auto stmt = m_db->new_statement("insert or replace into tls_sessions"
178 " values(?1, ?2, ?3, ?4, ?5)");
179
180 stmt->bind(1, hex_encode(session.session_id()));
181 stmt->bind(2, session.start_time());
182 stmt->bind(3, session.server_info().hostname());
183 stmt->bind(4, session.server_info().port());
184 stmt->bind(5, session.encrypt(m_session_key, m_rng));
185
186 stmt->spin();
187
188 prune_session_cache();
189 }
190
191void Session_Manager_SQL::prune_session_cache()
192 {
193 // First expire old sessions
194 auto remove_expired = m_db->new_statement("delete from tls_sessions where session_start <= ?1");
195 remove_expired->bind(1, std::chrono::system_clock::now() - m_session_lifetime);
196 remove_expired->spin();
197
198 const size_t sessions = m_db->row_count("tls_sessions");
199
200 // Then if needed expire some more sessions at random
201 if(sessions > m_max_sessions)
202 {
203 auto remove_some = m_db->new_statement("delete from tls_sessions where session_id in "
204 "(select session_id from tls_sessions limit ?1)");
205
206 remove_some->bind(1, sessions - m_max_sessions);
207 remove_some->spin();
208 }
209 }
210
211}
212
213}
secure_vector< uint8_t > random_vec(size_t bytes)
Definition rng.h:143
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))
void save(const Session &session_data) override
bool load_from_server_info(const Server_Information &info, Session &session) override
bool load_from_session_id(const std::vector< uint8_t > &session_id, Session &session) override
void remove_entry(const std::vector< uint8_t > &session_id) override
const Server_Information & server_info() const
std::vector< uint8_t > encrypt(const SymmetricKey &key, RandomNumberGenerator &rng) const
std::chrono::system_clock::time_point start_time() const
const std::vector< uint8_t > & session_id() const
static Session decrypt(const uint8_t ctext[], size_t ctext_size, const SymmetricKey &key)
PBKDF * get_pbkdf(const std::string &algo_spec, const std::string &provider="")
Definition pbkdf.h:232
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition hex.cpp:31
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:65
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition loadstor.h:54