From: Martin Willi Date: Mon, 25 Jan 2010 16:31:55 +0000 (+0100) Subject: Process ServerHello(Done), Certificate(Request) messages X-Git-Tag: 4.5.0~656 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3e962b0843c17e220592718f498c513aa52796fd;p=thirdparty%2Fstrongswan.git Process ServerHello(Done), Certificate(Request) messages --- diff --git a/src/charon/plugins/eap_tls/eap_tls.c b/src/charon/plugins/eap_tls/eap_tls.c index 548364946f..1232b14b2a 100644 --- a/src/charon/plugins/eap_tls/eap_tls.c +++ b/src/charon/plugins/eap_tls/eap_tls.c @@ -262,8 +262,15 @@ static status_t process_buf(private_eap_tls_t *this) if (len > data.len - sizeof(tls_record_t)) { DBG1(DBG_IKE, "TLS record length invalid"); - break; + return FAILED; } + if (untoh16(&in->version) < TLS_1_0) + { + DBG1(DBG_IKE, "%N invalid with EAP-TLS", + tls_version_names, untoh16(&in->version)); + return FAILED; + } + status = this->tls->process(this->tls, in->type, chunk_create(in->data, len)); if (status != NEED_MORE) @@ -297,7 +304,7 @@ static status_t process_buf(private_eap_tls_t *this) return FAILED; } out.type = type; - htoun16(&out.version, TLS_1_2); + htoun16(&out.version, this->tls->get_version(this->tls)); htoun16(&out.length, data.len); this->output = chunk_cat("mcm", this->output, header, data); } diff --git a/src/charon/plugins/eap_tls/tls/tls.c b/src/charon/plugins/eap_tls/tls/tls.c index 9912393691..85c68e4082 100644 --- a/src/charon/plugins/eap_tls/tls/tls.c +++ b/src/charon/plugins/eap_tls/tls/tls.c @@ -24,13 +24,14 @@ #include -ENUM(tls_version_names, SSL_2_0, TLS_1_2, - "SSLv2", +ENUM_BEGIN(tls_version_names, SSL_2_0, SSL_2_0, + "SSLv2"); +ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_2, SSL_2_0, "SSLv3", "TLS 1.0", "TLS 1.1", - "TLS 1.2", -); + "TLS 1.2"); +ENUM_END(tls_version_names, TLS_1_2); ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA, "ChangeCipherSpec", @@ -72,6 +73,11 @@ struct private_tls_t { */ bool is_server; + /** + * Negotiated TLS version + */ + tls_version_t version; + /** * TLS record protection layer */ @@ -110,6 +116,18 @@ METHOD(tls_t, build, status_t, return this->protection->build(this->protection, type, data); } +METHOD(tls_t, get_version, tls_version_t, + private_tls_t *this) +{ + return this->version; +} + +METHOD(tls_t, set_version, void, + private_tls_t *this, tls_version_t version) +{ + this->version = version; +} + METHOD(tls_t, destroy, void, private_tls_t *this) { @@ -133,19 +151,24 @@ tls_t *tls_create(bool is_server) .public = { .process = _process, .build = _build, + .get_version = _get_version, + .set_version = _set_version, .destroy = _destroy, }, .is_server = is_server, .crypto = tls_crypto_create(), + .version = TLS_1_2, ); if (is_server) { - this->handshake = &tls_server_create(this->crypto)->handshake; + this->handshake = &tls_server_create(&this->public, + this->crypto)->handshake; } else { - this->handshake = &tls_peer_create(this->crypto)->handshake; + this->handshake = &tls_peer_create(&this->public, + this->crypto)->handshake; } this->fragmentation = tls_fragmentation_create(this->handshake); this->compression = tls_compression_create(this->fragmentation); diff --git a/src/charon/plugins/eap_tls/tls/tls.h b/src/charon/plugins/eap_tls/tls/tls.h index 2e3e046913..f46756e0e2 100644 --- a/src/charon/plugins/eap_tls/tls/tls.h +++ b/src/charon/plugins/eap_tls/tls/tls.h @@ -154,6 +154,20 @@ struct tls_t { */ status_t (*build)(tls_t *this, tls_content_type_t *type, chunk_t *data); + /** + * Get the negotiated TLS/SSL version. + * + * @return negotiated TLS version + */ + tls_version_t (*get_version)(tls_t *this); + + /** + * Set the negotiated TLS/SSL version. + * + * @param version negotiated TLS version + */ + void (*set_version)(tls_t *this, tls_version_t version); + /** * Destroy a tls_t. */ diff --git a/src/charon/plugins/eap_tls/tls/tls_peer.c b/src/charon/plugins/eap_tls/tls/tls_peer.c index 17f19f14da..bb108fe9bb 100644 --- a/src/charon/plugins/eap_tls/tls/tls_peer.c +++ b/src/charon/plugins/eap_tls/tls/tls_peer.c @@ -24,6 +24,7 @@ typedef struct private_tls_peer_t private_tls_peer_t; typedef enum { STATE_INIT, STATE_HELLO_SENT, + STATE_HELLO_DONE, } peer_state_t; /** @@ -36,6 +37,11 @@ struct private_tls_peer_t { */ tls_peer_t public; + /** + * TLS stack + */ + tls_t *tls; + /** * TLS crypto context */ @@ -47,16 +53,183 @@ struct private_tls_peer_t { peer_state_t state; }; +/** + * Process a server hello message + */ +static status_t process_server_hello(private_tls_peer_t *this, chunk_t data) +{ + if (data.len >= 38) + { + tls_version_t version; + + struct __attribute__((packed)) { + u_int16_t version; + struct __attribute__((packed)) { + u_int32_t gmt; + u_int8_t bytes[28]; + } random; + struct __attribute__((packed)) { + u_int8_t len; + /* points to len */ + u_int8_t id[data.ptr[34]]; + } session; + u_int16_t cipher; + u_int8_t compression; + char extensions[]; + } *hello = (void*)data.ptr; + + if (sizeof(*hello) > data.len) + { + DBG1(DBG_IKE, "received invalid ServerHello"); + return FAILED; + } + + version = untoh16(&hello->version); + if (version < this->tls->get_version(this->tls)) + { + this->tls->set_version(this->tls, version); + } + return NEED_MORE; + } + DBG1(DBG_IKE, "server hello has %d bytes", data.len); + return FAILED; +} + +/** + * Process a Certificate message + */ +static status_t process_certificate(private_tls_peer_t *this, chunk_t data) +{ + if (data.len > 3) + { + u_int32_t total; + + total = untoh32(data.ptr) >> 8; + data = chunk_skip(data, 3); + if (total != data.len) + { + DBG1(DBG_IKE, "certificate chain length invalid"); + return FAILED; + } + while (data.len > 3) + { + certificate_t *cert; + u_int32_t len; + + len = untoh32(data.ptr) >> 8; + data = chunk_skip(data, 3); + if (len > data.len) + { + DBG1(DBG_IKE, "certificate length invalid"); + return FAILED; + } + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, chunk_create(data.ptr, len), BUILD_END); + if (cert) + { + DBG1(DBG_IKE, "got certificate: %Y", cert->get_subject(cert)); + cert->destroy(cert); + } + data = chunk_skip(data, len); + } + } + return NEED_MORE; +} + +/** + * Process a Certificate message + */ +static status_t process_certreq(private_tls_peer_t *this, chunk_t data) +{ + struct __attribute__((packed)) { + u_int8_t len; + u_int8_t types[]; + } *certificate; + struct __attribute__((packed)) { + u_int16_t len; + struct __attribute__((packed)) { + u_int8_t hash; + u_int8_t sig; + } types[]; + } *alg; + u_int16_t len; + identification_t *id; + + certificate = (void*)data.ptr; + data = chunk_skip(data, 1); + if (!data.len || certificate->len > data.len) + { + return FAILED; + } + data = chunk_skip(data, certificate->len); + + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + alg = (void*)data.ptr; + data = chunk_skip(data, 2); + if (!data.len || untoh16(&alg->len) > data.len) + { + return FAILED; + } + data = chunk_skip(data, untoh16(&alg->len)); + } + if (data.len < 2 || untoh16(data.ptr) != data.len - 2) + { + return FAILED; + } + data = chunk_skip(data, 2); + + while (data.len >= 2) + { + len = untoh16(data.ptr); + data = chunk_skip(data, 2); + if (len > data.len) + { + return FAILED; + } + id = identification_create_from_encoding(ID_DER_ASN1_DN, + chunk_create(data.ptr, len)); + DBG1(DBG_IKE, "received certificate request for %Y", id); + id->destroy(id); + data = chunk_skip(data, len); + } + return NEED_MORE; +} + METHOD(tls_handshake_t, process, status_t, private_tls_peer_t *this, tls_handshake_type_t type, chunk_t data) { + switch (this->state) + { + case STATE_HELLO_SENT: + switch (type) + { + case TLS_SERVER_HELLO: + return process_server_hello(this, data); + case TLS_CERTIFICATE: + return process_certificate(this, data); + case TLS_CERTIFICATE_REQUEST: + return process_certreq(this, data); + case TLS_SERVER_HELLO_DONE: + this->state = STATE_HELLO_DONE; + return NEED_MORE; + default: + break; + } + break; + default: + break; + } + DBG1(DBG_IKE, "received TLS handshake message %N, ignored", + tls_handshake_type_names, type); return NEED_MORE; } /** * Build the Client Hello using a given set of ciphers */ -static chunk_t build_hello(int count, tls_cipher_suite_t *suite, rng_t *rng) +static chunk_t build_hello(private_tls_peer_t *this, + int count, tls_cipher_suite_t *suite, rng_t *rng) { int i; @@ -84,7 +257,7 @@ static chunk_t build_hello(int count, tls_cipher_suite_t *suite, rng_t *rng) } hello; htoun16(&hello.session.len, 0); - htoun16(&hello.version, TLS_1_2); + htoun16(&hello.version, this->tls->get_version(this->tls)); htoun32(&hello.random.gmt, time(NULL)); rng->get_bytes(rng, sizeof(hello.random.bytes), (char*)&hello.random.bytes); htoun16(&hello.cipher.len, count * 2); @@ -113,10 +286,11 @@ static status_t send_hello(private_tls_peer_t *this, return FAILED; } count = this->crypto->get_cipher_suites(this->crypto, &suite); - *data = build_hello(count, suite, rng); + *data = build_hello(this, count, suite, rng); *type = TLS_CLIENT_HELLO; free(suite); rng->destroy(rng); + this->state = STATE_HELLO_SENT; return NEED_MORE; } @@ -126,7 +300,6 @@ METHOD(tls_handshake_t, build, status_t, switch (this->state) { case STATE_INIT: - this->state = STATE_HELLO_SENT; return send_hello(this, type, data); default: return INVALID_STATE; @@ -142,7 +315,7 @@ METHOD(tls_handshake_t, destroy, void, /** * See header */ -tls_peer_t *tls_peer_create(tls_crypto_t *crypto) +tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto) { private_tls_peer_t *this; @@ -153,6 +326,7 @@ tls_peer_t *tls_peer_create(tls_crypto_t *crypto) .destroy = _destroy, }, .state = STATE_INIT, + .tls = tls, .crypto = crypto, ); diff --git a/src/charon/plugins/eap_tls/tls/tls_peer.h b/src/charon/plugins/eap_tls/tls/tls_peer.h index cef792a0b8..1243a5a246 100644 --- a/src/charon/plugins/eap_tls/tls/tls_peer.h +++ b/src/charon/plugins/eap_tls/tls/tls_peer.h @@ -40,6 +40,6 @@ struct tls_peer_t { /** * Create a tls_peer instance. */ -tls_peer_t *tls_peer_create(tls_crypto_t *crypto); +tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto); #endif /** TLS_PEER_H_ @}*/ diff --git a/src/charon/plugins/eap_tls/tls/tls_server.c b/src/charon/plugins/eap_tls/tls/tls_server.c index 101be5c608..80b0fae629 100644 --- a/src/charon/plugins/eap_tls/tls/tls_server.c +++ b/src/charon/plugins/eap_tls/tls/tls_server.c @@ -29,6 +29,11 @@ struct private_tls_server_t { */ tls_server_t public; + /** + * TLS stack + */ + tls_t *tls; + /** * TLS crypto context */ @@ -57,7 +62,7 @@ METHOD(tls_handshake_t, destroy, void, /** * See header */ -tls_server_t *tls_server_create(tls_crypto_t *crypto) +tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto) { private_tls_server_t *this; @@ -67,6 +72,7 @@ tls_server_t *tls_server_create(tls_crypto_t *crypto) .build = _build, .destroy = _destroy, }, + .tls = tls, .crypto = crypto, ); diff --git a/src/charon/plugins/eap_tls/tls/tls_server.h b/src/charon/plugins/eap_tls/tls/tls_server.h index 7e83c31c78..182c4b1ac7 100644 --- a/src/charon/plugins/eap_tls/tls/tls_server.h +++ b/src/charon/plugins/eap_tls/tls/tls_server.h @@ -40,6 +40,6 @@ struct tls_server_t { /** * Create a tls_server instance. */ -tls_server_t *tls_server_create(tls_crypto_t *crypto); +tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto); #endif /** TLS_SERVER_H_ @}*/