]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Process ServerHello(Done), Certificate(Request) messages
authorMartin Willi <martin@revosec.ch>
Mon, 25 Jan 2010 16:31:55 +0000 (17:31 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 3 Aug 2010 13:39:24 +0000 (15:39 +0200)
src/charon/plugins/eap_tls/eap_tls.c
src/charon/plugins/eap_tls/tls/tls.c
src/charon/plugins/eap_tls/tls/tls.h
src/charon/plugins/eap_tls/tls/tls_peer.c
src/charon/plugins/eap_tls/tls/tls_peer.h
src/charon/plugins/eap_tls/tls/tls_server.c
src/charon/plugins/eap_tls/tls/tls_server.h

index 548364946f43b6a391efdc120d255d708cd7e297..1232b14b2a21a4e27f159346b2210b199730db4f 100644 (file)
@@ -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);
        }
index 9912393691ac982a8b5f1ee3e55a8aed00c2a13c..85c68e408258d6e3a143d93928d81017e4d4ce0d 100644 (file)
 
 #include <daemon.h>
 
-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);
index 2e3e04691303075f481f77767989bae1085ad4d7..f46756e0e2b2c22bb46c1df84fbe22148971c55b 100644 (file)
@@ -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.
         */
index 17f19f14da5c06d42fbfaf162949229c576502ee..bb108fe9bb25d6c5efa232c8861a96798b526694 100644 (file)
@@ -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,
        );
 
index cef792a0b89f5388654c2a3964bb9e68757f897c..1243a5a24616109842a66fb9cc74bd9dc1c8fd39 100644 (file)
@@ -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_ @}*/
index 101be5c608c3ab3d599c6bfcf8a4a9e16e1df9c1..80b0fae629c389e6f9e165c806811dce916e4a12 100644 (file)
@@ -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,
        );
 
index 7e83c31c78fc38dd66eacf1d54b6de62efdbd9b8..182c4b1ac7ecbf049b05030d58b52c78088ee134 100644 (file)
@@ -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_ @}*/