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