Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / tls / tlsv1_client_read.c
index ed3f260..9ce9680 100644 (file)
@@ -1,15 +1,9 @@
 /*
  * TLSv1 client - read handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -17,6 +11,7 @@
 #include "common.h"
 #include "crypto/md5.h"
 #include "crypto/sha1.h"
+#include "crypto/sha256.h"
 #include "crypto/tls.h"
 #include "x509v3.h"
 #include "tlsv1_common.h"
@@ -38,6 +33,7 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
        const u8 *pos, *end;
        size_t left, len, i;
        u16 cipher_suite;
+       u16 tls_version;
 
        if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
                wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
@@ -79,15 +75,20 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
        /* ProtocolVersion server_version */
        if (end - pos < 2)
                goto decode_error;
-       if (WPA_GET_BE16(pos) != TLS_VERSION) {
+       tls_version = WPA_GET_BE16(pos);
+       if (!tls_version_ok(tls_version)) {
                wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
-                          "ServerHello");
+                          "ServerHello %u.%u", pos[0], pos[1]);
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                          TLS_ALERT_PROTOCOL_VERSION);
                return -1;
        }
        pos += 2;
 
+       wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+                  tls_version_str(tls_version));
+       conn->rl.tls_version = tls_version;
+
        /* Random random */
        if (end - pos < TLS_RANDOM_LEN)
                goto decode_error;
@@ -365,7 +366,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
 
        if (conn->cred &&
            x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
-                                           &reason) < 0) {
+                                           &reason, conn->disable_time_checks)
+           < 0) {
                int tls_reason;
                wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
                           "validation failed (reason=%d)", reason);
@@ -407,10 +409,38 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
 }
 
 
+static unsigned int count_bits(const u8 *val, size_t len)
+{
+       size_t i;
+       unsigned int bits;
+       u8 tmp;
+
+       for (i = 0; i < len; i++) {
+               if (val[i])
+                       break;
+       }
+       if (i == len)
+               return 0;
+
+       bits = (len - i - 1) * 8;
+       tmp = val[i];
+       while (tmp) {
+               bits++;
+               tmp >>= 1;
+       }
+
+       return bits;
+}
+
+
 static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
-                                       const u8 *buf, size_t len)
+                                       const u8 *buf, size_t len,
+                                       tls_key_exchange key_exchange)
 {
-       const u8 *pos, *end;
+       const u8 *pos, *end, *server_params, *server_params_end;
+       u8 alert;
+       unsigned int bits;
+       u16 val;
 
        tlsv1_client_free_dh(conn);
 
@@ -419,11 +449,20 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
 
        if (end - pos < 3)
                goto fail;
-       conn->dh_p_len = WPA_GET_BE16(pos);
+       server_params = pos;
+       val = WPA_GET_BE16(pos);
        pos += 2;
-       if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
-                          (unsigned long) conn->dh_p_len);
+       if (val == 0 || val > (size_t) (end - pos)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val);
+               goto fail;
+       }
+       conn->dh_p_len = val;
+       bits = count_bits(pos, conn->dh_p_len);
+       if (bits < 768) {
+               wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)",
+                          bits);
+               wpa_hexdump(MSG_DEBUG, "TLSv1: Rejected DH prime",
+                           pos, conn->dh_p_len);
                goto fail;
        }
        conn->dh_p = os_malloc(conn->dh_p_len);
@@ -436,10 +475,11 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
 
        if (end - pos < 3)
                goto fail;
-       conn->dh_g_len = WPA_GET_BE16(pos);
+       val = WPA_GET_BE16(pos);
        pos += 2;
-       if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
+       if (val == 0 || val > (size_t) (end - pos))
                goto fail;
+       conn->dh_g_len = val;
        conn->dh_g = os_malloc(conn->dh_g_len);
        if (conn->dh_g == NULL)
                goto fail;
