]>
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 | ||
0f82a470 | 18 | #include <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 MW |
31 | STATE_CERT_RECEIVED, |
32 | STATE_CERTREQ_RECEIVED, | |
3ddd164e MW |
33 | STATE_KEY_EXCHANGE_SENT, |
34 | STATE_VERIFY_SENT, | |
f139b578 | 35 | STATE_CIPHERSPEC_CHANGED_OUT, |
3ddd164e | 36 | STATE_FINISHED_SENT, |
f139b578 | 37 | STATE_CIPHERSPEC_CHANGED_IN, |
110364b0 | 38 | STATE_COMPLETE, |
698674c7 MW |
39 | } peer_state_t; |
40 | ||
4c0c2283 MW |
41 | /** |
42 | * Private data of an tls_peer_t object. | |
43 | */ | |
44 | struct private_tls_peer_t { | |
45 | ||
46 | /** | |
47 | * Public tls_peer_t interface. | |
48 | */ | |
49 | tls_peer_t public; | |
536dbc00 | 50 | |
3e962b08 MW |
51 | /** |
52 | * TLS stack | |
53 | */ | |
54 | tls_t *tls; | |
55 | ||
536dbc00 MW |
56 | /** |
57 | * TLS crypto context | |
58 | */ | |
59 | tls_crypto_t *crypto; | |
698674c7 | 60 | |
e6f3ef13 MW |
61 | /** |
62 | * TLS alert handler | |
63 | */ | |
64 | tls_alert_t *alert; | |
65 | ||
3ddd164e | 66 | /** |
69e8bb2e | 67 | * Peer identity, NULL for no client authentication |
3ddd164e MW |
68 | */ |
69 | identification_t *peer; | |
70 | ||
71 | /** | |
72 | * Server identity | |
73 | */ | |
74 | identification_t *server; | |
75 | ||
698674c7 MW |
76 | /** |
77 | * State we are in | |
78 | */ | |
79 | peer_state_t state; | |
3ddd164e | 80 | |
18010de2 MW |
81 | /** |
82 | * Hello random data selected by client | |
83 | */ | |
84 | char client_random[32]; | |
85 | ||
86 | /** | |
87 | * Hello random data selected by server | |
88 | */ | |
89 | char server_random[32]; | |
90 | ||
3ddd164e MW |
91 | /** |
92 | * Auth helper for peer authentication | |
93 | */ | |
94 | auth_cfg_t *peer_auth; | |
95 | ||
96 | /** | |
97 | * Auth helper for server authentication | |
98 | */ | |
99 | auth_cfg_t *server_auth; | |
100 | ||
101 | /** | |
102 | * Peer private key | |
103 | */ | |
104 | private_key_t *private; | |
dbb7c030 MW |
105 | |
106 | /** | |
107 | * List of server-supported hashsig algorithms | |
108 | */ | |
109 | chunk_t hashsig; | |
110 | ||
111 | /** | |
112 | * List of server-supported client certificate types | |
113 | */ | |
114 | chunk_t cert_types; | |
4c0c2283 MW |
115 | }; |
116 | ||
3e962b08 MW |
117 | /** |
118 | * Process a server hello message | |
119 | */ | |
4ef946dd MW |
120 | static status_t process_server_hello(private_tls_peer_t *this, |
121 | tls_reader_t *reader) | |
3e962b08 | 122 | { |
4ef946dd MW |
123 | u_int8_t compression; |
124 | u_int16_t version, cipher; | |
4ef946dd | 125 | chunk_t random, session, ext = chunk_empty; |
18010de2 | 126 | tls_cipher_suite_t suite; |
4ef946dd | 127 | |
84d67ead MW |
128 | this->crypto->append_handshake(this->crypto, |
129 | TLS_SERVER_HELLO, reader->peek(reader)); | |
3ddd164e | 130 | |
4ef946dd | 131 | if (!reader->read_uint16(reader, &version) || |
18010de2 | 132 | !reader->read_data(reader, sizeof(this->server_random), &random) || |
4ef946dd MW |
133 | !reader->read_data8(reader, &session) || |
134 | !reader->read_uint16(reader, &cipher) || | |
135 | !reader->read_uint8(reader, &compression) || | |
136 | (reader->remaining(reader) && !reader->read_data16(reader, &ext))) | |
3e962b08 | 137 | { |
3c19b346 | 138 | DBG1(DBG_TLS, "received invalid ServerHello"); |
e6f3ef13 MW |
139 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
140 | return NEED_MORE; | |
4ef946dd | 141 | } |
18010de2 MW |
142 | |
143 | memcpy(this->server_random, random.ptr, sizeof(this->server_random)); | |
144 | ||
f154e304 | 145 | if (!this->tls->set_version(this->tls, version)) |
4ef946dd | 146 | { |
f154e304 MW |
147 | DBG1(DBG_TLS, "negotiated version %N not supported", |
148 | tls_version_names, version); | |
e6f3ef13 MW |
149 | this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); |
150 | return NEED_MORE; | |
3e962b08 | 151 | } |
18010de2 MW |
152 | suite = cipher; |
153 | if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1)) | |
154 | { | |
3c19b346 | 155 | DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", |
0bcef5fe | 156 | tls_cipher_suite_names, suite); |
e6f3ef13 MW |
157 | this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); |
158 | return NEED_MORE; | |
18010de2 | 159 | } |
3c19b346 | 160 | DBG1(DBG_TLS, "negotiated TLS version %N with suite %N", |
0bcef5fe | 161 | tls_version_names, version, tls_cipher_suite_names, suite); |
8fef06a6 | 162 | this->state = STATE_HELLO_RECEIVED; |
4ef946dd | 163 | return NEED_MORE; |
3e962b08 MW |
164 | } |
165 | ||
c8114799 MW |
166 | /** |
167 | * Check if a server certificate is acceptable for the given server identity | |
168 | */ | |
169 | static bool check_certificate(private_tls_peer_t *this, certificate_t *cert) | |
170 | { | |
171 | identification_t *id; | |
172 | ||
173 | if (cert->has_subject(cert, this->server)) | |
174 | { | |
175 | return TRUE; | |
176 | } | |
177 | id = cert->get_subject(cert); | |
178 | if (id->matches(id, this->server)) | |
179 | { | |
180 | return TRUE; | |
181 | } | |
182 | if (cert->get_type(cert) == CERT_X509) | |
183 | { | |
184 | x509_t *x509 = (x509_t*)cert; | |
185 | enumerator_t *enumerator; | |
186 | ||
187 | enumerator = x509->create_subjectAltName_enumerator(x509); | |
188 | while (enumerator->enumerate(enumerator, &id)) | |
189 | { | |
190 | if (id->matches(id, this->server)) | |
191 | { | |
192 | enumerator->destroy(enumerator); | |
193 | return TRUE; | |
194 | } | |
195 | } | |
196 | enumerator->destroy(enumerator); | |
197 | } | |
198 | DBG1(DBG_TLS, "server certificate does not match to '%Y'", this->server); | |
199 | return FALSE; | |
200 | } | |
201 | ||
3e962b08 MW |
202 | /** |
203 | * Process a Certificate message | |
204 | */ | |
4ef946dd MW |
205 | static status_t process_certificate(private_tls_peer_t *this, |
206 | tls_reader_t *reader) | |
3e962b08 | 207 | { |
4ef946dd MW |
208 | certificate_t *cert; |
209 | tls_reader_t *certs; | |
210 | chunk_t data; | |
3ddd164e MW |
211 | bool first = TRUE; |
212 | ||
84d67ead MW |
213 | this->crypto->append_handshake(this->crypto, |
214 | TLS_CERTIFICATE, reader->peek(reader)); | |
3e962b08 | 215 | |
4ef946dd MW |
216 | if (!reader->read_data24(reader, &data)) |
217 | { | |
e6f3ef13 MW |
218 | DBG1(DBG_TLS, "certificate message header invalid"); |
219 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
220 | return NEED_MORE; | |
4ef946dd MW |
221 | } |
222 | certs = tls_reader_create(data); | |
223 | while (certs->remaining(certs)) | |
224 | { | |
225 | if (!certs->read_data24(certs, &data)) | |
3e962b08 | 226 | { |
e6f3ef13 MW |
227 | DBG1(DBG_TLS, "certificate message invalid"); |
228 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
4ef946dd | 229 | certs->destroy(certs); |
e6f3ef13 | 230 | return NEED_MORE; |
3e962b08 | 231 | } |
4ef946dd | 232 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, |
3ddd164e | 233 | BUILD_BLOB_ASN1_DER, data, BUILD_END); |
4ef946dd | 234 | if (cert) |
3e962b08 | 235 | { |
3ddd164e MW |
236 | if (first) |
237 | { | |
c8114799 MW |
238 | if (!check_certificate(this, cert)) |
239 | { | |
240 | cert->destroy(cert); | |
241 | certs->destroy(certs); | |
242 | this->alert->add(this->alert, TLS_FATAL, TLS_ACCESS_DENIED); | |
243 | return NEED_MORE; | |
244 | } | |
3ddd164e | 245 | this->server_auth->add(this->server_auth, |
400df4ca | 246 | AUTH_HELPER_SUBJECT_CERT, cert); |
3c19b346 | 247 | DBG1(DBG_TLS, "received TLS server certificate '%Y'", |
3ddd164e MW |
248 | cert->get_subject(cert)); |
249 | first = FALSE; | |
250 | } | |
251 | else | |
252 | { | |
3c19b346 | 253 | DBG1(DBG_TLS, "received TLS intermediate certificate '%Y'", |
3ddd164e MW |
254 | cert->get_subject(cert)); |
255 | this->server_auth->add(this->server_auth, | |
400df4ca | 256 | AUTH_HELPER_IM_CERT, cert); |
3ddd164e MW |
257 | } |
258 | } | |
259 | else | |
260 | { | |
3c19b346 | 261 | DBG1(DBG_TLS, "parsing TLS certificate failed, skipped"); |
e6f3ef13 | 262 | this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE); |
3e962b08 MW |
263 | } |
264 | } | |
4ef946dd | 265 | certs->destroy(certs); |
8fef06a6 | 266 | this->state = STATE_CERT_RECEIVED; |
3e962b08 MW |
267 | return NEED_MORE; |
268 | } | |
269 | ||
270 | /** | |
69e8bb2e | 271 | * Process a Certificate Request message |
3e962b08 | 272 | */ |
4ef946dd | 273 | static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) |
3e962b08 | 274 | { |
4ef946dd MW |
275 | chunk_t types, hashsig, data; |
276 | tls_reader_t *authorities; | |
3e962b08 | 277 | identification_t *id; |
3ddd164e MW |
278 | certificate_t *cert; |
279 | ||
69e8bb2e MW |
280 | if (!this->peer) |
281 | { | |
282 | DBG1(DBG_TLS, "server requested a certificate, but client " | |
283 | "authentication disabled"); | |
284 | this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); | |
285 | return NEED_MORE; | |
286 | } | |
84d67ead MW |
287 | this->crypto->append_handshake(this->crypto, |
288 | TLS_CERTIFICATE_REQUEST, reader->peek(reader)); | |
3e962b08 | 289 | |
4ef946dd | 290 | if (!reader->read_data8(reader, &types)) |
3e962b08 | 291 | { |
e6f3ef13 MW |
292 | DBG1(DBG_TLS, "certreq message header invalid"); |
293 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
294 | return NEED_MORE; | |
3e962b08 | 295 | } |
dbb7c030 | 296 | this->cert_types = chunk_clone(types); |
3e962b08 MW |
297 | if (this->tls->get_version(this->tls) >= TLS_1_2) |
298 | { | |
4ef946dd | 299 | if (!reader->read_data16(reader, &hashsig)) |
3e962b08 | 300 | { |
e6f3ef13 MW |
301 | DBG1(DBG_TLS, "certreq message invalid"); |
302 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
303 | return NEED_MORE; | |
3e962b08 | 304 | } |
dbb7c030 | 305 | this->hashsig = chunk_clone(hashsig); |
3e962b08 | 306 | } |
4ef946dd | 307 | if (!reader->read_data16(reader, &data)) |
3e962b08 | 308 | { |
e6f3ef13 MW |
309 | DBG1(DBG_TLS, "certreq message invalid"); |
310 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
311 | return NEED_MORE; | |
3e962b08 | 312 | } |
4ef946dd MW |
313 | authorities = tls_reader_create(data); |
314 | while (authorities->remaining(authorities)) | |
3e962b08 | 315 | { |
4ef946dd | 316 | if (!authorities->read_data16(authorities, &data)) |
3e962b08 | 317 | { |
e6f3ef13 MW |
318 | DBG1(DBG_TLS, "certreq message invalid"); |
319 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); | |
4ef946dd | 320 | authorities->destroy(authorities); |
e6f3ef13 | 321 | return NEED_MORE; |
3e962b08 | 322 | } |
4ef946dd | 323 | id = identification_create_from_encoding(ID_DER_ASN1_DN, data); |
0b71bc7a MW |
324 | cert = lib->credmgr->get_cert(lib->credmgr, |
325 | CERT_X509, KEY_ANY, id, TRUE); | |
3ddd164e MW |
326 | if (cert) |
327 | { | |
3c19b346 | 328 | DBG1(DBG_TLS, "received TLS cert request for '%Y", id); |
3ddd164e MW |
329 | this->peer_auth->add(this->peer_auth, AUTH_RULE_CA_CERT, cert); |
330 | } | |
331 | else | |
332 | { | |
3c19b346 | 333 | DBG1(DBG_TLS, "received TLS cert request for unknown CA '%Y'", id); |
3ddd164e | 334 | } |
3e962b08 | 335 | id->destroy(id); |
3e962b08 | 336 | } |
4ef946dd | 337 | authorities->destroy(authorities); |
8fef06a6 | 338 | this->state = STATE_CERTREQ_RECEIVED; |
3e962b08 MW |
339 | return NEED_MORE; |
340 | } | |
341 | ||
3ddd164e MW |
342 | /** |
343 | * Process Hello Done message | |
344 | */ | |
345 | static status_t process_hello_done(private_tls_peer_t *this, | |
346 | tls_reader_t *reader) | |
347 | { | |
84d67ead MW |
348 | this->crypto->append_handshake(this->crypto, |
349 | TLS_SERVER_HELLO_DONE, reader->peek(reader)); | |
3ddd164e MW |
350 | this->state = STATE_HELLO_DONE; |
351 | return NEED_MORE; | |
352 | } | |
353 | ||
f139b578 MW |
354 | /** |
355 | * Process finished message | |
356 | */ | |
357 | static status_t process_finished(private_tls_peer_t *this, tls_reader_t *reader) | |
358 | { | |
84d67ead MW |
359 | chunk_t received; |
360 | char buf[12]; | |
110364b0 | 361 | |
84d67ead | 362 | if (!reader->read_data(reader, sizeof(buf), &received)) |
110364b0 | 363 | { |
3c19b346 | 364 | DBG1(DBG_TLS, "received server finished too short"); |
e6f3ef13 MW |
365 | this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); |
366 | return NEED_MORE; | |
110364b0 | 367 | } |
84d67ead | 368 | if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) |
110364b0 | 369 | { |
3c19b346 | 370 | DBG1(DBG_TLS, "calculating server finished failed"); |
e6f3ef13 MW |
371 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
372 | return NEED_MORE; | |
110364b0 | 373 | } |
84d67ead | 374 | if (!chunk_equals(received, chunk_from_thing(buf))) |
110364b0 | 375 | { |
3c19b346 | 376 | DBG1(DBG_TLS, "received server finished invalid"); |
e6f3ef13 MW |
377 | this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); |
378 | return NEED_MORE; | |
110364b0 MW |
379 | } |
380 | this->state = STATE_COMPLETE; | |
84d67ead MW |
381 | this->crypto->derive_eap_msk(this->crypto, |
382 | chunk_from_thing(this->client_random), | |
383 | chunk_from_thing(this->server_random)); | |
110364b0 | 384 | return NEED_MORE; |
f139b578 MW |
385 | } |
386 | ||
4c0c2283 | 387 | METHOD(tls_handshake_t, process, status_t, |
4ef946dd | 388 | private_tls_peer_t *this, tls_handshake_type_t type, tls_reader_t *reader) |
4c0c2283 | 389 | { |
8fef06a6 MW |
390 | tls_handshake_type_t expected; |
391 | ||
3e962b08 MW |
392 | switch (this->state) |
393 | { | |
394 | case STATE_HELLO_SENT: | |
8fef06a6 MW |
395 | if (type == TLS_SERVER_HELLO) |
396 | { | |
397 | return process_server_hello(this, reader); | |
398 | } | |
399 | expected = TLS_SERVER_HELLO; | |
400 | break; | |
401 | case STATE_HELLO_RECEIVED: | |
402 | if (type == TLS_CERTIFICATE) | |
3e962b08 | 403 | { |
8fef06a6 | 404 | return process_certificate(this, reader); |
3e962b08 | 405 | } |
8fef06a6 MW |
406 | expected = TLS_CERTIFICATE; |
407 | break; | |
408 | case STATE_CERT_RECEIVED: | |
409 | if (type == TLS_CERTIFICATE_REQUEST) | |
410 | { | |
411 | return process_certreq(this, reader); | |
412 | } | |
69e8bb2e | 413 | this->peer = NULL; |
b4d30a42 | 414 | /* fall through since TLS_CERTIFICATE_REQUEST is optional */ |
8fef06a6 MW |
415 | case STATE_CERTREQ_RECEIVED: |
416 | if (type == TLS_SERVER_HELLO_DONE) | |
417 | { | |
418 | return process_hello_done(this, reader); | |
419 | } | |
420 | expected = TLS_SERVER_HELLO_DONE; | |
3e962b08 | 421 | break; |
f139b578 | 422 | case STATE_CIPHERSPEC_CHANGED_IN: |
8fef06a6 | 423 | if (type == TLS_FINISHED) |
f139b578 | 424 | { |
8fef06a6 | 425 | return process_finished(this, reader); |
f139b578 | 426 | } |
8fef06a6 | 427 | expected = TLS_FINISHED; |
f139b578 | 428 | break; |
3e962b08 | 429 | default: |
3c19b346 | 430 | DBG1(DBG_TLS, "TLS %N not expected in current state", |
8fef06a6 | 431 | tls_handshake_type_names, type); |
e6f3ef13 MW |
432 | this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); |
433 | return NEED_MORE; | |
3e962b08 | 434 | } |
3c19b346 | 435 | DBG1(DBG_TLS, "TLS %N expected, but received %N", |
8fef06a6 | 436 | tls_handshake_type_names, expected, tls_handshake_type_names, type); |
e6f3ef13 MW |
437 | this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); |
438 | return NEED_MORE; | |
4c0c2283 MW |
439 | } |
440 | ||
698674c7 MW |
441 | /** |
442 | * Send a client hello | |
443 | */ | |
8fef06a6 MW |
444 | static status_t send_client_hello(private_tls_peer_t *this, |
445 | tls_handshake_type_t *type, tls_writer_t *writer) | |
698674c7 | 446 | { |
7ea87db0 AS |
447 | tls_cipher_suite_t *suites; |
448 | tls_version_t version; | |
3a1640de | 449 | int count, i; |
698674c7 MW |
450 | rng_t *rng; |
451 | ||
18010de2 | 452 | htoun32(&this->client_random, time(NULL)); |
698674c7 MW |
453 | rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); |
454 | if (!rng) | |
455 | { | |
e6f3ef13 MW |
456 | DBG1(DBG_TLS, "no suitable RNG found to generate client random"); |
457 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); | |
458 | return NEED_MORE; | |
698674c7 | 459 | } |
18010de2 | 460 | rng->get_bytes(rng, sizeof(this->client_random) - 4, this->client_random + 4); |
3a1640de MW |
461 | rng->destroy(rng); |
462 | ||
7ea87db0 AS |
463 | /* TLS version */ |
464 | version = this->tls->get_version(this->tls); | |
7ea87db0 | 465 | writer->write_uint16(writer, version); |
18010de2 | 466 | writer->write_data(writer, chunk_from_thing(this->client_random)); |
7ea87db0 | 467 | |
3a1640de MW |
468 | /* session identifier => none */ |
469 | writer->write_data8(writer, chunk_empty); | |
470 | ||
7ea87db0 AS |
471 | /* add TLS cipher suites */ |
472 | count = this->crypto->get_cipher_suites(this->crypto, &suites); | |
3a1640de MW |
473 | writer->write_uint16(writer, count * 2); |
474 | for (i = 0; i < count; i++) | |
475 | { | |
7ea87db0 | 476 | writer->write_uint16(writer, suites[i]); |
3a1640de | 477 | } |
7ea87db0 | 478 | |
3a1640de MW |
479 | /* NULL compression only */ |
480 | writer->write_uint8(writer, 1); | |
481 | writer->write_uint8(writer, 0); | |
482 | ||
483 | *type = TLS_CLIENT_HELLO; | |
3e962b08 | 484 | this->state = STATE_HELLO_SENT; |
84d67ead | 485 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
3ddd164e MW |
486 | return NEED_MORE; |
487 | } | |
488 | ||
dbb7c030 MW |
489 | /** |
490 | * Find a private key suitable to sign Certificate Verify | |
491 | */ | |
492 | static private_key_t *find_private_key(private_tls_peer_t *this) | |
493 | { | |
494 | private_key_t *key = NULL; | |
495 | tls_reader_t *reader; | |
496 | key_type_t type; | |
497 | u_int8_t cert; | |
498 | ||
499 | if (!this->peer) | |
500 | { | |
501 | return NULL; | |
502 | } | |
503 | reader = tls_reader_create(this->cert_types); | |
504 | while (reader->remaining(reader) && reader->read_uint8(reader, &cert)) | |
505 | { | |
506 | switch (cert) | |
507 | { | |
508 | case TLS_RSA_SIGN: | |
509 | type = KEY_RSA; | |
510 | break; | |
511 | case TLS_ECDSA_SIGN: | |
512 | type = KEY_ECDSA; | |
513 | break; | |
514 | default: | |
515 | continue; | |
516 | } | |
517 | key = lib->credmgr->get_private(lib->credmgr, type, | |
518 | this->peer, this->peer_auth); | |
519 | if (key) | |
520 | { | |
521 | break; | |
522 | } | |
523 | } | |
524 | reader->destroy(reader); | |
525 | return key; | |
526 | } | |
527 | ||
3ddd164e MW |
528 | /** |
529 | * Send Certificate | |
530 | */ | |
531 | static status_t send_certificate(private_tls_peer_t *this, | |
532 | tls_handshake_type_t *type, tls_writer_t *writer) | |
533 | { | |
534 | enumerator_t *enumerator; | |
535 | certificate_t *cert; | |
536 | auth_rule_t rule; | |
537 | tls_writer_t *certs; | |
538 | chunk_t data; | |
539 | ||
dbb7c030 | 540 | this->private = find_private_key(this); |
3ddd164e MW |
541 | if (!this->private) |
542 | { | |
3c19b346 | 543 | DBG1(DBG_TLS, "no TLS peer certificate found for '%Y'", this->peer); |
a596006e | 544 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
69e8bb2e | 545 | return FAILED; |
3ddd164e MW |
546 | } |
547 | ||
548 | /* generate certificate payload */ | |
549 | certs = tls_writer_create(256); | |
550 | cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT); | |
551 | if (cert) | |
552 | { | |
0b71bc7a MW |
553 | if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) |
554 | { | |
3c19b346 | 555 | DBG1(DBG_TLS, "sending TLS peer certificate '%Y'", |
0b71bc7a MW |
556 | cert->get_subject(cert)); |
557 | certs->write_data24(certs, data); | |
558 | free(data.ptr); | |
559 | } | |
3ddd164e MW |
560 | } |
561 | enumerator = this->peer_auth->create_enumerator(this->peer_auth); | |
562 | while (enumerator->enumerate(enumerator, &rule, &cert)) | |
563 | { | |
564 | if (rule == AUTH_RULE_IM_CERT) | |
565 | { | |
0b71bc7a MW |
566 | if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) |
567 | { | |
3c19b346 | 568 | DBG1(DBG_TLS, "sending TLS intermediate certificate '%Y'", |
0b71bc7a MW |
569 | cert->get_subject(cert)); |
570 | certs->write_data24(certs, data); | |
571 | free(data.ptr); | |
572 | } | |
3ddd164e MW |
573 | } |
574 | } | |
575 | enumerator->destroy(enumerator); | |
576 | ||
577 | writer->write_data24(writer, certs->get_buf(certs)); | |
578 | certs->destroy(certs); | |
579 | ||
580 | *type = TLS_CERTIFICATE; | |
581 | this->state = STATE_CERT_SENT; | |
84d67ead | 582 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
3ddd164e MW |
583 | return NEED_MORE; |
584 | } | |
585 | ||
586 | /** | |
587 | * Send client key exchange | |
588 | */ | |
589 | static status_t send_key_exchange(private_tls_peer_t *this, | |
590 | tls_handshake_type_t *type, tls_writer_t *writer) | |
591 | { | |
592 | public_key_t *public = NULL, *current; | |
e85bca7f | 593 | certificate_t *cert; |
3ddd164e MW |
594 | enumerator_t *enumerator; |
595 | auth_cfg_t *auth; | |
596 | rng_t *rng; | |
597 | char premaster[48]; | |
598 | chunk_t encrypted; | |
599 | ||
600 | rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); | |
601 | if (!rng) | |
602 | { | |
3c19b346 | 603 | DBG1(DBG_TLS, "no suitable RNG found for TLS premaster secret"); |
a596006e | 604 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
3ddd164e MW |
605 | return FAILED; |
606 | } | |
607 | rng->get_bytes(rng, sizeof(premaster) - 2, premaster + 2); | |
608 | rng->destroy(rng); | |
609 | htoun16(premaster, TLS_1_2); | |
610 | ||
84d67ead MW |
611 | this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster), |
612 | chunk_from_thing(this->client_random), | |
613 | chunk_from_thing(this->server_random)); | |
18010de2 | 614 | |
e85bca7f MW |
615 | cert = this->server_auth->get(this->server_auth, AUTH_HELPER_SUBJECT_CERT); |
616 | if (cert) | |
3ddd164e | 617 | { |
e85bca7f MW |
618 | enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, |
619 | KEY_ANY, cert->get_subject(cert), this->server_auth); | |
620 | while (enumerator->enumerate(enumerator, ¤t, &auth)) | |
621 | { | |
622 | public = current->get_ref(current); | |
623 | break; | |
624 | } | |
625 | enumerator->destroy(enumerator); | |
3ddd164e | 626 | } |
3ddd164e MW |
627 | if (!public) |
628 | { | |
3c19b346 | 629 | DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); |
a596006e | 630 | this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); |
3ddd164e MW |
631 | return FAILED; |
632 | } | |
33ddaaab MW |
633 | if (!public->encrypt(public, ENCRYPT_RSA_PKCS1, |
634 | chunk_from_thing(premaster), &encrypted)) | |
3ddd164e MW |
635 | { |
636 | public->destroy(public); | |
3c19b346 | 637 | DBG1(DBG_TLS, "encrypting TLS premaster secret failed"); |
a596006e | 638 | this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE); |
3ddd164e MW |
639 | return FAILED; |
640 | } | |
400df4ca | 641 | |
3ddd164e MW |
642 | public->destroy(public); |
643 | ||
644 | writer->write_data16(writer, encrypted); | |
645 | free(encrypted.ptr); | |
646 | ||
647 | *type = TLS_CLIENT_KEY_EXCHANGE; | |
648 | this->state = STATE_KEY_EXCHANGE_SENT; | |
84d67ead | 649 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
3ddd164e MW |
650 | return NEED_MORE; |
651 | } | |
652 | ||
653 | /** | |
654 | * Send certificate verify | |
655 | */ | |
656 | static status_t send_certificate_verify(private_tls_peer_t *this, | |
657 | tls_handshake_type_t *type, tls_writer_t *writer) | |
658 | { | |
84d67ead | 659 | if (!this->private || |
dbb7c030 MW |
660 | !this->crypto->sign_handshake(this->crypto, this->private, |
661 | writer, this->hashsig)) | |
3ddd164e | 662 | { |
3c19b346 | 663 | DBG1(DBG_TLS, "creating TLS Certificate Verify signature failed"); |
a596006e | 664 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
3ddd164e MW |
665 | return FAILED; |
666 | } | |
3ddd164e MW |
667 | |
668 | *type = TLS_CERTIFICATE_VERIFY; | |
669 | this->state = STATE_VERIFY_SENT; | |
84d67ead | 670 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
3ddd164e MW |
671 | return NEED_MORE; |
672 | } | |
673 | ||
674 | /** | |
675 | * Send Finished | |
676 | */ | |
677 | static status_t send_finished(private_tls_peer_t *this, | |
678 | tls_handshake_type_t *type, tls_writer_t *writer) | |
679 | { | |
84d67ead | 680 | char buf[12]; |
18010de2 | 681 | |
84d67ead | 682 | if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) |
18010de2 | 683 | { |
3c19b346 | 684 | DBG1(DBG_TLS, "calculating client finished data failed"); |
a596006e | 685 | this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); |
18010de2 MW |
686 | return FAILED; |
687 | } | |
18010de2 | 688 | |
84d67ead | 689 | writer->write_data(writer, chunk_from_thing(buf)); |
18010de2 | 690 | |
3ddd164e MW |
691 | *type = TLS_FINISHED; |
692 | this->state = STATE_FINISHED_SENT; | |
84d67ead | 693 | this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); |
698674c7 MW |
694 | return NEED_MORE; |
695 | } | |
696 | ||
4c0c2283 | 697 | METHOD(tls_handshake_t, build, status_t, |
3a1640de | 698 | private_tls_peer_t *this, tls_handshake_type_t *type, tls_writer_t *writer) |
4c0c2283 | 699 | { |
698674c7 MW |
700 | switch (this->state) |
701 | { | |
702 | case STATE_INIT: | |
8fef06a6 | 703 | return send_client_hello(this, type, writer); |
3ddd164e | 704 | case STATE_HELLO_DONE: |
69e8bb2e | 705 | if (this->peer) |
b4d30a42 AS |
706 | { |
707 | return send_certificate(this, type, writer); | |
708 | } | |
709 | /* otherwise fall through to next state */ | |
3ddd164e MW |
710 | case STATE_CERT_SENT: |
711 | return send_key_exchange(this, type, writer); | |
712 | case STATE_KEY_EXCHANGE_SENT: | |
69e8bb2e | 713 | if (this->peer) |
b4d30a42 AS |
714 | { |
715 | return send_certificate_verify(this, type, writer); | |
716 | } | |
717 | else | |
718 | { | |
719 | return INVALID_STATE; | |
720 | } | |
f139b578 | 721 | case STATE_CIPHERSPEC_CHANGED_OUT: |
3ddd164e | 722 | return send_finished(this, type, writer); |
698674c7 MW |
723 | default: |
724 | return INVALID_STATE; | |
725 | } | |
4c0c2283 MW |
726 | } |
727 | ||
3ddd164e MW |
728 | METHOD(tls_handshake_t, cipherspec_changed, bool, |
729 | private_tls_peer_t *this) | |
730 | { | |
69e8bb2e MW |
731 | if ((this->peer && this->state == STATE_VERIFY_SENT) || |
732 | (!this->peer && this->state == STATE_KEY_EXCHANGE_SENT)) | |
3ddd164e | 733 | { |
84543e6e | 734 | this->crypto->change_cipher(this->crypto, FALSE); |
f139b578 | 735 | this->state = STATE_CIPHERSPEC_CHANGED_OUT; |
3ddd164e MW |
736 | return TRUE; |
737 | } | |
738 | return FALSE; | |
739 | } | |
740 | ||
f139b578 | 741 | METHOD(tls_handshake_t, change_cipherspec, bool, |
3ddd164e MW |
742 | private_tls_peer_t *this) |
743 | { | |
f139b578 MW |
744 | if (this->state == STATE_FINISHED_SENT) |
745 | { | |
746 | this->crypto->change_cipher(this->crypto, TRUE); | |
747 | this->state = STATE_CIPHERSPEC_CHANGED_IN; | |
748 | return TRUE; | |
749 | } | |
750 | return FALSE; | |
3ddd164e MW |
751 | } |
752 | ||
1327839d AS |
753 | METHOD(tls_handshake_t, finished, bool, |
754 | private_tls_peer_t *this) | |
755 | { | |
756 | return this->state == STATE_COMPLETE; | |
757 | } | |
758 | ||
4c0c2283 MW |
759 | METHOD(tls_handshake_t, destroy, void, |
760 | private_tls_peer_t *this) | |
761 | { | |
3ddd164e | 762 | DESTROY_IF(this->private); |
3ddd164e MW |
763 | this->peer_auth->destroy(this->peer_auth); |
764 | this->server_auth->destroy(this->server_auth); | |
dbb7c030 MW |
765 | free(this->hashsig.ptr); |
766 | free(this->cert_types.ptr); | |
4c0c2283 MW |
767 | free(this); |
768 | } | |
769 | ||
770 | /** | |
771 | * See header | |
772 | */ | |
e6f3ef13 | 773 | tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert, |
3ddd164e | 774 | identification_t *peer, identification_t *server) |
4c0c2283 MW |
775 | { |
776 | private_tls_peer_t *this; | |
777 | ||
778 | INIT(this, | |
ba31fe1f MW |
779 | .public = { |
780 | .handshake = { | |
781 | .process = _process, | |
782 | .build = _build, | |
783 | .cipherspec_changed = _cipherspec_changed, | |
784 | .change_cipherspec = _change_cipherspec, | |
785 | .finished = _finished, | |
786 | .destroy = _destroy, | |
787 | }, | |
4c0c2283 | 788 | }, |
698674c7 | 789 | .state = STATE_INIT, |
3e962b08 | 790 | .tls = tls, |
536dbc00 | 791 | .crypto = crypto, |
e6f3ef13 | 792 | .alert = alert, |
3ddd164e MW |
793 | .peer = peer, |
794 | .server = server, | |
795 | .peer_auth = auth_cfg_create(), | |
796 | .server_auth = auth_cfg_create(), | |
4c0c2283 MW |
797 | ); |
798 | ||
799 | return &this->public; | |
800 | } |