aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2019-05-20 16:15:16 -0400
committerJack Lloyd <[email protected]>2019-05-20 16:15:16 -0400
commit4658a632c1a2e01c9b4487aa19269fc627108ce4 (patch)
tree9a4bdb9fa1caa6681d670e5b4b8b68d57008ccea /src/lib
parent81ea951957a133fcb7c8a6645312edf7904b26e9 (diff)
parent67df17d31d61f013d537abc7744f707435351125 (diff)
Merge GH #1954 Add BoGo test shim
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/tls/msg_cert_req.cpp2
-rw-r--r--src/lib/tls/msg_cert_status.cpp19
-rw-r--r--src/lib/tls/msg_cert_verify.cpp1
-rw-r--r--src/lib/tls/msg_certificate.cpp6
-rw-r--r--src/lib/tls/msg_client_hello.cpp15
-rw-r--r--src/lib/tls/msg_client_kex.cpp3
-rw-r--r--src/lib/tls/msg_server_hello.cpp2
-rw-r--r--src/lib/tls/msg_session_ticket.cpp1
-rw-r--r--src/lib/tls/tls_alert.cpp10
-rw-r--r--src/lib/tls/tls_alert.h1
-rw-r--r--src/lib/tls/tls_algos.h6
-rw-r--r--src/lib/tls/tls_channel.cpp73
-rw-r--r--src/lib/tls/tls_channel.h2
-rw-r--r--src/lib/tls/tls_ciphersuite.cpp25
-rw-r--r--src/lib/tls/tls_ciphersuite.h10
-rw-r--r--src/lib/tls/tls_client.cpp174
-rw-r--r--src/lib/tls/tls_client.h2
-rw-r--r--src/lib/tls/tls_extensions.cpp20
-rw-r--r--src/lib/tls/tls_extensions.h10
-rw-r--r--src/lib/tls/tls_handshake_state.cpp31
-rw-r--r--src/lib/tls/tls_messages.h8
-rw-r--r--src/lib/tls/tls_policy.cpp38
-rw-r--r--src/lib/tls/tls_policy.h31
-rw-r--r--src/lib/tls/tls_server.cpp154
-rw-r--r--src/lib/tls/tls_server.h2
-rw-r--r--src/lib/tls/tls_text_policy.cpp5
-rw-r--r--src/lib/tls/tls_version.h5
27 files changed, 469 insertions, 187 deletions
diff --git a/src/lib/tls/msg_cert_req.cpp b/src/lib/tls/msg_cert_req.cpp
index 3bbdcb1f1..b6fc3825b 100644
--- a/src/lib/tls/msg_cert_req.cpp
+++ b/src/lib/tls/msg_cert_req.cpp
@@ -57,7 +57,7 @@ Certificate_Req::Certificate_Req(Handshake_IO& io,
const std::vector<X509_DN>& ca_certs,
Protocol_Version version) :
m_names(ca_certs),
- m_cert_key_types({ "RSA", "DSA", "ECDSA" })
+ m_cert_key_types({ "RSA", "ECDSA", "DSA" })
{
if(version.supports_negotiable_signature_algorithms())
{
diff --git a/src/lib/tls/msg_cert_status.cpp b/src/lib/tls/msg_cert_status.cpp
index 8ad37336b..c0cd82a28 100644
--- a/src/lib/tls/msg_cert_status.cpp
+++ b/src/lib/tls/msg_cert_status.cpp
@@ -22,8 +22,8 @@ Certificate_Status::Certificate_Status(const std::vector<uint8_t>& buf)
if(buf.size() < 5)
throw Decoding_Error("Invalid Certificate_Status message: too small");
- if(buf[0] != 1)
- throw Decoding_Error("Unexpected Certificate_Status message: unexpected message type");
+ if(buf[0] != 1) // not OCSP
+ throw Decoding_Error("Unexpected Certificate_Status message: unexpected response type");
size_t len = make_uint32(0, buf[1], buf[2], buf[3]);
@@ -31,33 +31,30 @@ Certificate_Status::Certificate_Status(const std::vector<uint8_t>& buf)
if(buf.size() != len + 4)
throw Decoding_Error("Invalid Certificate_Status: invalid length field");
- m_response = std::make_shared<OCSP::Response>(buf.data() + 4, buf.size() - 4);
+ m_response.assign(buf.begin() + 4, buf.end());
}
Certificate_Status::Certificate_Status(Handshake_IO& io,
Handshake_Hash& hash,
std::shared_ptr<const OCSP::Response> ocsp) :
- m_response(ocsp)
+ m_response(ocsp->raw_bits())
{
hash.update(io.send(*this));
}
std::vector<uint8_t> Certificate_Status::serialize() const
{
- BOTAN_ASSERT_NONNULL(m_response);
- const std::vector<uint8_t>& m_resp_bits = m_response->raw_bits();
-
- if(m_resp_bits.size() > 0xFFFFFF) // unlikely
+ if(m_response.size() > 0xFFFFFF) // unlikely
throw Encoding_Error("OCSP response too long to encode in TLS");
- const uint32_t m_resp_bits_len = static_cast<uint32_t>(m_resp_bits.size());
+ const uint32_t m_response_len = static_cast<uint32_t>(m_response.size());
std::vector<uint8_t> buf;
buf.push_back(1); // type OCSP
for(size_t i = 1; i < 4; ++i)
- buf[i] = get_byte(i, m_resp_bits_len);
+ buf[i] = get_byte(i, m_response_len);
- buf += m_resp_bits;
+ buf += m_response;
return buf;
}
diff --git a/src/lib/tls/msg_cert_verify.cpp b/src/lib/tls/msg_cert_verify.cpp
index 230474e7a..021185003 100644
--- a/src/lib/tls/msg_cert_verify.cpp
+++ b/src/lib/tls/msg_cert_verify.cpp
@@ -51,6 +51,7 @@ Certificate_Verify::Certificate_Verify(const std::vector<uint8_t>& buf,
}
m_signature = reader.get_range<uint8_t>(2, 0, 65535);
+ reader.assert_done();
}
/*
diff --git a/src/lib/tls/msg_certificate.cpp b/src/lib/tls/msg_certificate.cpp
index a2a48ceea..1ec0766ce 100644
--- a/src/lib/tls/msg_certificate.cpp
+++ b/src/lib/tls/msg_certificate.cpp
@@ -31,7 +31,7 @@ Certificate::Certificate(Handshake_IO& io,
/**
* Deserialize a Certificate message
*/
-Certificate::Certificate(const std::vector<uint8_t>& buf, const Policy& /*policy_currently_unused*/)
+Certificate::Certificate(const std::vector<uint8_t>& buf, const Policy& policy)
{
if(buf.size() < 3)
throw Decoding_Error("Certificate: Message malformed");
@@ -41,6 +41,10 @@ Certificate::Certificate(const std::vector<uint8_t>& buf, const Policy& /*policy
if(total_size != buf.size() - 3)
throw Decoding_Error("Certificate: Message malformed");
+ const size_t max_size = policy.maximum_certificate_chain_size();
+ if(max_size > 0 && total_size > max_size)
+ throw Decoding_Error("Certificate chain exceeds policy specified maximum size");
+
const uint8_t* certs = buf.data() + 3;
while(size_t remaining_bytes = buf.data() + buf.size() - certs)
diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp
index 2d303a77e..539e2a780 100644
--- a/src/lib/tls/msg_client_hello.cpp
+++ b/src/lib/tls/msg_client_hello.cpp
@@ -92,8 +92,9 @@ Client_Hello::Client_Hello(Handshake_IO& io,
m_suites(policy.ciphersuite_list(m_version, !client_settings.srp_identifier().empty())),
m_comp_methods(1)
{
- BOTAN_ASSERT(policy.acceptable_protocol_version(client_settings.protocol_version()),
- "Our policy accepts the version we are offering");
+ if(!policy.acceptable_protocol_version(m_version))
+ throw Internal_Error("Offering " + m_version.to_string() +
+ " but our own policy does not accept it");
/*
* Place all empty extensions in front to avoid a bug in some systems
@@ -106,7 +107,9 @@ Client_Hello::Client_Hello(Handshake_IO& io,
m_extensions.add(new Encrypt_then_MAC);
m_extensions.add(new Renegotiation_Extension(reneg_info));
- m_extensions.add(new Server_Name_Indicator(client_settings.hostname()));
+
+ if(client_settings.hostname() != "")
+ m_extensions.add(new Server_Name_Indicator(client_settings.hostname()));
if(policy.support_cert_status_message())
m_extensions.add(new Certificate_Status_Request({}, {}));
@@ -163,6 +166,10 @@ Client_Hello::Client_Hello(Handshake_IO& io,
m_suites(policy.ciphersuite_list(m_version, (session.srp_identifier() != ""))),
m_comp_methods(1)
{
+ if(!policy.acceptable_protocol_version(m_version))
+ throw Internal_Error("Offering " + m_version.to_string() +
+ " but our own policy does not accept it");
+
if(!value_exists(m_suites, session.ciphersuite_code()))
m_suites.push_back(session.ciphersuite_code());
@@ -273,7 +280,7 @@ Client_Hello::Client_Hello(const std::vector<uint8_t>& buf)
m_comp_methods = reader.get_range_vector<uint8_t>(1, 1, 255);
- m_extensions.deserialize(reader);
+ m_extensions.deserialize(reader, Connection_Side::SERVER);
if(offered_suite(static_cast<uint16_t>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)))
{
diff --git a/src/lib/tls/msg_client_kex.cpp b/src/lib/tls/msg_client_kex.cpp
index b3dff072e..f55568c8e 100644
--- a/src/lib/tls/msg_client_kex.cpp
+++ b/src/lib/tls/msg_client_kex.cpp
@@ -256,6 +256,7 @@ Client_Key_Exchange::Client_Key_Exchange(const std::vector<uint8_t>& contents,
TLS_Data_Reader reader("ClientKeyExchange", contents);
const std::vector<uint8_t> encrypted_pre_master = reader.get_range<uint8_t>(2, 0, 65535);
+ reader.assert_done();
PK_Decryptor_EME decryptor(*server_rsa_kex_key, rng, "PKCS1v15");
@@ -386,6 +387,8 @@ Client_Key_Exchange::Client_Key_Exchange(const std::vector<uint8_t>& contents,
*/
m_pre_master = rng.random_vec(ka_key->public_value().size());
}
+
+ reader.assert_done();
}
else
throw Internal_Error("Client_Key_Exchange: Unknown key exchange negotiated");
diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp
index 223bddde5..b4c47f516 100644
--- a/src/lib/tls/msg_server_hello.cpp
+++ b/src/lib/tls/msg_server_hello.cpp
@@ -163,7 +163,7 @@ Server_Hello::Server_Hello(const std::vector<uint8_t>& buf)
m_comp_method = reader.get_byte();
- m_extensions.deserialize(reader);
+ m_extensions.deserialize(reader, Connection_Side::CLIENT);
}
/*
diff --git a/src/lib/tls/msg_session_ticket.cpp b/src/lib/tls/msg_session_ticket.cpp
index b9bf7e94d..bd0d74ceb 100644
--- a/src/lib/tls/msg_session_ticket.cpp
+++ b/src/lib/tls/msg_session_ticket.cpp
@@ -40,6 +40,7 @@ New_Session_Ticket::New_Session_Ticket(const std::vector<uint8_t>& buf)
m_ticket_lifetime_hint = reader.get_uint32_t();
m_ticket = reader.get_range<uint8_t>(2, 0, 65535);
+ reader.assert_done();
}
std::vector<uint8_t> New_Session_Ticket::serialize() const
diff --git a/src/lib/tls/tls_alert.cpp b/src/lib/tls/tls_alert.cpp
index e1e8c6eb6..60c9c4b98 100644
--- a/src/lib/tls/tls_alert.cpp
+++ b/src/lib/tls/tls_alert.cpp
@@ -6,7 +6,7 @@
*/
#include <botan/tls_alert.h>
-#include <botan/exceptn.h>
+#include <botan/tls_exceptn.h>
namespace Botan {
@@ -15,13 +15,13 @@ namespace TLS {
Alert::Alert(const secure_vector<uint8_t>& buf)
{
if(buf.size() != 2)
- throw Decoding_Error("Alert: Bad size " + std::to_string(buf.size()) +
- " for alert message");
+ throw Decoding_Error("Bad size (" + std::to_string(buf.size()) +
+ ") for TLS alert message");
if(buf[0] == 1) m_fatal = false;
else if(buf[0] == 2) m_fatal = true;
else
- throw Decoding_Error("Alert: Bad code for alert level");
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Bad code for TLS alert level");
const uint8_t dc = buf[1];
@@ -103,6 +103,8 @@ std::string Alert::type_string() const
return "bad_certificate_hash_value";
case UNKNOWN_PSK_IDENTITY:
return "unknown_psk_identity";
+ case CERTIFICATE_REQUIRED:
+ return "certificate_required";
case NO_APPLICATION_PROTOCOL:
return "no_application_protocol";
diff --git a/src/lib/tls/tls_alert.h b/src/lib/tls/tls_alert.h
index 89530e238..d9d3fe313 100644
--- a/src/lib/tls/tls_alert.h
+++ b/src/lib/tls/tls_alert.h
@@ -56,6 +56,7 @@ class BOTAN_PUBLIC_API(2,0) Alert final
BAD_CERTIFICATE_STATUS_RESPONSE = 113,
BAD_CERTIFICATE_HASH_VALUE = 114,
UNKNOWN_PSK_IDENTITY = 115,
+ CERTIFICATE_REQUIRED = 116, // RFC 8446
NO_APPLICATION_PROTOCOL = 120, // RFC 7301
diff --git a/src/lib/tls/tls_algos.h b/src/lib/tls/tls_algos.h
index 19612be2e..4852a4349 100644
--- a/src/lib/tls/tls_algos.h
+++ b/src/lib/tls/tls_algos.h
@@ -109,10 +109,10 @@ enum class Signature_Scheme : uint16_t {
BOTAN_UNSTABLE_API const std::vector<Signature_Scheme>& all_signature_schemes();
-bool signature_scheme_is_known(Signature_Scheme scheme);
+bool BOTAN_UNSTABLE_API signature_scheme_is_known(Signature_Scheme scheme);
std::string BOTAN_UNSTABLE_API sig_scheme_to_string(Signature_Scheme scheme);
-std::string hash_function_of_scheme(Signature_Scheme scheme);
-std::string padding_string_for_scheme(Signature_Scheme scheme);
+std::string BOTAN_UNSTABLE_API hash_function_of_scheme(Signature_Scheme scheme);
+std::string BOTAN_UNSTABLE_API padding_string_for_scheme(Signature_Scheme scheme);
std::string signature_algorithm_of_scheme(Signature_Scheme scheme);
/*
diff --git a/src/lib/tls/tls_channel.cpp b/src/lib/tls/tls_channel.cpp
index e021be518..0ee77c83c 100644
--- a/src/lib/tls/tls_channel.cpp
+++ b/src/lib/tls/tls_channel.cpp
@@ -187,8 +187,13 @@ void Channel::renegotiate(bool force_full_renegotiation)
return;
if(auto active = active_state())
+ {
+ if(force_full_renegotiation == false)
+ force_full_renegotiation = !policy().allow_resumption_for_renegotiation();
+
initiate_handshake(create_handshake_state(active->version()),
force_full_renegotiation);
+ }
else
throw Invalid_State("Cannot renegotiate on inactive connection");
}
@@ -334,12 +339,41 @@ size_t Channel::received_data(const uint8_t input[], size_t input_size)
throw TLS_Exception(Alert::RECORD_OVERFLOW,
"TLS plaintext record is larger than allowed maximum");
+ if(auto pending = pending_state())
+ {
+ if(pending->server_hello() != nullptr && record_version != pending->version())
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Received unexpected record version");
+ }
+ }
+ else if(auto active = active_state())
+ {
+ if(record_version != active->version())
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Received unexpected record version");
+ }
+ }
+ else
+ {
+ // For initial records just check for basic sanity
+ if(record_version.major_version() != 3 &&
+ record_version.major_version() != 0xFE)
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Received unexpected record version in initial record");
+ }
+ }
+
if(record_type == HANDSHAKE || record_type == CHANGE_CIPHER_SPEC)
{
process_handshake_ccs(record_data, record_sequence, record_type, record_version);
}
else if(record_type == APPLICATION_DATA)
{
+ if(pending_state() != nullptr)
+ throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Can't interleave application and handshake data");
process_application_data(record_sequence, record_data);
}
else if(record_type == ALERT)
@@ -444,13 +478,7 @@ void Channel::process_application_data(uint64_t seq_no, const secure_vector<uint
if(!active_state())
throw Unexpected_Message("Application data before handshake done");
- /*
- * OpenSSL among others sends empty records in versions
- * before TLS v1.1 in order to randomize the IV of the
- * following record. Avoid spurious callbacks.
- */
- if(record.size() > 0)
- callbacks().tls_record_received(seq_no, record.data(), record.size());
+ callbacks().tls_record_received(seq_no, record.data(), record.size());
}
void Channel::process_alert(const secure_vector<uint8_t>& record)
@@ -483,7 +511,7 @@ void Channel::write_record(Connection_Cipher_State* cipher_state, uint16_t epoch
{
BOTAN_ASSERT(m_pending_state || m_active_state, "Some connection state exists");
- Protocol_Version record_version =
+ const Protocol_Version record_version =
(m_pending_state) ? (m_pending_state->version()) : (m_active_state->version());
Record_Message record_message(record_type, 0, input, length);
@@ -520,18 +548,29 @@ void Channel::send_record_array(uint16_t epoch, uint8_t type, const uint8_t inpu
if(type == APPLICATION_DATA && m_active_state->version().supports_explicit_cbc_ivs() == false)
{
- write_record(cipher_state.get(), epoch, type, input, 1);
- input += 1;
- length -= 1;
- }
+ while(length)
+ {
+ write_record(cipher_state.get(), epoch, type, input, 1);
+ input += 1;
+ length -= 1;
- while(length)
+ const size_t sending = std::min<size_t>(length, MAX_PLAINTEXT_SIZE);
+ write_record(cipher_state.get(), epoch, type, input, sending);
+
+ input += sending;
+ length -= sending;
+ }
+ }
+ else
{
- const size_t sending = std::min<size_t>(length, MAX_PLAINTEXT_SIZE);
- write_record(cipher_state.get(), epoch, type, input, sending);
+ while(length)
+ {
+ const size_t sending = std::min<size_t>(length, MAX_PLAINTEXT_SIZE);
+ write_record(cipher_state.get(), epoch, type, input, sending);
- input += sending;
- length -= sending;
+ input += sending;
+ length -= sending;
+ }
}
}
diff --git a/src/lib/tls/tls_channel.h b/src/lib/tls/tls_channel.h
index 0362faaa8..63cbcf0fc 100644
--- a/src/lib/tls/tls_channel.h
+++ b/src/lib/tls/tls_channel.h
@@ -198,6 +198,8 @@ class BOTAN_PUBLIC_API(2,0) Channel
*/
bool timeout_check();
+ virtual std::string application_protocol() const = 0;
+
protected:
virtual void process_handshake_msg(const Handshake_State* active_state,
diff --git a/src/lib/tls/tls_ciphersuite.cpp b/src/lib/tls/tls_ciphersuite.cpp
index b8a7e70d7..88837387e 100644
--- a/src/lib/tls/tls_ciphersuite.cpp
+++ b/src/lib/tls/tls_ciphersuite.cpp
@@ -57,6 +57,18 @@ bool Ciphersuite::ecc_ciphersuite() const
auth_method() == Auth_Method::ECDSA;
}
+bool Ciphersuite::usable_in_version(Protocol_Version version) const
+ {
+ if(!version.supports_aead_modes())
+ {
+ // Old versions do not support AEAD, or any MAC but SHA-1
+ if(mac_algo() != "SHA-1")
+ return false;
+ }
+
+ return true;
+ }
+
bool Ciphersuite::cbc_ciphersuite() const
{
return (mac_algo() != "AEAD");
@@ -81,6 +93,19 @@ Ciphersuite Ciphersuite::by_id(uint16_t suite)
return Ciphersuite(); // some unknown ciphersuite
}
+Ciphersuite Ciphersuite::from_name(const std::string& name)
+ {
+ const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites();
+
+ for(auto suite : all_suites)
+ {
+ if(suite.to_string() == name)
+ return suite;
+ }
+
+ return Ciphersuite(); // some unknown ciphersuite
+ }
+
namespace {
bool have_hash(const std::string& prf)
diff --git a/src/lib/tls/tls_ciphersuite.h b/src/lib/tls/tls_ciphersuite.h
index 2ee3df20e..7ef7623bb 100644
--- a/src/lib/tls/tls_ciphersuite.h
+++ b/src/lib/tls/tls_ciphersuite.h
@@ -10,6 +10,7 @@
#include <botan/types.h>
#include <botan/tls_algos.h>
+#include <botan/tls_version.h>
#include <string>
#include <vector>
@@ -31,6 +32,13 @@ class BOTAN_PUBLIC_API(2,0) Ciphersuite final
static Ciphersuite by_id(uint16_t suite);
/**
+ * Convert an SSL/TLS ciphersuite name to algorithm fields
+ * @param name the IANA name for the desired ciphersuite
+ * @return ciphersuite object
+ */
+ static Ciphersuite from_name(const std::string& name);
+
+ /**
* Returns true iff this suite is a known SCSV
*/
static bool is_scsv(uint16_t suite);
@@ -115,6 +123,8 @@ class BOTAN_PUBLIC_API(2,0) Ciphersuite final
*/
bool valid() const { return m_usable; }
+ bool usable_in_version(Protocol_Version version) const;
+
bool operator<(const Ciphersuite& o) const { return ciphersuite_code() < o.ciphersuite_code(); }
bool operator<(const uint16_t c) const { return ciphersuite_code() < c; }
diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp
index 94616c60b..eb6d21b14 100644
--- a/src/lib/tls/tls_client.cpp
+++ b/src/lib/tls/tls_client.cpp
@@ -23,9 +23,10 @@ namespace {
class Client_Handshake_State final : public Handshake_State
{
public:
- // using Handshake_State::Handshake_State;
-
- Client_Handshake_State(Handshake_IO* io, Callbacks& cb) : Handshake_State(io, cb) {}
+ Client_Handshake_State(Handshake_IO* io, Callbacks& cb) :
+ Handshake_State(io, cb),
+ m_is_reneg(false)
+ {}
const Public_Key& get_server_public_key() const
{
@@ -33,13 +34,26 @@ class Client_Handshake_State final : public Handshake_State
return *server_public_key.get();
}
- bool is_a_resumption() const { return (resume_master_secret.empty() == false); }
+ bool is_a_resumption() const { return (resumed_session != nullptr); }
- std::unique_ptr<Public_Key> server_public_key;
+ bool is_a_renegotiation() const { return m_is_reneg; }
+
+ const secure_vector<uint8_t>& resume_master_secret() const
+ {
+ BOTAN_STATE_CHECK(is_a_resumption());
+ return resumed_session->master_secret();
+ }
+
+ const std::vector<X509_Certificate>& resume_peer_certs() const
+ {
+ BOTAN_STATE_CHECK(is_a_resumption());
+ return resumed_session->peer_certs();
+ }
+ std::unique_ptr<Public_Key> server_public_key;
// Used during session resumption
- secure_vector<uint8_t> resume_master_secret;
- std::vector<X509_Certificate> resume_peer_certs;
+ std::unique_ptr<Session> resumed_session;
+ bool m_is_reneg = false;
};
}
@@ -123,8 +137,9 @@ std::vector<X509_Certificate>
Client::get_peer_cert_chain(const Handshake_State& state) const
{
const Client_Handshake_State& cstate = dynamic_cast<const Client_Handshake_State&>(state);
- if(cstate.resume_peer_certs.size() > 0)
- return cstate.resume_peer_certs;
+
+ if(cstate.is_a_resumption())
+ return cstate.resume_peer_certs();
if(state.server_certs())
return state.server_certs()->cert_chain();
@@ -137,7 +152,8 @@ Client::get_peer_cert_chain(const Handshake_State& state) const
void Client::initiate_handshake(Handshake_State& state,
bool force_full_renegotiation)
{
- send_client_hello(state, force_full_renegotiation, state.version());
+ send_client_hello(state, force_full_renegotiation,
+ policy().latest_supported_version(state.version().is_datagram_protocol()));
}
void Client::send_client_hello(Handshake_State& state_base,
@@ -154,16 +170,23 @@ void Client::send_client_hello(Handshake_State& state_base,
if(!force_full_renegotiation && !m_info.empty())
{
- Session session_info;
- if(session_manager().load_from_server_info(m_info, session_info))
+ std::unique_ptr<Session> session_info(new Session);;
+ if(session_manager().load_from_server_info(m_info, *session_info))
{
/*
- Ensure that the session protocol type matches what we want to use
+ Ensure that the session protocol cipher and version are acceptable
If not skip the resume and establish a new session
*/
- if(version == session_info.version() && policy().acceptable_ciphersuite(session_info.ciphersuite()))
+ const bool exact_version = session_info->version() == version;
+ const bool ok_version =
+ (session_info->version().is_datagram_protocol() == version.is_datagram_protocol()) &&
+ policy().acceptable_protocol_version(session_info->version());
+
+ const bool session_version_ok = policy().only_resume_with_exact_version() ? exact_version : ok_version;
+
+ if(policy().acceptable_ciphersuite(session_info->ciphersuite()) && session_version_ok)
{
- if(srp_identifier == "" || session_info.srp_identifier() == srp_identifier)
+ if(srp_identifier == "" || session_info->srp_identifier() == srp_identifier)
{
state.client_hello(
new Client_Hello(state.handshake_io(),
@@ -172,11 +195,10 @@ void Client::send_client_hello(Handshake_State& state_base,
callbacks(),
rng(),
secure_renegotiation_data_for_client_hello(),
- session_info,
+ *session_info,
next_protocols));
- state.resume_master_secret = session_info.master_secret();
- state.resume_peer_certs = session_info.peer_certs();
+ state.resumed_session = std::move(session_info);
}
}
}
@@ -215,19 +237,33 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
// Ignore request entirely if we are currently negotiating a handshake
if(state.client_hello())
- return;
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Cannot renegotiate during a handshake");
+ }
if(policy().allow_server_initiated_renegotiation())
{
- if(!secure_renegotiation_supported() && policy().allow_insecure_renegotiation() == false)
- send_warning_alert(Alert::NO_RENEGOTIATION);
+ if(secure_renegotiation_supported() || policy().allow_insecure_renegotiation())
+ {
+ state.m_is_reneg = true;
+ this->initiate_handshake(state, true);
+ }
else
- this->initiate_handshake(state, false);
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Client policy prohibits insecure renegotiation");
+ }
}
else
{
- // RFC 5746 section 4.2
- send_warning_alert(Alert::NO_RENEGOTIATION);
+ if(policy().abort_connection_on_undesired_renegotiation())
+ {
+ throw TLS_Exception(Alert::NO_RENEGOTIATION, "Client policy prohibits renegotiation");
+ }
+ else
+ {
+ // RFC 5746 section 4.2
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ }
}
return;
@@ -257,6 +293,12 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
"Server replied with ciphersuite we didn't send");
}
+ if(!Ciphersuite::by_id(state.server_hello()->ciphersuite()).usable_in_version(state.server_hello()->version()))
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server replied using a ciphersuite not allowed in version it offered");
+ }
+
if(Ciphersuite::is_scsv(state.server_hello()->ciphersuite()))
{
throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
@@ -265,7 +307,7 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
if(state.server_hello()->compression_method() != 0)
{
- throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
"Server replied with non-null compression method");
}
@@ -283,10 +325,10 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
// Server sent us back an extension we did not send!
std::ostringstream msg;
- msg << "Server replied with " << diff.size() << " unsupported extensions:";
+ msg << "Server replied with unsupported extensions:";
for(auto&& d : diff)
msg << " " << static_cast<int>(d);
- throw TLS_Exception(Alert::HANDSHAKE_FAILURE, msg.str());
+ throw TLS_Exception(Alert::UNSUPPORTED_EXTENSION, msg.str());
}
if(uint16_t srtp = state.server_hello()->srtp_profile())
@@ -319,10 +361,26 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
"Server resumed session but with wrong version");
- state.compute_session_keys(state.resume_master_secret);
+ if(state.server_hello()->supports_extended_master_secret() &&
+ !state.resumed_session->supports_extended_master_secret())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server resumed session but added extended master secret");
+ }
+
+ if(!state.server_hello()->supports_extended_master_secret() &&
+ state.resumed_session->supports_extended_master_secret())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server resumed session and removed extended master secret");
+ }
+
+ state.compute_session_keys(state.resume_master_secret());
if(state.server_hello()->supports_session_ticket())
+ {
state.set_expected_next(NEW_SESSION_TICKET);
+ }
else
{
state.set_expected_next(HANDSHAKE_CCS);
@@ -332,8 +390,25 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
{
// new session
- state.resume_master_secret.clear();
- state.resume_peer_certs.clear();
+ if(active_state)
+ {
+ // Here we are testing things that should not change during a renegotation,
+ // even if the server creates a new session. Howerver they might change
+ // in a resumption scenario.
+
+ if(active_state->version() != state.server_hello()->version())
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Server changed version after renegotiation");
+
+ if(state.server_hello()->supports_extended_master_secret() !=
+ active_state->server_hello()->supports_extended_master_secret())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server changed its mind about extended master secret");
+ }
+ }
+
+ state.resumed_session.reset(); // non-null if we were attempting a resumption
if(state.client_hello()->version().is_datagram_protocol() !=
state.server_hello()->version().is_datagram_protocol())
@@ -372,7 +447,7 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
depending on if it has an identity hint for us.
(EC)DHE_PSK always sends a server key exchange for the
- DH exchange portion.
+ DH exchange portion, and is covered by block below
*/
state.set_expected_next(SERVER_KEX);
@@ -406,7 +481,17 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
in case an OCSP response was also available
*/
- std::unique_ptr<Public_Key> peer_key(server_certs[0].subject_public_key());
+ X509_Certificate server_cert = server_certs[0];
+
+ if(active_state && active_state->server_certs())
+ {
+ X509_Certificate current_cert = active_state->server_certs()->cert_chain().at(0);
+
+ if(current_cert != server_cert)
+ throw TLS_Exception(Alert::BAD_CERTIFICATE, "Server certificate changed during renegotiation");
+ }
+
+ std::unique_ptr<Public_Key> peer_key(server_cert.subject_public_key());
const std::string expected_key_type =
state.ciphersuite().signature_used() ? state.ciphersuite().sig_algo() : "RSA";
@@ -444,9 +529,13 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
m_info.hostname(),
policy());
}
+ catch(TLS_Exception& e)
+ {
+ throw;
+ }
catch(std::exception& e)
{
- throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what());
+ throw TLS_Exception(Alert::INTERNAL_ERROR, e.what());
}
}
}
@@ -466,7 +555,8 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
}
else if(type == SERVER_KEX)
{
- state.set_expected_next(CERTIFICATE_REQUEST); // optional
+ if(state.ciphersuite().psk_ciphersuite() == false)
+ state.set_expected_next(CERTIFICATE_REQUEST); // optional
state.set_expected_next(SERVER_HELLO_DONE);
state.server_kex(
@@ -505,7 +595,15 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
std::vector<std::shared_ptr<const OCSP::Response>> ocsp;
if(state.server_cert_status() != nullptr)
- ocsp.push_back(state.server_cert_status()->response());
+ {
+ try {
+ ocsp.push_back(std::make_shared<OCSP::Response>(state.server_cert_status()->response()));
+ }
+ catch(Decoding_Error&)
+ {
+ // ignore it here because it might be our fault
+ }
+ }
callbacks().tls_verify_cert_chain(state.server_certs()->cert_chain(),
ocsp,
@@ -514,9 +612,13 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
m_info.hostname(),
policy());
}
+ catch(TLS_Exception& e)
+ {
+ throw;
+ }
catch(std::exception& e)
{
- throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what());
+ throw TLS_Exception(Alert::INTERNAL_ERROR, e.what());
}
}
diff --git a/src/lib/tls/tls_client.h b/src/lib/tls/tls_client.h
index 63c26b9cd..005370e78 100644
--- a/src/lib/tls/tls_client.h
+++ b/src/lib/tls/tls_client.h
@@ -132,7 +132,7 @@ class BOTAN_PUBLIC_API(2,0) Client final : public Channel
/**
* @return network protocol as advertised by the TLS server, if server sent the ALPN extension
*/
- const std::string& application_protocol() const { return m_application_protocol; }
+ std::string application_protocol() const override { return m_application_protocol; }
private:
void init(const Protocol_Version& protocol_version,
const std::vector<std::string>& next_protocols);
diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp
index c5e6f2831..a673f867b 100644
--- a/src/lib/tls/tls_extensions.cpp
+++ b/src/lib/tls/tls_extensions.cpp
@@ -16,8 +16,10 @@ namespace TLS {
namespace {
-Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size)
+Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size, Connection_Side side)
{
+ BOTAN_UNUSED(side);
+
switch(code)
{
case TLSEXT_SERVER_NAME_INDICATION:
@@ -65,7 +67,7 @@ Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size)
}
-void Extensions::deserialize(TLS_Data_Reader& reader)
+void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side side)
{
if(reader.has_remaining())
{
@@ -79,9 +81,14 @@ void Extensions::deserialize(TLS_Data_Reader& reader)
const uint16_t extension_code = reader.get_uint16_t();
const uint16_t extension_size = reader.get_uint16_t();
- Extension* extn = make_extension(reader,
- extension_code,
- extension_size);
+ const auto type = static_cast<Handshake_Extension_Type>(extension_code);
+
+ if(m_extensions.find(type) != m_extensions.end())
+ throw TLS_Exception(TLS::Alert::DECODE_ERROR,
+ "Peer sent duplicated extensions");
+
+ Extension* extn = make_extension(
+ reader, extension_code, extension_size, side);
this->add(extn);
}
@@ -259,6 +266,9 @@ Application_Layer_Protocol_Notification::Application_Layer_Protocol_Notification
if(bytes_remaining < p.size() + 1)
throw Decoding_Error("Bad encoding of ALPN, length field too long");
+ if(p.empty())
+ throw Decoding_Error("Empty ALPN protocol not allowed");
+
bytes_remaining -= (p.size() + 1);
m_protocols.push_back(p);
diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h
index 98856b951..3ecfb7c0f 100644
--- a/src/lib/tls/tls_extensions.h
+++ b/src/lib/tls/tls_extensions.h
@@ -1,6 +1,6 @@
/*
* TLS Extensions
-* (C) 2011,2012,2016,2018 Jack Lloyd
+* (C) 2011,2012,2016,2018,2019 Jack Lloyd
* (C) 2016 Juraj Somorovsky
* (C) 2016 Matthias Gierlings
*
@@ -11,6 +11,7 @@
#define BOTAN_TLS_EXTENSIONS_H_
#include <botan/tls_algos.h>
+#include <botan/tls_magic.h>
#include <botan/secmem.h>
#include <botan/x509_dn.h>
#include <vector>
@@ -471,7 +472,7 @@ class BOTAN_UNSTABLE_API Extensions final
std::vector<uint8_t> serialize() const;
- void deserialize(TLS_Data_Reader& reader);
+ void deserialize(TLS_Data_Reader& reader, Connection_Side side);
/**
* Remvoe an extension from this extensions object, if it exists.
@@ -482,7 +483,10 @@ class BOTAN_UNSTABLE_API Extensions final
Extensions() = default;
- explicit Extensions(TLS_Data_Reader& reader) { deserialize(reader); }
+ Extensions(TLS_Data_Reader& reader, Connection_Side side)
+ {
+ deserialize(reader, side);
+ }
private:
Extensions(const Extensions&) = delete;
diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp
index a9c7514c1..8bc603a43 100644
--- a/src/lib/tls/tls_handshake_state.cpp
+++ b/src/lib/tls/tls_handshake_state.cpp
@@ -138,7 +138,7 @@ uint32_t bitmask_for_handshake_type(Handshake_Type type)
"Unknown TLS handshake message type " + std::to_string(type));
}
-std::string handshake_mask_to_string(uint32_t mask)
+std::string handshake_mask_to_string(uint32_t mask, char combiner)
{
const Handshake_Type types[] = {
HELLO_VERIFY_REQUEST,
@@ -165,7 +165,7 @@ std::string handshake_mask_to_string(uint32_t mask)
if(mask & bitmask_for_handshake_type(t))
{
if(!empty)
- o << ",";
+ o << combiner;
o << handshake_type_to_string(t);
empty = false;
}
@@ -304,10 +304,9 @@ void Handshake_State::confirm_transition_to(Handshake_Type handshake_msg)
const bool ok = (m_hand_expecting_mask & mask) != 0; // overlap?
if(!ok)
- throw Unexpected_Message("Unexpected state transition in handshake, got type " +
- std::to_string(handshake_msg) +
- " expected " + handshake_mask_to_string(m_hand_expecting_mask) +
- " received " + handshake_mask_to_string(m_hand_received_mask));
+ throw Unexpected_Message("Unexpected state transition in handshake, expected " +
+ handshake_mask_to_string(m_hand_expecting_mask, '|') +
+ " received " + handshake_mask_to_string(m_hand_received_mask, '+'));
/* We don't know what to expect next, so force a call to
set_expected_next; if it doesn't happen, the next transition
@@ -385,18 +384,18 @@ Handshake_State::choose_sig_format(const Private_Key& key,
{
const std::vector<Signature_Scheme> allowed = policy.allowed_signature_schemes();
- std::vector<Signature_Scheme> schemes =
+ std::vector<Signature_Scheme> requested =
(for_client_auth) ? cert_req()->signature_schemes() : client_hello()->signature_schemes();
- if(schemes.empty())
+ if(requested.empty())
{
// Implicit SHA-1
- schemes.push_back(Signature_Scheme::RSA_PKCS1_SHA1);
- schemes.push_back(Signature_Scheme::ECDSA_SHA1);
- schemes.push_back(Signature_Scheme::DSA_SHA1);
+ requested.push_back(Signature_Scheme::RSA_PKCS1_SHA1);
+ requested.push_back(Signature_Scheme::ECDSA_SHA1);
+ requested.push_back(Signature_Scheme::DSA_SHA1);
}
- for(Signature_Scheme scheme : schemes)
+ for(Signature_Scheme scheme : allowed)
{
if(signature_scheme_is_known(scheme) == false)
{
@@ -405,7 +404,7 @@ Handshake_State::choose_sig_format(const Private_Key& key,
if(signature_algorithm_of_scheme(scheme) == sig_algo)
{
- if(std::find(allowed.begin(), allowed.end(), scheme) != allowed.end())
+ if(std::find(requested.begin(), requested.end(), scheme) != requested.end())
{
chosen_scheme = scheme;
break;
@@ -528,11 +527,15 @@ Handshake_State::parse_sig_format(const Public_Key& key,
for_client_auth ? cert_req()->signature_schemes() :
client_hello()->signature_schemes();
+ if(!signature_scheme_is_known(scheme))
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Peer sent unknown signature scheme");
+
const std::string hash_algo = hash_function_of_scheme(scheme);
if(!supported_algos_include(supported_algos, key_type, hash_algo))
{
- throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
"TLS signature extension did not allow for " +
key_type + "/" + hash_algo + " signature");
}
diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h
index 98c46dfa8..0549a66a6 100644
--- a/src/lib/tls/tls_messages.h
+++ b/src/lib/tls/tls_messages.h
@@ -98,6 +98,8 @@ class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message
const std::vector<uint8_t>& session_id() const { return m_session_id; }
+ const std::vector<uint8_t>& compression_methods() const { return m_comp_methods; }
+
const std::vector<uint16_t>& ciphersuites() const { return m_suites; }
bool offered_suite(uint16_t ciphersuite) const;
@@ -387,7 +389,9 @@ class BOTAN_UNSTABLE_API Certificate_Status final : public Handshake_Message
public:
Handshake_Type type() const override { return CERTIFICATE_STATUS; }
- std::shared_ptr<const OCSP::Response> response() const { return m_response; }
+ //std::shared_ptr<const OCSP::Response> response() const { return m_response; }
+
+ const std::vector<uint8_t>& response() const { return m_response; }
Certificate_Status(const std::vector<uint8_t>& buf);
@@ -397,7 +401,7 @@ class BOTAN_UNSTABLE_API Certificate_Status final : public Handshake_Message
private:
std::vector<uint8_t> serialize() const override;
- std::shared_ptr<const OCSP::Response> m_response;
+ std::vector<uint8_t> m_response;
};
/**
diff --git a/src/lib/tls/tls_policy.cpp b/src/lib/tls/tls_policy.cpp
index 4caaf623a..58ba73ade 100644
--- a/src/lib/tls/tls_policy.cpp
+++ b/src/lib/tls/tls_policy.cpp
@@ -25,6 +25,8 @@ std::vector<Signature_Scheme> Policy::allowed_signature_schemes() const
for(Signature_Scheme scheme : all_signature_schemes())
{
+ if(signature_scheme_is_known(scheme) == false)
+ continue;
const bool sig_allowed = allowed_signature_method(signature_algorithm_of_scheme(scheme));
const bool hash_allowed = allowed_signature_hash(hash_function_of_scheme(scheme));
@@ -57,7 +59,7 @@ std::vector<std::string> Policy::allowed_ciphers() const
//"AES-128",
//"Camellia-256",
//"Camellia-128",
- //"SEED"
+ //"SEED",
//"3DES",
};
}
@@ -292,19 +294,19 @@ Protocol_Version Policy::latest_supported_version(bool datagram) const
{
if(datagram)
{
- if(allow_dtls12())
+ if(acceptable_protocol_version(Protocol_Version::DTLS_V12))
return Protocol_Version::DTLS_V12;
- if(allow_dtls10())
+ if(acceptable_protocol_version(Protocol_Version::DTLS_V10))
return Protocol_Version::DTLS_V10;
throw Invalid_State("Policy forbids all available DTLS version");
}
else
{
- if(allow_tls12())
+ if(acceptable_protocol_version(Protocol_Version::TLS_V12))
return Protocol_Version::TLS_V12;
- if(allow_tls11())
+ if(acceptable_protocol_version(Protocol_Version::TLS_V11))
return Protocol_Version::TLS_V11;
- if(allow_tls10())
+ if(acceptable_protocol_version(Protocol_Version::TLS_V10))
return Protocol_Version::TLS_V10;
throw Invalid_State("Policy forbids all available TLS version");
}
@@ -329,6 +331,13 @@ bool Policy::hide_unknown_users() const { return false; }
bool Policy::server_uses_own_ciphersuite_preferences() const { return true; }
bool Policy::negotiate_encrypt_then_mac() const { return true; }
bool Policy::support_cert_status_message() const { return true; }
+bool Policy::allow_resumption_for_renegotiation() const { return true; }
+bool Policy::only_resume_with_exact_version() const { return true; }
+bool Policy::require_client_certificate_authentication() const { return false; }
+bool Policy::request_client_certificate_authentication() const { return require_client_certificate_authentication(); }
+bool Policy::abort_connection_on_undesired_renegotiation() const { return false; }
+
+size_t Policy::maximum_certificate_chain_size() const { return 0; }
// 1 second initial timeout, 60 second max - see RFC 6347 sec 4.2.4.1
size_t Policy::dtls_initial_timeout() const { return 1*1000; }
@@ -431,7 +440,11 @@ std::vector<uint16_t> Policy::ciphersuite_list(Protocol_Version version,
for(auto&& suite : Ciphersuite::all_known_ciphersuites())
{
// Can we use it?
- if(suite.valid() == false)
+ if(!suite.valid())
+ continue;
+
+ // Can we use it in this version?
+ if(!suite.usable_in_version(version))
continue;
// Is it acceptable to the policy?
@@ -442,17 +455,6 @@ std::vector<uint16_t> Policy::ciphersuite_list(Protocol_Version version,
if(!have_srp && suite.kex_method() == Kex_Algo::SRP_SHA)
continue;
- if(!version.supports_aead_modes())
- {
- // Are we doing AEAD in a non-AEAD version?
- if(suite.mac_algo() == "AEAD")
- continue;
-
- // Older (v1.0/v1.1) versions also do not support any hash but SHA-1
- if(suite.mac_algo() != "SHA-1")
- continue;
- }
-
if(!value_exists(kex, suite.kex_algo()))
continue; // unsupported key exchange
diff --git a/src/lib/tls/tls_policy.h b/src/lib/tls/tls_policy.h
index 7b00b2e01..3a5be83d9 100644
--- a/src/lib/tls/tls_policy.h
+++ b/src/lib/tls/tls_policy.h
@@ -125,6 +125,14 @@ class BOTAN_PUBLIC_API(2,0) Policy
virtual bool allow_server_initiated_renegotiation() const;
/**
+ * If true, a request to renegotiate will close the connection with
+ * a fatal alert. Otherwise, a warning alert is sent.
+ */
+ virtual bool abort_connection_on_undesired_renegotiation() const;
+
+ virtual bool only_resume_with_exact_version() const;
+
+ /**
* Allow TLS v1.0
*/
virtual bool allow_tls10() const;
@@ -271,6 +279,19 @@ class BOTAN_PUBLIC_API(2,0) Policy
virtual bool support_cert_status_message() const;
/**
+ * Indicate if client certificate authentication is required.
+ * If true, then a cert will be requested and if the client does
+ * not send a certificate the connection will be closed.
+ */
+ virtual bool require_client_certificate_authentication() const;
+
+ /**
+ * Indicate if client certificate authentication is requested.
+ * If true, then a cert will be requested.
+ */
+ virtual bool request_client_certificate_authentication() const;
+
+ /**
* Return allowed ciphersuites, in order of preference
*/
virtual std::vector<uint16_t> ciphersuite_list(Protocol_Version version,
@@ -292,6 +313,14 @@ class BOTAN_PUBLIC_API(2,0) Policy
virtual size_t dtls_maximum_timeout() const;
/**
+ * @return the maximum size of the certificate chain, in bytes.
+ * Return 0 to disable this and accept any size.
+ */
+ virtual size_t maximum_certificate_chain_size() const;
+
+ virtual bool allow_resumption_for_renegotiation() const;
+
+ /**
* Convert this policy to a printable format.
* @param o stream to be printed to
*/
@@ -525,6 +554,8 @@ class BOTAN_PUBLIC_API(2,0) Text_Policy : public Policy
bool support_cert_status_message() const override;
+ bool require_client_certificate_authentication() const override;
+
size_t minimum_ecdh_group_size() const override;
size_t minimum_ecdsa_group_size() const override;
diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp
index 2b1885e45..d0c3baa2e 100644
--- a/src/lib/tls/tls_server.cpp
+++ b/src/lib/tls/tls_server.cpp
@@ -153,7 +153,7 @@ uint16_t choose_ciphersuite(
const Policy& policy,
Protocol_Version version,
Credentials_Manager& creds,
- const std::map<std::string, std::vector<X509_Certificate> >& cert_chains,
+ const std::map<std::string, std::vector<X509_Certificate>>& cert_chains,
const Client_Hello& client_hello)
{
const bool our_choice = policy.server_uses_own_ciphersuite_preferences();
@@ -264,17 +264,17 @@ uint16_t choose_ciphersuite(
"Can't agree on a ciphersuite with client");
}
-std::map<std::string, std::vector<X509_Certificate> >
+std::map<std::string, std::vector<X509_Certificate>>
get_server_certs(const std::string& hostname,
Credentials_Manager& creds)
{
- const char* cert_types[] = { "RSA", "DSA", "ECDSA", nullptr };
+ const char* cert_types[] = { "RSA", "ECDSA", "DSA", nullptr };
- std::map<std::string, std::vector<X509_Certificate> > cert_chains;
+ std::map<std::string, std::vector<X509_Certificate>> cert_chains;
for(size_t i = 0; cert_types[i]; ++i)
{
- std::vector<X509_Certificate> certs =
+ const std::vector<X509_Certificate> certs =
creds.cert_chain_single_type(cert_types[i], "tls-server", hostname);
if(!certs.empty())
@@ -321,7 +321,6 @@ Server::Server(output_fn output,
{
}
-
Server::Server(output_fn output,
data_cb got_data_cb,
alert_cb recv_alert_cb,
@@ -372,53 +371,29 @@ void Server::initiate_handshake(Handshake_State& state,
Hello_Request hello_req(state.handshake_io());
}
-/*
-* Process a CLIENT HELLO Message
-*/
-void Server::process_client_hello_msg(const Handshake_State* active_state,
- Server_Handshake_State& pending_state,
- const std::vector<uint8_t>& contents)
- {
- const bool initial_handshake = !active_state;
+namespace {
- if(initial_handshake == false && policy().allow_client_initiated_renegotiation() == false)
- {
- send_warning_alert(Alert::NO_RENEGOTIATION);
- return;
- }
+Protocol_Version select_version(const Botan::TLS::Policy& policy,
+ Protocol_Version client_offer,
+ Protocol_Version active_version,
+ bool is_fallback)
+ {
+ const Protocol_Version latest_supported =
+ policy.latest_supported_version(client_offer.is_datagram_protocol());
- if(!policy().allow_insecure_renegotiation() &&
- !(initial_handshake || secure_renegotiation_supported()))
+ if(is_fallback)
{
- send_warning_alert(Alert::NO_RENEGOTIATION);
- return;
+ if(latest_supported > client_offer)
+ throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK,
+ "Client signalled fallback SCSV, possible attack");
}
- pending_state.client_hello(new Client_Hello(contents));
- const Protocol_Version client_version = pending_state.client_hello()->version();
-
- if(client_version.major_version() < 3)
- throw TLS_Exception(Alert::PROTOCOL_VERSION, "Client offered version with major version under 3");
- if(client_version.major_version() == 3 && client_version.minor_version() == 0)
- throw TLS_Exception(Alert::PROTOCOL_VERSION, "SSLv3 is not supported");
+ const bool initial_handshake = (active_version.valid() == false);
- Protocol_Version negotiated_version;
-
- const Protocol_Version latest_supported =
- policy().latest_supported_version(client_version.is_datagram_protocol());
+ const bool client_offer_acceptable =
+ client_offer.known_version() && policy.acceptable_protocol_version(client_offer);
- if((initial_handshake && client_version.known_version()) ||
- (!initial_handshake && client_version == active_state->version()))
- {
- /*
- Common cases: new client hello with some known version, or a
- renegotiation using the same version as previously
- negotiated.
- */
-
- negotiated_version = client_version;
- }
- else if(!initial_handshake && (client_version != active_state->version()))
+ if(!initial_handshake)
{
/*
* If this is a renegotiation, and the client has offered a
@@ -427,40 +402,80 @@ void Server::process_client_hello_msg(const Handshake_State* active_state,
* client is offering a version earlier than what it initially
* negotiated, reject as a probable attack.
*/
- if(active_state->version() > client_version)
+ if(active_version > client_offer)
{
throw TLS_Exception(Alert::PROTOCOL_VERSION,
"Client negotiated " +
- active_state->version().to_string() +
+ active_version.to_string() +
" then renegotiated with " +
- client_version.to_string());
+ client_offer.to_string());
}
else
- negotiated_version = active_state->version();
+ {
+ return active_version;
+ }
}
- else
+ else if(client_offer_acceptable)
+ {
+ return client_offer;
+ }
+ else if(!client_offer.known_version() || client_offer > latest_supported)
{
/*
- New negotiation using a version we don't know. Offer them the
- best we currently know and support
+ The client offered some version newer than the latest we
+ support. Offer them the best we know.
*/
- negotiated_version = latest_supported;
+ return latest_supported;
}
-
- if(!policy().acceptable_protocol_version(negotiated_version))
+ else
{
throw TLS_Exception(Alert::PROTOCOL_VERSION,
- "Client version " + negotiated_version.to_string() +
+ "Client version " + client_offer.to_string() +
" is unacceptable by policy");
}
+ }
+
+}
- if(pending_state.client_hello()->sent_fallback_scsv())
+/*
+* Process a CLIENT HELLO Message
+*/
+void Server::process_client_hello_msg(const Handshake_State* active_state,
+ Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents)
+ {
+ const bool initial_handshake = !active_state;
+
+ if(initial_handshake == false && policy().allow_client_initiated_renegotiation() == false)
{
- if(latest_supported > client_version)
- throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK,
- "Client signalled fallback SCSV, possible attack");
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ return;
}
+ if(!policy().allow_insecure_renegotiation() &&
+ !(initial_handshake || secure_renegotiation_supported()))
+ {
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ return;
+ }
+
+ pending_state.client_hello(new Client_Hello(contents));
+ const Protocol_Version client_offer = pending_state.client_hello()->version();
+
+ if(client_offer.major_version() < 3)
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "Client offered version with major version under 3");
+ if(client_offer.major_version() == 3 && client_offer.minor_version() == 0)
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "SSLv3 is not supported");
+
+ const Protocol_Version negotiated_version =
+ select_version(policy(), client_offer,
+ active_state ? active_state->version() : Protocol_Version(),
+ pending_state.client_hello()->sent_fallback_scsv());
+
+ const auto compression_methods = pending_state.client_hello()->compression_methods();
+ if(!value_exists(compression_methods, uint8_t(0)))
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Client did not offer NULL compression");
+
secure_renegotiation_check(pending_state.client_hello());
pending_state.set_version(negotiated_version);
@@ -511,6 +526,11 @@ void Server::process_certificate_msg(Server_Handshake_State& pending_state,
const std::vector<uint8_t>& contents)
{
pending_state.client_certs(new Certificate(contents, policy()));
+
+ // CERTIFICATE_REQUIRED would make more sense but BoGo expects handshake failure alert
+ if(pending_state.client_certs()->empty() && policy().require_client_certificate_authentication())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Policy requires client send a certificate, but it did not");
+
pending_state.set_expected_next(CLIENT_KEX);
}
@@ -767,7 +787,7 @@ void Server::session_resume(Server_Handshake_State& pending_state,
void Server::session_create(Server_Handshake_State& pending_state,
bool have_session_ticket_key)
{
- std::map<std::string, std::vector<X509_Certificate> > cert_chains;
+ std::map<std::string, std::vector<X509_Certificate>> cert_chains;
const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
@@ -782,10 +802,10 @@ void Server::session_create(Server_Handshake_State& pending_state,
* find any certs for the requested name but did find at
* least one cert to use in general. That avoids sending an
* unrecognized_name when a server is configured for purely
- * anonymous operation.
+ * anonymous/PSK operation.
*/
if(!cert_chains.empty())
- send_alert(Alert(Alert::UNRECOGNIZED_NAME));
+ send_warning_alert(Alert::UNRECOGNIZED_NAME);
}
const uint16_t ciphersuite = choose_ciphersuite(policy(), pending_state.version(),
@@ -857,7 +877,11 @@ void Server::session_create(Server_Handshake_State& pending_state,
client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end());
}
- if(!client_auth_CAs.empty() && pending_state.ciphersuite().signature_used())
+ const bool request_cert =
+ (client_auth_CAs.empty() == false) ||
+ policy().request_client_certificate_authentication();
+
+ if(request_cert && pending_state.ciphersuite().signature_used())
{
pending_state.cert_req(
new Certificate_Req(pending_state.handshake_io(),
diff --git a/src/lib/tls/tls_server.h b/src/lib/tls/tls_server.h
index c55c3f93d..e6536934a 100644
--- a/src/lib/tls/tls_server.h
+++ b/src/lib/tls/tls_server.h
@@ -110,7 +110,7 @@ class BOTAN_PUBLIC_API(2,0) Server final : public Channel
* tied to the session and a later renegotiation of the same
* session can choose a new protocol.
*/
- std::string application_protocol() const { return m_next_protocol; }
+ std::string application_protocol() const override { return m_next_protocol; }
private:
std::vector<X509_Certificate>
diff --git a/src/lib/tls/tls_text_policy.cpp b/src/lib/tls/tls_text_policy.cpp
index 829899fbc..1b83f0dbd 100644
--- a/src/lib/tls/tls_text_policy.cpp
+++ b/src/lib/tls/tls_text_policy.cpp
@@ -80,6 +80,11 @@ bool Text_Policy::include_time_in_hello_random() const
return get_bool("include_time_in_hello_random", Policy::include_time_in_hello_random());
}
+bool Text_Policy::require_client_certificate_authentication() const
+ {
+ return get_bool("require_client_certificate_authentication", Policy::require_client_certificate_authentication());
+ }
+
bool Text_Policy::allow_client_initiated_renegotiation() const
{
return get_bool("allow_client_initiated_renegotiation", Policy::allow_client_initiated_renegotiation());
diff --git a/src/lib/tls/tls_version.h b/src/lib/tls/tls_version.h
index 569030085..13be64316 100644
--- a/src/lib/tls/tls_version.h
+++ b/src/lib/tls/tls_version.h
@@ -82,6 +82,11 @@ class BOTAN_PUBLIC_API(2,0) Protocol_Version final
uint8_t minor_version() const { return get_byte(1, m_version); }
/**
+ * @return the version code
+ */
+ uint16_t version_code() const { return m_version; }
+
+ /**
* @return human-readable description of this version
*/
std::string to_string() const;