aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls/tls_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tls/tls_server.cpp')
-rw-r--r--src/tls/tls_server.cpp296
1 files changed, 164 insertions, 132 deletions
diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp
index ccba16629..eacbc02e0 100644
--- a/src/tls/tls_server.cpp
+++ b/src/tls/tls_server.cpp
@@ -1,37 +1,25 @@
/*
* TLS Server
-* (C) 2004-2011 Jack Lloyd
+* (C) 2004-2011,2012 Jack Lloyd
*
* Released under the terms of the Botan license
*/
#include <botan/tls_server.h>
-#include <botan/internal/tls_session_key.h>
#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/tls_messages.h>
#include <botan/internal/stl_util.h>
-#include <botan/rsa.h>
-#include <botan/dh.h>
+#include <botan/internal/assert.h>
+#include <memory>
namespace Botan {
-namespace {
+namespace TLS {
-/*
-* Choose what version to respond with
-*/
-Version_Code choose_version(Version_Code client, Version_Code minimum)
- {
- if(client < minimum)
- throw TLS_Exception(PROTOCOL_VERSION,
- "Client version is unacceptable by policy");
-
- if(client == SSL_V3 || client == TLS_V10 || client == TLS_V11)
- return client;
- return TLS_V11;
- }
+namespace {
-bool check_for_resume(TLS_Session& session_info,
- TLS_Session_Manager& session_manager,
+bool check_for_resume(Session& session_info,
+ Session_Manager& session_manager,
Client_Hello* client_hello)
{
MemoryVector<byte> client_session_id = client_hello->session_id();
@@ -49,7 +37,7 @@ bool check_for_resume(TLS_Session& session_info,
// client didn't send original ciphersuite
if(!value_exists(client_hello->ciphersuites(),
- session_info.ciphersuite()))
+ session_info.ciphersuite_code()))
return false;
// client didn't send original compression method
@@ -74,20 +62,40 @@ bool check_for_resume(TLS_Session& session_info,
return true;
}
+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", 0 };
+
+ std::map<std::string, std::vector<X509_Certificate> > cert_chains;
+
+ for(size_t i = 0; cert_types[i]; ++i)
+ {
+ std::vector<X509_Certificate> certs =
+ creds.cert_chain_single_type(cert_types[i], "tls-server", hostname);
+
+ if(!certs.empty())
+ cert_chains[cert_types[i]] = certs;
+ }
+
+ return cert_chains;
+ }
+
}
/*
* TLS Server Constructor
*/
-TLS_Server::TLS_Server(std::tr1::function<void (const byte[], size_t)> output_fn,
- std::tr1::function<void (const byte[], size_t, u16bit)> proc_fn,
- std::tr1::function<bool (const TLS_Session&)> handshake_fn,
- TLS_Session_Manager& session_manager,
- Credentials_Manager& creds,
- const TLS_Policy& policy,
- RandomNumberGenerator& rng,
- const std::vector<std::string>& next_protocols) :
- TLS_Channel(output_fn, proc_fn, handshake_fn),
+Server::Server(std::tr1::function<void (const byte[], size_t)> output_fn,
+ std::tr1::function<void (const byte[], size_t, Alert)> proc_fn,
+ std::tr1::function<bool (const Session&)> handshake_fn,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const std::vector<std::string>& next_protocols) :
+ Channel(output_fn, proc_fn, handshake_fn),
policy(policy),
rng(rng),
session_manager(session_manager),
@@ -99,19 +107,19 @@ TLS_Server::TLS_Server(std::tr1::function<void (const byte[], size_t)> output_fn
/*
* Send a hello request to the client
*/
-void TLS_Server::renegotiate()
+void Server::renegotiate()
{
if(state)
return; // currently in handshake
- state = new Handshake_State;
+ state = new Handshake_State(new Stream_Handshake_Reader);
state->set_expected_next(CLIENT_HELLO);
Hello_Request hello_req(writer);
}
-void TLS_Server::alert_notify(bool, Alert_Type type)
+void Server::alert_notify(const Alert& alert)
{
- if(type == NO_RENEGOTIATION)
+ if(alert.type() == Alert::NO_RENEGOTIATION)
{
if(handshake_completed && state)
{
@@ -124,23 +132,23 @@ void TLS_Server::alert_notify(bool, Alert_Type type)
/*
* Split up and process handshake messages
*/
-void TLS_Server::read_handshake(byte rec_type,
- const MemoryRegion<byte>& rec_buf)
+void Server::read_handshake(byte rec_type,
+ const MemoryRegion<byte>& rec_buf)
{
if(rec_type == HANDSHAKE && !state)
{
- state = new Handshake_State;
+ state = new Handshake_State(new Stream_Handshake_Reader);
state->set_expected_next(CLIENT_HELLO);
}
- TLS_Channel::read_handshake(rec_type, rec_buf);
+ Channel::read_handshake(rec_type, rec_buf);
}
/*
* Process a handshake message
*/
-void TLS_Server::process_handshake_msg(Handshake_Type type,
- const MemoryRegion<byte>& contents)
+void Server::process_handshake_msg(Handshake_Type type,
+ const MemoryRegion<byte>& contents)
{
if(state == 0)
throw Unexpected_Message("Unexpected handshake message from client");
@@ -168,15 +176,23 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
m_hostname = state->client_hello->sni_hostname();
- state->version = choose_version(state->client_hello->version(),
- policy.min_version());
+ Protocol_Version client_version = state->client_hello->version();
+
+ if(client_version < policy.min_version())
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Client version is unacceptable by policy");
+
+ if(client_version <= policy.pref_version())
+ state->set_version(client_version);
+ else
+ state->set_version(policy.pref_version());
secure_renegotiation.update(state->client_hello);
- writer.set_version(state->version);
- reader.set_version(state->version);
+ writer.set_version(state->version());
+ reader.set_version(state->version());
- TLS_Session session_info;
+ Session session_info;
const bool resuming = check_for_resume(session_info,
session_manager,
state->client_hello);
@@ -189,8 +205,8 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
writer,
state->hash,
session_info.session_id(),
- Version_Code(session_info.version()),
- session_info.ciphersuite(),
+ Protocol_Version(session_info.version()),
+ session_info.ciphersuite_code(),
session_info.compression_method(),
session_info.fragment_size(),
secure_renegotiation.supported(),
@@ -205,21 +221,16 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
writer.set_maximum_fragment_size(session_info.fragment_size());
}
- state->suite = TLS_Cipher_Suite(state->server_hello->ciphersuite());
+ state->suite = Ciphersuite::by_id(state->server_hello->ciphersuite());
- state->keys = SessionKeys(state->suite, state->version,
- session_info.master_secret(),
- state->client_hello->random(),
- state->server_hello->random(),
- true);
+ state->keys = Session_Keys(state, session_info.master_secret(), true);
writer.send(CHANGE_CIPHER_SPEC, 1);
- writer.activate(state->suite, state->keys, SERVER);
+ writer.activate(SERVER, state->suite, state->keys,
+ state->server_hello->compression_method());
- state->server_finished = new Finished(writer, state->hash,
- state->version, SERVER,
- state->keys.master_secret());
+ state->server_finished = new Finished(writer, state, SERVER);
if(!handshake_fn(session_info))
session_manager.remove_entry(session_info.session_id());
@@ -228,23 +239,31 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
}
else // new session
{
- std::vector<X509_Certificate> server_certs =
- creds.cert_chain("",
- "tls-server",
- m_hostname);
+ std::map<std::string, std::vector<X509_Certificate> > cert_chains;
+
+ cert_chains = get_server_certs(m_hostname, creds);
- Private_Key* private_key =
- server_certs.empty() ? 0 :
- (creds.private_key_for(server_certs[0],
- "tls-server",
- m_hostname));
+ if(m_hostname != "" && cert_chains.empty())
+ {
+ send_alert(Alert(Alert::UNRECOGNIZED_NAME));
+ cert_chains = get_server_certs("", creds);
+ }
+
+ std::vector<std::string> available_cert_types;
+
+ for(std::map<std::string, std::vector<X509_Certificate> >::const_iterator i = cert_chains.begin();
+ i != cert_chains.end(); ++i)
+ {
+ if(!i->second.empty())
+ available_cert_types.push_back(i->first);
+ }
state->server_hello = new Server_Hello(
writer,
state->hash,
- state->version,
+ state->version(),
*(state->client_hello),
- server_certs,
+ available_cert_types,
policy,
secure_renegotiation.supported(),
secure_renegotiation.for_server_hello(),
@@ -258,37 +277,53 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
writer.set_maximum_fragment_size(state->client_hello->fragment_size());
}
- state->suite = TLS_Cipher_Suite(state->server_hello->ciphersuite());
+ state->suite = Ciphersuite::by_id(state->server_hello->ciphersuite());
- if(state->suite.sig_type() != TLS_ALGO_SIGNER_ANON)
+ const std::string sig_algo = state->suite.sig_algo();
+ const std::string kex_algo = state->suite.kex_algo();
+
+ if(sig_algo != "")
{
+ BOTAN_ASSERT(!cert_chains[sig_algo].empty(),
+ "Attempting to send empty certificate chain");
+
state->server_certs = new Certificate(writer,
state->hash,
- server_certs);
+ cert_chains[sig_algo]);
}
- if(state->suite.kex_type() != TLS_ALGO_KEYEXCH_NOKEX)
+ Private_Key* private_key = 0;
+
+ if(kex_algo == "RSA" || sig_algo != "")
{
- if(state->suite.kex_type() == TLS_ALGO_KEYEXCH_DH)
- state->kex_priv = new DH_PrivateKey(rng, policy.dh_group());
- else
- throw Internal_Error("TLS_Server: Unknown ciphersuite kex type");
+ private_key = creds.private_key_for(state->server_certs->cert_chain()[0],
+ "tls-server",
+ m_hostname);
- state->server_kex =
- new Server_Key_Exchange(writer, state->hash, rng,
- state->kex_priv, private_key,
- state->client_hello->random(),
- state->server_hello->random());
+ if(!private_key)
+ throw Internal_Error("No private key located for associated server cert");
}
- else
- state->kex_priv = PKCS8::copy_key(*private_key, rng);
- if(policy.require_client_auth())
+ if(kex_algo == "RSA")
{
- // FIXME: figure out the allowed CAs/cert types
+ state->server_rsa_kex_key = private_key;
+ }
+ else
+ {
+ state->server_kex =
+ new Server_Key_Exchange(writer, state, policy, creds, rng, private_key);
+ }
- state->cert_req = new Certificate_Req(writer, state->hash,
- std::vector<X509_Certificate>());
+ std::vector<X509_Certificate> client_auth_CAs =
+ creds.trusted_certificate_authorities("tls-server", m_hostname);
+
+ if(!client_auth_CAs.empty() && state->suite.sig_algo() != "")
+ {
+ state->cert_req = new Certificate_Req(writer,
+ state->hash,
+ policy,
+ client_auth_CAs,
+ state->version());
state->set_expected_next(CERTIFICATE);
}
@@ -311,7 +346,7 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
// Is this allowed by the protocol?
if(state->client_certs->count() > 1)
- throw TLS_Exception(CERTIFICATE_UNKNOWN,
+ throw TLS_Exception(Alert::CERTIFICATE_UNKNOWN,
"Client sent more than one certificate");
state->set_expected_next(CLIENT_KEX);
@@ -323,29 +358,19 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
else
state->set_expected_next(HANDSHAKE_CCS);
- state->client_kex = new Client_Key_Exchange(contents, state->suite,
- state->version);
+ state->client_kex = new Client_Key_Exchange(contents, state, creds, policy, rng);
- SecureVector<byte> pre_master =
- state->client_kex->pre_master_secret(rng, state->kex_priv,
- state->client_hello->version());
-
- state->keys = SessionKeys(state->suite, state->version, pre_master,
- state->client_hello->random(),
- state->server_hello->random());
+ state->keys = Session_Keys(state, state->client_kex->pre_master_secret(), false);
}
else if(type == CERTIFICATE_VERIFY)
{
- state->client_verify = new Certificate_Verify(contents);
+ state->client_verify = new Certificate_Verify(contents, state->version());
const std::vector<X509_Certificate>& client_certs =
state->client_certs->cert_chain();
const bool sig_valid =
- state->client_verify->verify(client_certs[0],
- state->hash,
- state->server_hello->version(),
- state->keys.master_secret());
+ state->client_verify->verify(client_certs[0], state);
state->hash.update(type, contents);
@@ -355,9 +380,16 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
* unable to correctly verify a signature, ..."
*/
if(!sig_valid)
- throw TLS_Exception(DECRYPT_ERROR, "Client cert verify failed");
+ throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed");
- // FIXME: check cert was issued by a CA we requested, signatures, etc.
+ try
+ {
+ creds.verify_certificate_chain("tls-server", "", client_certs);
+ }
+ catch(std::exception& e)
+ {
+ throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what());
+ }
state->set_expected_next(HANDSHAKE_CCS);
}
@@ -368,7 +400,8 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
else
state->set_expected_next(FINISHED);
- reader.activate(state->suite, state->keys, SERVER);
+ reader.activate(SERVER, state->suite, state->keys,
+ state->server_hello->compression_method());
}
else if(type == NEXT_PROTOCOL)
{
@@ -384,46 +417,43 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
state->client_finished = new Finished(contents);
- if(!state->client_finished->verify(state->keys.master_secret(),
- state->version, state->hash, CLIENT))
- throw TLS_Exception(DECRYPT_ERROR,
+ if(!state->client_finished->verify(state, CLIENT))
+ throw TLS_Exception(Alert::DECRYPT_ERROR,
"Finished message didn't verify");
- // already sent it if resuming
if(!state->server_finished)
{
state->hash.update(type, contents);
writer.send(CHANGE_CIPHER_SPEC, 1);
- writer.activate(state->suite, state->keys, SERVER);
+ writer.activate(SERVER, state->suite, state->keys,
+ state->server_hello->compression_method());
- state->server_finished = new Finished(writer, state->hash,
- state->version, SERVER,
- state->keys.master_secret());
+ state->server_finished = new Finished(writer, state, SERVER);
if(state->client_certs && state->client_verify)
peer_certs = state->client_certs->cert_chain();
- }
- TLS_Session session_info(
- state->server_hello->session_id(),
- state->keys.master_secret(),
- state->server_hello->version(),
- state->server_hello->ciphersuite(),
- state->server_hello->compression_method(),
- SERVER,
- secure_renegotiation.supported(),
- state->server_hello->fragment_size(),
- peer_certs,
- m_hostname,
- ""
- );
-
- if(handshake_fn(session_info))
- session_manager.save(session_info);
- else
- session_manager.remove_entry(session_info.session_id());
+ // already sent finished if resuming, so this is a new session
+
+ Session session_info(
+ state->server_hello->session_id(),
+ state->keys.master_secret(),
+ state->server_hello->version(),
+ state->server_hello->ciphersuite(),
+ state->server_hello->compression_method(),
+ SERVER,
+ secure_renegotiation.supported(),
+ state->server_hello->fragment_size(),
+ peer_certs,
+ m_hostname,
+ ""
+ );
+
+ if(handshake_fn(session_info))
+ session_manager.save(session_info);
+ }
secure_renegotiation.update(state->client_finished,
state->server_finished);
@@ -437,3 +467,5 @@ void TLS_Server::process_handshake_msg(Handshake_Type type,
}
}
+
+}