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