| /* |
| * TLSv1 client - read handshake message |
| * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> |
| * |
| * This software may be distributed under the terms of the BSD license. |
| * See README for more details. |
| */ |
| |
| #include "includes.h" |
| |
| #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" |
| #include "tlsv1_record.h" |
| #include "tlsv1_client.h" |
| #include "tlsv1_client_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_version_disabled(struct tlsv1_client *conn, u16 ver) |
| { |
| return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && |
| ver == TLS_VERSION_1) || |
| ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && |
| ver == TLS_VERSION_1_1) || |
| ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && |
| ver == TLS_VERSION_1_2)); |
| } |
| |
| |
| static int tls_process_server_hello_extensions(struct tlsv1_client *conn, |
| const u8 *pos, size_t len) |
| { |
| const u8 *end = pos + len; |
| |
| wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello extensions", |
| pos, len); |
| while (pos < end) { |
| u16 ext, elen; |
| |
| if (end - pos < 4) { |
| wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension header"); |
| return -1; |
| } |
| |
| ext = WPA_GET_BE16(pos); |
| pos += 2; |
| elen = WPA_GET_BE16(pos); |
| pos += 2; |
| |
| if (elen > end - pos) { |
| wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension"); |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "TLSv1: ServerHello ExtensionType %u", |
| ext); |
| wpa_hexdump(MSG_DEBUG, "TLSv1: ServerHello extension data", |
| pos, elen); |
| |
| pos += elen; |
| } |
| |
| return 0; |
| } |
| |
| |
| 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) || |
| tls_version_disabled(conn, 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 >= 2) { |
| u16 ext_len; |
| |
| ext_len = WPA_GET_BE16(pos); |
| pos += 2; |
| if (end - pos < ext_len) { |
| wpa_printf(MSG_INFO, |
| "TLSv1: Invalid ServerHello extension length: %u (left: %u)", |
| ext_len, (unsigned int) (end - pos)); |
| goto decode_error; |
| } |
| |
| if (tls_process_server_hello_extensions(conn, pos, ext_len)) |
| goto decode_error; |
| pos += ext_len; |
| } |
| |
| if (end != pos) { |
| 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 void tls_peer_cert_event(struct tlsv1_client *conn, int depth, |
| struct x509_certificate *cert) |
| { |
| union tls_event_data ev; |
| struct wpabuf *cert_buf = NULL; |
| #ifdef CONFIG_SHA256 |
| u8 hash[32]; |
| #endif /* CONFIG_SHA256 */ |
| char subject[128]; |
| |
| if (!conn->event_cb) |
| return; |
| |
| os_memset(&ev, 0, sizeof(ev)); |
| if ((conn->cred && conn->cred->cert_probe) || conn->cert_in_cb) { |
| cert_buf = wpabuf_alloc_copy(cert->cert_start, |
| cert->cert_len); |
| ev.peer_cert.cert = cert_buf; |
| } |
| #ifdef CONFIG_SHA256 |
| if (cert_buf) { |
| const u8 *addr[1]; |
| size_t len[1]; |
| addr[0] = wpabuf_head(cert_buf); |
| len[0] = wpabuf_len(cert_buf); |
| if (sha256_vector(1, addr, len, hash) == 0) { |
| ev.peer_cert.hash = hash; |
| ev.peer_cert.hash_len = sizeof(hash); |
| } |
| } |
| #endif /* CONFIG_SHA256 */ |
| |
| ev.peer_cert.depth = depth; |
| x509_name_string(&cert->subject, subject, sizeof(subject)); |
| ev.peer_cert.subject = subject; |
| |
| if (cert->extensions_present & X509_EXT_CERTIFICATE_POLICY) { |
| if (cert->certificate_policy & X509_EXT_CERT_POLICY_TOD_STRICT) |
| ev.peer_cert.tod = 1; |
| else if (cert->certificate_policy & |
| X509_EXT_CERT_POLICY_TOD_TOFU) |
| ev.peer_cert.tod = 2; |
| } |
| |
| conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev); |
| wpabuf_free(cert_buf); |
| } |
| |
| |
| static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth, |
| struct x509_certificate *cert, |
| enum tls_fail_reason reason, |
| const char *reason_txt) |
| { |
| struct wpabuf *cert_buf = NULL; |
| union tls_event_data ev; |
| char subject[128]; |
| |
| if (!conn->event_cb || !cert) |
| return; |
| |
| os_memset(&ev, 0, sizeof(ev)); |
| ev.cert_fail.depth = depth; |
| x509_name_string(&cert->subject, subject, sizeof(subject)); |
| ev.peer_cert.subject = subject; |
| ev.cert_fail.reason = reason; |
| ev.cert_fail.reason_txt = reason_txt; |
| cert_buf = wpabuf_alloc_copy(cert->cert_start, |
| cert->cert_len); |
| ev.cert_fail.cert = cert_buf; |
| conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); |
| wpabuf_free(cert_buf); |
| } |
| |
| |
| 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; |
| } |
| |
| tls_peer_cert_event(conn, idx, cert); |
| |
| if (last == NULL) |
| chain = cert; |
| else |
| last->next = cert; |
| last = cert; |
| |
| idx++; |
| pos += cert_len; |
| } |
| |
| if (conn->cred && conn->cred->server_cert_only && chain) { |
| u8 hash[SHA256_MAC_LEN]; |
| char buf[128]; |
| |
| wpa_printf(MSG_DEBUG, |
| "TLSv1: Validate server certificate hash"); |
| x509_name_string(&chain->subject, buf, sizeof(buf)); |
| wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf); |
| if (sha256_vector(1, &chain->cert_start, &chain->cert_len, |
| hash) < 0 || |
| os_memcmp(conn->cred->srv_cert_hash, hash, |
| SHA256_MAC_LEN) != 0) { |
| wpa_printf(MSG_DEBUG, |
| "TLSv1: Server certificate hash mismatch"); |
| wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash", |
| hash, SHA256_MAC_LEN); |
| if (conn->event_cb) { |
| union tls_event_data ev; |
| |
| os_memset(&ev, 0, sizeof(ev)); |
| ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED; |
| ev.cert_fail.reason_txt = |
| "Server certificate mismatch"; |
| ev.cert_fail.subject = buf; |
| conn->event_cb(conn->cb_ctx, |
| TLS_CERT_CHAIN_FAILURE, &ev); |
| } |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, |
| TLS_ALERT_BAD_CERTIFICATE); |
| x509_certificate_chain_free(chain); |
| return -1; |
| } |
| } else if (conn->cred && conn->cred->cert_probe) { |
| wpa_printf(MSG_DEBUG, |
| "TLSv1: Reject server certificate on probe-only run"); |
| if (conn->event_cb) { |
| union tls_event_data ev; |
| char buf[128]; |
| |
| os_memset(&ev, 0, sizeof(ev)); |
| ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE; |
| ev.cert_fail.reason_txt = |
| "Server certificate chain probe"; |
| if (chain) { |
| x509_name_string(&chain->subject, buf, |
| sizeof(buf)); |
| ev.cert_fail.subject = buf; |
| } |
| conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, |
| &ev); |
| } |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, |
| TLS_ALERT_BAD_CERTIFICATE); |
| x509_certificate_chain_free(chain); |
| return -1; |
| } else if (conn->cred && conn->cred->ca_cert_verify && |
| x509_certificate_chain_validate( |
| conn->cred->trusted_certs, chain, &reason, |
| !!(conn->flags & TLS_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; |
| tls_cert_chain_failure_event( |
| conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, |
| "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; |
| tls_cert_chain_failure_event( |
| conn, 0, chain, TLS_FAIL_REVOKED, |
| "certificate revoked"); |
| break; |
| case X509_VALIDATE_CERTIFICATE_EXPIRED: |
| tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; |
| tls_cert_chain_failure_event( |
| conn, 0, chain, TLS_FAIL_EXPIRED, |
| "certificate has expired or is not yet valid"); |
| break; |
| case X509_VALIDATE_CERTIFICATE_UNKNOWN: |
| tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; |
| break; |
| case X509_VALIDATE_UNKNOWN_CA: |
| tls_reason = TLS_ALERT_UNKNOWN_CA; |
| tls_cert_chain_failure_event( |
| conn, 0, chain, TLS_FAIL_UNTRUSTED, |
| "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; |
| } |
| |
| if (conn->cred && !conn->cred->server_cert_only && chain && |
| (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && |
| !(chain->ext_key_usage & |
| (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) { |
| tls_cert_chain_failure_event( |
| conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, |
| "certificate not allowed for server authentication"); |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, |
| TLS_ALERT_BAD_CERTIFICATE); |
| x509_certificate_chain_free(chain); |
| return -1; |
| } |
| |
| if (conn->flags & TLS_CONN_REQUEST_OCSP) { |
| x509_certificate_chain_free(conn->server_cert); |
| conn->server_cert = chain; |
| } else { |
| x509_certificate_chain_free(chain); |
| } |
| |
| *in_len = end - in_data; |
| |
| conn->state = SERVER_KEY_EXCHANGE; |
| |
| return 0; |
| } |
| |
| |
| 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, |
| tls_key_exchange key_exchange) |
| { |
| const u8 *pos, *end, *server_params, *server_params_end; |
| u8 alert; |
| unsigned int bits; |
| u16 val; |
| |
| tlsv1_client_free_dh(conn); |
| |
| pos = buf; |
| end = buf + len; |
| |
| if (end - pos < 3) |
| goto fail; |
| server_params = pos; |
| val = WPA_GET_BE16(pos); |
| pos += 2; |
| 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_memdup(pos, conn->dh_p_len); |
| if (conn->dh_p == NULL) |
| goto fail; |
| 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; |
| val = WPA_GET_BE16(pos); |
| pos += 2; |
| if (val == 0 || val > (size_t) (end - pos)) |
| goto fail; |
| conn->dh_g_len = val; |
| conn->dh_g = os_memdup(pos, conn->dh_g_len); |
| if (conn->dh_g == NULL) |
| goto fail; |
| 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; |
| val = WPA_GET_BE16(pos); |
| pos += 2; |
| if (val == 0 || val > (size_t) (end - pos)) |
| goto fail; |
| conn->dh_ys_len = val; |
| conn->dh_ys = os_memdup(pos, conn->dh_ys_len); |
| if (conn->dh_ys == NULL) |
| goto fail; |
| 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[0] != TLS_HASH_ALG_SHA384 && |
| pos[0] != TLS_HASH_ALG_SHA512) || |
| 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, |
| sizeof(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 enum tls_ocsp_result |
| tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn, |
| const u8 *pos, size_t len) |
| { |
| const u8 *end = pos + len; |
| u32 ocsp_resp_len; |
| |
| /* opaque OCSPResponse<1..2^24-1>; */ |
| if (end - pos < 3) { |
| wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse"); |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); |
| return TLS_OCSP_INVALID; |
| } |
| ocsp_resp_len = WPA_GET_BE24(pos); |
| pos += 3; |
| if (end - pos < ocsp_resp_len) { |
| wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse"); |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); |
| return TLS_OCSP_INVALID; |
| } |
| |
| return tls_process_ocsp_response(conn, pos, ocsp_resp_len); |
| } |
| |
| |
| static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, |
| const u8 *in_data, size_t *in_len) |
| { |
| const u8 *pos, *end; |
| size_t left, len; |
| u8 type, status_type; |
| enum tls_ocsp_result res; |
| struct x509_certificate *cert; |
| int depth; |
| |
| 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 CertificateStatus (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 CertificateStatus 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_STATUS) { |
| wpa_printf(MSG_DEBUG, |
| "TLSv1: Received unexpected handshake message %d (expected CertificateStatus)", |
| type); |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, |
| TLS_ALERT_UNEXPECTED_MESSAGE); |
| return -1; |
| } |
| |
| wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus"); |
| |
| /* |
| * struct { |
| * CertificateStatusType status_type; |
| * select (status_type) { |
| * case ocsp: OCSPResponse; |
| * case ocsp_multi: OCSPResponseList; |
| * } response; |
| * } CertificateStatus; |
| */ |
| if (end - pos < 1) { |
| wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus"); |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); |
| return -1; |
| } |
| status_type = *pos++; |
| wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u", |
| status_type); |
| |
| if (status_type == 1 /* ocsp */) { |
| res = tls_process_certificate_status_ocsp_response( |
| conn, pos, end - pos); |
| } else if (status_type == 2 /* ocsp_multi */) { |
| int good = 0, revoked = 0; |
| u32 resp_len; |
| |
| res = TLS_OCSP_NO_RESPONSE; |
| |
| /* |
| * opaque OCSPResponse<0..2^24-1>; |
| * |
| * struct { |
| * OCSPResponse ocsp_response_list<1..2^24-1>; |
| * } OCSPResponseList; |
| */ |
| if (end - pos < 3) { |
| wpa_printf(MSG_DEBUG, |
| "TLSv1: Truncated OCSPResponseList"); |
| res = TLS_OCSP_INVALID; |
| goto done; |
| } |
| resp_len = WPA_GET_BE24(pos); |
| pos += 3; |
| if (end - pos < resp_len) { |
| wpa_printf(MSG_DEBUG, |
| "TLSv1: Truncated OCSPResponseList(len=%u)", |
| resp_len); |
| res = TLS_OCSP_INVALID; |
| goto done; |
| } |
| end = pos + resp_len; |
| |
| while (end - pos >= 3) { |
| resp_len = WPA_GET_BE24(pos); |
| pos += 3; |
| if (resp_len > end - pos) { |
| wpa_printf(MSG_DEBUG, |
| "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi", |
| resp_len, (int) (end - pos)); |
| res = TLS_OCSP_INVALID; |
| break; |
| } |
| if (!resp_len) |
| continue; /* Skip an empty response */ |
| res = tls_process_certificate_status_ocsp_response( |
| conn, pos - 3, resp_len + 3); |
| if (res == TLS_OCSP_REVOKED) |
| revoked++; |
| else if (res == TLS_OCSP_GOOD) |
| good++; |
| pos += resp_len; |
| } |
| |
| if (revoked) |
| res = TLS_OCSP_REVOKED; |
| else if (good) |
| res = TLS_OCSP_GOOD; |
| } else { |
| wpa_printf(MSG_DEBUG, |
| "TLSv1: Ignore unsupported CertificateStatus"); |
| goto skip; |
| } |
| |
| done: |
| if (res == TLS_OCSP_REVOKED) { |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, |
| TLS_ALERT_CERTIFICATE_REVOKED); |
| for (cert = conn->server_cert, depth = 0; cert; |
| cert = cert->next, depth++) { |
| if (cert->ocsp_revoked) { |
| tls_cert_chain_failure_event( |
| conn, depth, cert, TLS_FAIL_REVOKED, |
| "certificate revoked"); |
| } |
| } |
| return -1; |
| } |
| |
| if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) { |
| /* |
| * Verify that each certificate on the chain that is not part |
| * of the trusted certificates has a good status. If not, |
| * terminate handshake. |
| */ |
| for (cert = conn->server_cert, depth = 0; cert; |
| cert = cert->next, depth++) { |
| if (!cert->ocsp_good) { |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, |
| TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); |
| tls_cert_chain_failure_event( |
| conn, depth, cert, |
| TLS_FAIL_UNSPECIFIED, |
| "bad certificate status response"); |
| return -1; |
| } |
| if (cert->issuer_trusted) |
| break; |
| } |
| } |
| |
| if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) { |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, |
| res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR : |
| TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); |
| if (conn->server_cert) |
| tls_cert_chain_failure_event( |
| conn, 0, conn->server_cert, |
| TLS_FAIL_UNSPECIFIED, |
| "bad certificate status response"); |
| return -1; |
| } |
| |
| conn->ocsp_resp_received = 1; |
| |
| skip: |
| *in_len = end - in_data; |
| |
| conn->state = SERVER_KEY_EXCHANGE; |
| |
| return 0; |
| } |
| |
| |
| 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 ((conn->flags & TLS_CONN_REQUEST_OCSP) && |
| type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) |
| return tls_process_certificate_status(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_SERVER_KEY_EXCHANGE) { |
| wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " |
| "message %d (expected ServerKeyExchange/" |
| "CertificateRequest/ServerHelloDone%s)", type, |
| (conn->flags & TLS_CONN_REQUEST_OCSP) ? |
| "/CertificateStatus" : ""); |
| 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"); |
| |
| if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && |
| !conn->ocsp_resp_received) { |
| wpa_printf(MSG_INFO, |
| "TLSv1: No OCSP response received - reject handshake"); |
| tls_alert(conn, TLS_ALERT_LEVEL_FATAL, |
| TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); |
| return -1; |
| } |
| |
| *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; |
| } |
| |
| 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_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; |
| } |
| |
| 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)) |
| 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; |
| } |