Botan  2.15.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 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 
106 bool 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 
157 void 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 
172 void Session_Manager_SQL::save(const Session& session)
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 
191 void 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 }
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
void save(const Session &session_data) override
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