]>
Commit | Line | Data |
---|---|---|
f7f63c52 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.h" | |
17 | ||
f05b4272 | 18 | #include <utils/debug.h> |
14758000 | 19 | |
40e384ea MW |
20 | #include "tls_protection.h" |
21 | #include "tls_compression.h" | |
22 | #include "tls_fragmentation.h" | |
536dbc00 | 23 | #include "tls_crypto.h" |
4c0c2283 MW |
24 | #include "tls_server.h" |
25 | #include "tls_peer.h" | |
40e384ea | 26 | |
3e962b08 MW |
27 | ENUM_BEGIN(tls_version_names, SSL_2_0, SSL_2_0, |
28 | "SSLv2"); | |
29 | ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_2, SSL_2_0, | |
f7f63c52 MW |
30 | "SSLv3", |
31 | "TLS 1.0", | |
32 | "TLS 1.1", | |
3e962b08 MW |
33 | "TLS 1.2"); |
34 | ENUM_END(tls_version_names, TLS_1_2); | |
f7f63c52 MW |
35 | |
36 | ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA, | |
37 | "ChangeCipherSpec", | |
38 | "Alert", | |
39 | "Handshake", | |
40 | "ApplicationData", | |
41 | ); | |
42 | ||
43 | ENUM_BEGIN(tls_handshake_type_names, TLS_HELLO_REQUEST, TLS_SERVER_HELLO, | |
44 | "HelloRequest", | |
45 | "ClientHello", | |
46 | "ServerHello"); | |
6cf85b35 MW |
47 | ENUM_NEXT(tls_handshake_type_names, |
48 | TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_SERVER_HELLO, | |
f7f63c52 MW |
49 | "Certificate", |
50 | "ServerKeyExchange", | |
51 | "CertificateRequest", | |
52 | "ServerHelloDone", | |
53 | "CertificateVerify", | |
54 | "ClientKeyExchange"); | |
6cf85b35 MW |
55 | ENUM_NEXT(tls_handshake_type_names, |
56 | TLS_FINISHED, TLS_FINISHED, TLS_CLIENT_KEY_EXCHANGE, | |
f7f63c52 MW |
57 | "Finished"); |
58 | ENUM_END(tls_handshake_type_names, TLS_FINISHED); | |
dcbbeb2d | 59 | |
6cf85b35 MW |
60 | ENUM_BEGIN(tls_extension_names, TLS_EXT_SERVER_NAME, TLS_EXT_STATUS_REQUEST, |
61 | "server name", | |
62 | "max fragment length", | |
63 | "client certificate url", | |
64 | "trusted ca keys", | |
65 | "truncated hmac", | |
66 | "status request"); | |
67 | ENUM_NEXT(tls_extension_names, | |
68 | TLS_EXT_ELLIPTIC_CURVES, TLS_EXT_EC_POINT_FORMATS, | |
69 | TLS_EXT_STATUS_REQUEST, | |
37a59a8f | 70 | "elliptic curves", |
6cf85b35 MW |
71 | "ec point formats"); |
72 | ENUM_NEXT(tls_extension_names, | |
73 | TLS_EXT_SIGNATURE_ALGORITHMS, TLS_EXT_SIGNATURE_ALGORITHMS, | |
74 | TLS_EXT_EC_POINT_FORMATS, | |
75 | "signature algorithms"); | |
a9ee43e9 AS |
76 | ENUM_NEXT(tls_extension_names, |
77 | TLS_EXT_RENEGOTIATION_INFO, TLS_EXT_RENEGOTIATION_INFO, | |
78 | TLS_EXT_SIGNATURE_ALGORITHMS, | |
79 | "renegotiation info"); | |
80 | ENUM_END(tls_extension_names, TLS_EXT_RENEGOTIATION_INFO); | |
731611c5 | 81 | |
743f9406 MW |
82 | /** |
83 | * TLS record | |
84 | */ | |
85 | typedef struct __attribute__((packed)) { | |
b12c53ce AS |
86 | uint8_t type; |
87 | uint16_t version; | |
88 | uint16_t length; | |
743f9406 MW |
89 | char data[]; |
90 | } tls_record_t; | |
dcbbeb2d MW |
91 | |
92 | typedef struct private_tls_t private_tls_t; | |
93 | ||
94 | /** | |
95 | * Private data of an tls_protection_t object. | |
96 | */ | |
97 | struct private_tls_t { | |
98 | ||
99 | /** | |
100 | * Public tls_t interface. | |
101 | */ | |
102 | tls_t public; | |
103 | ||
104 | /** | |
105 | * Role this TLS stack acts as. | |
106 | */ | |
107 | bool is_server; | |
40e384ea | 108 | |
3e962b08 MW |
109 | /** |
110 | * Negotiated TLS version | |
111 | */ | |
112 | tls_version_t version; | |
113 | ||
96b2fbcc MW |
114 | /** |
115 | * TLS stack purpose, as given to constructor | |
116 | */ | |
117 | tls_purpose_t purpose; | |
118 | ||
40e384ea MW |
119 | /** |
120 | * TLS record protection layer | |
121 | */ | |
122 | tls_protection_t *protection; | |
123 | ||
124 | /** | |
125 | * TLS record compression layer | |
126 | */ | |
127 | tls_compression_t *compression; | |
128 | ||
129 | /** | |
130 | * TLS record fragmentation layer | |
131 | */ | |
132 | tls_fragmentation_t *fragmentation; | |
4c0c2283 | 133 | |
e6f3ef13 MW |
134 | /** |
135 | * TLS alert handler | |
136 | */ | |
137 | tls_alert_t *alert; | |
138 | ||
536dbc00 MW |
139 | /** |
140 | * TLS crypto helper context | |
141 | */ | |
142 | tls_crypto_t *crypto; | |
143 | ||
4c0c2283 MW |
144 | /** |
145 | * TLS handshake protocol handler | |
146 | */ | |
147 | tls_handshake_t *handshake; | |
1327839d AS |
148 | |
149 | /** | |
150 | * TLS application data handler | |
151 | */ | |
152 | tls_application_t *application; | |
ce1af739 MW |
153 | |
154 | /** | |
155 | * Allocated input buffer | |
156 | */ | |
157 | chunk_t input; | |
158 | ||
159 | /** | |
160 | * Number of bytes read in input buffer | |
161 | */ | |
162 | size_t inpos; | |
ecd98efa MW |
163 | |
164 | /** | |
165 | * Allocated output buffer | |
166 | */ | |
167 | chunk_t output; | |
168 | ||
169 | /** | |
170 | * Number of bytes processed from output buffer | |
171 | */ | |
172 | size_t outpos; | |
dcbbeb2d | 173 | |
743f9406 | 174 | /** |
e2bf45a4 | 175 | * Position in partially received record header |
743f9406 | 176 | */ |
e2bf45a4 | 177 | size_t headpos; |
743f9406 MW |
178 | |
179 | /** | |
e2bf45a4 | 180 | * Partial TLS record header received |
743f9406 | 181 | */ |
e2bf45a4 | 182 | tls_record_t head; |
743f9406 | 183 | }; |
14758000 | 184 | |
e7cb8f9b AS |
185 | /** |
186 | * Described in header. | |
187 | */ | |
188 | void libtls_init(void) | |
189 | { | |
190 | /* empty */ | |
191 | } | |
192 | ||
dcbbeb2d | 193 | METHOD(tls_t, process, status_t, |
ecd98efa | 194 | private_tls_t *this, void *buf, size_t buflen) |
dcbbeb2d | 195 | { |
14758000 | 196 | tls_record_t *record; |
14758000 | 197 | status_t status; |
ce1af739 | 198 | u_int len; |
14758000 | 199 | |
743f9406 MW |
200 | if (this->headpos) |
201 | { /* have a partial TLS record header, try to complete it */ | |
202 | len = min(buflen, sizeof(this->head) - this->headpos); | |
203 | memcpy(((char*)&this->head) + this->headpos, buf, len); | |
204 | this->headpos += len; | |
205 | buflen -= len; | |
206 | buf += len; | |
207 | if (this->headpos == sizeof(this->head)) | |
208 | { /* header complete, allocate space with new header */ | |
209 | len = untoh16(&this->head.length); | |
210 | this->input = chunk_alloc(len + sizeof(tls_record_t)); | |
211 | memcpy(this->input.ptr, &this->head, sizeof(this->head)); | |
212 | this->inpos = sizeof(this->head); | |
213 | this->headpos = 0; | |
214 | } | |
215 | } | |
216 | ||
ecd98efa | 217 | while (buflen) |
14758000 | 218 | { |
ce1af739 | 219 | if (this->input.len == 0) |
14758000 | 220 | { |
f9349750 | 221 | while (buflen >= sizeof(tls_record_t)) |
ce1af739 MW |
222 | { |
223 | /* try to process records inline */ | |
ecd98efa | 224 | record = buf; |
ce1af739 MW |
225 | len = untoh16(&record->length); |
226 | ||
ecd98efa | 227 | if (len + sizeof(tls_record_t) > buflen) |
ce1af739 MW |
228 | { /* not a full record, read to buffer */ |
229 | this->input = chunk_alloc(len + sizeof(tls_record_t)); | |
230 | this->inpos = 0; | |
231 | break; | |
232 | } | |
233 | DBG2(DBG_TLS, "processing TLS %N record (%d bytes)", | |
234 | tls_content_type_names, record->type, len); | |
235 | status = this->protection->process(this->protection, | |
236 | record->type, chunk_create(record->data, len)); | |
237 | if (status != NEED_MORE) | |
238 | { | |
239 | return status; | |
240 | } | |
ecd98efa MW |
241 | buf += len + sizeof(tls_record_t); |
242 | buflen -= len + sizeof(tls_record_t); | |
243 | if (buflen == 0) | |
ce1af739 MW |
244 | { |
245 | return NEED_MORE; | |
246 | } | |
247 | } | |
f9349750 MW |
248 | if (buflen < sizeof(tls_record_t)) |
249 | { | |
250 | DBG2(DBG_TLS, "received incomplete TLS record header"); | |
251 | memcpy(&this->head, buf, buflen); | |
252 | this->headpos = buflen; | |
253 | break; | |
254 | } | |
14758000 | 255 | } |
ecd98efa MW |
256 | len = min(buflen, this->input.len - this->inpos); |
257 | memcpy(this->input.ptr + this->inpos, buf, len); | |
258 | buf += len; | |
259 | buflen -= len; | |
ce1af739 | 260 | this->inpos += len; |
5fb1311b | 261 | DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte TLS record received", |
ce1af739 MW |
262 | len, this->inpos, this->input.len); |
263 | if (this->input.len == this->inpos) | |
14758000 | 264 | { |
ce1af739 MW |
265 | record = (tls_record_t*)this->input.ptr; |
266 | len = untoh16(&record->length); | |
267 | ||
268 | DBG2(DBG_TLS, "processing buffered TLS %N record (%d bytes)", | |
269 | tls_content_type_names, record->type, len); | |
270 | status = this->protection->process(this->protection, | |
271 | record->type, chunk_create(record->data, len)); | |
272 | chunk_free(&this->input); | |
273 | this->inpos = 0; | |
274 | if (status != NEED_MORE) | |
275 | { | |
276 | return status; | |
277 | } | |
14758000 | 278 | } |
14758000 | 279 | } |
14758000 | 280 | return NEED_MORE; |
dcbbeb2d MW |
281 | } |
282 | ||
283 | METHOD(tls_t, build, status_t, | |
ecd98efa | 284 | private_tls_t *this, void *buf, size_t *buflen, size_t *msglen) |
dcbbeb2d | 285 | { |
ecd98efa | 286 | tls_content_type_t type; |
14758000 MW |
287 | tls_record_t record; |
288 | status_t status; | |
ecd98efa MW |
289 | chunk_t data; |
290 | size_t len; | |
14758000 | 291 | |
ecd98efa MW |
292 | len = *buflen; |
293 | if (this->output.len == 0) | |
14758000 | 294 | { |
ecd98efa MW |
295 | /* query upper layers for new records, as many as we can get */ |
296 | while (TRUE) | |
14758000 | 297 | { |
ecd98efa MW |
298 | status = this->protection->build(this->protection, &type, &data); |
299 | switch (status) | |
300 | { | |
301 | case NEED_MORE: | |
302 | record.type = type; | |
303 | htoun16(&record.version, this->version); | |
304 | htoun16(&record.length, data.len); | |
305 | this->output = chunk_cat("mcm", this->output, | |
306 | chunk_from_thing(record), data); | |
307 | DBG2(DBG_TLS, "sending TLS %N record (%d bytes)", | |
308 | tls_content_type_names, type, data.len); | |
309 | continue; | |
310 | case INVALID_STATE: | |
311 | if (this->output.len == 0) | |
312 | { | |
313 | return INVALID_STATE; | |
314 | } | |
315 | break; | |
316 | default: | |
317 | return status; | |
318 | } | |
319 | break; | |
320 | } | |
321 | if (msglen) | |
322 | { | |
323 | *msglen = this->output.len; | |
14758000 | 324 | } |
14758000 | 325 | } |
ecd98efa MW |
326 | else |
327 | { | |
ecd98efa MW |
328 | if (msglen) |
329 | { | |
330 | *msglen = 0; | |
331 | } | |
332 | } | |
333 | len = min(len, this->output.len - this->outpos); | |
334 | memcpy(buf, this->output.ptr + this->outpos, len); | |
335 | this->outpos += len; | |
336 | *buflen = len; | |
337 | if (this->outpos == this->output.len) | |
338 | { | |
339 | chunk_free(&this->output); | |
340 | this->outpos = 0; | |
341 | return ALREADY_DONE; | |
342 | } | |
343 | return NEED_MORE; | |
dcbbeb2d MW |
344 | } |
345 | ||
84543e6e MW |
346 | METHOD(tls_t, is_server, bool, |
347 | private_tls_t *this) | |
348 | { | |
349 | return this->is_server; | |
350 | } | |
351 | ||
bd1ee5bd AS |
352 | METHOD(tls_t, get_server_id, identification_t*, |
353 | private_tls_t *this) | |
354 | { | |
2de481e3 | 355 | return this->handshake->get_server_id(this->handshake); |
bd1ee5bd AS |
356 | } |
357 | ||
358 | METHOD(tls_t, get_peer_id, identification_t*, | |
359 | private_tls_t *this) | |
360 | { | |
2de481e3 | 361 | return this->handshake->get_peer_id(this->handshake); |
bd1ee5bd AS |
362 | } |
363 | ||
3e962b08 MW |
364 | METHOD(tls_t, get_version, tls_version_t, |
365 | private_tls_t *this) | |
366 | { | |
367 | return this->version; | |
368 | } | |
369 | ||
f154e304 | 370 | METHOD(tls_t, set_version, bool, |
3e962b08 MW |
371 | private_tls_t *this, tls_version_t version) |
372 | { | |
f154e304 MW |
373 | if (version > this->version) |
374 | { | |
375 | return FALSE; | |
376 | } | |
377 | switch (version) | |
378 | { | |
379 | case TLS_1_0: | |
380 | case TLS_1_1: | |
381 | case TLS_1_2: | |
382 | this->version = version; | |
e6f3ef13 | 383 | this->protection->set_version(this->protection, version); |
f154e304 MW |
384 | return TRUE; |
385 | case SSL_2_0: | |
386 | case SSL_3_0: | |
387 | default: | |
388 | return FALSE; | |
389 | } | |
3e962b08 MW |
390 | } |
391 | ||
96b2fbcc MW |
392 | METHOD(tls_t, get_purpose, tls_purpose_t, |
393 | private_tls_t *this) | |
394 | { | |
395 | return this->purpose; | |
396 | } | |
397 | ||
400df4ca MW |
398 | METHOD(tls_t, is_complete, bool, |
399 | private_tls_t *this) | |
400 | { | |
c5142f11 MW |
401 | if (this->handshake->finished(this->handshake)) |
402 | { | |
403 | if (!this->application) | |
404 | { | |
405 | return TRUE; | |
406 | } | |
407 | return this->fragmentation->application_finished(this->fragmentation); | |
408 | } | |
409 | return FALSE; | |
400df4ca MW |
410 | } |
411 | ||
51313a39 MW |
412 | METHOD(tls_t, get_eap_msk, chunk_t, |
413 | private_tls_t *this) | |
414 | { | |
415 | return this->crypto->get_eap_msk(this->crypto); | |
416 | } | |
417 | ||
666c5523 MW |
418 | METHOD(tls_t, get_auth, auth_cfg_t*, |
419 | private_tls_t *this) | |
420 | { | |
421 | return this->handshake->get_auth(this->handshake); | |
422 | } | |
423 | ||
dcbbeb2d MW |
424 | METHOD(tls_t, destroy, void, |
425 | private_tls_t *this) | |
426 | { | |
40e384ea MW |
427 | this->protection->destroy(this->protection); |
428 | this->compression->destroy(this->compression); | |
429 | this->fragmentation->destroy(this->fragmentation); | |
536dbc00 | 430 | this->crypto->destroy(this->crypto); |
4c0c2283 | 431 | this->handshake->destroy(this->handshake); |
1327839d | 432 | DESTROY_IF(this->application); |
e6f3ef13 | 433 | this->alert->destroy(this->alert); |
40e384ea | 434 | |
ce1af739 | 435 | free(this->input.ptr); |
ecd98efa | 436 | free(this->output.ptr); |
ce1af739 | 437 | |
dcbbeb2d MW |
438 | free(this); |
439 | } | |
440 | ||
441 | /** | |
442 | * See header | |
443 | */ | |
3ddd164e | 444 | tls_t *tls_create(bool is_server, identification_t *server, |
96b2fbcc | 445 | identification_t *peer, tls_purpose_t purpose, |
6a5c86b7 | 446 | tls_application_t *application, tls_cache_t *cache) |
dcbbeb2d MW |
447 | { |
448 | private_tls_t *this; | |
449 | ||
96b2fbcc MW |
450 | switch (purpose) |
451 | { | |
452 | case TLS_PURPOSE_EAP_TLS: | |
453 | case TLS_PURPOSE_EAP_TTLS: | |
1bee89d3 | 454 | case TLS_PURPOSE_EAP_PEAP: |
17102f7b | 455 | case TLS_PURPOSE_GENERIC: |
ddf52220 | 456 | case TLS_PURPOSE_GENERIC_NULLOK: |
96b2fbcc MW |
457 | break; |
458 | default: | |
459 | return NULL; | |
460 | } | |
461 | ||
dcbbeb2d MW |
462 | INIT(this, |
463 | .public = { | |
464 | .process = _process, | |
465 | .build = _build, | |
84543e6e | 466 | .is_server = _is_server, |
bd1ee5bd AS |
467 | .get_server_id = _get_server_id, |
468 | .get_peer_id = _get_peer_id, | |
3e962b08 MW |
469 | .get_version = _get_version, |
470 | .set_version = _set_version, | |
96b2fbcc | 471 | .get_purpose = _get_purpose, |
400df4ca | 472 | .is_complete = _is_complete, |
51313a39 | 473 | .get_eap_msk = _get_eap_msk, |
666c5523 | 474 | .get_auth = _get_auth, |
dcbbeb2d MW |
475 | .destroy = _destroy, |
476 | }, | |
477 | .is_server = is_server, | |
3e962b08 | 478 | .version = TLS_1_2, |
1327839d | 479 | .application = application, |
96b2fbcc | 480 | .purpose = purpose, |
dcbbeb2d | 481 | ); |
409adef4 | 482 | lib->settings->add_fallback(lib->settings, "%s.tls", "libtls", lib->ns); |
dcbbeb2d | 483 | |
6a5c86b7 | 484 | this->crypto = tls_crypto_create(&this->public, cache); |
e6f3ef13 | 485 | this->alert = tls_alert_create(); |
4c0c2283 MW |
486 | if (is_server) |
487 | { | |
3ddd164e | 488 | this->handshake = &tls_server_create(&this->public, this->crypto, |
2de481e3 | 489 | this->alert, server, peer)->handshake; |
4c0c2283 MW |
490 | } |
491 | else | |
492 | { | |
3ddd164e | 493 | this->handshake = &tls_peer_create(&this->public, this->crypto, |
2de481e3 | 494 | this->alert, peer, server)->handshake; |
4c0c2283 | 495 | } |
e6f3ef13 | 496 | this->fragmentation = tls_fragmentation_create(this->handshake, this->alert, |
970378c5 | 497 | this->application, purpose); |
e6f3ef13 MW |
498 | this->compression = tls_compression_create(this->fragmentation, this->alert); |
499 | this->protection = tls_protection_create(this->compression, this->alert); | |
dc9f34be | 500 | this->crypto->set_protection(this->crypto, this->protection); |
40e384ea | 501 | |
dcbbeb2d MW |
502 | return &this->public; |
503 | } |