diff options
author | lloyd <[email protected]> | 2011-12-30 15:37:36 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2011-12-30 15:37:36 +0000 |
commit | fc626d62f464a7dee5e62134781f5a6ccdfafc72 (patch) | |
tree | 6658020f1836b2faacffabca03897ca871b2880d | |
parent | ca850112360e48a3a140f08fa07f02806d61393b (diff) |
Many renegotiation fixes. Add support for the secure renegotiation
extension (client side only at the moment). Add an interface that
allows applications to request renegotiation.
-rw-r--r-- | src/tls/finished.cpp | 16 | ||||
-rw-r--r-- | src/tls/hello.cpp | 32 | ||||
-rw-r--r-- | src/tls/tls_channel.cpp | 48 | ||||
-rw-r--r-- | src/tls/tls_channel.h | 39 | ||||
-rw-r--r-- | src/tls/tls_client.cpp | 70 | ||||
-rw-r--r-- | src/tls/tls_client.h | 2 | ||||
-rw-r--r-- | src/tls/tls_extensions.cpp | 2 | ||||
-rw-r--r-- | src/tls/tls_magic.h | 5 | ||||
-rw-r--r-- | src/tls/tls_messages.h | 6 | ||||
-rw-r--r-- | src/tls/tls_policy.h | 2 | ||||
-rw-r--r-- | src/tls/tls_server.cpp | 18 | ||||
-rw-r--r-- | src/tls/tls_server.h | 2 | ||||
-rw-r--r-- | src/tls/tls_session_state.cpp | 3 |
13 files changed, 209 insertions, 36 deletions
diff --git a/src/tls/finished.cpp b/src/tls/finished.cpp index c8d0173da..70b714bfd 100644 --- a/src/tls/finished.cpp +++ b/src/tls/finished.cpp @@ -10,7 +10,7 @@ namespace Botan { -/** +/* * Create a new Finished message */ Finished::Finished(Record_Writer& writer, @@ -23,7 +23,7 @@ Finished::Finished(Record_Writer& writer, send(writer, hash); } -/** +/* * Serialize a Finished message */ MemoryVector<byte> Finished::serialize() const @@ -31,7 +31,7 @@ MemoryVector<byte> Finished::serialize() const return verification_data; } -/** +/* * Deserialize a Finished message */ void Finished::deserialize(const MemoryRegion<byte>& buf) @@ -39,11 +39,13 @@ void Finished::deserialize(const MemoryRegion<byte>& buf) verification_data = buf; } -/** +/* * Verify a Finished message */ -bool Finished::verify(const MemoryRegion<byte>& secret, Version_Code version, - const TLS_Handshake_Hash& hash, Connection_Side side) +bool Finished::verify(const MemoryRegion<byte>& secret, + Version_Code version, + const TLS_Handshake_Hash& hash, + Connection_Side side) { MemoryVector<byte> computed = compute_verify(secret, hash, side, version); if(computed == verification_data) @@ -51,7 +53,7 @@ bool Finished::verify(const MemoryRegion<byte>& secret, Version_Code version, return false; } -/** +/* * Compute the verify_data */ MemoryVector<byte> Finished::compute_verify(const MemoryRegion<byte>& secret, diff --git a/src/tls/hello.cpp b/src/tls/hello.cpp index 8136eaa6b..055f92018 100644 --- a/src/tls/hello.cpp +++ b/src/tls/hello.cpp @@ -70,6 +70,7 @@ Client_Hello::Client_Hello(Record_Writer& writer, TLS_Handshake_Hash& hash, const TLS_Policy& policy, RandomNumberGenerator& rng, + const MemoryRegion<byte>& reneg_info, const std::string& hostname, const std::string& srp_identifier) : c_version(policy.pref_version()), @@ -77,7 +78,9 @@ Client_Hello::Client_Hello(Record_Writer& writer, suites(policy.ciphersuites()), comp_methods(policy.compression()), requested_hostname(hostname), - requested_srp_id(srp_identifier) + requested_srp_id(srp_identifier), + has_secure_renegotiation(false), + renegotiation_info_bits(reneg_info) { send(writer, hash); } @@ -97,17 +100,28 @@ MemoryVector<byte> Client_Hello::serialize() const append_tls_length_value(buf, suites, 2); append_tls_length_value(buf, comp_methods, 1); - printf("Requesting hostname '%s'\n", requested_hostname.c_str()); - /* * May not want to send extensions at all in some cases. - * If so, should include SCSV value + * If so, should include SCSV value (if reneg info is empty, if + * not we are renegotiating with a modern server and should only + * send that extension. */ TLS_Extensions extensions; - extensions.push_back(new Renegotation_Extension()); - extensions.push_back(new Server_Name_Indicator(requested_hostname)); - extensions.push_back(new SRP_Identifier(requested_srp_id)); + + // Initial handshake + if(renegotiation_info_bits.empty()) + { + extensions.push_back(new Renegotation_Extension(renegotiation_info_bits)); + extensions.push_back(new Server_Name_Indicator(requested_hostname)); + extensions.push_back(new SRP_Identifier(requested_srp_id)); + } + else + { + // renegotiation + extensions.push_back(new Renegotation_Extension(renegotiation_info_bits)); + } + buf += extensions.serialize(); return buf; @@ -285,6 +299,8 @@ MemoryVector<byte> Server_Hello::serialize() const */ void Server_Hello::deserialize(const MemoryRegion<byte>& buf) { + has_secure_renegotiation = false; + if(buf.size() < 38) throw Decoding_Error("Server_Hello: Packet corrupted"); @@ -312,6 +328,8 @@ void Server_Hello::deserialize(const MemoryRegion<byte>& buf) { TLS_Extension* extn = extensions.at(i); + printf("Extension from server %d\n", extn->type()); + if(Renegotation_Extension* reneg = dynamic_cast<Renegotation_Extension*>(extn)) { // checked by TLS_Client / TLS_Server as they know the handshake state diff --git a/src/tls/tls_channel.cpp b/src/tls/tls_channel.cpp index 8cfb15ad6..144ca659f 100644 --- a/src/tls/tls_channel.cpp +++ b/src/tls/tls_channel.cpp @@ -10,6 +10,8 @@ #include <botan/internal/tls_state.h> #include <botan/loadstor.h> +#include <stdio.h> + namespace Botan { TLS_Channel::TLS_Channel(std::tr1::function<void (const byte[], size_t)> socket_output_fn, @@ -17,8 +19,7 @@ TLS_Channel::TLS_Channel(std::tr1::function<void (const byte[], size_t)> socket_ proc_fn(proc_fn), writer(socket_output_fn), state(0), - active(false), - secure_renegotiation(false) + active(false) { } @@ -110,7 +111,11 @@ void TLS_Channel::read_handshake(byte rec_type, const MemoryRegion<byte>& rec_buf) { if(rec_type == HANDSHAKE) + { + if(!state) + state = new Handshake_State; state->queue.write(&rec_buf[0], rec_buf.size()); + } while(true) { @@ -193,4 +198,43 @@ void TLS_Channel::alert(Alert_Level alert_level, Alert_Type alert_code) } } +void TLS_Channel::Secure_Renegotiation_State::update(Client_Hello* client_hello) + { + + } + +void TLS_Channel::Secure_Renegotiation_State::update(Server_Hello* server_hello) + { + secure_renegotiation = server_hello->secure_renegotiation(); + + printf("server hello says sec reneg: %d\n", secure_renegotiation); + + if(secure_renegotiation) + { + const MemoryVector<byte>& data = server_hello->renegotiation_info(); + + if(initial_handshake) + { + if(!data.empty()) + throw TLS_Exception(HANDSHAKE_FAILURE, + "Server sent renegotiation data on initial handshake"); + } + else + { + if(data != for_server_hello()) + throw TLS_Exception(HANDSHAKE_FAILURE, + "Server sent bad renegotiation data"); + } + } + + initial_handshake = false; + } + +void TLS_Channel::Secure_Renegotiation_State::update(Finished* client_finished, + Finished* server_finished) + { + client_verify = client_finished->verify_data(); + server_verify = server_finished->verify_data(); + } + } diff --git a/src/tls/tls_channel.h b/src/tls/tls_channel.h index f8b53684d..5f06181de 100644 --- a/src/tls/tls_channel.h +++ b/src/tls/tls_channel.h @@ -51,6 +51,11 @@ class BOTAN_DLL TLS_Channel bool is_active() const { return active; } /** + * Attempt to renegotiate the session + */ + virtual void renegotiate() = 0; + + /** * Return the certificates of the peer */ std::vector<X509_Certificate> peer_cert_chain() const { return peer_certs; } @@ -77,8 +82,38 @@ class BOTAN_DLL TLS_Channel class Handshake_State* state; - bool secure_renegotiation; - MemoryVector<byte> client_verify_data, server_verify_data; + class Secure_Renegotiation_State + { + public: + Secure_Renegotiation_State() : initial_handshake(true), + secure_renegotiation(false) + {} + + void update(class Client_Hello* client_hello); + void update(class Server_Hello* server_hello); + + void update(class Finished* client_finished, + class Finished* server_finished); + + const MemoryVector<byte>& for_client_hello() const + { return client_verify; } + + MemoryVector<byte> for_server_hello() const + { + MemoryVector<byte> buf = client_verify; + buf += server_verify; + return buf; + } + + bool supported() const { return secure_renegotiation; } + bool renegotiation() const { return !initial_handshake; } + private: + bool initial_handshake; + bool secure_renegotiation; + MemoryVector<byte> client_verify, server_verify; + }; + + Secure_Renegotiation_State secure_renegotiation; bool active; }; diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp index 2119ca44e..a97ac65b3 100644 --- a/src/tls/tls_client.cpp +++ b/src/tls/tls_client.cpp @@ -12,6 +12,8 @@ #include <botan/dsa.h> #include <botan/dh.h> +#include <stdio.h> + namespace Botan { /* @@ -37,8 +39,11 @@ TLS_Client::TLS_Client(std::tr1::function<void (const byte[], size_t)> output_fn state->hash, policy, rng, + secure_renegotiation.for_client_hello(), hostname, srp_username); + + secure_renegotiation.update(state->client_hello); } void TLS_Client::add_client_cert(const X509_Certificate& cert, @@ -57,33 +62,66 @@ TLS_Client::~TLS_Client() } /* +* Send a new client hello to renegotiate +*/ +void TLS_Client::renegotiate() + { + if(state) + return; // currently in handshake + + state = new Handshake_State; + state->set_expected_next(SERVER_HELLO); + + state->client_hello = new Client_Hello(writer, state->hash, policy, rng, + secure_renegotiation.for_client_hello()); + + secure_renegotiation.update(state->client_hello); + } + +/* * Process a handshake message */ void TLS_Client::process_handshake_msg(Handshake_Type type, const MemoryRegion<byte>& contents) { + if(state == 0) + throw Unexpected_Message("Unexpected handshake message from server"); + if(type == HELLO_REQUEST) { - if(state == 0) - state = new Handshake_State(); - else - return; // hello request in middle of handshake? - } + printf("got a hello request\n"); - if(state == 0) - throw Unexpected_Message("Unexpected handshake message"); + Hello_Request hello_request(contents); + + // Ignore request entirely if we are currently negotiating a handshake + if(state->client_hello) + return; + + if(!secure_renegotiation.supported() && policy.require_secure_renegotiation()) + { + delete state; + state = 0; + + // RFC 5746 section 4.2 + alert(WARNING, NO_RENEGOTIATION); + return; + } + + state->set_expected_next(SERVER_HELLO); + state->client_hello = new Client_Hello(writer, state->hash, policy, rng, + secure_renegotiation.for_client_hello()); + + secure_renegotiation.update(state->client_hello); + + return; + } state->confirm_transition_to(type); - if(type != HANDSHAKE_CCS && type != HELLO_REQUEST && type != FINISHED) + if(type != HANDSHAKE_CCS && type != FINISHED) state->hash.update(type, contents); - if(type == HELLO_REQUEST) - { - Hello_Request hello_request(contents); - state->client_hello = new Client_Hello(writer, state->hash, policy, rng); - } - else if(type == SERVER_HELLO) + if(type == SERVER_HELLO) { state->server_hello = new Server_Hello(contents); @@ -110,6 +148,8 @@ void TLS_Client::process_handshake_msg(Handshake_Type type, writer.set_version(state->version); reader.set_version(state->version); + secure_renegotiation.update(state->server_hello); + state->suite = CipherSuite(state->server_hello->ciphersuite()); // if resuming, next is HANDSHAKE_CCS @@ -271,6 +311,8 @@ void TLS_Client::process_handshake_msg(Handshake_Type type, throw TLS_Exception(DECRYPT_ERROR, "Finished message didn't verify"); + secure_renegotiation.update(state->client_finished, state->server_finished); + delete state; state = 0; active = true; diff --git a/src/tls/tls_client.h b/src/tls/tls_client.h index a2ad232c8..279f4bc16 100644 --- a/src/tls/tls_client.h +++ b/src/tls/tls_client.h @@ -41,6 +41,8 @@ class BOTAN_DLL TLS_Client : public TLS_Channel void add_client_cert(const X509_Certificate& cert, Private_Key* cert_key); + void renegotiate(); + ~TLS_Client(); private: void process_handshake_msg(Handshake_Type type, diff --git a/src/tls/tls_extensions.cpp b/src/tls/tls_extensions.cpp index 28523abf2..1e8060821 100644 --- a/src/tls/tls_extensions.cpp +++ b/src/tls/tls_extensions.cpp @@ -30,6 +30,8 @@ TLS_Extensions::TLS_Extensions(class TLS_Data_Reader& reader) extensions.push_back(new Server_Name_Indicator(reader)); else if(extension_code == TLSEXT_SRP_IDENTIFIER) extensions.push_back(new SRP_Identifier(reader)); + else if(extension_code == TLSEXT_SAFE_RENEGOTIATION) + extensions.push_back(new Renegotation_Extension(reader)); else // unknown/unhandled extension { printf("Unknown extension code %d\n", extension_code); diff --git a/src/tls/tls_magic.h b/src/tls/tls_magic.h index 535319d41..e20788ea3 100644 --- a/src/tls/tls_magic.h +++ b/src/tls/tls_magic.h @@ -148,7 +148,10 @@ enum Ciphersuite_Code { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028, + + /* signalling values that cannot be negotiated */ + TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF }; /* diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h index 38d347d28..5ac655bec 100644 --- a/src/tls/tls_messages.h +++ b/src/tls/tls_messages.h @@ -76,6 +76,7 @@ class Client_Hello : public HandshakeMessage TLS_Handshake_Hash& hash, const TLS_Policy& policy, RandomNumberGenerator& rng, + const MemoryRegion<byte>& reneg_info, const std::string& hostname = "", const std::string& srp_identifier = ""); @@ -284,6 +285,9 @@ class Finished : public HandshakeMessage public: Handshake_Type type() const { return FINISHED; } + MemoryVector<byte> verify_data() const + { return verification_data; } + bool verify(const MemoryRegion<byte>& buf, Version_Code version, const TLS_Handshake_Hash& hash, @@ -317,7 +321,7 @@ class Hello_Request : public HandshakeMessage public: Handshake_Type type() const { return HELLO_REQUEST; } - Hello_Request(Record_Writer&); + Hello_Request(Record_Writer& writer); Hello_Request(const MemoryRegion<byte>& buf) { deserialize(buf); } private: MemoryVector<byte> serialize() const; diff --git a/src/tls/tls_policy.h b/src/tls/tls_policy.h index 461164d2f..dd38f3574 100644 --- a/src/tls/tls_policy.h +++ b/src/tls/tls_policy.h @@ -36,6 +36,8 @@ class BOTAN_DLL TLS_Policy virtual bool allow_edh_dsa() const { return true; } virtual bool require_client_auth() const { return false; } + virtual bool require_secure_renegotiation() const { return true; } + virtual DL_Group dh_group() const; virtual size_t rsa_export_keysize() const { return 512; } diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp index 090a55566..197ed4b2c 100644 --- a/src/tls/tls_server.cpp +++ b/src/tls/tls_server.cpp @@ -12,6 +12,8 @@ #include <botan/rsa.h> #include <botan/dh.h> +#include <stdio.h> + namespace Botan { namespace { @@ -106,6 +108,20 @@ TLS_Server::~TLS_Server() } /* +* Send a hello request to the client +*/ +void TLS_Server::renegotiate() + { + if(state) + return; // currently in handshake + + state = new Handshake_State; + state->set_expected_next(CLIENT_HELLO); + Hello_Request hello_req(writer); + printf("sent new hello request\n"); + } + +/* * Split up and process handshake messages */ void TLS_Server::read_handshake(byte rec_type, @@ -127,7 +143,7 @@ void TLS_Server::process_handshake_msg(Handshake_Type type, const MemoryRegion<byte>& contents) { if(state == 0) - throw Unexpected_Message("Unexpected handshake message"); + throw Unexpected_Message("Unexpected handshake message from client"); state->confirm_transition_to(type); diff --git a/src/tls/tls_server.h b/src/tls/tls_server.h index a1f99a0ff..45e25f0f9 100644 --- a/src/tls/tls_server.h +++ b/src/tls/tls_server.h @@ -37,6 +37,8 @@ class BOTAN_DLL TLS_Server : public TLS_Channel ~TLS_Server(); + void renegotiate(); + /** * Return the server name indicator, if set by the client */ diff --git a/src/tls/tls_session_state.cpp b/src/tls/tls_session_state.cpp index 3b54a795e..9d8d4215d 100644 --- a/src/tls/tls_session_state.cpp +++ b/src/tls/tls_session_state.cpp @@ -104,7 +104,8 @@ bool TLS_Session_Manager_In_Memory::find(const MemoryVector<byte>& session_id, return true; } -void TLS_Session_Manager_In_Memory::prohibit_resumption(const MemoryVector<byte>& session_id) +void TLS_Session_Manager_In_Memory::prohibit_resumption( + const MemoryVector<byte>& session_id) { std::map<std::string, TLS_Session_Params>::iterator i = sessions.find(hex_encode(session_id)); |