diff options
Diffstat (limited to 'src/ssl/tls_client.cpp')
-rw-r--r-- | src/ssl/tls_client.cpp | 586 |
1 files changed, 0 insertions, 586 deletions
diff --git a/src/ssl/tls_client.cpp b/src/ssl/tls_client.cpp deleted file mode 100644 index a136752fd..000000000 --- a/src/ssl/tls_client.cpp +++ /dev/null @@ -1,586 +0,0 @@ -/* -* TLS Client -* (C) 2004-2010 Jack Lloyd -* -* Released under the terms of the Botan license -*/ - -#include <botan/tls_client.h> -#include <botan/internal/tls_alerts.h> -#include <botan/internal/tls_state.h> -#include <botan/loadstor.h> -#include <botan/rsa.h> -#include <botan/dsa.h> -#include <botan/dh.h> - -namespace Botan { - -namespace { - -/** -* Verify the state transition is allowed -* FIXME: checks are wrong for session reuse (add a flag for that) -*/ -void client_check_state(Handshake_Type new_msg, Handshake_State* state) - { - class State_Transition_Error : public Unexpected_Message - { - public: - State_Transition_Error(const std::string& err) : - Unexpected_Message("State transition error from " + err) {} - }; - - if(new_msg == HELLO_REQUEST) - { - if(state->client_hello) - throw State_Transition_Error("HelloRequest"); - } - else if(new_msg == SERVER_HELLO) - { - if(!state->client_hello || state->server_hello) - throw State_Transition_Error("ServerHello"); - } - else if(new_msg == CERTIFICATE) - { - if(!state->server_hello || state->server_kex || - state->cert_req || state->server_hello_done) - throw State_Transition_Error("ServerCertificate"); - } - else if(new_msg == SERVER_KEX) - { - if(!state->server_hello || state->server_kex || - state->cert_req || state->server_hello_done) - throw State_Transition_Error("ServerKeyExchange"); - } - else if(new_msg == CERTIFICATE_REQUEST) - { - if(!state->server_certs || state->cert_req || state->server_hello_done) - throw State_Transition_Error("CertificateRequest"); - } - else if(new_msg == SERVER_HELLO_DONE) - { - if(!state->server_hello || state->server_hello_done) - throw State_Transition_Error("ServerHelloDone"); - } - else if(new_msg == HANDSHAKE_CCS) - { - if(!state->client_finished || state->server_finished) - throw State_Transition_Error("ServerChangeCipherSpec"); - } - else if(new_msg == FINISHED) - { - if(!state->got_server_ccs) - throw State_Transition_Error("ServerFinished"); - } - else - throw Unexpected_Message("Unexpected message in handshake"); - } - -} - -/** -* TLS Client Constructor -*/ -TLS_Client::TLS_Client(std::tr1::function<size_t (byte[], size_t)> input_fn, - std::tr1::function<void (const byte[], size_t)> output_fn, - const TLS_Policy& policy, - RandomNumberGenerator& rng) : - input_fn(input_fn), - policy(policy), - rng(rng), - writer(output_fn) - { - initialize(); - } - -void TLS_Client::add_client_cert(const X509_Certificate& cert, - Private_Key* cert_key) - { - certs.push_back(std::make_pair(cert, cert_key)); - } - -/** -* TLS Client Destructor -*/ -TLS_Client::~TLS_Client() - { - close(); - for(size_t i = 0; i != certs.size(); i++) - delete certs[i].second; - delete state; - } - -/** -* Initialize a TLS client connection -*/ -void TLS_Client::initialize() - { - std::string error_str; - Alert_Type error_type = NO_ALERT_TYPE; - - try { - state = 0; - active = false; - writer.set_version(policy.pref_version()); - do_handshake(); - } - catch(TLS_Exception& e) - { - error_str = e.what(); - error_type = e.type(); - } - catch(std::exception& e) - { - error_str = e.what(); - error_type = HANDSHAKE_FAILURE; - } - - if(error_type != NO_ALERT_TYPE) - { - if(active) - { - active = false; - reader.reset(); - - writer.alert(FATAL, error_type); - writer.reset(); - } - - if(state) - { - delete state; - state = 0; - } - - throw Stream_IO_Error("TLS_Client: Handshake failed: " + error_str); - } - } - -/** -* Return the peer's certificate chain -*/ -std::vector<X509_Certificate> TLS_Client::peer_cert_chain() const - { - return peer_certs; - } - -/** -* Write to a TLS connection -*/ -void TLS_Client::write(const byte buf[], size_t length) - { - if(!active) - throw TLS_Exception(INTERNAL_ERROR, - "TLS_Client::write called while closed"); - - writer.send(APPLICATION_DATA, buf, length); - } - -/** -* Read from a TLS connection -*/ -size_t TLS_Client::read(byte out[], size_t length) - { - if(!active) - return 0; - - writer.flush(); - - while(read_buf.size() == 0) - { - state_machine(); - if(active == false) - break; - } - - size_t got = std::min<size_t>(read_buf.size(), length); - read_buf.read(out, got); - return got; - } - -/** -* Close a TLS connection -*/ -void TLS_Client::close() - { - close(WARNING, CLOSE_NOTIFY); - } - -/** -* Check connection status -*/ -bool TLS_Client::is_closed() const - { - if(!active) - return true; - return false; - } - -/** -* Close a TLS connection -*/ -void TLS_Client::close(Alert_Level level, Alert_Type alert_code) - { - if(active) - { - try { - writer.alert(level, alert_code); - writer.flush(); - } - catch(...) {} - - active = false; - } - } - -/** -* Iterate the TLS state machine -*/ -void TLS_Client::state_machine() - { - byte rec_type = CONNECTION_CLOSED; - SecureVector<byte> record(1024); - - size_t bytes_needed = reader.get_record(rec_type, record); - - while(bytes_needed) - { - size_t to_get = std::min<size_t>(record.size(), bytes_needed); - size_t got = input_fn(&record[0], to_get); - - if(got == 0) - { - rec_type = CONNECTION_CLOSED; - break; - } - - reader.add_input(&record[0], got); - - bytes_needed = reader.get_record(rec_type, record); - } - - if(rec_type == CONNECTION_CLOSED) - { - active = false; - reader.reset(); - writer.reset(); - } - else if(rec_type == APPLICATION_DATA) - { - if(active) - read_buf.write(&record[0], record.size()); - else - throw Unexpected_Message("Application data before handshake done"); - } - else if(rec_type == HANDSHAKE || rec_type == CHANGE_CIPHER_SPEC) - read_handshake(rec_type, record); - else if(rec_type == ALERT) - { - Alert alert(record); - - if(alert.is_fatal() || alert.type() == CLOSE_NOTIFY) - { - if(alert.type() == CLOSE_NOTIFY) - writer.alert(WARNING, CLOSE_NOTIFY); - - reader.reset(); - writer.reset(); - active = false; - if(state) - { - delete state; - state = 0; - } - } - } - else - throw Unexpected_Message("Unknown message type received"); - } - -/** -* Split up and process handshake messages -*/ -void TLS_Client::read_handshake(byte rec_type, - const MemoryRegion<byte>& rec_buf) - { - if(rec_type == HANDSHAKE) - state->queue.write(&rec_buf[0], rec_buf.size()); - - while(true) - { - Handshake_Type type = HANDSHAKE_NONE; - SecureVector<byte> contents; - - if(rec_type == HANDSHAKE) - { - if(state->queue.size() >= 4) - { - byte head[4] = { 0 }; - state->queue.peek(head, 4); - - const size_t length = make_u32bit(0, head[1], head[2], head[3]); - - if(state->queue.size() >= length + 4) - { - type = static_cast<Handshake_Type>(head[0]); - contents.resize(length); - state->queue.read(head, 4); - state->queue.read(&contents[0], contents.size()); - } - } - } - else if(rec_type == CHANGE_CIPHER_SPEC) - { - if(state->queue.size() == 0 && rec_buf.size() == 1 && rec_buf[0] == 1) - type = HANDSHAKE_CCS; - else - throw Decoding_Error("Malformed ChangeCipherSpec message"); - } - else - throw Decoding_Error("Unknown message type in handshake processing"); - - if(type == HANDSHAKE_NONE) - break; - - process_handshake_msg(type, contents); - - if(type == HANDSHAKE_CCS || !state) - break; - } - } - -/** -* Process a handshake message -*/ -void TLS_Client::process_handshake_msg(Handshake_Type type, - const MemoryRegion<byte>& contents) - { - rng.add_entropy(&contents[0], contents.size()); - - if(type == HELLO_REQUEST) - { - if(state == 0) - state = new Handshake_State(); - else - return; - } - - if(state == 0) - throw Unexpected_Message("Unexpected handshake message"); - - if(type != HANDSHAKE_CCS && type != HELLO_REQUEST && type != FINISHED) - { - state->hash.update(static_cast<byte>(type)); - const size_t record_length = contents.size(); - for(size_t i = 0; i != 3; i++) - state->hash.update(get_byte<u32bit>(i+1, record_length)); - state->hash.update(contents); - } - - if(type == HELLO_REQUEST) - { - client_check_state(type, state); - - Hello_Request hello_request(contents); - state->client_hello = new Client_Hello(rng, writer, policy, state->hash); - } - else if(type == SERVER_HELLO) - { - client_check_state(type, state); - - state->server_hello = new Server_Hello(contents); - - if(!state->client_hello->offered_suite( - state->server_hello->ciphersuite() - ) - ) - throw TLS_Exception(HANDSHAKE_FAILURE, - "TLS_Client: Server replied with bad ciphersuite"); - - state->version = state->server_hello->version(); - - if(state->version > state->client_hello->version()) - throw TLS_Exception(HANDSHAKE_FAILURE, - "TLS_Client: Server replied with bad version"); - - if(state->version < policy.min_version()) - throw TLS_Exception(PROTOCOL_VERSION, - "TLS_Client: Server is too old for specified policy"); - - writer.set_version(state->version); - reader.set_version(state->version); - - state->suite = CipherSuite(state->server_hello->ciphersuite()); - } - else if(type == CERTIFICATE) - { - client_check_state(type, state); - - if(state->suite.sig_type() == TLS_ALGO_SIGNER_ANON) - throw Unexpected_Message("Recived certificate from anonymous server"); - - state->server_certs = new Certificate(contents); - - peer_certs = state->server_certs->cert_chain(); - if(peer_certs.size() == 0) - throw TLS_Exception(HANDSHAKE_FAILURE, - "TLS_Client: No certificates sent by server"); - - if(!policy.check_cert(peer_certs)) - throw TLS_Exception(BAD_CERTIFICATE, - "TLS_Client: Server certificate is not valid"); - - state->kex_pub = peer_certs[0].subject_public_key(); - - bool is_dsa = false, is_rsa = false; - - if(dynamic_cast<DSA_PublicKey*>(state->kex_pub)) - is_dsa = true; - else if(dynamic_cast<RSA_PublicKey*>(state->kex_pub)) - is_rsa = true; - else - throw TLS_Exception(UNSUPPORTED_CERTIFICATE, - "Unknown key type received in server kex"); - - if((is_dsa && state->suite.sig_type() != TLS_ALGO_SIGNER_DSA) || - (is_rsa && state->suite.sig_type() != TLS_ALGO_SIGNER_RSA)) - throw TLS_Exception(ILLEGAL_PARAMETER, - "Certificate key type did not match ciphersuite"); - } - else if(type == SERVER_KEX) - { - client_check_state(type, state); - - if(state->suite.kex_type() == TLS_ALGO_KEYEXCH_NOKEX) - throw Unexpected_Message("Unexpected key exchange from server"); - - state->server_kex = new Server_Key_Exchange(contents); - - if(state->kex_pub) - delete state->kex_pub; - - state->kex_pub = state->server_kex->key(); - - bool is_dh = false, is_rsa = false; - - if(dynamic_cast<DH_PublicKey*>(state->kex_pub)) - is_dh = true; - else if(dynamic_cast<RSA_PublicKey*>(state->kex_pub)) - is_rsa = true; - else - throw TLS_Exception(HANDSHAKE_FAILURE, - "Unknown key type received in server kex"); - - if((is_dh && state->suite.kex_type() != TLS_ALGO_KEYEXCH_DH) || - (is_rsa && state->suite.kex_type() != TLS_ALGO_KEYEXCH_RSA)) - throw TLS_Exception(ILLEGAL_PARAMETER, - "Certificate key type did not match ciphersuite"); - - if(state->suite.sig_type() != TLS_ALGO_SIGNER_ANON) - { - if(!state->server_kex->verify(peer_certs[0], - state->client_hello->random(), - state->server_hello->random())) - throw TLS_Exception(DECRYPT_ERROR, - "Bad signature on server key exchange"); - } - } - else if(type == CERTIFICATE_REQUEST) - { - client_check_state(type, state); - - state->cert_req = new Certificate_Req(contents); - state->do_client_auth = true; - } - else if(type == SERVER_HELLO_DONE) - { - client_check_state(type, state); - - state->server_hello_done = new Server_Hello_Done(contents); - - if(state->do_client_auth) - { - std::vector<X509_Certificate> send_certs; - - std::vector<Certificate_Type> types = - state->cert_req->acceptable_types(); - - // FIXME: Fill in useful certs here, if any - state->client_certs = new Certificate(writer, send_certs, - state->hash); - } - - state->client_kex = - new Client_Key_Exchange(rng, writer, state->hash, - state->kex_pub, state->version, - state->client_hello->version()); - - if(state->do_client_auth) - { - Private_Key* key_matching_cert = 0; // FIXME - state->client_verify = new Certificate_Verify(rng, - writer, state->hash, - key_matching_cert); - } - - state->keys = SessionKeys(state->suite, state->version, - state->client_kex->pre_master_secret(), - state->client_hello->random(), - state->server_hello->random()); - - writer.send(CHANGE_CIPHER_SPEC, 1); - writer.flush(); - - writer.set_keys(state->suite, state->keys, CLIENT); - - state->client_finished = new Finished(writer, state->version, CLIENT, - state->keys.master_secret(), - state->hash); - } - else if(type == HANDSHAKE_CCS) - { - client_check_state(type, state); - - reader.set_keys(state->suite, state->keys, CLIENT); - state->got_server_ccs = true; - } - else if(type == FINISHED) - { - client_check_state(type, state); - - state->server_finished = new Finished(contents); - - if(!state->server_finished->verify(state->keys.master_secret(), - state->version, state->hash, SERVER)) - throw TLS_Exception(DECRYPT_ERROR, - "Finished message didn't verify"); - - delete state; - state = 0; - active = true; - } - else - throw Unexpected_Message("Unknown handshake message received"); - } - -/** -* Perform a client-side TLS handshake -*/ -void TLS_Client::do_handshake() - { - state = new Handshake_State; - - state->client_hello = new Client_Hello(rng, writer, policy, state->hash); - - while(true) - { - if(active && !state) - break; - if(!active && !state) - throw TLS_Exception(HANDSHAKE_FAILURE, "TLS_Client: Handshake failed (do_handshake)"); - - state_machine(); - } - } - -} |