Botan  2.8.0
Crypto and TLS for C++11
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 
16 namespace Botan {
17 
18 namespace TLS {
19 
20 Session_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 Exception("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 Exception("Seemingly corrupted database, multiple salts found");
80 
81  // new database case
82 
83  std::vector<uint8_t> salt = unlock(rng.random_vec(16));
84  size_t iterations = 0;
85 
86  secure_vector<uint8_t> x = pbkdf->pbkdf_timed(32 + 2,
87  passphrase,
88  salt.data(), salt.size(),
89  std::chrono::milliseconds(100),
90  iterations);
91 
92  size_t check_val = make_uint16(x[0], x[1]);
93  m_session_key.assign(x.begin() + 2, x.end());
94 
95  auto stmt = m_db->new_statement("insert into tls_sessions_metadata values(?1, ?2, ?3)");
96 
97  stmt->bind(1, salt);
98  stmt->bind(2, iterations);
99  stmt->bind(3, check_val);
100 
101  stmt->spin();
102  }
103  }
104 
105 bool Session_Manager_SQL::load_from_session_id(const std::vector<uint8_t>& session_id,
106  Session& session)
107  {
108  auto stmt = m_db->new_statement("select session from tls_sessions where session_id = ?1");
109 
110  stmt->bind(1, hex_encode(session_id));
111 
112  while(stmt->step())
113  {
114  std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
115 
116  try
117  {
118  session = Session::decrypt(blob.first, blob.second, m_session_key);
119  return true;
120  }
121  catch(...)
122  {
123  }
124  }
125 
126  return false;
127  }
128 
130  Session& session)
131  {
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");
135 
136  stmt->bind(1, server.hostname());
137  stmt->bind(2, server.port());
138 
139  while(stmt->step())
140  {
141  std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
142 
143  try
144  {
145  session = Session::decrypt(blob.first, blob.second, m_session_key);
146  return true;
147  }
148  catch(...)
149  {
150  }
151  }
152 
153  return false;
154  }
155 
156 void Session_Manager_SQL::remove_entry(const std::vector<uint8_t>& session_id)
157  {
158  auto stmt = m_db->new_statement("delete from tls_sessions where session_id = ?1");
159 
160  stmt->bind(1, hex_encode(session_id));
161 
162  stmt->spin();
163  }
164 
166  {
167  auto stmt = m_db->new_statement("delete from tls_sessions");
168  return stmt->spin();
169  }
170 
171 void Session_Manager_SQL::save(const Session& session)
172  {
173  if(session.server_info().hostname().empty())
174  return;
175 
176  auto stmt = m_db->new_statement("insert or replace into tls_sessions"
177  " values(?1, ?2, ?3, ?4, ?5)");
178 
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));
184 
185  stmt->spin();
186 
187  prune_session_cache();
188  }
189 
190 void Session_Manager_SQL::prune_session_cache()
191  {
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();
196 
197  const size_t sessions = m_db->row_count("tls_sessions");
198 
199  // Then if needed expire some more sessions at random
200  if(sessions > m_max_sessions)
201  {
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)");
204 
205  remove_some->bind(1, sessions - m_max_sessions);
206  remove_some->spin();
207  }
208  }
209 
210 }
211 
212 }
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition: hex.cpp:14
const std::vector< uint8_t > & session_id() const
Definition: tls_session.h:149
static Session decrypt(const uint8_t ctext[], size_t ctext_size, const SymmetricKey &key)
secure_vector< uint8_t > random_vec(size_t bytes)
Definition: rng.h:141
std::chrono::system_clock::time_point start_time() const
Definition: tls_session.h:168
bool load_from_server_info(const Server_Information &info, Session &session) override
std::string hostname() const
const Server_Information & server_info() const
Definition: tls_session.h:183
std::vector< uint8_t > encrypt(const SymmetricKey &key, RandomNumberGenerator &rng) const
void remove_entry(const std::vector< uint8_t > &session_id) override
bool load_from_session_id(const std::vector< uint8_t > &session_id, Session &session) override
PBKDF * get_pbkdf(const std::string &algo_spec, const std::string &provider="")
Definition: pbkdf.h:232
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))
Definition: alg_id.cpp:13
std::vector< T > unlock(const secure_vector< T > &in)
Definition: secmem.h:95
void save(const Session &session_data) override
uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition: loadstor.h:52
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:88