diff options
-rw-r--r-- | doc/manual/tls.rst | 44 | ||||
-rw-r--r-- | src/lib/tls/tls_callbacks.h | 35 | ||||
-rw-r--r-- | src/lib/tls/tls_server.cpp | 16 | ||||
-rw-r--r-- | src/lib/tls/tls_server.h | 7 | ||||
-rw-r--r-- | src/tests/unit_tls.cpp | 10 |
5 files changed, 80 insertions, 32 deletions
diff --git a/doc/manual/tls.rst b/doc/manual/tls.rst index 7210fc092..6c1ca42f2 100644 --- a/doc/manual/tls.rst +++ b/doc/manual/tls.rst @@ -38,17 +38,21 @@ information about the connection. .. cpp:function:: void tls_emit_data(const byte data[], size_t data_len) - The TLS stack requests that all bytes of *data* be queued up to send to the + Mandatory. The TLS stack requests that all bytes of *data* be queued up to send to the counterparty. After this function returns, the buffer containing *data* will be overwritten, so a copy of the input must be made if the callback cannot send the data immediately. - The write can be deferred but for TLS all writes must occur *in order*. - For DTLS this is not strictly required, but is still recommended. + As an example you could ``send`` to perform a blocking write on a socket, + or append the data to a queue managed by your application, and initiate + an asyncronous write. + + For TLS all writes must occur *in the order requested*. + For DTLS this ordering is not strictly required, but is still recommended. .. cpp:function:: void tls_record_received(uint64_t rec_no, const byte data[], size_t data_len) - Called once for each application_data record which is received, with the + Mandatory. Called once for each application_data record which is received, with the matching (TLS level) record sequence number. Currently empty records are ignored and do not instigate a callback, @@ -57,15 +61,21 @@ information about the connection. As with ``tls_emit_data``, the array will be overwritten sometime after the callback returns, so a copy should be made if needed. + For TLS the record number will always increase. + + For DTLS, it is possible to receive records with the `rec_no` field + field out of order or repeated. It is even possible (from a malicious or + faulty peer) to receive multiple copies of a single record with differing plaintexts. + .. cpp:function:: void tls_alert(Alert alert) - Called when an alert is received from the peer. Note that alerts + Mandatory. Called when an alert is received from the peer. Note that alerts received before the handshake is complete are not authenticated and could have been inserted by a MITM attacker. .. cpp:function:: bool tls_session_established(const TLS::Session& session) - Called whenever a negotiation completes. This can happen more + Mandatory. Called whenever a negotiation completes. This can happen more than once on any connection, if renegotiation occurs. The *session* parameter provides information about the session which was just established. @@ -76,6 +86,13 @@ information about the connection. exception which will send a close message to the counterparty and reset the connection state. + .. cpp:function:: std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) + + Optional. Called by the server when a client includes a list of protocols in the ALPN extension. + The server then choose which protocol to use, or "" to disable sending any ALPN response. + The default implementation returns the empty string all of the time, effectively disabling + ALPN responses. + .. cpp:function:: void tls_inspect_handshake_msg(const Handshake_Message&) This callback is optional, and can be used to inspect all handshake messages @@ -90,7 +107,7 @@ Versions from 1.11.0 to 1.11.30 did not have ``TLS::Callbacks` and instead used independent std::functions to pass the various callback functions. This interface is currently still included but is deprecated and will be removed in a future release. For the documentation for this interface, please check -the docs in 1.11.30. This version of the manual only documents the new interface +the docs for 1.11.30. This version of the manual only documents the new interface added in 1.11.31. TLS Channels @@ -278,7 +295,6 @@ TLS Servers Credentials_Manager& creds, \ const Policy& policy, \ RandomNumberGenerator& rng, \ - next_protocol_fn next_proto = next_protocol_fn(), \ bool is_datagram = false, \ size_t reserved_io_buffer_size = 16*1024 \ ) @@ -287,12 +303,12 @@ The first 5 arguments as well as the final argument *reserved_io_buffer_size*, are treated similiarly to the :ref:`client <tls_client>`. -The (optional) argument, *next_proto*, is a function called if the -client sent the ALPN extension to negotiate an application -protocol. In that case, the function should choose a protocol to use -and return it. Alternately it can throw an exception to abort the -exchange; the ALPN specification says that if this occurs the alert -should be of type `NO_APPLICATION_PROTOCOL`. +If a client sends the ALPN extension, the ``callbacks`` function +``tls_server_choose_app_protocol`` will be called and the result +sent back to the client. If the empty string is returned, the server +will not send an ALPN response. The function can also throw an exception +to abort the handshake entirely, the ALPN specification says that if this +occurs the alert should be of type `NO_APPLICATION_PROTOCOL`. The optional argument *is_datagram* specifies if this is a TLS or DTLS server; unlike clients, which know what type of protocol (TLS vs DTLS) diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h index db1b70693..5c7b21a99 100644 --- a/src/lib/tls/tls_callbacks.h +++ b/src/lib/tls/tls_callbacks.h @@ -61,10 +61,26 @@ class BOTAN_DLL Callbacks /** * Optional callback: inspect handshake message + * Throw an exception to abort the handshake. */ virtual void tls_inspect_handshake_msg(const Handshake_Message&) {} /** + * Optional callback for server: choose ALPN protocol + * ALPN (RFC 7301) works by the client sending a list of application + * protocols it is willing to negotiate. The server then selects which + * protocol to use, which is not necessarily even on the list that + * the client sent. + * + * If the empty string is returned from this function the server will + * just ignore the client ALPN extension. + */ + virtual std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) + { + return ""; + } + + /** * Optional callback: debug logging. (not currently used) */ virtual bool tls_log_debug(const char*) { return false; } @@ -83,6 +99,7 @@ class BOTAN_DLL Compat_Callbacks final : public Callbacks typedef std::function<void (Alert, const byte[], size_t)> alert_cb; typedef std::function<bool (const Session&)> handshake_cb; typedef std::function<void (const Handshake_Message&)> handshake_msg_cb; + typedef std::function<std::string (std::vector<std::string>)> next_protocol_fn; /** * @param output_fn is called with data for the outbound socket @@ -95,18 +112,21 @@ class BOTAN_DLL Compat_Callbacks final : public Callbacks */ BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).") Compat_Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb, - handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr) + handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr, + next_protocol_fn next_proto = nullptr) : m_output_function(out), m_app_data_cb(app_data_cb), m_alert_cb(std::bind(alert_cb, std::placeholders::_1, nullptr, 0)), - m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb) {} + m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {} BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).") Compat_Callbacks(output_fn out, data_cb app_data_cb, std::function<void (Alert)> alert_cb, - handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr) + handshake_cb hs_cb, + handshake_msg_cb hs_msg_cb = nullptr, + next_protocol_fn next_proto = nullptr) : m_output_function(out), m_app_data_cb(app_data_cb), m_alert_cb(alert_cb), - m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb) {} + m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {} void tls_emit_data(const byte data[], size_t size) override { @@ -136,6 +156,12 @@ class BOTAN_DLL Compat_Callbacks final : public Callbacks return m_hs_cb(session); } + std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override + { + if(m_next_proto != nullptr) { return m_next_proto(client_protos); } + return ""; + } + void tls_inspect_handshake_msg(const Handshake_Message& hmsg) override { // The handshake message callback is optional so we can @@ -149,6 +175,7 @@ class BOTAN_DLL Compat_Callbacks final : public Callbacks const std::function<void (Alert)> m_alert_cb; const handshake_cb m_hs_cb; const handshake_msg_cb m_hs_msg_cb; + const next_protocol_fn m_next_proto; }; } diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp index 5eccec2a7..2e546eab1 100644 --- a/src/lib/tls/tls_server.cpp +++ b/src/lib/tls/tls_server.cpp @@ -242,13 +242,11 @@ Server::Server(Callbacks& callbacks, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, - next_protocol_fn next_proto, bool is_datagram, size_t io_buf_sz) : Channel(callbacks, session_manager, rng, policy, is_datagram, io_buf_sz), - m_creds(creds), - m_choose_next_protocol(next_proto) + m_creds(creds) { } @@ -419,8 +417,16 @@ void Server::process_client_hello_msg(const Handshake_State* active_state, catch(...) {} m_next_protocol = ""; - if(m_choose_next_protocol && pending_state.client_hello()->supports_alpn()) - m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols()); + if(pending_state.client_hello()->supports_alpn()) + { + m_next_protocol = callbacks().tls_server_choose_app_protocol(pending_state.client_hello()->next_protocols()); + + // if the callback return was empty, fall back to the (deprecated) std::function + if(m_next_protocol.empty() && m_choose_next_protocol) + { + m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols()); + } + } if(resuming) { diff --git a/src/lib/tls/tls_server.h b/src/lib/tls/tls_server.h index 508dde440..051eda445 100644 --- a/src/lib/tls/tls_server.h +++ b/src/lib/tls/tls_server.h @@ -42,9 +42,6 @@ class BOTAN_DLL Server final : public Channel * * @param rng a random number generator * - * @param next_proto is called with client's ALPN protocol list - * and returns chosen protocol. - * * @param is_datagram set to true if this server should expect DTLS * connections. Otherwise TLS connections are expected. * @@ -57,7 +54,6 @@ class BOTAN_DLL Server final : public Channel Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, - next_protocol_fn next_proto = next_protocol_fn(), bool is_datagram = false, size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE ); @@ -148,9 +144,10 @@ class BOTAN_DLL Server final : public Channel Handshake_State* new_handshake_state(Handshake_IO* io) override; Credentials_Manager& m_creds; + std::string m_next_protocol; + // Set by deprecated constructor, Server calls both this fn and Callbacks version next_protocol_fn m_choose_next_protocol; - std::string m_next_protocol; }; } diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index 2797e7d21..f0caa4aef 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -227,7 +227,9 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, queue_inserter(s2c_traffic), queue_inserter(server_recv), std::function<void (Botan::TLS::Alert, const byte[], size_t)>(alert_cb_with_data), - handshake_complete)); + handshake_complete, + nullptr, + next_protocol_chooser)); // TLS::Server object constructed by new constructor using virtual callback interface. std::unique_ptr<Botan::TLS::Server> server( @@ -236,7 +238,6 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, creds, policy, rng, - next_protocol_chooser, false)); std::unique_ptr<Botan::TLS::Callbacks> client_cb(new Botan::TLS::Compat_Callbacks( @@ -504,7 +505,9 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, queue_inserter(s2c_traffic), queue_inserter(server_recv), std::function<void (Botan::TLS::Alert)>(print_alert), - handshake_complete)); + handshake_complete, + nullptr, + next_protocol_chooser)); std::unique_ptr<Botan::TLS::Callbacks> client_cb(new Botan::TLS::Compat_Callbacks( queue_inserter(c2s_traffic), @@ -519,7 +522,6 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, creds, policy, rng, - next_protocol_chooser, true)); // TLS::Client object constructed by new constructor using virtual callback interface. |