]>
Commit | Line | Data |
---|---|---|
4c0c2283 MW |
1 | /* |
2 | * Copyright (C) 2010 Martin Willi | |
3 | * Copyright (C) 2010 revosec AG | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation; either version 2 of the License, or (at your | |
8 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
13 | * for more details. | |
14 | */ | |
15 | ||
16 | #include "tls_peer.h" | |
17 | ||
f05b4272 | 18 | #include <utils/debug.h> |
c8114799 | 19 | #include <credentials/certificates/x509.h> |
4c0c2283 MW |
20 | |
21 | #include <time.h> | |
22 | ||
23 | typedef struct private_tls_peer_t private_tls_peer_t; | |
24 | ||
698674c7 MW |
25 | typedef enum { |
26 | STATE_INIT, | |
27 | STATE_HELLO_SENT, | |
8fef06a6 | 28 | STATE_HELLO_RECEIVED, |
3e962b08 | 29 | STATE_HELLO_DONE, |
3ddd164e | 30 | STATE_CERT_SENT, |
8fef06a6 | 31 | STATE_CERT_RECEIVED, |
da3f4a9f | 32 | STATE_KEY_EXCHANGE_RECEIVED, |
8fef06a6 | 33 | STATE_CERTREQ_RECEIVED, |
3ddd164e MW |
34 | STATE_KEY_EXCHANGE_SENT, |
35 | STATE_VERIFY_SENT, | |
f139b578 | 36 | STATE_CIPHERSPEC_CHANGED_OUT, |
3ddd164e | 37 | STATE_FINISHED_SENT, |
f139b578 | 38 | STATE_CIPHERSPEC_CHANGED_IN, |
6a5c86b7 | 39 | STATE_FINISHED_RECEIVED, |
698674c7 MW |
40 | } peer_state_t; |
41 | ||
4c0c2283 MW |
42 | /** |
43 | * Private data of an tls_peer_t object. | |
44 | */ | |
45 | struct private_tls_peer_t { | |
46 | ||
47 | /** | |
48 | * Public tls_peer_t interface. | |
49 | */ | |
50 | tls_peer_t public; | |
536dbc00 | 51 | |
3e962b08 MW |
52 | /** |
53 | * TLS stack | |
54 | */ | |
55 | tls_t *tls; | |
56 | ||
536dbc00 MW |
57 | /** |
58 | * TLS crypto context | |
59 | */ | |
60 | tls_crypto_t *crypto; | |
698674c7 | 61 | |
e6f3ef13 MW |
62 | /** |
63 | * TLS alert handler | |
64 | */ | |
65 | tls_alert_t *alert; | |
66 | ||
3ddd164e | 67 | /** |
69e8bb2e | 68 | * Peer identity, NULL for no client authentication |
3ddd164e MW |
69 | */ |
70 | identification_t *peer; | |
71 | ||
72 | /** | |
73 | * Server identity | |
74 | */ | |
75 | identification_t *server; | |
76 | ||
698674c7 MW |
77 | /** |
78 | * State we are in | |
79 | */ | |
80 | peer_state_t state; | |
3ddd164e | 81 | |
b37080f8 MW |
82 | /** |
83 | * TLS version we offered in hello | |
84 | */ | |
85 | tls_version_t hello_version; | |
86 | ||
18010de2 MW |
87 | /** |
88 | * Hello random data selected by client | |
89 | */ | |
90 | char client_random[32]; | |
91 | ||
92 | /** | |
93 | * Hello random data selected by server | |
94 | */ | |
95 | char server_random[32]; | |
96 | ||
3ddd164e MW |
97 | /** |
98 | * Auth helper for peer authentication | |
99 | */ | |
100 | auth_cfg_t *peer_auth; | |
101 | ||
102 | /** | |
103 | * Auth helper for server authentication | |
104 | */ | |
105 | auth_cfg_t *server_auth; | |
106 | ||
107 | /** | |
108 | * Peer private key | |
109 | */ | |
110 | private_key_t *private; | |
dbb7c030 | 111 | |
da3f4a9f MW |
112 | /** |
113 | * DHE exchange | |
114 | */ | |
115 | diffie_hellman_t *dh; | |
116 | ||
6a5c86b7 MW |
117 | /** |
118 | * Resuming a session? | |
119 | */ | |
120 | bool resume; | |
121 | ||
122 | /** | |
123 | * TLS session identifier | |
124 | */ | |
125 | chunk_t session; | |
126 | ||
dbb7c030 MW |
127 | /** |
128 | * List of server-supported hashsig algorithms | |
129 | */ | |
130 | chunk_t hashsig; | |
131 | ||
132 | /** | |
133 | * List of server-supported client certificate types | |
134 | */ | |
135 | chunk_t cert_types; | |
4c0c2283 MW |
136 | }; |
137 | ||
3e962b08 MW |
138 | /** |
139 | * Process a server hello message | |
140 | */ | |
4ef946dd | 141 | static status_t process_server_hello(private_tls_peer_t *this, |
7e432eff | 142 | bio_reader_t *reader) |
3e962b08 | 143 | { |
4ef946dd MW |
144 | u_int8_t compression; |
145 | u_int16_t version, cipher; | |
4ef946dd | 146 | chunk_t random, session, ext = chunk_empty; |
6a5c86b7 | 147 | tls_cipher_suite_t suite = 0; |
4ef946dd | 148 | |
84d67ead MW |
149 | this->crypto->append_handshake(this->crypto, |
150 | TLS_SERVER_HELLO, reader->peek(reader)); | |
3ddd164e | 151 | |
4ef946dd | 152 | if (!reader->read_uint16(reader, &version) || |
18010de2 | 153 | !reader->read_data(reader, sizeof(this->server_random), &random) || |
4ef946dd MW |
154 | !reader->read_data8(reader, &session) || |
155 | !reader->read_uint16(reader, &cipher) || | |
156 | !reader->read_uint8(reader, &compression) || | |
157 | (reader->remaining(reader) && !reader->read_data16(reader, &ext))) | |
3e962b08 | 158 | { |
3c19b346 | 159 | DBG1(DBG_TLS, "received invalid ServerHello"); |
e6f3ef13 MW |
160 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
161 | return NEED_MORE; | |
4ef946dd | 162 | } |
18010de2 MW |
163 | |
164 | memcpy(this->server_random, random.ptr, sizeof(this->server_random)); | |
165 | ||
f154e304 | 166 | if (!this->tls->set_version(this->tls, version)) |
4ef946dd | 167 | { |
f154e304 MW |
168 | DBG1(DBG_TLS, "negotiated version %N not supported", |
169 | tls_version_names, version); | |
e6f3ef13 MW |
170 | this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); |
171 | return NEED_MORE; | |
3e962b08 | 172 | } |
6a5c86b7 MW |
173 | |
174 | if (chunk_equals(this->session, session)) | |
18010de2 | 175 | { |
6a5c86b7 MW |
176 | suite = this->crypto->resume_session(this->crypto, session, this->server, |
177 | chunk_from_thing(this->client_random), | |
178 | chunk_from_thing(this->server_random)); | |
179 | if (suite) | |
180 | { | |
181 | DBG1(DBG_TLS, "resumed %N using suite %N", | |
182 | tls_version_names, version, tls_cipher_suite_names, suite); | |
183 | this->resume = TRUE; | |
184 | } | |
185 | } | |
186 | if (!suite) | |
187 | { | |
188 | suite = cipher; | |
189 | if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY)) | |
190 | { | |
191 | DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", | |
192 | tls_cipher_suite_names, suite); | |
193 | this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); | |
194 | return NEED_MORE; | |
195 | } | |
196 | DBG1(DBG_TLS, "negotiated %N using suite %N", | |
197 | tls_version_names, version, tls_cipher_suite_names, suite); | |
198 | free(this->session.ptr); | |
199 | this->session = chunk_clone(session); | |
18010de2 | 200 | } |
8fef06a6 | 201 | this->state = STATE_HELLO_RECEIVED; |
4ef946dd | 202 | return NEED_MORE; |
3e962b08 MW |
203 | } |
204 | ||
c8114799 MW |
205 | /** |
206 | * Check if a server certificate is acceptable for the given server identity | |
207 | */ | |
208 | static bool check_certificate(private_tls_peer_t *this, certificate_t *cert) | |
209 | { | |
210 | identification_t *id; | |
211 | ||
212 | if (cert->has_subject(cert, this->server)) | |
213 | { | |
214 | return TRUE; | |
215 | } | |
216 | id = cert->get_subject(cert); | |
217 | if (id->matches(id, this->server)) | |
218 | { | |
219 | return TRUE; | |
220 | } | |
221 | if (cert->get_type(cert) == CERT_X509) | |
222 | { | |
223 | x509_t *x509 = (x509_t*)cert; | |
224 | enumerator_t *enumerator; | |
225 | ||
226 | enumerator = x509->create_subjectAltName_enumerator(x509); | |
227 | while (enumerator->enumerate(enumerator, &id)) | |
228 | { | |
229 | if (id->matches(id, this->server)) | |
230 | { | |
231 | enumerator->destroy(enumerator); | |
232 | return TRUE; | |
233 | } | |
234 | } | |
235 | enumerator->destroy(enumerator); | |
236 | } | |
237 | DBG1(DBG_TLS, "server certificate does not match to '%Y'", this->server); | |
238 | return FALSE; | |
239 | } | |
240 | ||
3e962b08 MW |
241 | /** |
242 | * Process a Certificate message | |
243 | */ | |
4ef946dd | 244 | static status_t process_certificate(private_tls_peer_t *this, |
7e432eff | 245 | bio_reader_t *reader) |
3e962b08 | 246 | { |
4ef946dd | 247 | certificate_t *cert; |
7e432eff | 248 | bio_reader_t *certs; |
4ef946dd | 249 | chunk_t data; |
3ddd164e MW |
250 | bool first = TRUE; |
251 | ||
84d67ead MW |
252 | this->crypto->append_handshake(this->crypto, |
253 | TLS_CERTIFICATE, reader->peek(reader)); | |
3e962b08 | 254 | |
4ef946dd MW |
255 | if (!reader->read_data24(reader, &data)) |
256 | { | |
e6f3ef13 MW |
257 | DBG1(DBG_TLS, "certificate message header invalid"); |
258 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
259 | return NEED_MORE; | |
4ef946dd | 260 | } |
7e432eff | 261 | certs = bio_reader_create(data); |
4ef946dd MW |
262 | while (certs->remaining(certs)) |
263 | { | |
264 | if (!certs->read_data24(certs, &data)) | |
3e962b08 | 265 | { |
e6f3ef13 MW |
266 | DBG1(DBG_TLS, "certificate message invalid"); |
267 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
4ef946dd | 268 | certs->destroy(certs); |
e6f3ef13 | 269 | return NEED_MORE; |
3e962b08 | 270 | } |
4ef946dd | 271 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, |
3ddd164e | 272 | BUILD_BLOB_ASN1_DER, data, BUILD_END); |
4ef946dd | 273 | if (cert) |
3e962b08 | 274 | { |
3ddd164e MW |
275 | if (first) |
276 | { | |
c8114799 MW |
277 | if (!check_certificate(this, cert)) |
278 | { | |
279 | cert->destroy(cert); | |
280 | certs->destroy(certs); | |
281 | this->alert->add(this->alert, TLS_FATAL, TLS_ACCESS_DENIED); | |
282 | return NEED_MORE; | |
283 | } | |
3ddd164e | 284 | this->server_auth->add(this->server_auth, |
400df4ca | 285 | AUTH_HELPER_SUBJECT_CERT, cert); |
3c19b346 | 286 | DBG1(DBG_TLS, "received TLS server certificate '%Y'", |
3ddd164e MW |
287 | cert->get_subject(cert)); |
288 | first = FALSE; | |
289 | } | |
290 | else | |
291 | { | |
3c19b346 | 292 | DBG1(DBG_TLS, "received TLS intermediate certificate '%Y'", |
3ddd164e MW |
293 | cert->get_subject(cert)); |
294 | this->server_auth->add(this->server_auth, | |
400df4ca | 295 | AUTH_HELPER_IM_CERT, cert); |
3ddd164e MW |
296 | } |
297 | } | |
298 | else | |
299 | { | |
3c19b346 | 300 | DBG1(DBG_TLS, "parsing TLS certificate failed, skipped"); |
e6f3ef13 | 301 | this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE); |
3e962b08 MW |
302 | } |
303 | } | |
4ef946dd | 304 | certs->destroy(certs); |
8fef06a6 | 305 | this->state = STATE_CERT_RECEIVED; |
3e962b08 MW |
306 | return NEED_MORE; |
307 | } | |
308 | ||
da3f4a9f MW |
309 | /** |
310 | * Find a trusted public key to encrypt/verify key exchange data | |
311 | */ | |
312 | static public_key_t *find_public_key(private_tls_peer_t *this) | |
313 | { | |
314 | public_key_t *public = NULL, *current; | |
18597950 | 315 | certificate_t *cert, *found; |
da3f4a9f MW |
316 | enumerator_t *enumerator; |
317 | auth_cfg_t *auth; | |
318 | ||
319 | cert = this->server_auth->get(this->server_auth, AUTH_HELPER_SUBJECT_CERT); | |
320 | if (cert) | |
321 | { | |
322 | enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, | |
323 | KEY_ANY, cert->get_subject(cert), this->server_auth); | |
324 | while (enumerator->enumerate(enumerator, ¤t, &auth)) | |
325 | { | |
18597950 TB |
326 | found = auth->get(auth, AUTH_RULE_SUBJECT_CERT); |
327 | if (found && cert->equals(cert, found)) | |
328 | { | |
329 | public = current->get_ref(current); | |
330 | this->server_auth->merge(this->server_auth, auth, FALSE); | |
331 | break; | |
332 | } | |
da3f4a9f MW |
333 | } |
334 | enumerator->destroy(enumerator); | |
335 | } | |
336 | return public; | |
337 | } | |
338 | ||
339 | /** | |
340 | * Process a Key Exchange message using MODP Diffie Hellman | |
341 | */ | |
342 | static status_t process_modp_key_exchange(private_tls_peer_t *this, | |
7e432eff | 343 | bio_reader_t *reader) |
da3f4a9f MW |
344 | { |
345 | chunk_t prime, generator, pub, chunk; | |
346 | public_key_t *public; | |
347 | ||
348 | chunk = reader->peek(reader); | |
349 | if (!reader->read_data16(reader, &prime) || | |
350 | !reader->read_data16(reader, &generator) || | |
351 | !reader->read_data16(reader, &pub)) | |
352 | { | |
353 | DBG1(DBG_TLS, "received invalid Server Key Exchange"); | |
354 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
355 | return NEED_MORE; | |
356 | } | |
357 | public = find_public_key(this); | |
358 | if (!public) | |
359 | { | |
360 | DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); | |
361 | this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); | |
362 | return NEED_MORE; | |
363 | } | |
364 | ||
365 | chunk.len = 2 + prime.len + 2 + generator.len + 2 + pub.len; | |
366 | chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), | |
367 | chunk_from_thing(this->server_random), chunk); | |
368 | if (!this->crypto->verify(this->crypto, public, reader, chunk)) | |
369 | { | |
370 | public->destroy(public); | |
371 | free(chunk.ptr); | |
372 | DBG1(DBG_TLS, "verifying DH parameters failed"); | |
373 | this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE); | |
374 | return NEED_MORE; | |
375 | } | |
376 | public->destroy(public); | |
377 | free(chunk.ptr); | |
378 | ||
379 | this->dh = lib->crypto->create_dh(lib->crypto, MODP_CUSTOM, | |
380 | generator, prime); | |
381 | if (!this->dh) | |
382 | { | |
383 | DBG1(DBG_TLS, "custom DH parameters not supported"); | |
384 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
385 | return NEED_MORE; | |
386 | } | |
a777155f MW |
387 | if (!this->dh->set_other_public_value(this->dh, pub)) |
388 | { | |
389 | DBG1(DBG_TLS, "applying DH public value failed"); | |
390 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
391 | return NEED_MORE; | |
392 | } | |
da3f4a9f MW |
393 | |
394 | this->state = STATE_KEY_EXCHANGE_RECEIVED; | |
395 | return NEED_MORE; | |
396 | } | |
397 | ||
3f7bb88b MW |
398 | /** |
399 | * Get the EC group for a TLS named curve | |
400 | */ | |
401 | static diffie_hellman_group_t curve_to_ec_group(private_tls_peer_t *this, | |
402 | tls_named_curve_t curve) | |
403 | { | |
404 | diffie_hellman_group_t group; | |
405 | tls_named_curve_t current; | |
406 | enumerator_t *enumerator; | |
407 | ||
408 | enumerator = this->crypto->create_ec_enumerator(this->crypto); | |
409 | while (enumerator->enumerate(enumerator, &group, ¤t)) | |
410 | { | |
411 | if (current == curve) | |
412 | { | |
413 | enumerator->destroy(enumerator); | |
414 | return group; | |
415 | } | |
416 | } | |
417 | enumerator->destroy(enumerator); | |
418 | return 0; | |
419 | } | |
5fc7297e MW |
420 | |
421 | /** | |
422 | * Process a Key Exchange message using EC Diffie Hellman | |
423 | */ | |
424 | static status_t process_ec_key_exchange(private_tls_peer_t *this, | |
7e432eff | 425 | bio_reader_t *reader) |
5fc7297e MW |
426 | { |
427 | diffie_hellman_group_t group; | |
428 | public_key_t *public; | |
429 | u_int8_t type; | |
430 | u_int16_t curve; | |
431 | chunk_t pub, chunk; | |
432 | ||
433 | chunk = reader->peek(reader); | |
434 | if (!reader->read_uint8(reader, &type)) | |
435 | { | |
436 | DBG1(DBG_TLS, "received invalid Server Key Exchange"); | |
437 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
438 | return NEED_MORE; | |
439 | } | |
440 | if (type != TLS_ECC_NAMED_CURVE) | |
441 | { | |
442 | DBG1(DBG_TLS, "ECDH curve type %N not supported", | |
443 | tls_ecc_curve_type_names, type); | |
444 | this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); | |
445 | return NEED_MORE; | |
446 | } | |
447 | if (!reader->read_uint16(reader, &curve) || | |
e6cce7ff | 448 | !reader->read_data8(reader, &pub) || pub.len == 0) |
5fc7297e MW |
449 | { |
450 | DBG1(DBG_TLS, "received invalid Server Key Exchange"); | |
451 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
452 | return NEED_MORE; | |
453 | } | |
3f7bb88b MW |
454 | |
455 | group = curve_to_ec_group(this, curve); | |
456 | if (!group) | |
5fc7297e | 457 | { |
3f7bb88b MW |
458 | DBG1(DBG_TLS, "ECDH curve %N not supported", |
459 | tls_named_curve_names, curve); | |
460 | this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); | |
461 | return NEED_MORE; | |
5fc7297e MW |
462 | } |
463 | ||
464 | public = find_public_key(this); | |
465 | if (!public) | |
466 | { | |
467 | DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); | |
468 | this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); | |
469 | return NEED_MORE; | |
470 | } | |
471 | ||
472 | chunk.len = 4 + pub.len; | |
473 | chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), | |
474 | chunk_from_thing(this->server_random), chunk); | |
475 | if (!this->crypto->verify(this->crypto, public, reader, chunk)) | |
476 | { | |
477 | public->destroy(public); | |
478 | free(chunk.ptr); | |
479 | DBG1(DBG_TLS, "verifying DH parameters failed"); | |
480 | this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE); | |
481 | return NEED_MORE; | |
482 | } | |
483 | public->destroy(public); | |
484 | free(chunk.ptr); | |
485 | ||
486 | this->dh = lib->crypto->create_dh(lib->crypto, group); | |
487 | if (!this->dh) | |
488 | { | |
489 | DBG1(DBG_TLS, "DH group %N not supported", | |
490 | diffie_hellman_group_names, group); | |
491 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
492 | return NEED_MORE; | |
493 | } | |
e6cce7ff | 494 | |
ec7d4e70 | 495 | if (pub.ptr[0] != TLS_ANSI_UNCOMPRESSED) |
e6cce7ff MW |
496 | { |
497 | DBG1(DBG_TLS, "DH point format '%N' not supported", | |
ec7d4e70 | 498 | tls_ansi_point_format_names, pub.ptr[0]); |
e6cce7ff MW |
499 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
500 | return NEED_MORE; | |
501 | } | |
a777155f MW |
502 | if (!this->dh->set_other_public_value(this->dh, chunk_skip(pub, 1))) |
503 | { | |
504 | DBG1(DBG_TLS, "applying DH public value failed"); | |
505 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
506 | return NEED_MORE; | |
507 | } | |
5fc7297e MW |
508 | |
509 | this->state = STATE_KEY_EXCHANGE_RECEIVED; | |
510 | return NEED_MORE; | |
511 | } | |
512 | ||
da3f4a9f MW |
513 | /** |
514 | * Process a Server Key Exchange | |
515 | */ | |
516 | static status_t process_key_exchange(private_tls_peer_t *this, | |
7e432eff | 517 | bio_reader_t *reader) |
da3f4a9f MW |
518 | { |
519 | diffie_hellman_group_t group; | |
520 | ||
521 | this->crypto->append_handshake(this->crypto, | |
522 | TLS_SERVER_KEY_EXCHANGE, reader->peek(reader)); | |
523 | ||
524 | group = this->crypto->get_dh_group(this->crypto); | |
f4c98ae6 | 525 | if (group == MODP_NONE) |
da3f4a9f | 526 | { |
f4c98ae6 MW |
527 | DBG1(DBG_TLS, "received Server Key Exchange, but not required " |
528 | "for current suite"); | |
529 | this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); | |
530 | return NEED_MORE; | |
531 | } | |
532 | if (diffie_hellman_group_is_ec(group)) | |
533 | { | |
534 | return process_ec_key_exchange(this, reader); | |
da3f4a9f | 535 | } |
f4c98ae6 | 536 | return process_modp_key_exchange(this, reader); |
da3f4a9f MW |
537 | } |
538 | ||
3e962b08 | 539 | /** |
69e8bb2e | 540 | * Process a Certificate Request message |
3e962b08 | 541 | */ |
7e432eff | 542 | static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader) |
3e962b08 | 543 | { |
4ef946dd | 544 | chunk_t types, hashsig, data; |
7e432eff | 545 | bio_reader_t *authorities; |
3e962b08 | 546 | identification_t *id; |
3ddd164e MW |
547 | certificate_t *cert; |
548 | ||
69e8bb2e MW |
549 | if (!this->peer) |
550 | { | |
551 | DBG1(DBG_TLS, "server requested a certificate, but client " | |
552 | "authentication disabled"); | |
69e8bb2e | 553 | } |
84d67ead MW |
554 | this->crypto->append_handshake(this->crypto, |
555 | TLS_CERTIFICATE_REQUEST, reader->peek(reader)); | |
3e962b08 | 556 | |
4ef946dd | 557 | if (!reader->read_data8(reader, &types)) |
3e962b08 | 558 | { |
e6f3ef13 MW |
559 | DBG1(DBG_TLS, "certreq message header invalid"); |
560 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
561 | return NEED_MORE; | |
3e962b08 | 562 | } |
dbb7c030 | 563 | this->cert_types = chunk_clone(types); |
3e962b08 MW |
564 | if (this->tls->get_version(this->tls) >= TLS_1_2) |
565 | { | |
4ef946dd | 566 | if (!reader->read_data16(reader, &hashsig)) |
3e962b08 | 567 | { |
e6f3ef13 MW |
568 | DBG1(DBG_TLS, "certreq message invalid"); |
569 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
570 | return NEED_MORE; | |
3e962b08 | 571 | } |
dbb7c030 | 572 | this->hashsig = chunk_clone(hashsig); |
3e962b08 | 573 | } |
4ef946dd | 574 | if (!reader->read_data16(reader, &data)) |
3e962b08 | 575 | { |
e6f3ef13 MW |
576 | DBG1(DBG_TLS, "certreq message invalid"); |
577 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
578 | return NEED_MORE; | |
3e962b08 | 579 | } |
7e432eff | 580 | authorities = bio_reader_create(data); |
4ef946dd | 581 | while (authorities->remaining(authorities)) |
3e962b08 | 582 | { |
4ef946dd | 583 | if (!authorities->read_data16(authorities, &data)) |
3e962b08 | 584 | { |
e6f3ef13 MW |
585 | DBG1(DBG_TLS, "certreq message invalid"); |
586 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
4ef946dd | 587 | authorities->destroy(authorities); |
e6f3ef13 | 588 | return NEED_MORE; |
3e962b08 | 589 | } |
6a8f1a57 | 590 | if (this->peer) |
3ddd164e | 591 | { |
6a8f1a57 MW |
592 | id = identification_create_from_encoding(ID_DER_ASN1_DN, data); |
593 | cert = lib->credmgr->get_cert(lib->credmgr, | |
594 | CERT_X509, KEY_ANY, id, TRUE); | |
595 | if (cert) | |
596 | { | |
597 | DBG1(DBG_TLS, "received TLS cert request for '%Y", id); | |
598 | this->peer_auth->add(this->peer_auth, AUTH_RULE_CA_CERT, cert); | |
599 | } | |
600 | else | |
601 | { | |
602 | DBG1(DBG_TLS, "received TLS cert request for unknown CA '%Y'", id); | |
603 | } | |
604 | id->destroy(id); | |
3ddd164e | 605 | } |
3e962b08 | 606 | } |
4ef946dd | 607 | authorities->destroy(authorities); |
8fef06a6 | 608 | this->state = STATE_CERTREQ_RECEIVED; |
3e962b08 MW |
609 | return NEED_MORE; |
610 | } | |
611 | ||
3ddd164e MW |
612 | /** |
613 | * Process Hello Done message | |
614 | */ | |
615 | static status_t process_hello_done(private_tls_peer_t *this, | |
7e432eff | 616 | bio_reader_t *reader) |
3ddd164e | 617 | { |
84d67ead MW |
618 | this->crypto->append_handshake(this->crypto, |
619 | TLS_SERVER_HELLO_DONE, reader->peek(reader)); | |
3ddd164e MW |
620 | this->state = STATE_HELLO_DONE; |
621 | return NEED_MORE; | |
622 | } | |
623 | ||
f139b578 MW |
624 | /** |
625 | * Process finished message | |
626 | */ | |
7e432eff | 627 | static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) |
f139b578 | 628 | { |
84d67ead MW |
629 | chunk_t received; |
630 | char buf[12]; | |
110364b0 | 631 | |
84d67ead | 632 | if (!reader->read_data(reader, sizeof(buf), &received)) |
110364b0 | 633 | { |
3c19b346 | 634 | DBG1(DBG_TLS, "received server finished too short"); |
e6f3ef13 MW |
635 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
636 | return NEED_MORE; | |
110364b0 | 637 | } |
84d67ead | 638 | if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) |
110364b0 | 639 | { |
3c19b346 | 640 | DBG1(DBG_TLS, "calculating server finished failed"); |
e6f3ef13 MW |
641 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
642 | return NEED_MORE; | |
110364b0 | 643 | } |
84d67ead | 644 | if (!chunk_equals(received, chunk_from_thing(buf))) |
110364b0 | 645 | { |
3c19b346 | 646 | DBG1(DBG_TLS, "received server finished invalid"); |
e6f3ef13 MW |
647 | this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); |
648 | return NEED_MORE; | |
110364b0 | 649 | } |
6a5c86b7 MW |
650 | this->state = STATE_FINISHED_RECEIVED; |
651 | this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); | |
652 | ||
110364b0 | 653 | return NEED_MORE; |
f139b578 MW |
654 | } |
655 | ||
4c0c2283 | 656 | METHOD(tls_handshake_t, process, status_t, |
7e432eff | 657 | private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader) |
4c0c2283 | 658 | { |
8fef06a6 MW |
659 | tls_handshake_type_t expected; |
660 | ||
3e962b08 MW |
661 | switch (this->state) |
662 | { | |
663 | case STATE_HELLO_SENT: | |
8fef06a6 MW |
664 | if (type == TLS_SERVER_HELLO) |
665 | { | |
666 | return process_server_hello(this, reader); | |
667 | } | |
668 | expected = TLS_SERVER_HELLO; | |
669 | break; | |
670 | case STATE_HELLO_RECEIVED: | |
671 | if (type == TLS_CERTIFICATE) | |
3e962b08 | 672 | { |
8fef06a6 | 673 | return process_certificate(this, reader); |
3e962b08 | 674 | } |
8fef06a6 MW |
675 | expected = TLS_CERTIFICATE; |
676 | break; | |
677 | case STATE_CERT_RECEIVED: | |
da3f4a9f MW |
678 | if (type == TLS_SERVER_KEY_EXCHANGE) |
679 | { | |
680 | return process_key_exchange(this, reader); | |
681 | } | |
682 | /* fall through since TLS_SERVER_KEY_EXCHANGE is optional */ | |
683 | case STATE_KEY_EXCHANGE_RECEIVED: | |
8fef06a6 MW |
684 | if (type == TLS_CERTIFICATE_REQUEST) |
685 | { | |
686 | return process_certreq(this, reader); | |
687 | } | |
2de481e3 MW |
688 | /* no cert request, server does not want to authenticate us */ |
689 | DESTROY_IF(this->peer); | |
69e8bb2e | 690 | this->peer = NULL; |
b4d30a42 | 691 | /* fall through since TLS_CERTIFICATE_REQUEST is optional */ |
8fef06a6 MW |
692 | case STATE_CERTREQ_RECEIVED: |
693 | if (type == TLS_SERVER_HELLO_DONE) | |
694 | { | |
695 | return process_hello_done(this, reader); | |
696 | } | |
697 | expected = TLS_SERVER_HELLO_DONE; | |
3e962b08 | 698 | break; |
f139b578 | 699 | case STATE_CIPHERSPEC_CHANGED_IN: |
8fef06a6 | 700 | if (type == TLS_FINISHED) |
f139b578 | 701 | { |
8fef06a6 | 702 | return process_finished(this, reader); |
f139b578 | 703 | } |
8fef06a6 | 704 | expected = TLS_FINISHED; |
f139b578 | 705 | break; |
3e962b08 | 706 | default: |
3c19b346 | 707 | DBG1(DBG_TLS, "TLS %N not expected in current state", |
8fef06a6 | 708 | tls_handshake_type_names, type); |
e6f3ef13 MW |
709 | this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); |
710 | return NEED_MORE; | |
3e962b08 | 711 | } |
3c19b346 | 712 | DBG1(DBG_TLS, "TLS %N expected, but received %N", |
8fef06a6 | 713 | tls_handshake_type_names, expected, tls_handshake_type_names, type); |
e6f3ef13 MW |
714 | this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); |
715 | return NEED_MORE; | |
4c0c2283 MW |
716 | } |
717 | ||
698674c7 MW |
718 | /** |
719 | * Send a client hello | |
720 | */ | |
8fef06a6 | 721 | static status_t send_client_hello(private_tls_peer_t *this, |
7e432eff | 722 | tls_handshake_type_t *type, bio_writer_t *writer) |
698674c7 | 723 | { |
7ea87db0 | 724 | tls_cipher_suite_t *suites; |
7e432eff | 725 | bio_writer_t *extensions, *curves = NULL; |
7ea87db0 | 726 | tls_version_t version; |
3f7bb88b MW |
727 | tls_named_curve_t curve; |
728 | enumerator_t *enumerator; | |
3a1640de | 729 | int count, i; |
698674c7 MW |
730 | rng_t *rng; |
731 | ||
18010de2 | 732 | htoun32(&this->client_random, time(NULL)); |
698674c7 | 733 | rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); |
126eb2af TB |
734 | if (!rng || |
735 | !rng->get_bytes(rng, sizeof(this->client_random) - 4, | |
736 | this->client_random + 4)) | |
698674c7 | 737 | { |
126eb2af | 738 | DBG1(DBG_TLS, "failed to generate client random"); |
e6f3ef13 | 739 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
126eb2af | 740 | DESTROY_IF(rng); |
e6f3ef13 | 741 | return NEED_MORE; |
698674c7 | 742 | } |
3a1640de MW |
743 | rng->destroy(rng); |
744 | ||
7ea87db0 AS |
745 | /* TLS version */ |
746 | version = this->tls->get_version(this->tls); | |
b37080f8 | 747 | this->hello_version = version; |
7ea87db0 | 748 | writer->write_uint16(writer, version); |
18010de2 | 749 | writer->write_data(writer, chunk_from_thing(this->client_random)); |
7ea87db0 | 750 | |
6a5c86b7 MW |
751 | /* session identifier */ |
752 | this->session = this->crypto->get_session(this->crypto, this->server); | |
753 | writer->write_data8(writer, this->session); | |
3a1640de | 754 | |
7ea87db0 AS |
755 | /* add TLS cipher suites */ |
756 | count = this->crypto->get_cipher_suites(this->crypto, &suites); | |
3a1640de MW |
757 | writer->write_uint16(writer, count * 2); |
758 | for (i = 0; i < count; i++) | |
759 | { | |
7ea87db0 | 760 | writer->write_uint16(writer, suites[i]); |
3a1640de | 761 | } |
7ea87db0 | 762 | |
3a1640de MW |
763 | /* NULL compression only */ |
764 | writer->write_uint8(writer, 1); | |
765 | writer->write_uint8(writer, 0); | |
766 | ||
7e432eff | 767 | extensions = bio_writer_create(32); |
37a59a8f | 768 | |
06109c47 MW |
769 | extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); |
770 | this->crypto->get_signature_algorithms(this->crypto, extensions); | |
37a59a8f | 771 | |
3f7bb88b MW |
772 | /* add supported Elliptic Curves, if any */ |
773 | enumerator = this->crypto->create_ec_enumerator(this->crypto); | |
774 | while (enumerator->enumerate(enumerator, NULL, &curve)) | |
775 | { | |
776 | if (!curves) | |
777 | { | |
778 | extensions->write_uint16(extensions, TLS_EXT_ELLIPTIC_CURVES); | |
7e432eff | 779 | curves = bio_writer_create(16); |
3f7bb88b MW |
780 | } |
781 | curves->write_uint16(curves, curve); | |
782 | } | |
783 | enumerator->destroy(enumerator); | |
784 | if (curves) | |
785 | { | |
07f826af | 786 | curves->wrap16(curves); |
3f7bb88b MW |
787 | extensions->write_data16(extensions, curves->get_buf(curves)); |
788 | curves->destroy(curves); | |
31c65eb3 MW |
789 | |
790 | /* if we support curves, add point format extension */ | |
791 | extensions->write_uint16(extensions, TLS_EXT_EC_POINT_FORMATS); | |
792 | extensions->write_uint16(extensions, 2); | |
793 | extensions->write_uint8(extensions, 1); | |
794 | extensions->write_uint8(extensions, TLS_EC_POINT_UNCOMPRESSED); | |
3f7bb88b | 795 | } |
1c21f47a MW |
796 | if (this->server->get_type(this->server) == ID_FQDN) |
797 | { | |
7e432eff | 798 | bio_writer_t *names; |
1c21f47a MW |
799 | |
800 | DBG2(DBG_TLS, "sending Server Name Indication for '%Y'", this->server); | |
801 | ||
7e432eff | 802 | names = bio_writer_create(8); |
1c21f47a MW |
803 | names->write_uint8(names, TLS_NAME_TYPE_HOST_NAME); |
804 | names->write_data16(names, this->server->get_encoding(this->server)); | |
805 | names->wrap16(names); | |
806 | extensions->write_uint16(extensions, TLS_EXT_SERVER_NAME); | |
807 | extensions->write_data16(extensions, names->get_buf(names)); | |
808 | names->destroy(names); | |
809 | } | |
37a59a8f | 810 | |
06109c47 MW |
811 | writer->write_data16(writer, extensions->get_buf(extensions)); |
812 | extensions->destroy(extensions); | |
813 | ||
3a1640de | 814 | *type = TLS_CLIENT_HELLO; |
3e962b08 | 815 | this->state = STATE_HELLO_SENT; |
84d67ead | 816 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
3ddd164e MW |
817 | return NEED_MORE; |
818 | } | |
819 | ||
dbb7c030 MW |
820 | /** |
821 | * Find a private key suitable to sign Certificate Verify | |
822 | */ | |
823 | static private_key_t *find_private_key(private_tls_peer_t *this) | |
824 | { | |
825 | private_key_t *key = NULL; | |
7e432eff | 826 | bio_reader_t *reader; |
dbb7c030 MW |
827 | key_type_t type; |
828 | u_int8_t cert; | |
829 | ||
830 | if (!this->peer) | |
831 | { | |
832 | return NULL; | |
833 | } | |
7e432eff | 834 | reader = bio_reader_create(this->cert_types); |
dbb7c030 MW |
835 | while (reader->remaining(reader) && reader->read_uint8(reader, &cert)) |
836 | { | |
837 | switch (cert) | |
838 | { | |
839 | case TLS_RSA_SIGN: | |
840 | type = KEY_RSA; | |
841 | break; | |
842 | case TLS_ECDSA_SIGN: | |
843 | type = KEY_ECDSA; | |
844 | break; | |
845 | default: | |
846 | continue; | |
847 | } | |
848 | key = lib->credmgr->get_private(lib->credmgr, type, | |
849 | this->peer, this->peer_auth); | |
850 | if (key) | |
851 | { | |
852 | break; | |
853 | } | |
854 | } | |
855 | reader->destroy(reader); | |
856 | return key; | |
857 | } | |
858 | ||
3ddd164e MW |
859 | /** |
860 | * Send Certificate | |
861 | */ | |
862 | static status_t send_certificate(private_tls_peer_t *this, | |
7e432eff | 863 | tls_handshake_type_t *type, bio_writer_t *writer) |
3ddd164e MW |
864 | { |
865 | enumerator_t *enumerator; | |
866 | certificate_t *cert; | |
867 | auth_rule_t rule; | |
7e432eff | 868 | bio_writer_t *certs; |
3ddd164e MW |
869 | chunk_t data; |
870 | ||
dbb7c030 | 871 | this->private = find_private_key(this); |
3ddd164e MW |
872 | if (!this->private) |
873 | { | |
2db8b58f MW |
874 | DBG1(DBG_TLS, "no TLS peer certificate found for '%Y', " |
875 | "skipping client authentication", this->peer); | |
2de481e3 | 876 | this->peer->destroy(this->peer); |
2db8b58f | 877 | this->peer = NULL; |
3ddd164e MW |
878 | } |
879 | ||
880 | /* generate certificate payload */ | |
7e432eff | 881 | certs = bio_writer_create(256); |
2db8b58f | 882 | if (this->peer) |
3ddd164e | 883 | { |
2db8b58f MW |
884 | cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT); |
885 | if (cert) | |
3ddd164e | 886 | { |
0b71bc7a MW |
887 | if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) |
888 | { | |
2db8b58f | 889 | DBG1(DBG_TLS, "sending TLS peer certificate '%Y'", |
0b71bc7a MW |
890 | cert->get_subject(cert)); |
891 | certs->write_data24(certs, data); | |
892 | free(data.ptr); | |
893 | } | |
3ddd164e | 894 | } |
2db8b58f MW |
895 | enumerator = this->peer_auth->create_enumerator(this->peer_auth); |
896 | while (enumerator->enumerate(enumerator, &rule, &cert)) | |
897 | { | |
898 | if (rule == AUTH_RULE_IM_CERT) | |
899 | { | |
900 | if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) | |
901 | { | |
902 | DBG1(DBG_TLS, "sending TLS intermediate certificate '%Y'", | |
903 | cert->get_subject(cert)); | |
904 | certs->write_data24(certs, data); | |
905 | free(data.ptr); | |
906 | } | |
907 | } | |
908 | } | |
909 | enumerator->destroy(enumerator); | |
3ddd164e | 910 | } |
3ddd164e MW |
911 | |
912 | writer->write_data24(writer, certs->get_buf(certs)); | |
913 | certs->destroy(certs); | |
914 | ||
915 | *type = TLS_CERTIFICATE; | |
916 | this->state = STATE_CERT_SENT; | |
84d67ead | 917 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
3ddd164e MW |
918 | return NEED_MORE; |
919 | } | |
920 | ||
921 | /** | |
da3f4a9f | 922 | * Send client key exchange, using premaster encryption |
3ddd164e | 923 | */ |
da3f4a9f | 924 | static status_t send_key_exchange_encrypt(private_tls_peer_t *this, |
7e432eff | 925 | tls_handshake_type_t *type, bio_writer_t *writer) |
3ddd164e | 926 | { |
da3f4a9f | 927 | public_key_t *public; |
3ddd164e MW |
928 | rng_t *rng; |
929 | char premaster[48]; | |
930 | chunk_t encrypted; | |
931 | ||
932 | rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); | |
126eb2af | 933 | if (!rng || !rng->get_bytes(rng, sizeof(premaster) - 2, premaster + 2)) |
3ddd164e | 934 | { |
126eb2af | 935 | DBG1(DBG_TLS, "failed to generate TLS premaster secret"); |
a596006e | 936 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
126eb2af | 937 | DESTROY_IF(rng); |
da3f4a9f | 938 | return NEED_MORE; |
3ddd164e | 939 | } |
3ddd164e | 940 | rng->destroy(rng); |
b37080f8 | 941 | htoun16(premaster, this->hello_version); |
3ddd164e | 942 | |
9020f7d0 MW |
943 | if (!this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster), |
944 | this->session, this->server, | |
945 | chunk_from_thing(this->client_random), | |
946 | chunk_from_thing(this->server_random))) | |
947 | { | |
948 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
949 | return NEED_MORE; | |
950 | } | |
18010de2 | 951 | |
da3f4a9f | 952 | public = find_public_key(this); |
3ddd164e MW |
953 | if (!public) |
954 | { | |
3c19b346 | 955 | DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); |
a596006e | 956 | this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); |
da3f4a9f | 957 | return NEED_MORE; |
3ddd164e | 958 | } |
33ddaaab MW |
959 | if (!public->encrypt(public, ENCRYPT_RSA_PKCS1, |
960 | chunk_from_thing(premaster), &encrypted)) | |
3ddd164e MW |
961 | { |
962 | public->destroy(public); | |
3c19b346 | 963 | DBG1(DBG_TLS, "encrypting TLS premaster secret failed"); |
a596006e | 964 | this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE); |
da3f4a9f | 965 | return NEED_MORE; |
3ddd164e MW |
966 | } |
967 | public->destroy(public); | |
968 | ||
969 | writer->write_data16(writer, encrypted); | |
970 | free(encrypted.ptr); | |
971 | ||
972 | *type = TLS_CLIENT_KEY_EXCHANGE; | |
973 | this->state = STATE_KEY_EXCHANGE_SENT; | |
84d67ead | 974 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
3ddd164e MW |
975 | return NEED_MORE; |
976 | } | |
977 | ||
da3f4a9f MW |
978 | /** |
979 | * Send client key exchange, using DHE exchange | |
980 | */ | |
981 | static status_t send_key_exchange_dhe(private_tls_peer_t *this, | |
7e432eff | 982 | tls_handshake_type_t *type, bio_writer_t *writer) |
da3f4a9f MW |
983 | { |
984 | chunk_t premaster, pub; | |
985 | ||
bace1d64 | 986 | if (!this->dh->get_shared_secret(this->dh, &premaster)) |
da3f4a9f MW |
987 | { |
988 | DBG1(DBG_TLS, "calculating premaster from DH failed"); | |
989 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
990 | return NEED_MORE; | |
991 | } | |
9020f7d0 MW |
992 | if (!this->crypto->derive_secrets(this->crypto, premaster, |
993 | this->session, this->server, | |
994 | chunk_from_thing(this->client_random), | |
995 | chunk_from_thing(this->server_random))) | |
996 | { | |
997 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
998 | chunk_clear(&premaster); | |
999 | return NEED_MORE; | |
1000 | } | |
da3f4a9f MW |
1001 | chunk_clear(&premaster); |
1002 | ||
42431690 MW |
1003 | if (!this->dh->get_my_public_value(this->dh, &pub)) |
1004 | { | |
1005 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
1006 | return NEED_MORE; | |
1007 | } | |
5fc7297e MW |
1008 | if (this->dh->get_dh_group(this->dh) == MODP_CUSTOM) |
1009 | { | |
1010 | writer->write_data16(writer, pub); | |
1011 | } | |
1012 | else | |
e6cce7ff MW |
1013 | { /* ECP uses 8bit length header only, but a point format */ |
1014 | writer->write_uint8(writer, pub.len + 1); | |
ec7d4e70 | 1015 | writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); |
e6cce7ff | 1016 | writer->write_data(writer, pub); |
5fc7297e | 1017 | } |
da3f4a9f MW |
1018 | free(pub.ptr); |
1019 | ||
1020 | *type = TLS_CLIENT_KEY_EXCHANGE; | |
1021 | this->state = STATE_KEY_EXCHANGE_SENT; | |
1022 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); | |
1023 | return NEED_MORE; | |
1024 | } | |
1025 | ||
1026 | /** | |
1027 | * Send client key exchange, depending on suite | |
1028 | */ | |
1029 | static status_t send_key_exchange(private_tls_peer_t *this, | |
7e432eff | 1030 | tls_handshake_type_t *type, bio_writer_t *writer) |
da3f4a9f MW |
1031 | { |
1032 | if (this->dh) | |
1033 | { | |
1034 | return send_key_exchange_dhe(this, type, writer); | |
1035 | } | |
1036 | return send_key_exchange_encrypt(this, type, writer); | |
1037 | } | |
1038 | ||
3ddd164e MW |
1039 | /** |
1040 | * Send certificate verify | |
1041 | */ | |
1042 | static status_t send_certificate_verify(private_tls_peer_t *this, | |
7e432eff | 1043 | tls_handshake_type_t *type, bio_writer_t *writer) |
3ddd164e | 1044 | { |
84d67ead | 1045 | if (!this->private || |
dbb7c030 MW |
1046 | !this->crypto->sign_handshake(this->crypto, this->private, |
1047 | writer, this->hashsig)) | |
3ddd164e | 1048 | { |
3c19b346 | 1049 | DBG1(DBG_TLS, "creating TLS Certificate Verify signature failed"); |
a596006e | 1050 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
da3f4a9f | 1051 | return NEED_MORE; |
3ddd164e | 1052 | } |
3ddd164e MW |
1053 | |
1054 | *type = TLS_CERTIFICATE_VERIFY; | |
1055 | this->state = STATE_VERIFY_SENT; | |
84d67ead | 1056 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
3ddd164e MW |
1057 | return NEED_MORE; |
1058 | } | |
1059 | ||
1060 | /** | |
1061 | * Send Finished | |
1062 | */ | |
1063 | static status_t send_finished(private_tls_peer_t *this, | |
7e432eff | 1064 | tls_handshake_type_t *type, bio_writer_t *writer) |
3ddd164e | 1065 | { |
84d67ead | 1066 | char buf[12]; |
18010de2 | 1067 | |
84d67ead | 1068 | if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) |
18010de2 | 1069 | { |
3c19b346 | 1070 | DBG1(DBG_TLS, "calculating client finished data failed"); |
a596006e | 1071 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
da3f4a9f | 1072 | return NEED_MORE; |
18010de2 | 1073 | } |
18010de2 | 1074 | |
84d67ead | 1075 | writer->write_data(writer, chunk_from_thing(buf)); |
18010de2 | 1076 | |
3ddd164e MW |
1077 | *type = TLS_FINISHED; |
1078 | this->state = STATE_FINISHED_SENT; | |
84d67ead | 1079 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
698674c7 MW |
1080 | return NEED_MORE; |
1081 | } | |
1082 | ||
4c0c2283 | 1083 | METHOD(tls_handshake_t, build, status_t, |
7e432eff | 1084 | private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer) |
4c0c2283 | 1085 | { |
698674c7 MW |
1086 | switch (this->state) |
1087 | { | |
1088 | case STATE_INIT: | |
8fef06a6 | 1089 | return send_client_hello(this, type, writer); |
3ddd164e | 1090 | case STATE_HELLO_DONE: |
69e8bb2e | 1091 | if (this->peer) |
b4d30a42 AS |
1092 | { |
1093 | return send_certificate(this, type, writer); | |
1094 | } | |
1095 | /* otherwise fall through to next state */ | |
3ddd164e MW |
1096 | case STATE_CERT_SENT: |
1097 | return send_key_exchange(this, type, writer); | |
1098 | case STATE_KEY_EXCHANGE_SENT: | |
69e8bb2e | 1099 | if (this->peer) |
b4d30a42 AS |
1100 | { |
1101 | return send_certificate_verify(this, type, writer); | |
1102 | } | |
1103 | else | |
1104 | { | |
1105 | return INVALID_STATE; | |
1106 | } | |
f139b578 | 1107 | case STATE_CIPHERSPEC_CHANGED_OUT: |
3ddd164e | 1108 | return send_finished(this, type, writer); |
698674c7 MW |
1109 | default: |
1110 | return INVALID_STATE; | |
1111 | } | |
4c0c2283 MW |
1112 | } |
1113 | ||
3ddd164e | 1114 | METHOD(tls_handshake_t, cipherspec_changed, bool, |
4caa3806 | 1115 | private_tls_peer_t *this, bool inbound) |
3ddd164e | 1116 | { |
4caa3806 | 1117 | if (inbound) |
3ddd164e | 1118 | { |
6a5c86b7 MW |
1119 | if (this->resume) |
1120 | { | |
1121 | return this->state == STATE_HELLO_RECEIVED; | |
1122 | } | |
4caa3806 MW |
1123 | return this->state == STATE_FINISHED_SENT; |
1124 | } | |
1125 | else | |
1126 | { | |
6a5c86b7 MW |
1127 | if (this->resume) |
1128 | { | |
1129 | return this->state == STATE_FINISHED_RECEIVED; | |
1130 | } | |
4caa3806 MW |
1131 | if (this->peer) |
1132 | { | |
1133 | return this->state == STATE_VERIFY_SENT; | |
1134 | } | |
1135 | return this->state == STATE_KEY_EXCHANGE_SENT; | |
3ddd164e | 1136 | } |
3ddd164e MW |
1137 | } |
1138 | ||
4caa3806 MW |
1139 | METHOD(tls_handshake_t, change_cipherspec, void, |
1140 | private_tls_peer_t *this, bool inbound) | |
3ddd164e | 1141 | { |
4caa3806 MW |
1142 | this->crypto->change_cipher(this->crypto, inbound); |
1143 | if (inbound) | |
f139b578 | 1144 | { |
f139b578 | 1145 | this->state = STATE_CIPHERSPEC_CHANGED_IN; |
f139b578 | 1146 | } |
4caa3806 MW |
1147 | else |
1148 | { | |
1149 | this->state = STATE_CIPHERSPEC_CHANGED_OUT; | |
1150 | } | |
3ddd164e MW |
1151 | } |
1152 | ||
1327839d AS |
1153 | METHOD(tls_handshake_t, finished, bool, |
1154 | private_tls_peer_t *this) | |
1155 | { | |
6a5c86b7 MW |
1156 | if (this->resume) |
1157 | { | |
1158 | return this->state == STATE_FINISHED_SENT; | |
1159 | } | |
1160 | return this->state == STATE_FINISHED_RECEIVED; | |
1327839d AS |
1161 | } |
1162 | ||
2de481e3 MW |
1163 | METHOD(tls_handshake_t, get_peer_id, identification_t*, |
1164 | private_tls_peer_t *this) | |
1165 | { | |
1166 | return this->peer; | |
1167 | } | |
1168 | ||
1169 | METHOD(tls_handshake_t, get_server_id, identification_t*, | |
1170 | private_tls_peer_t *this) | |
1171 | { | |
1172 | return this->server; | |
1173 | } | |
1174 | ||
666c5523 MW |
1175 | METHOD(tls_handshake_t, get_auth, auth_cfg_t*, |
1176 | private_tls_peer_t *this) | |
1177 | { | |
1178 | return this->server_auth; | |
1179 | } | |
1180 | ||
4c0c2283 MW |
1181 | METHOD(tls_handshake_t, destroy, void, |
1182 | private_tls_peer_t *this) | |
1183 | { | |
3ddd164e | 1184 | DESTROY_IF(this->private); |
da3f4a9f | 1185 | DESTROY_IF(this->dh); |
2de481e3 MW |
1186 | DESTROY_IF(this->peer); |
1187 | this->server->destroy(this->server); | |
3ddd164e MW |
1188 | this->peer_auth->destroy(this->peer_auth); |
1189 | this->server_auth->destroy(this->server_auth); | |
dbb7c030 MW |
1190 | free(this->hashsig.ptr); |
1191 | free(this->cert_types.ptr); | |
6a5c86b7 | 1192 | free(this->session.ptr); |
4c0c2283 MW |
1193 | free(this); |
1194 | } | |
1195 | ||
1196 | /** | |
1197 | * See header | |
1198 | */ | |
e6f3ef13 | 1199 | tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert, |
3ddd164e | 1200 | identification_t *peer, identification_t *server) |
4c0c2283 MW |
1201 | { |
1202 | private_tls_peer_t *this; | |
1203 | ||
1204 | INIT(this, | |
ba31fe1f MW |
1205 | .public = { |
1206 | .handshake = { | |
1207 | .process = _process, | |
1208 | .build = _build, | |
1209 | .cipherspec_changed = _cipherspec_changed, | |
1210 | .change_cipherspec = _change_cipherspec, | |
1211 | .finished = _finished, | |
2de481e3 MW |
1212 | .get_peer_id = _get_peer_id, |
1213 | .get_server_id = _get_server_id, | |
666c5523 | 1214 | .get_auth = _get_auth, |
ba31fe1f MW |
1215 | .destroy = _destroy, |
1216 | }, | |
4c0c2283 | 1217 | }, |
698674c7 | 1218 | .state = STATE_INIT, |
3e962b08 | 1219 | .tls = tls, |
536dbc00 | 1220 | .crypto = crypto, |
e6f3ef13 | 1221 | .alert = alert, |
2de481e3 MW |
1222 | .peer = peer ? peer->clone(peer) : NULL, |
1223 | .server = server->clone(server), | |
3ddd164e MW |
1224 | .peer_auth = auth_cfg_create(), |
1225 | .server_auth = auth_cfg_create(), | |
4c0c2283 MW |
1226 | ); |
1227 | ||
1228 | return &this->public; | |
1229 | } |