@@ -452,10 +492,11 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
 
        if (end - pos < 3)
                goto fail;
-       conn->dh_ys_len = WPA_GET_BE16(pos);
+       val = WPA_GET_BE16(pos);
        pos += 2;
-       if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
+       if (val == 0 || val > (size_t) (end - pos))
                goto fail;
+       conn->dh_ys_len = val;
        conn->dh_ys = os_malloc(conn->dh_ys_len);
        if (conn->dh_ys == NULL)
                goto fail;
@@ -463,6 +504,59 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
        pos += conn->dh_ys_len;
        wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
                    conn->dh_ys, conn->dh_ys_len);
+       server_params_end = pos;
+
+       if (key_exchange == TLS_KEY_X_DHE_RSA) {
+               u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+               int hlen;
+
+               if (conn->rl.tls_version == TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+                       /*
+                        * RFC 5246, 4.7:
+                        * TLS v1.2 adds explicit indication of the used
+                        * signature and hash algorithms.
+                        *
+                        * struct {
+                        *   HashAlgorithm hash;
+                        *   SignatureAlgorithm signature;
+                        * } SignatureAndHashAlgorithm;
+                        */
+                       if (end - pos < 2)
+                               goto fail;
+                       if (pos[0] != TLS_HASH_ALG_SHA256 ||
+                           pos[1] != TLS_SIGN_ALG_RSA) {
+                               wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm",
+                                          pos[0], pos[1]);
+                               goto fail;
+                       }
+                       pos += 2;
+
+                       hlen = tlsv12_key_x_server_params_hash(
+                               conn->rl.tls_version, conn->client_random,
+                               conn->server_random, server_params,
+                               server_params_end - server_params, hash);
+#else /* CONFIG_TLSV12 */
+                       goto fail;
+#endif /* CONFIG_TLSV12 */
+               } else {
+                       hlen = tls_key_x_server_params_hash(
+                               conn->rl.tls_version, conn->client_random,
+                               conn->server_random, server_params,
+                               server_params_end - server_params, hash);
+               }
+
+               if (hlen < 0)
+                       goto fail;
+               wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash",
+                           hash, hlen);
+
+               if (tls_verify_signature(conn->rl.tls_version,
+                                        conn->server_rsa_key,
+                                        hash, hlen, pos, end - pos,
+                                        &alert) < 0)
+                       goto fail;
+       }
 
        return 0;
 
@@ -541,8 +635,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
 
        wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
        suite = tls_get_cipher_suite(conn->rl.cipher_suite);
-       if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
-               if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
+       if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon ||
+                     suite->key_exchange == TLS_KEY_X_DHE_RSA)) {
+               if (tlsv1_process_diffie_hellman(conn, pos, len,
+                                                suite->key_exchange) < 0) {
                        tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                  TLS_ALERT_DECODE_ERROR);
                        return -1;
@@ -815,6 +911,21 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
        wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
                    pos, TLS_VERIFY_DATA_LEN);
 
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               hlen = SHA256_MAC_LEN;
+               if (conn->verify.sha256_server == NULL ||
+                   crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+                   < 0) {
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       conn->verify.sha256_server = NULL;
+                       return -1;
+               }
+               conn->verify.sha256_server = NULL;
+       } else {
+#endif /* CONFIG_TLSV12 */
+
        hlen = MD5_MAC_LEN;
        if (conn->verify.md5_server == NULL ||
            crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
@@ -836,9 +947,15 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
                return -1;
        }
        conn->verify.sha1_server = NULL;
+       hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
 
-       if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
-                   "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
+       if (tls_prf(conn->rl.tls_version,
+                   conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "server finished", hash, hlen,
                    verify_data, TLS_VERIFY_DATA_LEN)) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -848,8 +965,10 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
        wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
                        verify_data, TLS_VERIFY_DATA_LEN);
 
-       if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+       if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
                wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECRYPT_ERROR);
                return -1;
        }