Implemented EAP-TLS server functionality
This commit is contained in:
parent
97abf95412
commit
400df4ca7c
|
@ -335,9 +335,17 @@ METHOD(eap_method_t, process, status_t,
|
|||
if (!(pkt->flags & EAP_TLS_START))
|
||||
{
|
||||
if (data.len == sizeof(eap_tls_packet_t))
|
||||
{ /* ACK to our fragment, send next */
|
||||
*out = read_buf(this, pkt->identifier);
|
||||
return NEED_MORE;
|
||||
{
|
||||
if (this->output.len)
|
||||
{ /* ACK to our fragment, send next */
|
||||
*out = read_buf(this, pkt->identifier);
|
||||
return NEED_MORE;
|
||||
}
|
||||
if (this->tls->is_complete(this->tls))
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAILED;
|
||||
}
|
||||
if (!write_buf(this, pkt))
|
||||
{
|
||||
|
|
|
@ -144,6 +144,12 @@ METHOD(tls_t, set_version, void,
|
|||
this->version = version;
|
||||
}
|
||||
|
||||
METHOD(tls_t, is_complete, bool,
|
||||
private_tls_t *this)
|
||||
{
|
||||
return this->crypto->get_eap_msk(this->crypto).len != 0;
|
||||
}
|
||||
|
||||
METHOD(tls_t, get_eap_msk, chunk_t,
|
||||
private_tls_t *this)
|
||||
{
|
||||
|
@ -179,6 +185,7 @@ tls_t *tls_create(bool is_server, identification_t *server,
|
|||
.is_server = _is_server,
|
||||
.get_version = _get_version,
|
||||
.set_version = _set_version,
|
||||
.is_complete = _is_complete,
|
||||
.get_eap_msk = _get_eap_msk,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
|
|
|
@ -134,6 +134,13 @@ struct tls_t {
|
|||
*/
|
||||
void (*set_version)(tls_t *this, tls_version_t version);
|
||||
|
||||
/**
|
||||
* Check if TLS negotiation completed successfully.
|
||||
*
|
||||
* @return TRUE if TLS negotation and authentication complete
|
||||
*/
|
||||
bool (*is_complete)(tls_t *this);
|
||||
|
||||
/**
|
||||
* Get the MSK for EAP-TLS.
|
||||
*
|
||||
|
|
|
@ -425,42 +425,81 @@ static bool hash_handshake(private_tls_crypto_t *this, chunk_t *hash)
|
|||
}
|
||||
|
||||
METHOD(tls_crypto_t, sign_handshake, bool,
|
||||
private_tls_crypto_t *this, private_key_t *key, chunk_t *sig)
|
||||
private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer)
|
||||
{
|
||||
chunk_t sig, hash;
|
||||
|
||||
if (this->tls->get_version(this->tls) >= TLS_1_2)
|
||||
{
|
||||
u_int16_t length;
|
||||
u_int8_t hash_alg;
|
||||
u_int8_t sig_alg;
|
||||
|
||||
if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig))
|
||||
/* TODO: use supported algorithms instead of fixed SHA1/RSA */
|
||||
if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, &sig))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
/* TODO: signature scheme to hashsign algorithm mapping */
|
||||
hash_alg = 2; /* sha1 */
|
||||
sig_alg = 1; /* RSA */
|
||||
length = htons(sig->len);
|
||||
*sig = chunk_cat("cccm", chunk_from_thing(hash_alg),
|
||||
chunk_from_thing(sig_alg), chunk_from_thing(length), *sig);
|
||||
writer->write_uint8(writer, 2);
|
||||
writer->write_uint8(writer, 1);
|
||||
writer->write_data16(writer, sig);
|
||||
free(sig.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
u_int16_t length;
|
||||
chunk_t hash;
|
||||
|
||||
if (!hash_handshake(this, &hash))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
|
||||
if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig))
|
||||
{
|
||||
free(hash.ptr);
|
||||
return FALSE;
|
||||
}
|
||||
writer->write_data16(writer, sig);
|
||||
free(hash.ptr);
|
||||
free(sig.ptr);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, verify_handshake, bool,
|
||||
private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader)
|
||||
{
|
||||
if (this->tls->get_version(this->tls) >= TLS_1_2)
|
||||
{
|
||||
u_int8_t hash, alg;
|
||||
chunk_t sig;
|
||||
|
||||
if (!reader->read_uint8(reader, &hash) ||
|
||||
!reader->read_uint8(reader, &alg) ||
|
||||
!reader->read_data16(reader, &sig))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid Certificate Verify");
|
||||
return FALSE;
|
||||
}
|
||||
/* TODO: map received hash/sig alg to signature scheme */
|
||||
if (hash != 2 || alg != 1 ||
|
||||
!key->verify(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_t sig, hash;
|
||||
|
||||
if (!reader->read_data16(reader, &sig))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid Certificate Verify");
|
||||
return FALSE;
|
||||
}
|
||||
if (!hash_handshake(this, &hash))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
|
||||
{
|
||||
free(hash.ptr);
|
||||
return FALSE;
|
||||
}
|
||||
free(hash.ptr);
|
||||
length = htons(sig->len);
|
||||
*sig = chunk_cat("cm", chunk_from_thing(length), *sig);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -635,6 +674,7 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
|
|||
.set_protection = _set_protection,
|
||||
.append_handshake = _append_handshake,
|
||||
.sign_handshake = _sign_handshake,
|
||||
.verify_handshake = _verify_handshake,
|
||||
.calculate_finished = _calculate_finished,
|
||||
.derive_secrets = _derive_secrets,
|
||||
.change_cipher = _change_cipher,
|
||||
|
|
|
@ -116,10 +116,21 @@ struct tls_crypto_t {
|
|||
* Create a signature of the handshake data using a given private key.
|
||||
*
|
||||
* @param key private key to use for signature
|
||||
* @param sig allocated signature
|
||||
* @param writer TLS writer to write signature to
|
||||
* @return TRUE if signature create successfully
|
||||
*/
|
||||
bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key, chunk_t *sig);
|
||||
bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key,
|
||||
tls_writer_t *writer);
|
||||
|
||||
/**
|
||||
* Verify the signature over handshake data using a given public key.
|
||||
*
|
||||
* @param key public key to verify signature with
|
||||
* @param reader TLS reader to read signature from
|
||||
* @return TRUE if signature valid
|
||||
*/
|
||||
bool (*verify_handshake)(tls_crypto_t *this, public_key_t *key,
|
||||
tls_reader_t *reader);
|
||||
|
||||
/**
|
||||
* Calculate the data of a TLS finished message.
|
||||
|
|
|
@ -172,7 +172,7 @@ static status_t process_certificate(private_tls_peer_t *this,
|
|||
if (first)
|
||||
{
|
||||
this->server_auth->add(this->server_auth,
|
||||
AUTH_RULE_SUBJECT_CERT, cert);
|
||||
AUTH_HELPER_SUBJECT_CERT, cert);
|
||||
DBG1(DBG_IKE, "received TLS server certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
first = FALSE;
|
||||
|
@ -182,7 +182,7 @@ static status_t process_certificate(private_tls_peer_t *this,
|
|||
DBG1(DBG_IKE, "received TLS intermediate certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
this->server_auth->add(this->server_auth,
|
||||
AUTH_RULE_IM_CERT, cert);
|
||||
AUTH_HELPER_IM_CERT, cert);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -486,6 +486,7 @@ static status_t send_key_exchange(private_tls_peer_t *this,
|
|||
DBG1(DBG_IKE, "encrypting TLS premaster secret failed");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
public->destroy(public);
|
||||
|
||||
writer->write_data16(writer, encrypted);
|
||||
|
@ -503,16 +504,12 @@ static status_t send_key_exchange(private_tls_peer_t *this,
|
|||
static status_t send_certificate_verify(private_tls_peer_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
chunk_t signature;
|
||||
|
||||
if (!this->private ||
|
||||
!this->crypto->sign_handshake(this->crypto, this->private, &signature))
|
||||
!this->crypto->sign_handshake(this->crypto, this->private, writer))
|
||||
{
|
||||
DBG1(DBG_IKE, "creating TLS Certificate Verify signature failed");
|
||||
return FAILED;
|
||||
}
|
||||
writer->write_data(writer, signature);
|
||||
free(signature.ptr);
|
||||
|
||||
*type = TLS_CERTIFICATE_VERIFY;
|
||||
this->state = STATE_VERIFY_SENT;
|
||||
|
|
|
@ -15,10 +15,29 @@
|
|||
|
||||
#include "tls_server.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct private_tls_server_t private_tls_server_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
STATE_INIT,
|
||||
STATE_HELLO_RECEIVED,
|
||||
STATE_HELLO_SENT,
|
||||
STATE_CERT_SENT,
|
||||
STATE_CERTREQ_SENT,
|
||||
STATE_HELLO_DONE,
|
||||
STATE_CERT_RECEIVED,
|
||||
STATE_KEY_EXCHANGE_RECEIVED,
|
||||
STATE_CERT_VERIFY_RECEIVED,
|
||||
STATE_CIPHERSPEC_CHANGED_IN,
|
||||
STATE_FINISHED_RECEIVED,
|
||||
STATE_CIPHERSPEC_CHANGED_OUT,
|
||||
STATE_FINISHED_SENT,
|
||||
} server_state_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_server_t object.
|
||||
*/
|
||||
|
@ -48,36 +67,510 @@ struct private_tls_server_t {
|
|||
* Peer identity
|
||||
*/
|
||||
identification_t *peer;
|
||||
|
||||
/**
|
||||
* State we are in
|
||||
*/
|
||||
server_state_t state;
|
||||
|
||||
/**
|
||||
* Hello random data selected by client
|
||||
*/
|
||||
char client_random[32];
|
||||
|
||||
/**
|
||||
* Hello random data selected by server
|
||||
*/
|
||||
char server_random[32];
|
||||
|
||||
/**
|
||||
* Auth helper for peer authentication
|
||||
*/
|
||||
auth_cfg_t *peer_auth;
|
||||
|
||||
/**
|
||||
* Auth helper for server authentication
|
||||
*/
|
||||
auth_cfg_t *server_auth;
|
||||
|
||||
/**
|
||||
* Peer private key
|
||||
*/
|
||||
private_key_t *private;
|
||||
|
||||
/**
|
||||
* Selected TLS cipher suite
|
||||
*/
|
||||
tls_cipher_suite_t suite;
|
||||
};
|
||||
|
||||
/**
|
||||
* Process client hello message
|
||||
*/
|
||||
static status_t process_client_hello(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
u_int16_t version;
|
||||
chunk_t random, session, ciphers, compression, ext = chunk_empty;
|
||||
tls_cipher_suite_t *suites;
|
||||
int count, i;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CLIENT_HELLO, reader->peek(reader));
|
||||
|
||||
if (!reader->read_uint16(reader, &version) ||
|
||||
!reader->read_data(reader, sizeof(this->client_random), &random) ||
|
||||
!reader->read_data8(reader, &session) ||
|
||||
!reader->read_data16(reader, &ciphers) ||
|
||||
!reader->read_data8(reader, &compression) ||
|
||||
(reader->remaining(reader) && !reader->read_data16(reader, &ext)))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid ClientHello");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
memcpy(this->client_random, random.ptr, sizeof(this->client_random));
|
||||
|
||||
if (version < this->tls->get_version(this->tls))
|
||||
{
|
||||
this->tls->set_version(this->tls, version);
|
||||
}
|
||||
count = ciphers.len / sizeof(u_int16_t);
|
||||
suites = alloca(count * sizeof(tls_cipher_suite_t));
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]);
|
||||
}
|
||||
this->suite = this->crypto->select_cipher_suite(this->crypto, suites, count);
|
||||
if (!this->suite)
|
||||
{
|
||||
DBG1(DBG_IKE, "received cipher suite inacceptable");
|
||||
return FAILED;
|
||||
}
|
||||
this->state = STATE_HELLO_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process certificate
|
||||
*/
|
||||
static status_t process_certificate(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
certificate_t *cert;
|
||||
tls_reader_t *certs;
|
||||
chunk_t data;
|
||||
bool first = TRUE;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CERTIFICATE, reader->peek(reader));
|
||||
|
||||
if (!reader->read_data24(reader, &data))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
certs = tls_reader_create(data);
|
||||
while (certs->remaining(certs))
|
||||
{
|
||||
if (!certs->read_data24(certs, &data))
|
||||
{
|
||||
certs->destroy(certs);
|
||||
return FAILED;
|
||||
}
|
||||
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
|
||||
BUILD_BLOB_ASN1_DER, data, BUILD_END);
|
||||
if (cert)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
this->peer_auth->add(this->peer_auth,
|
||||
AUTH_HELPER_SUBJECT_CERT, cert);
|
||||
DBG1(DBG_IKE, "received TLS peer certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
first = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_IKE, "received TLS intermediate certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
this->peer_auth->add(this->peer_auth, AUTH_HELPER_IM_CERT, cert);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_IKE, "parsing TLS certificate failed, skipped");
|
||||
}
|
||||
}
|
||||
certs->destroy(certs);
|
||||
this->state = STATE_CERT_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Client Key Exchange
|
||||
*/
|
||||
static status_t process_key_exchange(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
chunk_t encrypted, premaster;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader));
|
||||
|
||||
if (!reader->read_data16(reader, &encrypted))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid Client Key Exchange");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (!this->private ||
|
||||
!this->private->decrypt(this->private, encrypted, &premaster))
|
||||
{
|
||||
DBG1(DBG_IKE, "decrypting Client Key Exchange data failed");
|
||||
return FAILED;
|
||||
}
|
||||
this->crypto->derive_secrets(this->crypto, premaster,
|
||||
chunk_from_thing(this->client_random),
|
||||
chunk_from_thing(this->server_random));
|
||||
chunk_clear(&premaster);
|
||||
|
||||
this->state = STATE_KEY_EXCHANGE_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Certificate verify
|
||||
*/
|
||||
static status_t process_cert_verify(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
bool verified = FALSE;
|
||||
enumerator_t *enumerator;
|
||||
public_key_t *public;
|
||||
auth_cfg_t *auth;
|
||||
tls_reader_t *sig;
|
||||
|
||||
enumerator = charon->credentials->create_public_enumerator(
|
||||
charon->credentials, KEY_ANY, this->peer, this->peer_auth);
|
||||
while (enumerator->enumerate(enumerator, &public, &auth))
|
||||
{
|
||||
sig = tls_reader_create(reader->peek(reader));
|
||||
verified = this->crypto->verify_handshake(this->crypto, public, sig);
|
||||
sig->destroy(sig);
|
||||
if (verified)
|
||||
{
|
||||
break;
|
||||
}
|
||||
DBG1(DBG_IKE, "signature verification failed, trying another key");
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (!verified)
|
||||
{
|
||||
DBG1(DBG_IKE, "no trusted certificate found for '%Y' to verify TLS peer",
|
||||
this->peer);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CERTIFICATE_VERIFY, reader->peek(reader));
|
||||
this->state = STATE_CERT_VERIFY_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process finished message
|
||||
*/
|
||||
static status_t process_finished(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
chunk_t received;
|
||||
char buf[12];
|
||||
|
||||
if (!reader->read_data(reader, sizeof(buf), &received))
|
||||
{
|
||||
DBG1(DBG_IKE, "received client finished too short");
|
||||
return FAILED;
|
||||
}
|
||||
if (!this->crypto->calculate_finished(this->crypto, "client finished", buf))
|
||||
{
|
||||
DBG1(DBG_IKE, "calculating client finished failed");
|
||||
return FAILED;
|
||||
}
|
||||
if (!chunk_equals(received, chunk_from_thing(buf)))
|
||||
{
|
||||
DBG1(DBG_IKE, "received client finished invalid");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);
|
||||
this->state = STATE_FINISHED_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, process, status_t,
|
||||
private_tls_server_t *this, tls_handshake_type_t type, tls_reader_t *reader)
|
||||
{
|
||||
tls_handshake_type_t expected;
|
||||
|
||||
switch (this->state)
|
||||
{
|
||||
case STATE_INIT:
|
||||
if (type == TLS_CLIENT_HELLO)
|
||||
{
|
||||
return process_client_hello(this, reader);
|
||||
}
|
||||
expected = TLS_CLIENT_HELLO;
|
||||
break;
|
||||
case STATE_HELLO_DONE:
|
||||
if (type == TLS_CERTIFICATE)
|
||||
{
|
||||
return process_certificate(this, reader);
|
||||
}
|
||||
expected = TLS_CERTIFICATE;
|
||||
break;
|
||||
case STATE_CERT_RECEIVED:
|
||||
if (type == TLS_CLIENT_KEY_EXCHANGE)
|
||||
{
|
||||
return process_key_exchange(this, reader);
|
||||
}
|
||||
expected = TLS_CLIENT_KEY_EXCHANGE;
|
||||
break;
|
||||
case STATE_KEY_EXCHANGE_RECEIVED:
|
||||
if (type == TLS_CERTIFICATE_VERIFY)
|
||||
{
|
||||
return process_cert_verify(this, reader);
|
||||
}
|
||||
expected = TLS_CERTIFICATE_VERIFY;
|
||||
break;
|
||||
case STATE_CIPHERSPEC_CHANGED_IN:
|
||||
if (type == TLS_FINISHED)
|
||||
{
|
||||
return process_finished(this, reader);
|
||||
}
|
||||
expected = TLS_FINISHED;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_IKE, "TLS %N not expected in current state",
|
||||
tls_handshake_type_names, type);
|
||||
return FAILED;
|
||||
}
|
||||
DBG1(DBG_IKE, "TLS %N expected, but received %N",
|
||||
tls_handshake_type_names, expected, tls_handshake_type_names, type);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send ServerHello message
|
||||
*/
|
||||
static status_t send_server_hello(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
rng_t *rng;
|
||||
|
||||
htoun32(&this->server_random, time(NULL));
|
||||
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||
if (!rng)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4);
|
||||
rng->destroy(rng);
|
||||
|
||||
writer->write_uint16(writer, this->tls->get_version(this->tls));
|
||||
writer->write_data(writer, chunk_from_thing(this->server_random));
|
||||
/* session identifier => none, we don't support session resumption */
|
||||
writer->write_data8(writer, chunk_empty);
|
||||
/* add selected suite */
|
||||
writer->write_uint16(writer, this->suite);
|
||||
/* NULL compression only */
|
||||
writer->write_uint8(writer, 0);
|
||||
|
||||
*type = TLS_SERVER_HELLO;
|
||||
this->state = STATE_HELLO_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Certificate
|
||||
*/
|
||||
static status_t send_certificate(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
certificate_t *cert;
|
||||
auth_rule_t rule;
|
||||
tls_writer_t *certs;
|
||||
chunk_t data;
|
||||
|
||||
this->private = charon->credentials->get_private(charon->credentials,
|
||||
KEY_ANY, this->server, this->server_auth);
|
||||
if (!this->private)
|
||||
{
|
||||
DBG1(DBG_IKE, "no TLS server certificate found for '%Y'", this->server);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* generate certificate payload */
|
||||
certs = tls_writer_create(256);
|
||||
cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT);
|
||||
if (cert)
|
||||
{
|
||||
DBG1(DBG_IKE, "sending TLS server certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
data = cert->get_encoding(cert);
|
||||
certs->write_data24(certs, data);
|
||||
free(data.ptr);
|
||||
}
|
||||
enumerator = this->server_auth->create_enumerator(this->server_auth);
|
||||
while (enumerator->enumerate(enumerator, &rule, &cert))
|
||||
{
|
||||
if (rule == AUTH_RULE_IM_CERT)
|
||||
{
|
||||
DBG1(DBG_IKE, "sending TLS intermediate certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
data = cert->get_encoding(cert);
|
||||
certs->write_data24(certs, data);
|
||||
free(data.ptr);
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
writer->write_data24(writer, certs->get_buf(certs));
|
||||
certs->destroy(certs);
|
||||
|
||||
*type = TLS_CERTIFICATE;
|
||||
this->state = STATE_CERT_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Certificate Request
|
||||
*/
|
||||
static status_t send_certificate_request(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
tls_writer_t *authorities;
|
||||
enumerator_t *enumerator;
|
||||
certificate_t *cert;
|
||||
identification_t *id;
|
||||
|
||||
/* currently only RSA signatures are supported */
|
||||
writer->write_data8(writer, chunk_from_chars(1));
|
||||
if (this->tls->get_version(this->tls) >= TLS_1_2)
|
||||
{
|
||||
/* enforce RSA with SHA1 signatures */
|
||||
writer->write_data16(writer, chunk_from_chars(2, 1));
|
||||
}
|
||||
|
||||
authorities = tls_writer_create(64);
|
||||
enumerator = charon->credentials->create_cert_enumerator(
|
||||
charon->credentials, CERT_X509, KEY_RSA, NULL, TRUE);
|
||||
while (enumerator->enumerate(enumerator, &cert))
|
||||
{
|
||||
id = cert->get_subject(cert);
|
||||
authorities->write_data16(authorities, id->get_encoding(id));
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
writer->write_data16(writer, authorities->get_buf(authorities));
|
||||
authorities->destroy(authorities);
|
||||
|
||||
*type = TLS_CERTIFICATE_REQUEST;
|
||||
this->state = STATE_CERTREQ_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Hello Done
|
||||
*/
|
||||
static status_t send_hello_done(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
*type = TLS_SERVER_HELLO_DONE;
|
||||
this->state = STATE_HELLO_DONE;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Finished
|
||||
*/
|
||||
static status_t send_finished(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
char buf[12];
|
||||
|
||||
if (!this->crypto->calculate_finished(this->crypto, "server finished", buf))
|
||||
{
|
||||
DBG1(DBG_IKE, "calculating server finished data failed");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
writer->write_data(writer, chunk_from_thing(buf));
|
||||
|
||||
*type = TLS_FINISHED;
|
||||
this->state = STATE_FINISHED_SENT;
|
||||
this->crypto->derive_eap_msk(this->crypto,
|
||||
chunk_from_thing(this->client_random),
|
||||
chunk_from_thing(this->server_random));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, build, status_t,
|
||||
private_tls_server_t *this, tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
return INVALID_STATE;
|
||||
switch (this->state)
|
||||
{
|
||||
case STATE_HELLO_RECEIVED:
|
||||
return send_server_hello(this, type, writer);
|
||||
case STATE_HELLO_SENT:
|
||||
return send_certificate(this, type, writer);
|
||||
case STATE_CERT_SENT:
|
||||
return send_certificate_request(this, type, writer);
|
||||
case STATE_CERTREQ_SENT:
|
||||
return send_hello_done(this, type, writer);
|
||||
case STATE_CIPHERSPEC_CHANGED_OUT:
|
||||
return send_finished(this, type, writer);
|
||||
case STATE_FINISHED_SENT:
|
||||
return INVALID_STATE;
|
||||
default:
|
||||
return INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, cipherspec_changed, bool,
|
||||
private_tls_server_t *this)
|
||||
{
|
||||
if (this->state == STATE_FINISHED_RECEIVED)
|
||||
{
|
||||
this->crypto->change_cipher(this->crypto, FALSE);
|
||||
this->state = STATE_CIPHERSPEC_CHANGED_OUT;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, change_cipherspec, bool,
|
||||
private_tls_server_t *this)
|
||||
{
|
||||
if (this->state == STATE_CERT_VERIFY_RECEIVED)
|
||||
{
|
||||
this->crypto->change_cipher(this->crypto, TRUE);
|
||||
this->state = STATE_CIPHERSPEC_CHANGED_IN;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, destroy, void,
|
||||
private_tls_server_t *this)
|
||||
{
|
||||
DESTROY_IF(this->private);
|
||||
this->peer_auth->destroy(this->peer_auth);
|
||||
this->server_auth->destroy(this->server_auth);
|
||||
free(this);
|
||||
}
|
||||
|
||||
|
@ -101,6 +594,9 @@ tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto,
|
|||
.crypto = crypto,
|
||||
.server = server,
|
||||
.peer = peer,
|
||||
.state = STATE_INIT,
|
||||
.peer_auth = auth_cfg_create(),
|
||||
.server_auth = auth_cfg_create(),
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
|
|
Loading…
Reference in New Issue