diff options
-rw-r--r-- | doc/credentials_manager.rst | 16 | ||||
-rw-r--r-- | doc/examples/tls_client.cpp | 17 | ||||
-rw-r--r-- | doc/tls.rst | 18 | ||||
-rw-r--r-- | src/tls/sessions_sqlite/tls_session_manager_sqlite.cpp | 7 | ||||
-rw-r--r-- | src/tls/sessions_sqlite/tls_session_manager_sqlite.h | 18 | ||||
-rw-r--r-- | src/tls/tls_client.cpp | 10 | ||||
-rw-r--r-- | src/tls/tls_client.h | 15 | ||||
-rw-r--r-- | src/tls/tls_session_manager.cpp | 23 | ||||
-rw-r--r-- | src/tls/tls_session_manager.h | 24 |
9 files changed, 100 insertions, 48 deletions
diff --git a/doc/credentials_manager.rst b/doc/credentials_manager.rst index b9718bf59..04e9e3f2e 100644 --- a/doc/credentials_manager.rst +++ b/doc/credentials_manager.rst @@ -122,6 +122,22 @@ authentication. Return a symmetric key for use with *identity* + One important special case for ``psk`` is where *type* is + "tls-server", *context* is "session-ticket" and *identity* is an + empty string. If a key is returned for this case, a TLS server + will offer session tickets to clients who can use them, and the + returned key will be used to encrypt the ticket. The server is + allowed to change the key at any time (though changing the key + means old session tickets can no longer be used for resumption, + forcing a full re-handshake when the client next connects). One + simple approach to add support for session tickets in your server + is to generate a random key the first time ``psk`` is called to + retrieve the session ticket key, cache it for later use in the + ``Credentials_Manager``, and simply let it be thrown away when the + process terminates. + + See :rfc:`4507` for more information about TLS session tickets. + .. cpp:function:: std::string psk_identity_hint(const std::string& type, \ const std::string& context) diff --git a/doc/examples/tls_client.cpp b/doc/examples/tls_client.cpp index d8d861e57..5373227ce 100644 --- a/doc/examples/tls_client.cpp +++ b/doc/examples/tls_client.cpp @@ -133,13 +133,14 @@ void doit(RandomNumberGenerator& rng, int sockfd = connect_to_host(host, port); TLS::Client client(std::bind(socket_write, sockfd, _1, _2), - process_data, - handshake_complete, - session_manager, - creds, - policy, - rng, - host); + process_data, + handshake_complete, + session_manager, + creds, + policy, + rng, + host, + port); fd_set readfds; @@ -225,7 +226,7 @@ int main(int argc, char* argv[]) AutoSeeded_RNG rng; TLS::Policy policy; -#if defined(BOTAN_HAS_TLS_SQLITE_SESSION_MANAGER) +#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) TLS::Session_Manager_SQLite session_manager("my secret passphrase", rng, "sessions.db"); #else diff --git a/doc/tls.rst b/doc/tls.rst index e5e9dc91d..09b9da3d3 100644 --- a/doc/tls.rst +++ b/doc/tls.rst @@ -309,12 +309,17 @@ implementation to the ``TLS::Client`` or ``TLS::Server`` constructor. .. cpp:class:: TLS::Session_Mananger - .. cpp:function:: void save(const Session& session) + .. cpp:function:: void save(const Session& session, u16bit port) Save a new *session*. It is possible that this sessions session ID will replicate a session ID already stored, in which case the new session information should overwrite the previous information. + Clients will specify *port* if they know it (it will be zero if + they do not, or for servers). It specifies the remote port of the + server which is used to assist with looking up the correct + session when using :cpp:func:`load_from_host_info`. + .. cpp:function:: void remove_entry(const std::vector<byte>& session_id) Remove the session identified by *session_id*. Future attempts @@ -328,13 +333,16 @@ implementation to the ``TLS::Client`` or ``TLS::Server`` constructor. to *save*, and ``true`` is returned. Otherwise *session* is not modified and ``false`` is returned. - .. cpp:function:: bool load_from_host_info(const std::string& hostname, u16bit port, \ + .. cpp:function:: bool load_from_host_info(const std::string& hostname, \ + u16bit port, \ Session& session) - Attempt to resume a session for *hostname* / *port*. If *port* - is zero, try to find a session for *hostname* on any port. With - the current client implementation, *port* is always zero. + Attempt to resume a session for *hostname* / *port*. + The session managers included in the library will, if they fail + to find an exact match for *hostname* and *port*, will also + check for a session saved using a matching hostname and a port + of zero. .. cpp:function:: std::chrono::seconds session_lifetime() const diff --git a/src/tls/sessions_sqlite/tls_session_manager_sqlite.cpp b/src/tls/sessions_sqlite/tls_session_manager_sqlite.cpp index 9d0cffa08..d10366c60 100644 --- a/src/tls/sessions_sqlite/tls_session_manager_sqlite.cpp +++ b/src/tls/sessions_sqlite/tls_session_manager_sqlite.cpp @@ -167,6 +167,9 @@ bool Session_Manager_SQLite::load_from_host_info(const std::string& hostname, } } + if(port != 0) + return load_from_host_info(hostname, 0, session); + return false; } @@ -179,7 +182,7 @@ void Session_Manager_SQLite::remove_entry(const std::vector<byte>& session_id) stmt.spin(); } -void Session_Manager_SQLite::save(const Session& session) +void Session_Manager_SQLite::save(const Session& session, u16bit port) { sqlite3_statement stmt(m_db, "insert or replace into tls_sessions" " values(?1, ?2, ?3, ?4, ?5)"); @@ -187,7 +190,7 @@ void Session_Manager_SQLite::save(const Session& session) 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(4, port); stmt.bind(5, session.encrypt(m_session_key, m_rng)); stmt.spin(); diff --git a/src/tls/sessions_sqlite/tls_session_manager_sqlite.h b/src/tls/sessions_sqlite/tls_session_manager_sqlite.h index 8950007ca..db74f54b7 100644 --- a/src/tls/sessions_sqlite/tls_session_manager_sqlite.h +++ b/src/tls/sessions_sqlite/tls_session_manager_sqlite.h @@ -21,9 +21,9 @@ namespace TLS { * An implementation of Session_Manager that saves values in a SQLite3 * database file, with the session data encrypted using a passphrase. * -* @warning The hostnames associated with the saved sessions are stored -* in the database in plaintext. This may be a serious privacy risk in -* some applications. +* @warning For clients, the hostnames associated with the saved +* sessions are stored in the database in plaintext. This may be a +* serious privacy risk in some situations. */ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager { @@ -48,16 +48,18 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager ~Session_Manager_SQLite(); bool load_from_session_id(const std::vector<byte>& session_id, - Session& session); + Session& session) override; bool load_from_host_info(const std::string& hostname, u16bit port, - Session& session); + Session& session) override; - void remove_entry(const std::vector<byte>& session_id); + void remove_entry(const std::vector<byte>& session_id) override; - void save(const Session& session_data); + void save(const Session& session_data, u16bit port) override; + + std::chrono::seconds session_lifetime() const override + { return m_session_lifetime; } - std::chrono::seconds session_lifetime() const { return m_session_lifetime; } private: Session_Manager_SQLite(const Session_Manager_SQLite&); Session_Manager_SQLite& operator=(const Session_Manager_SQLite&); diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp index 5b9b4868c..e4652fd7e 100644 --- a/src/tls/tls_client.cpp +++ b/src/tls/tls_client.cpp @@ -26,12 +26,14 @@ Client::Client(std::function<void (const byte[], size_t)> output_fn, const Policy& policy, RandomNumberGenerator& rng, const std::string& hostname, + u16bit port, std::function<std::string (std::vector<std::string>)> next_protocol) : Channel(output_fn, proc_fn, handshake_fn, session_manager, rng), m_policy(policy), m_rng(rng), m_creds(creds), - m_hostname(hostname) + m_hostname(hostname), + m_port(port) { m_writer.set_version(Protocol_Version::SSL_V3); @@ -47,7 +49,7 @@ Client::Client(std::function<void (const byte[], size_t)> output_fn, if(hostname != "") { Session session_info; - if(m_session_manager.load_from_host_info(m_hostname, 0, session_info)) + if(m_session_manager.load_from_host_info(m_hostname, m_port, session_info)) { if(session_info.srp_identifier() == srp_identifier) { @@ -103,7 +105,7 @@ void Client::renegotiate(bool force_full_renegotiation) if(!force_full_renegotiation) { Session session_info; - if(m_session_manager.load_from_host_info(m_hostname, 0, session_info)) + if(m_session_manager.load_from_host_info(m_hostname, m_port, session_info)) { m_state->client_hello = new Client_Hello( m_writer, @@ -482,7 +484,7 @@ void Client::process_handshake_msg(Handshake_Type type, if(!session_id.empty()) { if(should_save) - m_session_manager.save(session_info); + m_session_manager.save(session_info, m_port); else m_session_manager.remove_entry(session_info.session_id()); } diff --git a/src/tls/tls_client.h b/src/tls/tls_client.h index cd9da78b9..dcd7aab83 100644 --- a/src/tls/tls_client.h +++ b/src/tls/tls_client.h @@ -24,14 +24,27 @@ class BOTAN_DLL Client : public Channel public: /** * Set up a new TLS client session + * * @param socket_output_fn is called with data for the outbound socket + * * @param proc_fn is called when new data (application or alerts) is received + * * @param handshake_complete is called when a handshake is completed + * * @param session_manager manages session state + * * @param creds manages application/user credentials + * * @param policy specifies other connection policy information + * * @param rng a random number generator + * * @param servername the server's DNS name, if known + * + * @param port specifies the protocol port of the server (eg for + * TCP/UDP). Only used if servername is also specified. + * Use 0 if unknown. + * * @param next_protocol allows the client to specify what the next * protocol will be. For more information read * http://technotes.googlecode.com/git/nextprotoneg.html. @@ -49,6 +62,7 @@ class BOTAN_DLL Client : public Channel const Policy& policy, RandomNumberGenerator& rng, const std::string& servername = "", + u16bit port = 0, std::function<std::string (std::vector<std::string>)> next_protocol = std::function<std::string (std::vector<std::string>)>()); @@ -65,6 +79,7 @@ class BOTAN_DLL Client : public Channel RandomNumberGenerator& m_rng; Credentials_Manager& m_creds; const std::string m_hostname; + const u16bit m_port; }; } diff --git a/src/tls/tls_session_manager.cpp b/src/tls/tls_session_manager.cpp index 55c06bc16..823f4c123 100644 --- a/src/tls/tls_session_manager.cpp +++ b/src/tls/tls_session_manager.cpp @@ -49,15 +49,16 @@ bool Session_Manager_In_Memory::load_from_host_info( { std::lock_guard<std::mutex> lock(m_mutex); - std::map<std::string, std::string>::iterator i; - - if(port > 0) - i = m_host_sessions.find(hostname + ":" + std::to_string(port)); - else - i = m_host_sessions.find(hostname); + auto i = m_host_sessions.find(hostname + ":" + std::to_string(port)); if(i == m_host_sessions.end()) - return false; + { + if(port > 0) + i = m_host_sessions.find(hostname + ":" + std::to_string(0)); + + if(i == m_host_sessions.end()) + return false; + } if(load_from_session_str(i->second, session)) return true; @@ -79,7 +80,7 @@ void Session_Manager_In_Memory::remove_entry( m_sessions.erase(i); } -void Session_Manager_In_Memory::save(const Session& session) +void Session_Manager_In_Memory::save(const Session& session, u16bit port) { std::lock_guard<std::mutex> lock(m_mutex); @@ -97,8 +98,10 @@ void Session_Manager_In_Memory::save(const Session& session) m_sessions[session_id_str] = session; - if(session.side() == CLIENT && session.sni_hostname() != "") - m_host_sessions[session.sni_hostname()] = session_id_str; + const std::string hostname = session.sni_hostname(); + + if(session.side() == CLIENT && hostname != "") + m_host_sessions[hostname + ":" + std::to_string(port)] = session_id_str; } } diff --git a/src/tls/tls_session_manager.h b/src/tls/tls_session_manager.h index c63ee39f6..4c979362f 100644 --- a/src/tls/tls_session_manager.h +++ b/src/tls/tls_session_manager.h @@ -62,8 +62,9 @@ class BOTAN_DLL Session_Manager * immediately by load_from_* will result in a successful lookup. * * @param session to save + * @param port the protocol port (if known) */ - virtual void save(const Session& session) = 0; + virtual void save(const Session& session, u16bit port = 0) = 0; /** * Return the allowed lifetime of a session; beyond this time, @@ -82,17 +83,17 @@ class BOTAN_DLL Session_Manager class BOTAN_DLL Session_Manager_Noop : public Session_Manager { public: - bool load_from_session_id(const std::vector<byte>&, Session&) + bool load_from_session_id(const std::vector<byte>&, Session&) override { return false; } - bool load_from_host_info(const std::string&, u16bit, Session&) + bool load_from_host_info(const std::string&, u16bit, Session&) override { return false; } - void remove_entry(const std::vector<byte>&) {} + void remove_entry(const std::vector<byte>&) override {} - void save(const Session&) {} + void save(const Session&, u16bit) override {} - std::chrono::seconds session_lifetime() const + std::chrono::seconds session_lifetime() const override { return std::chrono::seconds(0); } }; @@ -115,16 +116,17 @@ class BOTAN_DLL Session_Manager_In_Memory : public Session_Manager {} bool load_from_session_id(const std::vector<byte>& session_id, - Session& session); + Session& session) override; bool load_from_host_info(const std::string& hostname, u16bit port, - Session& session); + Session& session) override; - void remove_entry(const std::vector<byte>& session_id); + void remove_entry(const std::vector<byte>& session_id) override; - void save(const Session& session_data); + void save(const Session& session_data, u16bit port) override; - std::chrono::seconds session_lifetime() const { return m_session_lifetime; } + std::chrono::seconds session_lifetime() const override + { return m_session_lifetime; } private: bool load_from_session_str(const std::string& session_str, |