aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/manual/tls.rst44
-rw-r--r--src/lib/tls/tls_callbacks.h35
-rw-r--r--src/lib/tls/tls_server.cpp16
-rw-r--r--src/lib/tls/tls_server.h7
-rw-r--r--src/tests/unit_tls.cpp10
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.