esp-idf/components/wpa_supplicant/src/tls/tlsv1_client_read.c
kapil.gupta 50b4cf9286 wpa_supplicant: Fix IOT issue with latest freeradius
Fix inter operability issue with freeradius version 3.0.21
and openssl 1.1.1f when internal tls client is used which
requires extension elements in client hello.

closes https://github.com/espressif/esp-idf/issues/5273
closes https://github.com/espressif/esp-idf/issues/5627
2020-08-04 04:50:58 +00:00

1054 lines
28 KiB
C

/*
* TLSv1 client - read handshake message
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "tls/tls.h"
#include "tls/x509v3.h"
#include "tls/tlsv1_common.h"
#include "tls/tlsv1_record.h"
#include "tls/tlsv1_client.h"
#include "tls/tlsv1_client_i.h"
#include "eap_peer/eap_i.h"
static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len);
static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len);
static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len);
static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
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; "
"received content type 0x%x", ct);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
if (left < 4)
goto decode_error;
/* HandshakeType msg_type */
if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) {
wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
"message %d (expected ServerHello)", *pos);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello");
pos++;
/* uint24 length */
len = WPA_GET_BE24(pos);
pos += 3;
left -= 4;
if (len > left)
goto decode_error;
/* body - ServerHello */
wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len);
end = pos + len;
/* ProtocolVersion server_version */
if (end - pos < 2)
goto decode_error;
tls_version = WPA_GET_BE16(pos);
if (!tls_version_ok(tls_version)) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
"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;
os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN);
pos += TLS_RANDOM_LEN;
wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
conn->server_random, TLS_RANDOM_LEN);
/* SessionID session_id */
if (end - pos < 1)
goto decode_error;
if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
goto decode_error;
if (conn->session_id_len && conn->session_id_len == *pos &&
os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) {
pos += 1 + conn->session_id_len;
wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session");
conn->session_resumed = 1;
} else {
conn->session_id_len = *pos;
pos++;
os_memcpy(conn->session_id, pos, conn->session_id_len);
pos += conn->session_id_len;
}
wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
conn->session_id, conn->session_id_len);
/* CipherSuite cipher_suite */
if (end - pos < 2)
goto decode_error;
cipher_suite = WPA_GET_BE16(pos);
pos += 2;
for (i = 0; i < conn->num_cipher_suites; i++) {
if (cipher_suite == conn->cipher_suites[i])
break;
}
if (i == conn->num_cipher_suites) {
wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
"cipher suite 0x%04x", cipher_suite);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_ILLEGAL_PARAMETER);
return -1;
}
if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) {
wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different "
"cipher suite for a resumed connection (0x%04x != "
"0x%04x)", cipher_suite, conn->prev_cipher_suite);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_ILLEGAL_PARAMETER);
return -1;
}
if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
"record layer");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
conn->prev_cipher_suite = cipher_suite;
/* CompressionMethod compression_method */
if (end - pos < 1)
goto decode_error;
if (*pos != TLS_COMPRESSION_NULL) {
wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
"compression 0x%02x", *pos);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_ILLEGAL_PARAMETER);
return -1;
}
pos++;
if (end != pos) {
/* TODO: ServerHello extensions */
wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
"end of ServerHello", pos, end - pos);
goto decode_error;
}
if (conn->session_ticket_included && conn->session_ticket_cb) {
/* TODO: include SessionTicket extension if one was included in
* ServerHello */
int res = conn->session_ticket_cb(
conn->session_ticket_cb_ctx, NULL, 0,
conn->client_random, conn->server_random,
conn->master_secret);
if (res < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
"indicated failure");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_HANDSHAKE_FAILURE);
return -1;
}
conn->use_session_ticket = !!res;
}
if ((conn->session_resumed || conn->use_session_ticket) &&
tls_derive_keys(conn, NULL, 0)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
*in_len = end - in_data;
conn->state = (conn->session_resumed || conn->use_session_ticket) ?
SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE;
return 0;
decode_error:
wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
const u8 *pos, *end;
size_t left, len, list_len, cert_len, idx;
u8 type;
struct x509_certificate *chain = NULL, *last = NULL, *cert;
int reason;
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
"received content type 0x%x", ct);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
if (left < 4) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
"(len=%lu)", (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
type = *pos++;
len = WPA_GET_BE24(pos);
pos += 3;
left -= 4;
if (len > left) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
"length (len=%lu != left=%lu)",
(unsigned long) len, (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE)
return tls_process_server_key_exchange(conn, ct, in_data,
in_len);
if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
return tls_process_certificate_request(conn, ct, in_data,
in_len);
if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
return tls_process_server_hello_done(conn, ct, in_data,
in_len);
if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
"message %d (expected Certificate/"
"ServerKeyExchange/CertificateRequest/"
"ServerHelloDone)", type);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)",
(unsigned long) len);
/*
* opaque ASN.1Cert<2^24-1>;
*
* struct {
* ASN.1Cert certificate_list<1..2^24-1>;
* } Certificate;
*/
end = pos + len;
if (end - pos < 3) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
"(left=%lu)", (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
list_len = WPA_GET_BE24(pos);
pos += 3;
if ((size_t) (end - pos) != list_len) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
"length (len=%lu left=%lu)",
(unsigned long) list_len,
(unsigned long) (end - pos));
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
idx = 0;
while (pos < end) {
if (end - pos < 3) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
"certificate_list");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
x509_certificate_chain_free(chain);
return -1;
}
cert_len = WPA_GET_BE24(pos);
pos += 3;
if ((size_t) (end - pos) < cert_len) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
"length (len=%lu left=%lu)",
(unsigned long) cert_len,
(unsigned long) (end - pos));
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
x509_certificate_chain_free(chain);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
(unsigned long) idx, (unsigned long) cert_len);
if (idx == 0) {
crypto_public_key_free(conn->server_rsa_key);
if (tls_parse_cert(pos, cert_len,
&conn->server_rsa_key)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
"the certificate");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_BAD_CERTIFICATE);
x509_certificate_chain_free(chain);
return -1;
}
}
cert = x509_certificate_parse(pos, cert_len);
if (cert == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
"the certificate");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_BAD_CERTIFICATE);
x509_certificate_chain_free(chain);
return -1;
}
if (last == NULL)
chain = cert;
else
last->next = cert;
last = cert;
idx++;
pos += cert_len;
}
if (conn->cred &&
x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
&reason, conn->disable_time_checks)
< 0) {
int tls_reason;
wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
"validation failed (reason=%d)", reason);
switch (reason) {
case X509_VALIDATE_BAD_CERTIFICATE:
tls_reason = TLS_ALERT_BAD_CERTIFICATE;
break;
case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
break;
case X509_VALIDATE_CERTIFICATE_REVOKED:
tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
break;
case X509_VALIDATE_CERTIFICATE_EXPIRED:
tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
break;
case X509_VALIDATE_CERTIFICATE_UNKNOWN:
tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
break;
case X509_VALIDATE_UNKNOWN_CA:
tls_reason = TLS_ALERT_UNKNOWN_CA;
break;
default:
tls_reason = TLS_ALERT_BAD_CERTIFICATE;
break;
}
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
x509_certificate_chain_free(chain);
return -1;
}
x509_certificate_chain_free(chain);
*in_len = end - in_data;
conn->state = SERVER_KEY_EXCHANGE;
return 0;
}
static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
const u8 *buf, size_t len,
tls_key_exchange key_exchange)
{
const u8 *pos, *end, *server_params, *server_params_end;
u8 alert;
tlsv1_client_free_dh(conn);
pos = buf;
end = buf + len;
if (end - pos < 3)
goto fail;
server_params = pos;
conn->dh_p_len = 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);
goto fail;
}
conn->dh_p = os_malloc(conn->dh_p_len);
if (conn->dh_p == NULL)
goto fail;
os_memcpy(conn->dh_p, pos, conn->dh_p_len);
pos += conn->dh_p_len;
wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
conn->dh_p, conn->dh_p_len);
if (end - pos < 3)
goto fail;
conn->dh_g_len = WPA_GET_BE16(pos);
pos += 2;
if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
goto fail;
conn->dh_g = os_malloc(conn->dh_g_len);
if (conn->dh_g == NULL)
goto fail;
os_memcpy(conn->dh_g, pos, conn->dh_g_len);
pos += conn->dh_g_len;
wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
conn->dh_g, conn->dh_g_len);
if (conn->dh_g_len == 1 && conn->dh_g[0] < 2)
goto fail;
if (end - pos < 3)
goto fail;
conn->dh_ys_len = WPA_GET_BE16(pos);
pos += 2;
if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
goto fail;
conn->dh_ys = os_malloc(conn->dh_ys_len);
if (conn->dh_ys == NULL)
goto fail;
os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
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[64];
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;
}
hlen = tlsv12_key_x_server_params_hash(
conn->rl.tls_version, pos[0],
conn->client_random,
conn->server_random, server_params,
server_params_end - server_params, hash);
pos += 2;
#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;
fail:
wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed");
tlsv1_client_free_dh(conn);
return -1;
}
static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
const u8 *pos, *end;
size_t left, len;
u8 type;
const struct tls_cipher_suite *suite;
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
"received content type 0x%x", ct);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
if (left < 4) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange "
"(Left=%lu)", (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
type = *pos++;
len = WPA_GET_BE24(pos);
pos += 3;
left -= 4;
if (len > left) {
wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange "
"length (len=%lu != left=%lu)",
(unsigned long) len, (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
end = pos + len;
if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
return tls_process_certificate_request(conn, ct, in_data,
in_len);
if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
return tls_process_server_hello_done(conn, ct, in_data,
in_len);
if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
"message %d (expected ServerKeyExchange/"
"CertificateRequest/ServerHelloDone)", type);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange");
if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed "
"with the selected cipher suite");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
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 ||
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;
}
} else {
wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
*in_len = end - in_data;
conn->state = SERVER_CERTIFICATE_REQUEST;
return 0;
}
static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
const u8 *pos, *end;
size_t left, len;
u8 type;
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
"received content type 0x%x", ct);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
if (left < 4) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest "
"(left=%lu)", (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
type = *pos++;
len = WPA_GET_BE24(pos);
pos += 3;
left -= 4;
if (len > left) {
wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest "
"length (len=%lu != left=%lu)",
(unsigned long) len, (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
end = pos + len;
if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
return tls_process_server_hello_done(conn, ct, in_data,
in_len);
if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {
wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
"message %d (expected CertificateRequest/"
"ServerHelloDone)", type);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest");
conn->certificate_requested = 1;
*in_len = end - in_data;
conn->state = SERVER_HELLO_DONE;
return 0;
}
static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
const u8 *pos, *end;
size_t left, len;
u8 type;
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
"received content type 0x%x", ct);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
if (left < 4) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone "
"(left=%lu)", (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
type = *pos++;
len = WPA_GET_BE24(pos);
pos += 3;
left -= 4;
if (len > left) {
wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone "
"length (len=%lu != left=%lu)",
(unsigned long) len, (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
end = pos + len;
if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) {
wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
"message %d (expected ServerHelloDone)", type);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
*in_len = end - in_data;
conn->state = CLIENT_KEY_EXCHANGE;
return 0;
}
static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn,
u8 ct, const u8 *in_data,
size_t *in_len)
{
const u8 *pos;
size_t left;
if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
"received content type 0x%x", ct);
if (conn->use_session_ticket) {
int res;
wpa_printf(MSG_DEBUG, "TLSv1: Server may have "
"rejected SessionTicket");
conn->use_session_ticket = 0;
/* Notify upper layers that SessionTicket failed */
res = conn->session_ticket_cb(
conn->session_ticket_cb_ctx, NULL, 0, NULL,
NULL, NULL);
if (res < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket "
"callback indicated failure");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_HANDSHAKE_FAILURE);
return -1;
}
printf("[Debug] set the state to server certificate \n");
conn->state = SERVER_CERTIFICATE;
return tls_process_certificate(conn, ct, in_data,
in_len);
}
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
if (left < 1) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
if (*pos != TLS_CHANGE_CIPHER_SPEC) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
"received data 0x%x", *pos);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
"for record layer");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
*in_len = pos + 1 - in_data;
conn->state = SERVER_FINISHED;
return 0;
}
static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
const u8 *pos, *end;
size_t left, len, hlen;
u8 verify_data[TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
"received content type 0x%x", ct);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
if (left < 4) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
"Finished",
(unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
}
if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
"type 0x%x", pos[0]);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
len = WPA_GET_BE24(pos + 1);
pos += 4;
left -= 4;
if (len > left) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
"(len=%lu > left=%lu)",
(unsigned long) len, (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
}
end = pos + len;
if (len != TLS_VERIFY_DATA_LEN) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
"in Finished: %lu (expected %d)",
(unsigned long) len, TLS_VERIFY_DATA_LEN);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
}
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) {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
conn->verify.md5_server = NULL;
crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
conn->verify.sha1_server = NULL;
return -1;
}
conn->verify.md5_server = NULL;
hlen = SHA1_MAC_LEN;
if (conn->verify.sha1_server == NULL ||
crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
&hlen) < 0) {
conn->verify.sha1_server = NULL;
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
conn->verify.sha1_server = NULL;
hlen = 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,
TLS_ALERT_DECRYPT_ERROR);
return -1;
}
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) {
wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
*in_len = end - in_data;
conn->state = (conn->session_resumed || conn->use_session_ticket) ?
CHANGE_CIPHER_SPEC : ACK_FINISHED;
return 0;
}
static int tls_process_application_data(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len,
u8 **out_data, size_t *out_len)
{
const u8 *pos;
size_t left;
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; "
"received content type 0x%x", ct);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake",
pos, left);
*out_data = os_malloc(left);
if (*out_data) {
os_memcpy(*out_data, pos, left);
*out_len = left;
}
return 0;
}
int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
const u8 *buf, size_t *len,
u8 **out_data, size_t *out_len)
{
if (ct == TLS_CONTENT_TYPE_ALERT) {
if (*len < 2) {
wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
buf[0], buf[1]);
*len = 2;
conn->state = FAILED;
return -1;
}
if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 &&
buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) {
size_t hr_len = WPA_GET_BE24(buf + 1);
if (hr_len > *len - 4) {
wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest");
*len = 4 + hr_len;
return 0;
}
switch (conn->state) {
case SERVER_HELLO:
if (tls_process_server_hello(conn, ct, buf, len))
return -1;
break;
case SERVER_CERTIFICATE:
if (tls_process_certificate(conn, ct, buf, len))
return -1;
break;
case SERVER_KEY_EXCHANGE:
if (tls_process_server_key_exchange(conn, ct, buf, len))
return -1;
break;
case SERVER_CERTIFICATE_REQUEST:
if (tls_process_certificate_request(conn, ct, buf, len))
return -1;
break;
case SERVER_HELLO_DONE:
if (tls_process_server_hello_done(conn, ct, buf, len))
return -1;
break;
case SERVER_CHANGE_CIPHER_SPEC:
if (tls_process_server_change_cipher_spec(conn, ct, buf, len))
return -1;
break;
case SERVER_FINISHED:
if (tls_process_server_finished(conn, ct, buf, len)) {
printf("[debug] server finish process fall \n");
return -1;
}
break;
case ACK_FINISHED:
if (out_data &&
tls_process_application_data(conn, ct, buf, len, out_data,
out_len))
return -1;
break;
default:
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
"while processing received message",
conn->state);
return -1;
}
if (ct == TLS_CONTENT_TYPE_HANDSHAKE) {
tls_verify_hash_add(&conn->verify, buf, *len);
}
return 0;
}