1189251Ssam/*
2189251Ssam * TLSv1 client - read handshake message
3252726Srpaulo * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12214734Srpaulo#include "crypto/md5.h"
13214734Srpaulo#include "crypto/sha1.h"
14252726Srpaulo#include "crypto/sha256.h"
15214734Srpaulo#include "crypto/tls.h"
16189251Ssam#include "x509v3.h"
17189251Ssam#include "tlsv1_common.h"
18189251Ssam#include "tlsv1_record.h"
19189251Ssam#include "tlsv1_client.h"
20189251Ssam#include "tlsv1_client_i.h"
21189251Ssam
22189251Ssamstatic int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
23189251Ssam					   const u8 *in_data, size_t *in_len);
24189251Ssamstatic int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
25189251Ssam					   const u8 *in_data, size_t *in_len);
26189251Ssamstatic int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
27189251Ssam					 const u8 *in_data, size_t *in_len);
28189251Ssam
29189251Ssam
30189251Ssamstatic int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
31189251Ssam				    const u8 *in_data, size_t *in_len)
32189251Ssam{
33189251Ssam	const u8 *pos, *end;
34189251Ssam	size_t left, len, i;
35189251Ssam	u16 cipher_suite;
36252726Srpaulo	u16 tls_version;
37189251Ssam
38189251Ssam	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
39189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
40189251Ssam			   "received content type 0x%x", ct);
41189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
42189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
43189251Ssam		return -1;
44189251Ssam	}
45189251Ssam
46189251Ssam	pos = in_data;
47189251Ssam	left = *in_len;
48189251Ssam
49189251Ssam	if (left < 4)
50189251Ssam		goto decode_error;
51189251Ssam
52189251Ssam	/* HandshakeType msg_type */
53189251Ssam	if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) {
54189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
55189251Ssam			   "message %d (expected ServerHello)", *pos);
56189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
57189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
58189251Ssam		return -1;
59189251Ssam	}
60189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello");
61189251Ssam	pos++;
62189251Ssam	/* uint24 length */
63189251Ssam	len = WPA_GET_BE24(pos);
64189251Ssam	pos += 3;
65189251Ssam	left -= 4;
66189251Ssam
67189251Ssam	if (len > left)
68189251Ssam		goto decode_error;
69189251Ssam
70189251Ssam	/* body - ServerHello */
71189251Ssam
72189251Ssam	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len);
73189251Ssam	end = pos + len;
74189251Ssam
75189251Ssam	/* ProtocolVersion server_version */
76189251Ssam	if (end - pos < 2)
77189251Ssam		goto decode_error;
78252726Srpaulo	tls_version = WPA_GET_BE16(pos);
79252726Srpaulo	if (!tls_version_ok(tls_version)) {
80189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
81252726Srpaulo			   "ServerHello %u.%u", pos[0], pos[1]);
82189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
83189251Ssam			  TLS_ALERT_PROTOCOL_VERSION);
84189251Ssam		return -1;
85189251Ssam	}
86189251Ssam	pos += 2;
87189251Ssam
88252726Srpaulo	wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
89252726Srpaulo		   tls_version_str(tls_version));
90252726Srpaulo	conn->rl.tls_version = tls_version;
91252726Srpaulo
92189251Ssam	/* Random random */
93189251Ssam	if (end - pos < TLS_RANDOM_LEN)
94189251Ssam		goto decode_error;
95189251Ssam
96189251Ssam	os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN);
97189251Ssam	pos += TLS_RANDOM_LEN;
98189251Ssam	wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
99189251Ssam		    conn->server_random, TLS_RANDOM_LEN);
100189251Ssam
101189251Ssam	/* SessionID session_id */
102189251Ssam	if (end - pos < 1)
103189251Ssam		goto decode_error;
104189251Ssam	if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
105189251Ssam		goto decode_error;
106189251Ssam	if (conn->session_id_len && conn->session_id_len == *pos &&
107189251Ssam	    os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) {
108189251Ssam		pos += 1 + conn->session_id_len;
109189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session");
110189251Ssam		conn->session_resumed = 1;
111189251Ssam	} else {
112189251Ssam		conn->session_id_len = *pos;
113189251Ssam		pos++;
114189251Ssam		os_memcpy(conn->session_id, pos, conn->session_id_len);
115189251Ssam		pos += conn->session_id_len;
116189251Ssam	}
117189251Ssam	wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
118189251Ssam		    conn->session_id, conn->session_id_len);
119189251Ssam
120189251Ssam	/* CipherSuite cipher_suite */
121189251Ssam	if (end - pos < 2)
122189251Ssam		goto decode_error;
123189251Ssam	cipher_suite = WPA_GET_BE16(pos);
124189251Ssam	pos += 2;
125189251Ssam	for (i = 0; i < conn->num_cipher_suites; i++) {
126189251Ssam		if (cipher_suite == conn->cipher_suites[i])
127189251Ssam			break;
128189251Ssam	}
129189251Ssam	if (i == conn->num_cipher_suites) {
130189251Ssam		wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
131189251Ssam			   "cipher suite 0x%04x", cipher_suite);
132189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
133189251Ssam			  TLS_ALERT_ILLEGAL_PARAMETER);
134189251Ssam		return -1;
135189251Ssam	}
136189251Ssam
137189251Ssam	if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) {
138189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different "
139189251Ssam			   "cipher suite for a resumed connection (0x%04x != "
140189251Ssam			   "0x%04x)", cipher_suite, conn->prev_cipher_suite);
141189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
142189251Ssam			  TLS_ALERT_ILLEGAL_PARAMETER);
143189251Ssam		return -1;
144189251Ssam	}
145189251Ssam
146189251Ssam	if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
147189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
148189251Ssam			   "record layer");
149189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
150189251Ssam			  TLS_ALERT_INTERNAL_ERROR);
151189251Ssam		return -1;
152189251Ssam	}
153189251Ssam
154189251Ssam	conn->prev_cipher_suite = cipher_suite;
155189251Ssam
156189251Ssam	/* CompressionMethod compression_method */
157189251Ssam	if (end - pos < 1)
158189251Ssam		goto decode_error;
159189251Ssam	if (*pos != TLS_COMPRESSION_NULL) {
160189251Ssam		wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
161189251Ssam			   "compression 0x%02x", *pos);
162189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
163189251Ssam			  TLS_ALERT_ILLEGAL_PARAMETER);
164189251Ssam		return -1;
165189251Ssam	}
166189251Ssam	pos++;
167189251Ssam
168189251Ssam	if (end != pos) {
169189251Ssam		/* TODO: ServerHello extensions */
170189251Ssam		wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
171189251Ssam			    "end of ServerHello", pos, end - pos);
172189251Ssam		goto decode_error;
173189251Ssam	}
174189251Ssam
175189251Ssam	if (conn->session_ticket_included && conn->session_ticket_cb) {
176189251Ssam		/* TODO: include SessionTicket extension if one was included in
177189251Ssam		 * ServerHello */
178189251Ssam		int res = conn->session_ticket_cb(
179189251Ssam			conn->session_ticket_cb_ctx, NULL, 0,
180189251Ssam			conn->client_random, conn->server_random,
181189251Ssam			conn->master_secret);
182189251Ssam		if (res < 0) {
183189251Ssam			wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
184189251Ssam				   "indicated failure");
185189251Ssam			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
186189251Ssam				  TLS_ALERT_HANDSHAKE_FAILURE);
187189251Ssam			return -1;
188189251Ssam		}
189189251Ssam		conn->use_session_ticket = !!res;
190189251Ssam	}
191189251Ssam
192189251Ssam	if ((conn->session_resumed || conn->use_session_ticket) &&
193189251Ssam	    tls_derive_keys(conn, NULL, 0)) {
194189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
195189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
196189251Ssam			  TLS_ALERT_INTERNAL_ERROR);
197189251Ssam		return -1;
198189251Ssam	}
199189251Ssam
200189251Ssam	*in_len = end - in_data;
201189251Ssam
202189251Ssam	conn->state = (conn->session_resumed || conn->use_session_ticket) ?
203189251Ssam		SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE;
204189251Ssam
205189251Ssam	return 0;
206189251Ssam
207189251Ssamdecode_error:
208189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello");
209189251Ssam	tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
210189251Ssam	return -1;
211189251Ssam}
212189251Ssam
213189251Ssam
214189251Ssamstatic int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
215189251Ssam				   const u8 *in_data, size_t *in_len)
216189251Ssam{
217189251Ssam	const u8 *pos, *end;
218189251Ssam	size_t left, len, list_len, cert_len, idx;
219189251Ssam	u8 type;
220189251Ssam	struct x509_certificate *chain = NULL, *last = NULL, *cert;
221189251Ssam	int reason;
222189251Ssam
223189251Ssam	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
224189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
225189251Ssam			   "received content type 0x%x", ct);
226189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
227189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
228189251Ssam		return -1;
229189251Ssam	}
230189251Ssam
231189251Ssam	pos = in_data;
232189251Ssam	left = *in_len;
233189251Ssam
234189251Ssam	if (left < 4) {
235189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
236189251Ssam			   "(len=%lu)", (unsigned long) left);
237189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
238189251Ssam		return -1;
239189251Ssam	}
240189251Ssam
241189251Ssam	type = *pos++;
242189251Ssam	len = WPA_GET_BE24(pos);
243189251Ssam	pos += 3;
244189251Ssam	left -= 4;
245189251Ssam
246189251Ssam	if (len > left) {
247189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
248189251Ssam			   "length (len=%lu != left=%lu)",
249189251Ssam			   (unsigned long) len, (unsigned long) left);
250189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
251189251Ssam		return -1;
252189251Ssam	}
253189251Ssam
254189251Ssam	if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE)
255189251Ssam		return tls_process_server_key_exchange(conn, ct, in_data,
256189251Ssam						       in_len);
257189251Ssam	if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
258189251Ssam		return tls_process_certificate_request(conn, ct, in_data,
259189251Ssam						       in_len);
260189251Ssam	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
261189251Ssam		return tls_process_server_hello_done(conn, ct, in_data,
262189251Ssam						     in_len);
263189251Ssam	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
264189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
265189251Ssam			   "message %d (expected Certificate/"
266189251Ssam			   "ServerKeyExchange/CertificateRequest/"
267189251Ssam			   "ServerHelloDone)", type);
268189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
269189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
270189251Ssam		return -1;
271189251Ssam	}
272189251Ssam
273189251Ssam	wpa_printf(MSG_DEBUG,
274189251Ssam		   "TLSv1: Received Certificate (certificate_list len %lu)",
275189251Ssam		   (unsigned long) len);
276189251Ssam
277189251Ssam	/*
278189251Ssam	 * opaque ASN.1Cert<2^24-1>;
279189251Ssam	 *
280189251Ssam	 * struct {
281189251Ssam	 *     ASN.1Cert certificate_list<1..2^24-1>;
282189251Ssam	 * } Certificate;
283189251Ssam	 */
284189251Ssam
285189251Ssam	end = pos + len;
286189251Ssam
287189251Ssam	if (end - pos < 3) {
288189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
289189251Ssam			   "(left=%lu)", (unsigned long) left);
290189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
291189251Ssam		return -1;
292189251Ssam	}
293189251Ssam
294189251Ssam	list_len = WPA_GET_BE24(pos);
295189251Ssam	pos += 3;
296189251Ssam
297189251Ssam	if ((size_t) (end - pos) != list_len) {
298189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
299189251Ssam			   "length (len=%lu left=%lu)",
300189251Ssam			   (unsigned long) list_len,
301189251Ssam			   (unsigned long) (end - pos));
302189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
303189251Ssam		return -1;
304189251Ssam	}
305189251Ssam
306189251Ssam	idx = 0;
307189251Ssam	while (pos < end) {
308189251Ssam		if (end - pos < 3) {
309189251Ssam			wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
310189251Ssam				   "certificate_list");
311189251Ssam			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
312189251Ssam				  TLS_ALERT_DECODE_ERROR);
313189251Ssam			x509_certificate_chain_free(chain);
314189251Ssam			return -1;
315189251Ssam		}
316189251Ssam
317189251Ssam		cert_len = WPA_GET_BE24(pos);
318189251Ssam		pos += 3;
319189251Ssam
320189251Ssam		if ((size_t) (end - pos) < cert_len) {
321189251Ssam			wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
322189251Ssam				   "length (len=%lu left=%lu)",
323189251Ssam				   (unsigned long) cert_len,
324189251Ssam				   (unsigned long) (end - pos));
325189251Ssam			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
326189251Ssam				  TLS_ALERT_DECODE_ERROR);
327189251Ssam			x509_certificate_chain_free(chain);
328189251Ssam			return -1;
329189251Ssam		}
330189251Ssam
331189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
332189251Ssam			   (unsigned long) idx, (unsigned long) cert_len);
333189251Ssam
334189251Ssam		if (idx == 0) {
335189251Ssam			crypto_public_key_free(conn->server_rsa_key);
336189251Ssam			if (tls_parse_cert(pos, cert_len,
337189251Ssam					   &conn->server_rsa_key)) {
338189251Ssam				wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
339189251Ssam					   "the certificate");
340189251Ssam				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
341189251Ssam					  TLS_ALERT_BAD_CERTIFICATE);
342189251Ssam				x509_certificate_chain_free(chain);
343189251Ssam				return -1;
344189251Ssam			}
345189251Ssam		}
346189251Ssam
347189251Ssam		cert = x509_certificate_parse(pos, cert_len);
348189251Ssam		if (cert == NULL) {
349189251Ssam			wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
350189251Ssam				   "the certificate");
351189251Ssam			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
352189251Ssam				  TLS_ALERT_BAD_CERTIFICATE);
353189251Ssam			x509_certificate_chain_free(chain);
354189251Ssam			return -1;
355189251Ssam		}
356189251Ssam
357189251Ssam		if (last == NULL)
358189251Ssam			chain = cert;
359189251Ssam		else
360189251Ssam			last->next = cert;
361189251Ssam		last = cert;
362189251Ssam
363189251Ssam		idx++;
364189251Ssam		pos += cert_len;
365189251Ssam	}
366189251Ssam
367189251Ssam	if (conn->cred &&
368189251Ssam	    x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
369252726Srpaulo					    &reason, conn->disable_time_checks)
370252726Srpaulo	    < 0) {
371189251Ssam		int tls_reason;
372189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
373189251Ssam			   "validation failed (reason=%d)", reason);
374189251Ssam		switch (reason) {
375189251Ssam		case X509_VALIDATE_BAD_CERTIFICATE:
376189251Ssam			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
377189251Ssam			break;
378189251Ssam		case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
379189251Ssam			tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
380189251Ssam			break;
381189251Ssam		case X509_VALIDATE_CERTIFICATE_REVOKED:
382189251Ssam			tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
383189251Ssam			break;
384189251Ssam		case X509_VALIDATE_CERTIFICATE_EXPIRED:
385189251Ssam			tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
386189251Ssam			break;
387189251Ssam		case X509_VALIDATE_CERTIFICATE_UNKNOWN:
388189251Ssam			tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
389189251Ssam			break;
390189251Ssam		case X509_VALIDATE_UNKNOWN_CA:
391189251Ssam			tls_reason = TLS_ALERT_UNKNOWN_CA;
392189251Ssam			break;
393189251Ssam		default:
394189251Ssam			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
395189251Ssam			break;
396189251Ssam		}
397189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
398189251Ssam		x509_certificate_chain_free(chain);
399189251Ssam		return -1;
400189251Ssam	}
401189251Ssam
402189251Ssam	x509_certificate_chain_free(chain);
403189251Ssam
404189251Ssam	*in_len = end - in_data;
405189251Ssam
406189251Ssam	conn->state = SERVER_KEY_EXCHANGE;
407189251Ssam
408189251Ssam	return 0;
409189251Ssam}
410189251Ssam
411189251Ssam
412189251Ssamstatic int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
413189251Ssam					const u8 *buf, size_t len)
414189251Ssam{
415189251Ssam	const u8 *pos, *end;
416189251Ssam
417189251Ssam	tlsv1_client_free_dh(conn);
418189251Ssam
419189251Ssam	pos = buf;
420189251Ssam	end = buf + len;
421189251Ssam
422189251Ssam	if (end - pos < 3)
423189251Ssam		goto fail;
424189251Ssam	conn->dh_p_len = WPA_GET_BE16(pos);
425189251Ssam	pos += 2;
426189251Ssam	if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
427189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
428189251Ssam			   (unsigned long) conn->dh_p_len);
429189251Ssam		goto fail;
430189251Ssam	}
431189251Ssam	conn->dh_p = os_malloc(conn->dh_p_len);
432189251Ssam	if (conn->dh_p == NULL)
433189251Ssam		goto fail;
434189251Ssam	os_memcpy(conn->dh_p, pos, conn->dh_p_len);
435189251Ssam	pos += conn->dh_p_len;
436189251Ssam	wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
437189251Ssam		    conn->dh_p, conn->dh_p_len);
438189251Ssam
439189251Ssam	if (end - pos < 3)
440189251Ssam		goto fail;
441189251Ssam	conn->dh_g_len = WPA_GET_BE16(pos);
442189251Ssam	pos += 2;
443189251Ssam	if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
444189251Ssam		goto fail;
445189251Ssam	conn->dh_g = os_malloc(conn->dh_g_len);
446189251Ssam	if (conn->dh_g == NULL)
447189251Ssam		goto fail;
448189251Ssam	os_memcpy(conn->dh_g, pos, conn->dh_g_len);
449189251Ssam	pos += conn->dh_g_len;
450189251Ssam	wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
451189251Ssam		    conn->dh_g, conn->dh_g_len);
452189251Ssam	if (conn->dh_g_len == 1 && conn->dh_g[0] < 2)
453189251Ssam		goto fail;
454189251Ssam
455189251Ssam	if (end - pos < 3)
456189251Ssam		goto fail;
457189251Ssam	conn->dh_ys_len = WPA_GET_BE16(pos);
458189251Ssam	pos += 2;
459189251Ssam	if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
460189251Ssam		goto fail;
461189251Ssam	conn->dh_ys = os_malloc(conn->dh_ys_len);
462189251Ssam	if (conn->dh_ys == NULL)
463189251Ssam		goto fail;
464189251Ssam	os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
465189251Ssam	pos += conn->dh_ys_len;
466189251Ssam	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
467189251Ssam		    conn->dh_ys, conn->dh_ys_len);
468189251Ssam
469189251Ssam	return 0;
470189251Ssam
471189251Ssamfail:
472189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed");
473189251Ssam	tlsv1_client_free_dh(conn);
474189251Ssam	return -1;
475189251Ssam}
476189251Ssam
477189251Ssam
478189251Ssamstatic int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
479189251Ssam					   const u8 *in_data, size_t *in_len)
480189251Ssam{
481189251Ssam	const u8 *pos, *end;
482189251Ssam	size_t left, len;
483189251Ssam	u8 type;
484189251Ssam	const struct tls_cipher_suite *suite;
485189251Ssam
486189251Ssam	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
487189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
488189251Ssam			   "received content type 0x%x", ct);
489189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
490189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
491189251Ssam		return -1;
492189251Ssam	}
493189251Ssam
494189251Ssam	pos = in_data;
495189251Ssam	left = *in_len;
496189251Ssam
497189251Ssam	if (left < 4) {
498189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange "
499189251Ssam			   "(Left=%lu)", (unsigned long) left);
500189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
501189251Ssam		return -1;
502189251Ssam	}
503189251Ssam
504189251Ssam	type = *pos++;
505189251Ssam	len = WPA_GET_BE24(pos);
506189251Ssam	pos += 3;
507189251Ssam	left -= 4;
508189251Ssam
509189251Ssam	if (len > left) {
510189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange "
511189251Ssam			   "length (len=%lu != left=%lu)",
512189251Ssam			   (unsigned long) len, (unsigned long) left);
513189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
514189251Ssam		return -1;
515189251Ssam	}
516189251Ssam
517189251Ssam	end = pos + len;
518189251Ssam
519189251Ssam	if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
520189251Ssam		return tls_process_certificate_request(conn, ct, in_data,
521189251Ssam						       in_len);
522189251Ssam	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
523189251Ssam		return tls_process_server_hello_done(conn, ct, in_data,
524189251Ssam						     in_len);
525189251Ssam	if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
526189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
527189251Ssam			   "message %d (expected ServerKeyExchange/"
528189251Ssam			   "CertificateRequest/ServerHelloDone)", type);
529189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
530189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
531189251Ssam		return -1;
532189251Ssam	}
533189251Ssam
534189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange");
535189251Ssam
536189251Ssam	if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
537189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed "
538189251Ssam			   "with the selected cipher suite");
539189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
540189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
541189251Ssam		return -1;
542189251Ssam	}
543189251Ssam
544189251Ssam	wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
545189251Ssam	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
546189251Ssam	if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
547189251Ssam		if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
548189251Ssam			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
549189251Ssam				  TLS_ALERT_DECODE_ERROR);
550189251Ssam			return -1;
551189251Ssam		}
552189251Ssam	} else {
553189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange");
554189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
555189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
556189251Ssam		return -1;
557189251Ssam	}
558189251Ssam
559189251Ssam	*in_len = end - in_data;
560189251Ssam
561189251Ssam	conn->state = SERVER_CERTIFICATE_REQUEST;
562189251Ssam
563189251Ssam	return 0;
564189251Ssam}
565189251Ssam
566189251Ssam
567189251Ssamstatic int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
568189251Ssam					   const u8 *in_data, size_t *in_len)
569189251Ssam{
570189251Ssam	const u8 *pos, *end;
571189251Ssam	size_t left, len;
572189251Ssam	u8 type;
573189251Ssam
574189251Ssam	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
575189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
576189251Ssam			   "received content type 0x%x", ct);
577189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
578189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
579189251Ssam		return -1;
580189251Ssam	}
581189251Ssam
582189251Ssam	pos = in_data;
583189251Ssam	left = *in_len;
584189251Ssam
585189251Ssam	if (left < 4) {
586189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest "
587189251Ssam			   "(left=%lu)", (unsigned long) left);
588189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
589189251Ssam		return -1;
590189251Ssam	}
591189251Ssam
592189251Ssam	type = *pos++;
593189251Ssam	len = WPA_GET_BE24(pos);
594189251Ssam	pos += 3;
595189251Ssam	left -= 4;
596189251Ssam
597189251Ssam	if (len > left) {
598189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest "
599189251Ssam			   "length (len=%lu != left=%lu)",
600189251Ssam			   (unsigned long) len, (unsigned long) left);
601189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
602189251Ssam		return -1;
603189251Ssam	}
604189251Ssam
605189251Ssam	end = pos + len;
606189251Ssam
607189251Ssam	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
608189251Ssam		return tls_process_server_hello_done(conn, ct, in_data,
609189251Ssam						     in_len);
610189251Ssam	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {
611189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
612189251Ssam			   "message %d (expected CertificateRequest/"
613189251Ssam			   "ServerHelloDone)", type);
614189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
615189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
616189251Ssam		return -1;
617189251Ssam	}
618189251Ssam
619189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest");
620189251Ssam
621189251Ssam	conn->certificate_requested = 1;
622189251Ssam
623189251Ssam	*in_len = end - in_data;
624189251Ssam
625189251Ssam	conn->state = SERVER_HELLO_DONE;
626189251Ssam
627189251Ssam	return 0;
628189251Ssam}
629189251Ssam
630189251Ssam
631189251Ssamstatic int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
632189251Ssam					 const u8 *in_data, size_t *in_len)
633189251Ssam{
634189251Ssam	const u8 *pos, *end;
635189251Ssam	size_t left, len;
636189251Ssam	u8 type;
637189251Ssam
638189251Ssam	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
639189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
640189251Ssam			   "received content type 0x%x", ct);
641189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
642189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
643189251Ssam		return -1;
644189251Ssam	}
645189251Ssam
646189251Ssam	pos = in_data;
647189251Ssam	left = *in_len;
648189251Ssam
649189251Ssam	if (left < 4) {
650189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone "
651189251Ssam			   "(left=%lu)", (unsigned long) left);
652189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
653189251Ssam		return -1;
654189251Ssam	}
655189251Ssam
656189251Ssam	type = *pos++;
657189251Ssam	len = WPA_GET_BE24(pos);
658189251Ssam	pos += 3;
659189251Ssam	left -= 4;
660189251Ssam
661189251Ssam	if (len > left) {
662189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone "
663189251Ssam			   "length (len=%lu != left=%lu)",
664189251Ssam			   (unsigned long) len, (unsigned long) left);
665189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
666189251Ssam		return -1;
667189251Ssam	}
668189251Ssam	end = pos + len;
669189251Ssam
670189251Ssam	if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) {
671189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
672189251Ssam			   "message %d (expected ServerHelloDone)", type);
673189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
674189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
675189251Ssam		return -1;
676189251Ssam	}
677189251Ssam
678189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
679189251Ssam
680189251Ssam	*in_len = end - in_data;
681189251Ssam
682189251Ssam	conn->state = CLIENT_KEY_EXCHANGE;
683189251Ssam
684189251Ssam	return 0;
685189251Ssam}
686189251Ssam
687189251Ssam
688189251Ssamstatic int tls_process_server_change_cipher_spec(struct tlsv1_client *conn,
689189251Ssam						 u8 ct, const u8 *in_data,
690189251Ssam						 size_t *in_len)
691189251Ssam{
692189251Ssam	const u8 *pos;
693189251Ssam	size_t left;
694189251Ssam
695189251Ssam	if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
696189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
697189251Ssam			   "received content type 0x%x", ct);
698189251Ssam		if (conn->use_session_ticket) {
699189251Ssam			int res;
700189251Ssam			wpa_printf(MSG_DEBUG, "TLSv1: Server may have "
701189251Ssam				   "rejected SessionTicket");
702189251Ssam			conn->use_session_ticket = 0;
703189251Ssam
704189251Ssam			/* Notify upper layers that SessionTicket failed */
705189251Ssam			res = conn->session_ticket_cb(
706189251Ssam				conn->session_ticket_cb_ctx, NULL, 0, NULL,
707189251Ssam				NULL, NULL);
708189251Ssam			if (res < 0) {
709189251Ssam				wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket "
710189251Ssam					   "callback indicated failure");
711189251Ssam				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
712189251Ssam					  TLS_ALERT_HANDSHAKE_FAILURE);
713189251Ssam				return -1;
714189251Ssam			}
715189251Ssam
716189251Ssam			conn->state = SERVER_CERTIFICATE;
717189251Ssam			return tls_process_certificate(conn, ct, in_data,
718189251Ssam						       in_len);
719189251Ssam		}
720189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
721189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
722189251Ssam		return -1;
723189251Ssam	}
724189251Ssam
725189251Ssam	pos = in_data;
726189251Ssam	left = *in_len;
727189251Ssam
728189251Ssam	if (left < 1) {
729189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
730189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
731189251Ssam		return -1;
732189251Ssam	}
733189251Ssam
734189251Ssam	if (*pos != TLS_CHANGE_CIPHER_SPEC) {
735189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
736189251Ssam			   "received data 0x%x", *pos);
737189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
738189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
739189251Ssam		return -1;
740189251Ssam	}
741189251Ssam
742189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
743189251Ssam	if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
744189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
745189251Ssam			   "for record layer");
746189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
747189251Ssam			  TLS_ALERT_INTERNAL_ERROR);
748189251Ssam		return -1;
749189251Ssam	}
750189251Ssam
751189251Ssam	*in_len = pos + 1 - in_data;
752189251Ssam
753189251Ssam	conn->state = SERVER_FINISHED;
754189251Ssam
755189251Ssam	return 0;
756189251Ssam}
757189251Ssam
758189251Ssam
759189251Ssamstatic int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
760189251Ssam				       const u8 *in_data, size_t *in_len)
761189251Ssam{
762189251Ssam	const u8 *pos, *end;
763189251Ssam	size_t left, len, hlen;
764189251Ssam	u8 verify_data[TLS_VERIFY_DATA_LEN];
765189251Ssam	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
766189251Ssam
767189251Ssam	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
768189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
769189251Ssam			   "received content type 0x%x", ct);
770189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
771189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
772189251Ssam		return -1;
773189251Ssam	}
774189251Ssam
775189251Ssam	pos = in_data;
776189251Ssam	left = *in_len;
777189251Ssam
778189251Ssam	if (left < 4) {
779189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
780189251Ssam			   "Finished",
781189251Ssam			   (unsigned long) left);
782189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
783189251Ssam			  TLS_ALERT_DECODE_ERROR);
784189251Ssam		return -1;
785189251Ssam	}
786189251Ssam
787189251Ssam	if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
788189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
789189251Ssam			   "type 0x%x", pos[0]);
790189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
791189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
792189251Ssam		return -1;
793189251Ssam	}
794189251Ssam
795189251Ssam	len = WPA_GET_BE24(pos + 1);
796189251Ssam
797189251Ssam	pos += 4;
798189251Ssam	left -= 4;
799189251Ssam
800189251Ssam	if (len > left) {
801189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
802189251Ssam			   "(len=%lu > left=%lu)",
803189251Ssam			   (unsigned long) len, (unsigned long) left);
804189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
805189251Ssam			  TLS_ALERT_DECODE_ERROR);
806189251Ssam		return -1;
807189251Ssam	}
808189251Ssam	end = pos + len;
809189251Ssam	if (len != TLS_VERIFY_DATA_LEN) {
810189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
811189251Ssam			   "in Finished: %lu (expected %d)",
812189251Ssam			   (unsigned long) len, TLS_VERIFY_DATA_LEN);
813189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
814189251Ssam			  TLS_ALERT_DECODE_ERROR);
815189251Ssam		return -1;
816189251Ssam	}
817189251Ssam	wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
818189251Ssam		    pos, TLS_VERIFY_DATA_LEN);
819189251Ssam
820252726Srpaulo#ifdef CONFIG_TLSV12
821252726Srpaulo	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
822252726Srpaulo		hlen = SHA256_MAC_LEN;
823252726Srpaulo		if (conn->verify.sha256_server == NULL ||
824252726Srpaulo		    crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
825252726Srpaulo		    < 0) {
826252726Srpaulo			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
827252726Srpaulo				  TLS_ALERT_INTERNAL_ERROR);
828252726Srpaulo			conn->verify.sha256_server = NULL;
829252726Srpaulo			return -1;
830252726Srpaulo		}
831252726Srpaulo		conn->verify.sha256_server = NULL;
832252726Srpaulo	} else {
833252726Srpaulo#endif /* CONFIG_TLSV12 */
834252726Srpaulo
835189251Ssam	hlen = MD5_MAC_LEN;
836189251Ssam	if (conn->verify.md5_server == NULL ||
837189251Ssam	    crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
838189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
839189251Ssam			  TLS_ALERT_INTERNAL_ERROR);
840189251Ssam		conn->verify.md5_server = NULL;
841189251Ssam		crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
842189251Ssam		conn->verify.sha1_server = NULL;
843189251Ssam		return -1;
844189251Ssam	}
845189251Ssam	conn->verify.md5_server = NULL;
846189251Ssam	hlen = SHA1_MAC_LEN;
847189251Ssam	if (conn->verify.sha1_server == NULL ||
848189251Ssam	    crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
849189251Ssam			       &hlen) < 0) {
850189251Ssam		conn->verify.sha1_server = NULL;
851189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
852189251Ssam			  TLS_ALERT_INTERNAL_ERROR);
853189251Ssam		return -1;
854189251Ssam	}
855189251Ssam	conn->verify.sha1_server = NULL;
856252726Srpaulo	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
857189251Ssam
858252726Srpaulo#ifdef CONFIG_TLSV12
859252726Srpaulo	}
860252726Srpaulo#endif /* CONFIG_TLSV12 */
861252726Srpaulo
862252726Srpaulo	if (tls_prf(conn->rl.tls_version,
863252726Srpaulo		    conn->master_secret, TLS_MASTER_SECRET_LEN,
864252726Srpaulo		    "server finished", hash, hlen,
865189251Ssam		    verify_data, TLS_VERIFY_DATA_LEN)) {
866189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
867189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
868189251Ssam			  TLS_ALERT_DECRYPT_ERROR);
869189251Ssam		return -1;
870189251Ssam	}
871189251Ssam	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
872189251Ssam			verify_data, TLS_VERIFY_DATA_LEN);
873189251Ssam
874189251Ssam	if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
875189251Ssam		wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
876189251Ssam		return -1;
877189251Ssam	}
878189251Ssam
879189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
880189251Ssam
881189251Ssam	*in_len = end - in_data;
882189251Ssam
883189251Ssam	conn->state = (conn->session_resumed || conn->use_session_ticket) ?
884189251Ssam		CHANGE_CIPHER_SPEC : ACK_FINISHED;
885189251Ssam
886189251Ssam	return 0;
887189251Ssam}
888189251Ssam
889189251Ssam
890189251Ssamstatic int tls_process_application_data(struct tlsv1_client *conn, u8 ct,
891189251Ssam					const u8 *in_data, size_t *in_len,
892189251Ssam					u8 **out_data, size_t *out_len)
893189251Ssam{
894189251Ssam	const u8 *pos;
895189251Ssam	size_t left;
896189251Ssam
897189251Ssam	if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
898189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; "
899189251Ssam			   "received content type 0x%x", ct);
900189251Ssam		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
901189251Ssam			  TLS_ALERT_UNEXPECTED_MESSAGE);
902189251Ssam		return -1;
903189251Ssam	}
904189251Ssam
905189251Ssam	pos = in_data;
906189251Ssam	left = *in_len;
907189251Ssam
908189251Ssam	wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake",
909189251Ssam		    pos, left);
910189251Ssam
911189251Ssam	*out_data = os_malloc(left);
912189251Ssam	if (*out_data) {
913189251Ssam		os_memcpy(*out_data, pos, left);
914189251Ssam		*out_len = left;
915189251Ssam	}
916189251Ssam
917189251Ssam	return 0;
918189251Ssam}
919189251Ssam
920189251Ssam
921189251Ssamint tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
922189251Ssam				   const u8 *buf, size_t *len,
923189251Ssam				   u8 **out_data, size_t *out_len)
924189251Ssam{
925189251Ssam	if (ct == TLS_CONTENT_TYPE_ALERT) {
926189251Ssam		if (*len < 2) {
927189251Ssam			wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
928189251Ssam			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
929189251Ssam				  TLS_ALERT_DECODE_ERROR);
930189251Ssam			return -1;
931189251Ssam		}
932189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
933189251Ssam			   buf[0], buf[1]);
934189251Ssam		*len = 2;
935189251Ssam		conn->state = FAILED;
936189251Ssam		return -1;
937189251Ssam	}
938189251Ssam
939189251Ssam	if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 &&
940189251Ssam	    buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) {
941189251Ssam		size_t hr_len = WPA_GET_BE24(buf + 1);
942189251Ssam		if (hr_len > *len - 4) {
943189251Ssam			wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow");
944189251Ssam			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
945189251Ssam				  TLS_ALERT_DECODE_ERROR);
946189251Ssam			return -1;
947189251Ssam		}
948189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest");
949189251Ssam		*len = 4 + hr_len;
950189251Ssam		return 0;
951189251Ssam	}
952189251Ssam
953189251Ssam	switch (conn->state) {
954189251Ssam	case SERVER_HELLO:
955189251Ssam		if (tls_process_server_hello(conn, ct, buf, len))
956189251Ssam			return -1;
957189251Ssam		break;
958189251Ssam	case SERVER_CERTIFICATE:
959189251Ssam		if (tls_process_certificate(conn, ct, buf, len))
960189251Ssam			return -1;
961189251Ssam		break;
962189251Ssam	case SERVER_KEY_EXCHANGE:
963189251Ssam		if (tls_process_server_key_exchange(conn, ct, buf, len))
964189251Ssam			return -1;
965189251Ssam		break;
966189251Ssam	case SERVER_CERTIFICATE_REQUEST:
967189251Ssam		if (tls_process_certificate_request(conn, ct, buf, len))
968189251Ssam			return -1;
969189251Ssam		break;
970189251Ssam	case SERVER_HELLO_DONE:
971189251Ssam		if (tls_process_server_hello_done(conn, ct, buf, len))
972189251Ssam			return -1;
973189251Ssam		break;
974189251Ssam	case SERVER_CHANGE_CIPHER_SPEC:
975189251Ssam		if (tls_process_server_change_cipher_spec(conn, ct, buf, len))
976189251Ssam			return -1;
977189251Ssam		break;
978189251Ssam	case SERVER_FINISHED:
979189251Ssam		if (tls_process_server_finished(conn, ct, buf, len))
980189251Ssam			return -1;
981189251Ssam		break;
982189251Ssam	case ACK_FINISHED:
983189251Ssam		if (out_data &&
984189251Ssam		    tls_process_application_data(conn, ct, buf, len, out_data,
985189251Ssam						 out_len))
986189251Ssam			return -1;
987189251Ssam		break;
988189251Ssam	default:
989189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
990189251Ssam			   "while processing received message",
991189251Ssam			   conn->state);
992189251Ssam		return -1;
993189251Ssam	}
994189251Ssam
995189251Ssam	if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
996189251Ssam		tls_verify_hash_add(&conn->verify, buf, *len);
997189251Ssam
998189251Ssam	return 0;
999189251Ssam}
1000