Botan
1.11.15
|
00001 /* 00002 * SQL TLS Session Manager 00003 * (C) 2012,2014 Jack Lloyd 00004 * 00005 * Botan is released under the Simplified BSD License (see license.txt) 00006 */ 00007 00008 #include <botan/tls_session_manager_sql.h> 00009 #include <botan/database.h> 00010 #include <botan/pbkdf.h> 00011 #include <botan/lookup.h> 00012 #include <botan/hex.h> 00013 #include <botan/loadstor.h> 00014 #include <chrono> 00015 00016 namespace Botan { 00017 00018 namespace TLS { 00019 00020 namespace { 00021 00022 SymmetricKey derive_key(const std::string& passphrase, 00023 const byte salt[], 00024 size_t salt_len, 00025 size_t iterations, 00026 size_t& check_val) 00027 { 00028 std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)")); 00029 00030 secure_vector<byte> x = pbkdf->derive_key(32 + 2, 00031 passphrase, 00032 salt, salt_len, 00033 iterations).bits_of(); 00034 00035 check_val = make_u16bit(x[0], x[1]); 00036 return SymmetricKey(&x[2], x.size() - 2); 00037 } 00038 00039 } 00040 00041 Session_Manager_SQL::Session_Manager_SQL(std::shared_ptr<SQL_Database> db, 00042 const std::string& passphrase, 00043 RandomNumberGenerator& rng, 00044 size_t max_sessions, 00045 std::chrono::seconds session_lifetime) : 00046 m_db(db), 00047 m_rng(rng), 00048 m_max_sessions(max_sessions), 00049 m_session_lifetime(session_lifetime) 00050 { 00051 m_db->create_table( 00052 "create table if not exists tls_sessions " 00053 "(" 00054 "session_id TEXT PRIMARY KEY, " 00055 "session_start INTEGER, " 00056 "hostname TEXT, " 00057 "hostport INTEGER, " 00058 "session BLOB" 00059 ")"); 00060 00061 m_db->create_table( 00062 "create table if not exists tls_sessions_metadata " 00063 "(" 00064 "passphrase_salt BLOB, " 00065 "passphrase_iterations INTEGER, " 00066 "passphrase_check INTEGER " 00067 ")"); 00068 00069 const size_t salts = m_db->row_count("tls_sessions_metadata"); 00070 00071 if(salts == 1) 00072 { 00073 // existing db 00074 auto stmt = m_db->new_statement("select * from tls_sessions_metadata"); 00075 00076 if(stmt->step()) 00077 { 00078 std::pair<const byte*, size_t> salt = stmt->get_blob(0); 00079 const size_t iterations = stmt->get_size_t(1); 00080 const size_t check_val_db = stmt->get_size_t(2); 00081 00082 size_t check_val_created; 00083 m_session_key = derive_key(passphrase, 00084 salt.first, 00085 salt.second, 00086 iterations, 00087 check_val_created); 00088 00089 if(check_val_created != check_val_db) 00090 throw std::runtime_error("Session database password not valid"); 00091 } 00092 } 00093 else 00094 { 00095 // maybe just zap the salts + sessions tables in this case? 00096 if(salts != 0) 00097 throw std::runtime_error("Seemingly corrupted database, multiple salts found"); 00098 00099 // new database case 00100 00101 std::vector<byte> salt = unlock(rng.random_vec(16)); 00102 const size_t iterations = 256 * 1024; 00103 size_t check_val = 0; 00104 00105 m_session_key = derive_key(passphrase, &salt[0], salt.size(), 00106 iterations, check_val); 00107 00108 auto stmt = m_db->new_statement("insert into tls_sessions_metadata values(?1, ?2, ?3)"); 00109 00110 stmt->bind(1, salt); 00111 stmt->bind(2, iterations); 00112 stmt->bind(3, check_val); 00113 00114 stmt->spin(); 00115 } 00116 } 00117 00118 bool Session_Manager_SQL::load_from_session_id(const std::vector<byte>& session_id, 00119 Session& session) 00120 { 00121 auto stmt = m_db->new_statement("select session from tls_sessions where session_id = ?1"); 00122 00123 stmt->bind(1, hex_encode(session_id)); 00124 00125 while(stmt->step()) 00126 { 00127 std::pair<const byte*, size_t> blob = stmt->get_blob(0); 00128 00129 try 00130 { 00131 session = Session::decrypt(blob.first, blob.second, m_session_key); 00132 return true; 00133 } 00134 catch(...) 00135 { 00136 } 00137 } 00138 00139 return false; 00140 } 00141 00142 bool Session_Manager_SQL::load_from_server_info(const Server_Information& server, 00143 Session& session) 00144 { 00145 auto stmt = m_db->new_statement("select session from tls_sessions" 00146 " where hostname = ?1 and hostport = ?2" 00147 " order by session_start desc"); 00148 00149 stmt->bind(1, server.hostname()); 00150 stmt->bind(2, server.port()); 00151 00152 while(stmt->step()) 00153 { 00154 std::pair<const byte*, size_t> blob = stmt->get_blob(0); 00155 00156 try 00157 { 00158 session = Session::decrypt(blob.first, blob.second, m_session_key); 00159 return true; 00160 } 00161 catch(...) 00162 { 00163 } 00164 } 00165 00166 return false; 00167 } 00168 00169 void Session_Manager_SQL::remove_entry(const std::vector<byte>& session_id) 00170 { 00171 auto stmt = m_db->new_statement("delete from tls_sessions where session_id = ?1"); 00172 00173 stmt->bind(1, hex_encode(session_id)); 00174 00175 stmt->spin(); 00176 } 00177 00178 void Session_Manager_SQL::save(const Session& session) 00179 { 00180 auto stmt = m_db->new_statement("insert or replace into tls_sessions" 00181 " values(?1, ?2, ?3, ?4, ?5)"); 00182 00183 stmt->bind(1, hex_encode(session.session_id())); 00184 stmt->bind(2, session.start_time()); 00185 stmt->bind(3, session.server_info().hostname()); 00186 stmt->bind(4, session.server_info().port()); 00187 stmt->bind(5, session.encrypt(m_session_key, m_rng)); 00188 00189 stmt->spin(); 00190 00191 prune_session_cache(); 00192 } 00193 00194 void Session_Manager_SQL::prune_session_cache() 00195 { 00196 // First expire old sessions 00197 auto remove_expired = m_db->new_statement("delete from tls_sessions where session_start <= ?1"); 00198 remove_expired->bind(1, std::chrono::system_clock::now() - m_session_lifetime); 00199 remove_expired->spin(); 00200 00201 const size_t sessions = m_db->row_count("tls_sessions"); 00202 00203 // Then if needed expire some more sessions at random 00204 if(sessions > m_max_sessions) 00205 { 00206 auto remove_some = m_db->new_statement("delete from tls_sessions where session_id in " 00207 "(select session_id from tls_sessions limit ?1)"); 00208 00209 remove_some->bind(1, sessions - m_max_sessions); 00210 remove_some->spin(); 00211 } 00212 } 00213 00214 } 00215 00216 }