aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-03-22 19:35:37 +0000
committerlloyd <[email protected]>2012-03-22 19:35:37 +0000
commit34940b08d7328a0baa51256781f5bb802a966217 (patch)
tree252b542ee2572a6f0e0d9b107c20ebb68cfe551c /src/tls
parentc670e02f82087945f2c96b7cf69177ba374b27fc (diff)
Add encryption for the SQLite sessions database
Diffstat (limited to 'src/tls')
-rw-r--r--src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp168
-rw-r--r--src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h19
-rw-r--r--src/tls/tls_session.cpp12
-rw-r--r--src/tls/tls_session.h18
4 files changed, 163 insertions, 54 deletions
diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp
index 3fea68a74..04201efe3 100644
--- a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp
+++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp
@@ -7,8 +7,12 @@
#include <botan/tls_sqlite_sess_mgr.h>
#include <botan/internal/assert.h>
+#include <botan/lookup.h>
#include <botan/hex.h>
#include <botan/time.h>
+#include <botan/loadstor.h>
+#include <memory>
+
#include <sqlite3.h>
namespace Botan {
@@ -93,17 +97,61 @@ class sqlite3_statement
sqlite3_stmt* m_stmt;
};
+size_t row_count(sqlite3* db, const std::string& table_name)
+ {
+ sqlite3_statement stmt(db, "select count(*) from " + table_name);
+
+ if(stmt.step() == SQLITE_ROW)
+ return stmt.get_size_t(0);
+ else
+ throw std::runtime_error("Querying size of table " + table_name + " failed");
+ }
+
+void create_table(sqlite3* db, const char* table_schema)
+ {
+ char* errmsg = 0;
+ int rc = sqlite3_exec(db, table_schema, 0, 0, &errmsg);
+
+ if(rc != SQLITE_OK)
+ {
+ const std::string err_msg = errmsg;
+ sqlite3_free(errmsg);
+ sqlite3_close(db);
+ throw std::runtime_error("sqlite3_exec for table failed - " + err_msg);
+ }
+ }
+
+
+SymmetricKey derive_key(const std::string& passphrase,
+ const byte salt[],
+ size_t salt_len,
+ size_t iterations,
+ size_t& check_val)
+ {
+ std::auto_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)"));
+
+ SecureVector<byte> x = pbkdf->derive_key(32 + 3,
+ passphrase,
+ salt, salt_len,
+ iterations).bits_of();
+
+ check_val = make_u32bit(0, x[0], x[1], x[2]);
+ return SymmetricKey(&x[3], x.size() - 3);
+ }
+
}
-Session_Manager_SQLite::Session_Manager_SQLite(const std::string& db_filename,
- const std::string& table_name,
+Session_Manager_SQLite::Session_Manager_SQLite(const std::string& passphrase,
+ RandomNumberGenerator& rng,
+ const std::string& db_filename,
size_t max_sessions,
size_t session_lifetime) :
- m_table_name(table_name),
+ m_rng(rng),
m_max_sessions(max_sessions),
m_session_lifetime(session_lifetime)
{
int rc = sqlite3_open(db_filename.c_str(), &m_db);
+
if(rc)
{
const std::string err_msg = sqlite3_errmsg(m_db);
@@ -111,25 +159,72 @@ Session_Manager_SQLite::Session_Manager_SQLite(const std::string& db_filename,
throw std::runtime_error("sqlite3_open failed - " + err_msg);
}
- const std::string table_sql =
- "create table if not exists " + m_table_name +
- "("
- "session_id TEXT PRIMARY KEY, "
- "session_start INTEGER, "
- "hostname TEXT, "
- "hostport INTEGER, "
- "session BLOB"
- ")";
-
- char* errmsg = 0;
- rc = sqlite3_exec(m_db, table_sql.c_str(), 0, 0, &errmsg);
+ create_table(m_db,
+ "create table if not exists tls_sessions "
+ "("
+ "session_id TEXT PRIMARY KEY, "
+ "session_start INTEGER, "
+ "hostname TEXT, "
+ "hostport INTEGER, "
+ "session BLOB"
+ ")");
+
+ create_table(m_db,
+ "create table if not exists tls_sessions_metadata "
+ "("
+ "passphrase_salt BLOB, "
+ "passphrase_iterations INTEGER, "
+ "passphrase_check INTEGER "
+ ")");
+
+ const size_t salts = row_count(m_db, "tls_sessions_metadata");
+
+ if(salts == 1)
+ {
+ // existing db
+ sqlite3_statement stmt(m_db, "select * from tls_sessions_metadata");
- if(rc != SQLITE_OK)
+ int rc = stmt.step();
+ if(rc == SQLITE_ROW)
+ {
+ std::pair<const byte*, size_t> salt = stmt.get_blob(0);
+ const size_t iterations = stmt.get_size_t(1);
+ const size_t check_val_db = stmt.get_size_t(2);
+
+ size_t check_val_created;
+ m_session_key = derive_key(passphrase,
+ salt.first,
+ salt.second,
+ iterations,
+ check_val_created);
+
+ if(check_val_created != check_val_db)
+ throw std::runtime_error("Session database password not valid");
+ }
+ }
+ else
{
- const std::string err_msg = errmsg;
- sqlite3_free(errmsg);
- sqlite3_close(m_db);
- throw std::runtime_error("sqlite3_exec for table failed - " + err_msg);
+ // maybe just zap the salts + sessions tables in this case?
+ if(salts != 0)
+ throw std::runtime_error("Seemingly corrupted database, multiple salts found");
+
+ // new database case
+
+ MemoryVector<byte> salt = rng.random_vec(16);
+ const size_t iterations = 32000;
+ size_t check_val = 0;
+
+ m_session_key = derive_key(passphrase, &salt[0], salt.size(),
+ iterations, check_val);
+
+ sqlite3_statement stmt(m_db, "insert into tls_sessions_metadata"
+ " values(?1, ?2, ?3)");
+
+ stmt.bind(1, salt);
+ stmt.bind(2, iterations);
+ stmt.bind(3, check_val);
+
+ stmt.spin();
}
}
@@ -141,7 +236,7 @@ Session_Manager_SQLite::~Session_Manager_SQLite()
bool Session_Manager_SQLite::load_from_session_id(const MemoryRegion<byte>& session_id,
Session& session)
{
- sqlite3_statement stmt(m_db, "select session from " + m_table_name + " where session_id = ?1");
+ sqlite3_statement stmt(m_db, "select session from tls_sessions where session_id = ?1");
stmt.bind(1, hex_encode(session_id));
@@ -153,7 +248,7 @@ bool Session_Manager_SQLite::load_from_session_id(const MemoryRegion<byte>& sess
try
{
- session = Session(blob.first, blob.second);
+ session = Session::decrypt(blob.first, blob.second, m_session_key);
return true;
}
catch(...)
@@ -170,7 +265,7 @@ bool Session_Manager_SQLite::load_from_host_info(const std::string& hostname,
u16bit port,
Session& session)
{
- sqlite3_statement stmt(m_db, "select session from " + m_table_name +
+ sqlite3_statement stmt(m_db, "select session from tls_sessions"
" where hostname = ?1 and hostport = ?2"
" order by session_start desc");
@@ -185,7 +280,7 @@ bool Session_Manager_SQLite::load_from_host_info(const std::string& hostname,
try
{
- session = Session(blob.first, blob.second);
+ session = Session::decrypt(blob.first, blob.second, m_session_key);
return true;
}
catch(...)
@@ -200,7 +295,7 @@ bool Session_Manager_SQLite::load_from_host_info(const std::string& hostname,
void Session_Manager_SQLite::remove_entry(const MemoryRegion<byte>& session_id)
{
- sqlite3_statement stmt(m_db, "delete from " + m_table_name + " where session_id = ?1");
+ sqlite3_statement stmt(m_db, "delete from tls_sessions where session_id = ?1");
stmt.bind(1, hex_encode(session_id));
@@ -209,14 +304,14 @@ void Session_Manager_SQLite::remove_entry(const MemoryRegion<byte>& session_id)
void Session_Manager_SQLite::save(const Session& session)
{
- sqlite3_statement stmt(m_db, "insert or replace into " + m_table_name +
+ sqlite3_statement stmt(m_db, "insert or replace into tls_sessions"
" values(?1, ?2, ?3, ?4, ?5)");
stmt.bind(1, hex_encode(session.session_id()));
stmt.bind(2, session.start_time());
stmt.bind(3, session.sni_hostname());
stmt.bind(4, 0);
- stmt.bind(5, session.DER_encode());
+ stmt.bind(5, session.encrypt(m_session_key, m_rng));
stmt.spin();
@@ -225,26 +320,21 @@ void Session_Manager_SQLite::save(const Session& session)
void Session_Manager_SQLite::prune_session_cache()
{
- sqlite3_statement remove_expired(m_db, "delete from " + m_table_name + " where session_start <= ?1");
+ sqlite3_statement remove_expired(m_db, "delete from tls_sessions where session_start <= ?1");
remove_expired.bind(1, system_time() - m_session_lifetime);
remove_expired.spin();
- sqlite3_statement row_count(m_db, "select count(*) from " + m_table_name);
+ const size_t sessions = row_count(m_db, "tls_sessions");
- if(row_count.step() == SQLITE_ROW)
+ if(sessions > m_max_sessions)
{
- const size_t sessions = row_count.get_size_t(0);
+ sqlite3_statement remove_some(m_db, "delete from tls_sessions where session_id in "
+ "(select session_id from tls_sessions limit ?1)");
- if(sessions > m_max_sessions)
- {
- sqlite3_statement remove_some(m_db, "delete from " + m_table_name + " where session_id in "
- "(select session_id from " + m_table_name + " limit ?1)");
-
- remove_some.bind(1, sessions - m_max_sessions);
- remove_some.spin();
- }
+ remove_some.bind(1, sessions - m_max_sessions);
+ remove_some.spin();
}
}
diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h
index 35a37d206..424db24e5 100644
--- a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h
+++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h
@@ -9,6 +9,7 @@
#define TLS_SQLITE_SESSION_MANAGER_H__
#include <botan/tls_session_manager.h>
+#include <botan/rng.h>
class sqlite3;
@@ -22,15 +23,18 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager
{
public:
/**
- * @param db_filename filename of the SQLite database file
- * @param table_name names the table to store sessions in
+ * @param passphrase used to encrypt the session data
+ * @param db_filename filename of the SQLite database file.
+ The table names tls_sessions and tls_sessions_metadata
+ will be used
* @param max_sessions a hint on the maximum number of sessions
* to keep in memory at any one time. (If zero, don't cap)
* @param session_lifetime sessions are expired after this many
* seconds have elapsed from initial handshake.
*/
- Session_Manager_SQLite(const std::string& db_filename,
- const std::string& table_name = "tls_sessions",
+ Session_Manager_SQLite(const std::string& passphrase,
+ RandomNumberGenerator& rng,
+ const std::string& db_filename,
size_t max_sessions = 1000,
size_t session_lifetime = 7200);
@@ -46,12 +50,13 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager
void save(const Session& session_data);
private:
- Session_Manager_SQLite(const Session_Manager_SQLite&) {}
- Session_Manager_SQLite& operator=(const Session_Manager_SQLite&) { return (*this); }
+ Session_Manager_SQLite(const Session_Manager_SQLite&);
+ Session_Manager_SQLite& operator=(const Session_Manager_SQLite&);
void prune_session_cache();
- std::string m_table_name;
+ SymmetricKey m_session_key;
+ RandomNumberGenerator& m_rng;
size_t m_max_sessions, m_session_lifetime;
class sqlite3* m_db;
};
diff --git a/src/tls/tls_session.cpp b/src/tls/tls_session.cpp
index a41112bf4..c01590000 100644
--- a/src/tls/tls_session.cpp
+++ b/src/tls/tls_session.cpp
@@ -139,7 +139,7 @@ const u64bit ENCRYPTED_SESSION_MAGIC = 0xACE4480800000000;
MemoryVector<byte>
Session::encrypt(const SymmetricKey& master_key,
- RandomNumberGenerator& rng)
+ RandomNumberGenerator& rng) const
{
std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)"));
@@ -169,7 +169,7 @@ Session::encrypt(const SymmetricKey& master_key,
return out;
}
-Session Session::decrypt(const MemoryRegion<byte>& buf,
+Session Session::decrypt(const byte buf[], size_t buf_len,
const SymmetricKey& master_key)
{
try
@@ -181,7 +181,7 @@ Session Session::decrypt(const MemoryRegion<byte>& buf,
16 bytes per AES block * 4 blocks (absolute min amount due to
48 bytes master secret)
*/
- if(buf.size() < (8 + 16 + 32 + 4*16))
+ if(buf_len < (8 + 16 + 32 + 4*16))
throw Decoding_Error("Encrypted TLS session too short to be valid");
std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)"));
@@ -192,10 +192,10 @@ Session Session::decrypt(const MemoryRegion<byte>& buf,
std::auto_ptr<MessageAuthenticationCode> mac(get_mac("HMAC(SHA-256)"));
mac->set_key(hmac_key);
- mac->update(&buf[0], buf.size() - 32);
+ mac->update(&buf[0], buf_len - 32);
MemoryVector<byte> computed_mac = mac->final();
- if(!same_mem(&buf[buf.size() - 32], &computed_mac[0], computed_mac.size()))
+ if(!same_mem(&buf[buf_len - 32], &computed_mac[0], computed_mac.size()))
throw Decoding_Error("MAC verification failed for encrypted session");
const u64bit header = load_be<u64bit>(buf, 0);
@@ -209,7 +209,7 @@ Session Session::decrypt(const MemoryRegion<byte>& buf,
InitializationVector aes_iv(&buf[8], 16);
Pipe pipe(get_cipher("AES-256/CBC", aes_key, aes_iv, DECRYPTION));
- pipe.process_msg(&buf[8+16], buf.size() - (32 + 8 + 16));
+ pipe.process_msg(&buf[8+16], buf_len - (32 + 8 + 16));
SecureVector<byte> ber = pipe.read_all();
return Session(&ber[0], ber.size());
diff --git a/src/tls/tls_session.h b/src/tls/tls_session.h
index ab11154c9..0c57201a4 100644
--- a/src/tls/tls_session.h
+++ b/src/tls/tls_session.h
@@ -76,17 +76,31 @@ class BOTAN_DLL Session
* Encrypt a session (useful for serialization or session tickets)
*/
MemoryVector<byte> encrypt(const SymmetricKey& key,
- RandomNumberGenerator& rng);
+ RandomNumberGenerator& rng) const;
+
/**
* Decrypt a session created by encrypt
* @param ctext the ciphertext returned by encrypt
+ * @param ctext_size the size of ctext in bytes
* @param key the same key used by the encrypting side
*/
- static Session decrypt(const MemoryRegion<byte>& ctext,
+ static Session decrypt(const byte ctext[],
+ size_t ctext_size,
const SymmetricKey& key);
/**
+ * Decrypt a session created by encrypt
+ * @param ctext the ciphertext returned by encrypt
+ * @param key the same key used by the encrypting side
+ */
+ static inline Session decrypt(const MemoryRegion<byte>& ctext,
+ const SymmetricKey& key)
+ {
+ return Session::decrypt(&ctext[0], ctext.size(), key);
+ }
+
+ /**
* Encode this session data for storage
* @warning if the master secret is compromised so is the
* session traffic