X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=libeap%2Fsrc%2Ftls%2Ftlsv1_client.c;h=a6f0587e34c57854693d3bf093dcea518bc48837;hb=4f319dde67a76fe0aaf33f6d2788968012584ada;hp=afb603175a11a80a53d0cb3577ad430af7d04bcc;hpb=d8f7e2ca1832320065405091d00e48368ef138a5;p=mech_eap.git diff --git a/libeap/src/tls/tlsv1_client.c b/libeap/src/tls/tlsv1_client.c index afb6031..a6f0587 100644 --- a/libeap/src/tls/tlsv1_client.c +++ b/libeap/src/tls/tlsv1_client.c @@ -1,15 +1,9 @@ /* - * TLSv1 client (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2014, Jouni Malinen * - * 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" @@ -67,7 +61,8 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); - if (tls_prf(pre_master_secret, pre_master_secret_len, + if (tls_prf(conn->rl.tls_version, + pre_master_secret, pre_master_secret_len, "master secret", seed, 2 * TLS_RANDOM_LEN, conn->master_secret, TLS_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " @@ -80,9 +75,11 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); - key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + - conn->rl.iv_size); - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); + if (conn->rl.tls_version == TLS_VERSION_1) + key_block_len += 2 * conn->rl.iv_size; + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); @@ -107,12 +104,21 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); pos += conn->rl.key_material_len; - /* client_write_IV */ - os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; - /* server_write_IV */ - os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; + if (conn->rl.tls_version == TLS_VERSION_1) { + /* client_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + } else { + /* + * Use IV field to set the mask value for TLS v1.1. A fixed + * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + os_memset(conn->rl.write_iv, 0, conn->rl.iv_size); + } return 0; } @@ -126,17 +132,23 @@ int tls_derive_keys(struct tlsv1_client *conn, * @out_len: Length of the output buffer. * @appl_data: Pointer to application data pointer, or %NULL if dropped * @appl_data_len: Pointer to variable that is set to appl_data length + * @need_more_data: Set to 1 if more data would be needed to complete + * processing * Returns: Pointer to output data, %NULL on failure */ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, - size_t *appl_data_len) + size_t *appl_data_len, int *need_more_data) { const u8 *pos, *end; - u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; + u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct; size_t in_msg_len; int no_appl_data; + int used; + + if (need_more_data) + *need_more_data = 0; if (conn->state == CLIENT_HELLO) { if (in_len) @@ -144,6 +156,19 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, return tls_send_client_hello(conn, out_len); } + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } + if (in_data == NULL || in_len == 0) return NULL; @@ -156,13 +181,33 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; - if (tlsv1_record_receive(&conn->rl, pos, end - pos, - in_msg, &in_msg_len, &alert)) { + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + os_free(in_msg); + if (need_more_data) + *need_more_data = 1; + return NULL; + } ct = pos[0]; in_pos = in_msg; @@ -180,7 +225,7 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, in_pos += in_msg_len; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } os_free(in_msg); @@ -192,6 +237,8 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, failed: os_free(in_msg); if (conn->alert_level) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; conn->state = FAILED; os_free(msg); msg = tlsv1_client_send_alert(conn, conn->alert_level, @@ -202,6 +249,11 @@ failed: *out_len = 0; } + if (need_more_data == NULL || !(*need_more_data)) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + } + return msg; } @@ -227,10 +279,8 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn, wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", in_data, in_len); - os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len); - if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, - out_data, out_len, in_len, &rlen) < 0) { + out_data, out_len, in_data, in_len, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -246,58 +296,116 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn, * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length - * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) - * @out_len: Maximum out_data length - * Returns: Number of bytes written to out_data, -1 on failure + * @need_more_data: Set to 1 if more data would be needed to complete + * processing + * Returns: Decrypted data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to * receive data from the encrypted tunnel. */ -int tlsv1_client_decrypt(struct tlsv1_client *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data) { const u8 *in_end, *pos; - int res; - u8 alert, *out_end, *out_pos; + int used; + u8 alert, *out_pos, ct; size_t olen; + struct wpabuf *buf = NULL; + + if (need_more_data) + *need_more_data = 0; + + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } pos = in_data; in_end = in_data + in_len; - out_pos = out_data; - out_end = out_data + out_len; while (pos < in_end) { - if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " - "0x%x", pos[0]); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_UNEXPECTED_MESSAGE); - return -1; + ct = pos[0]; + if (wpabuf_resize(&buf, in_end - pos) < 0) { + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; } - - olen = out_end - out_pos; - res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, - out_pos, &olen, &alert); - if (res < 0) { + out_pos = wpabuf_put(buf, 0); + olen = wpabuf_tailroom(buf); + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " "failed"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); - return -1; + goto fail; } - out_pos += olen; - if (out_pos > out_end) { - wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " - "for processing the received record"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - return -1; + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, in_end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + if (need_more_data) + *need_more_data = 1; + return buf; + } + + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert " + "underflow"); + alert = TLS_ALERT_DECODE_ERROR; + goto fail; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + alert = out_pos[1]; + goto fail; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " + "0x%x when decrypting application data", + pos[0]); + alert = TLS_ALERT_UNEXPECTED_MESSAGE; + goto fail; + } + + wpabuf_put(buf, olen); + + pos += used; } - return out_pos - out_data; + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + return buf; + +fail: + wpabuf_free(buf); + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return NULL; } @@ -351,15 +459,22 @@ struct tlsv1_client * tlsv1_client_init(void) count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; conn->num_cipher_suites = count; + conn->rl.tls_version = TLS_VERSION; + return conn; } @@ -378,6 +493,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn) os_free(conn->client_hello_ext); tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); + wpabuf_free(conn->partial_input); os_free(conn); } @@ -421,7 +537,8 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } @@ -453,14 +570,56 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; + case TLS_DHE_RSA_WITH_DES_CBC_SHA: + cipher = "DHE-RSA-DES-CBC-SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DHE-RSA-DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_RC4_128_MD5: + cipher = "ADH-RC4-MD5"; + break; + case TLS_DH_anon_WITH_DES_CBC_SHA: + cipher = "ADH-DES-SHA"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + cipher = "ADH-DES-CBC3-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + cipher = "DHE-RSA-AES-128-SHA"; + break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - cipher = "AES-128-SHA"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + cipher = "DHE-RSA-AES-256-SHA"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + cipher = "ADH-AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + cipher = "AES-128-SHA256"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + cipher = "DHE-RSA-AES-128-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + cipher = "DHE-RSA-AES-256-SHA256"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + cipher = "ADH-AES-256-SHA256"; break; default: return -1; @@ -555,12 +714,12 @@ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, /** - * tlsv1_client_get_keys - Get master key and random data from TLS connection + * tlsv1_client_get_random - Get random data from TLS connection * @conn: TLSv1 client connection data from tlsv1_client_init() - * @keys: Structure of key/random data (filled on success) + * @keys: Structure of random data (filled on success) * Returns: 0 on success, -1 on failure */ -int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) +int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) @@ -572,8 +731,6 @@ int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; - keys->master_key = conn->master_secret; - keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; @@ -612,9 +769,9 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; @@ -656,6 +813,12 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn, } +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +{ + conn->disable_time_checks = !enabled; +} + + void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx)