/* * TLS Channels * (C) 2011 Jack Lloyd * * Released under the terms of the Botan license */ #include #include #include #include namespace Botan { TLS_Channel::TLS_Channel(std::tr1::function socket_output_fn, std::tr1::function proc_fn, std::tr1::function handshake_complete) : proc_fn(proc_fn), handshake_fn(handshake_complete), writer(socket_output_fn), state(0), handshake_completed(false), connection_closed(false) { } TLS_Channel::~TLS_Channel() { close(); delete state; state = 0; } Version_Code TLS_Channel::protocol_version() const { if(!handshake_completed) throw std::logic_error("Version not known until handshake complete"); return writer.get_version(); } size_t TLS_Channel::received_data(const byte buf[], size_t buf_size) { try { reader.add_input(buf, buf_size); byte rec_type = CONNECTION_CLOSED; MemoryVector record; while(!reader.currently_empty()) { const size_t bytes_needed = reader.get_record(rec_type, record); if(bytes_needed > 0) return bytes_needed; if(rec_type == APPLICATION_DATA) { if(handshake_completed) { /* * 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) proc_fn(&record[0], record.size(), NULL_ALERT); } 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_msg(record); alert_notify(alert_msg.is_fatal(), alert_msg.type()); proc_fn(0, 0, alert_msg.type()); if(!connection_closed) { if(alert_msg.is_fatal() || alert_msg.type() == CLOSE_NOTIFY) { if(alert_msg.type() == CLOSE_NOTIFY) alert(FATAL, CLOSE_NOTIFY); else alert(FATAL, NULL_ALERT); } } } else throw Unexpected_Message("Unknown message type received"); } return 0; // on a record boundary } catch(TLS_Exception& e) { alert(FATAL, e.type()); throw; } catch(Decoding_Error& e) { alert(FATAL, DECODE_ERROR); throw; } catch(std::exception& e) { alert(FATAL, INTERNAL_ERROR); throw; } } /* * Split up and process handshake messages */ void TLS_Channel::read_handshake(byte rec_type, const MemoryRegion& rec_buf) { if(rec_type == HANDSHAKE) { if(!state) state = new Handshake_State; state->queue.write(&rec_buf[0], rec_buf.size()); } while(true) { Handshake_Type type = HANDSHAKE_NONE; MemoryVector 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(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; } } void TLS_Channel::queue_for_sending(const byte buf[], size_t buf_size) { if(!handshake_completed) throw std::runtime_error("Application data cannot be queued before handshake"); writer.send(APPLICATION_DATA, buf, buf_size); } void TLS_Channel::alert(Alert_Level alert_level, Alert_Type alert_code) { if(alert_code != NULL_ALERT && !connection_closed) { try { writer.alert(alert_level, alert_code); } catch(...) { /* swallow it */ } } if(!connection_closed && (alert_code == CLOSE_NOTIFY || alert_level == FATAL)) { connection_closed = true; delete state; state = 0; reader.reset(); writer.reset(); } } void TLS_Channel::Secure_Renegotiation_State::update(Client_Hello* client_hello) { if(initial_handshake) { secure_renegotiation = client_hello->secure_renegotiation(); } else { if(secure_renegotiation != client_hello->secure_renegotiation()) throw TLS_Exception(HANDSHAKE_FAILURE, "Client changed its mind about secure renegotiation"); } if(client_hello->secure_renegotiation()) { const MemoryVector& data = client_hello->renegotiation_info(); if(initial_handshake) { if(!data.empty()) throw TLS_Exception(HANDSHAKE_FAILURE, "Client sent renegotiation data on initial handshake"); } else { if(data != for_client_hello()) throw TLS_Exception(HANDSHAKE_FAILURE, "Client sent bad renegotiation data"); } } } void TLS_Channel::Secure_Renegotiation_State::update(Server_Hello* server_hello) { if(initial_handshake) { /* If the client offered but server rejected, then this toggles * secure_renegotiation to off */ secure_renegotiation = server_hello->secure_renegotiation(); } else { if(secure_renegotiation != server_hello->secure_renegotiation()) throw TLS_Exception(HANDSHAKE_FAILURE, "Server changed its mind about secure renegotiation"); } if(secure_renegotiation) { const MemoryVector& 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(); } }