added TLS record debug output
[strongswan.git] / src / libcharon / plugins / eap_tls / eap_tls.c
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 "eap_tls.h"
17
18 #include <tls.h>
19
20 #include <daemon.h>
21 #include <library.h>
22
23 typedef struct private_eap_tls_t private_eap_tls_t;
24
25 /**
26 * Private data of an eap_tls_t object.
27 */
28 struct private_eap_tls_t {
29
30 /**
31 * Public interface.
32 */
33 eap_tls_t public;
34
35 /**
36 * Number of EAP-TLS messages processed so far
37 */
38 int processed;
39
40 /**
41 * Is this method instance acting as server?
42 */
43 bool is_server;
44
45 /**
46 * TLS layers
47 */
48 tls_t *tls;
49
50 /**
51 * Allocated input buffer
52 */
53 chunk_t input;
54
55 /**
56 * Number of bytes read in input buffer
57 */
58 size_t inpos;
59
60 /**
61 * Allocated ouput buffer
62 */
63 chunk_t output;
64
65 /**
66 * Number of bytes sent from output buffer
67 */
68 size_t outpos;
69 };
70
71 /** Size limit for a single TLS message */
72 #define MAX_TLS_MESSAGE_LEN 65536
73 /** Size of a EAP-TLS fragment */
74 #define EAP_TLS_FRAGMENT_LEN 1014
75 /** Maximum number of EAP-TLS messages/fragments allowed */
76 #define MAX_EAP_TLS_MESSAGE_COUNT 32
77
78 /**
79 * Flags of an EAP-TLS message
80 */
81 typedef enum {
82 EAP_TLS_LENGTH = (1<<7),
83 EAP_TLS_MORE_FRAGS = (1<<6),
84 EAP_TLS_START = (1<<5),
85 } eap_tls_flags_t;
86
87 /**
88 * EAP-TLS packet format
89 */
90 typedef struct __attribute__((packed)) {
91 u_int8_t code;
92 u_int8_t identifier;
93 u_int16_t length;
94 u_int8_t type;
95 u_int8_t flags;
96 } eap_tls_packet_t;
97
98 /**
99 * TLS record
100 */
101 typedef struct __attribute__((packed)) {
102 u_int8_t type;
103 u_int16_t version;
104 u_int16_t length;
105 char data[];
106 } tls_record_t;
107
108 METHOD(eap_method_t, initiate, status_t,
109 private_eap_tls_t *this, eap_payload_t **out)
110 {
111 if (this->is_server)
112 {
113 eap_tls_packet_t pkt = {
114 .type = EAP_TLS,
115 .code = EAP_REQUEST,
116 .flags = EAP_TLS_START,
117 };
118 htoun16(&pkt.length, sizeof(eap_tls_packet_t));
119 /* start with non-zero random identifier */
120 do {
121 pkt.identifier = random();
122 } while (!pkt.identifier);
123 DBG2(DBG_IKE, "sending EAP-TLS start packet");
124
125 *out = eap_payload_create_data(chunk_from_thing(pkt));
126 return NEED_MORE;
127 }
128 return FAILED;
129 }
130
131 /**
132 * Write received TLS data to the input buffer
133 */
134 static bool write_buf(private_eap_tls_t *this, eap_tls_packet_t *pkt)
135 {
136 u_int32_t msg_len;
137 u_int16_t pkt_len;
138 chunk_t data;
139
140 pkt_len = untoh16(&pkt->length);
141
142 if (pkt->flags & EAP_TLS_LENGTH)
143 {
144 if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
145 {
146 DBG1(DBG_IKE, "EAP-TLS packet too short");
147 return FALSE;
148 }
149 msg_len = untoh32(pkt + 1);
150 if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
151 msg_len > MAX_TLS_MESSAGE_LEN)
152 {
153 DBG1(DBG_IKE, "invalid EAP-TLS packet length");
154 return FALSE;
155 }
156 if (this->input.ptr)
157 {
158 if (msg_len != this->input.len)
159 {
160 DBG1(DBG_IKE, "received unexpected TLS message length");
161 return FALSE;
162 }
163 }
164 else
165 {
166 this->input = chunk_alloc(msg_len);
167 this->inpos = 0;
168 }
169 data = chunk_create((char*)(pkt + 1) + sizeof(msg_len),
170 pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len));
171 }
172 else
173 {
174 data = chunk_create((char*)(pkt + 1),
175 pkt_len - sizeof(eap_tls_packet_t));
176 }
177 DBG2(DBG_IKE, "received EAP-TLS %s (%u bytes)",
178 (pkt->flags & EAP_TLS_MORE_FRAGS) ? "fragment" : "packet", pkt_len);
179
180 if (data.len > this->input.len - this->inpos)
181 {
182 DBG1(DBG_IKE, "EAP-TLS fragment exceeds TLS message length");
183 return FALSE;
184 }
185 memcpy(this->input.ptr + this->inpos, data.ptr, data.len);
186 this->inpos += data.len;
187 return TRUE;
188 }
189
190 /**
191 * Send an ack to request next fragment
192 */
193 static eap_payload_t *create_ack(private_eap_tls_t *this, u_int8_t identifier)
194 {
195 eap_tls_packet_t pkt = {
196 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
197 .identifier = this->is_server ? identifier + 1 : identifier,
198 .type = EAP_TLS,
199 };
200 htoun16(&pkt.length, sizeof(pkt));
201 DBG2(DBG_IKE, "sending EAP-TLS acknowledgement packet");
202
203 return eap_payload_create_data(chunk_from_thing(pkt));
204 }
205
206 /**
207 * Create a eap response from data in the TLS output buffer
208 */
209 static eap_payload_t *read_buf(private_eap_tls_t *this, u_int8_t identifier)
210 {
211 char buf[EAP_TLS_FRAGMENT_LEN + sizeof(eap_tls_packet_t) + 4], *start;
212 eap_tls_packet_t *pkt = (eap_tls_packet_t*)buf;
213 u_int16_t pkt_len = sizeof(eap_tls_packet_t);
214
215 pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
216 pkt->identifier = this->is_server ? identifier + 1 : identifier;
217 pkt->type = EAP_TLS;
218 pkt->flags = 0;
219
220 if (this->output.len)
221 {
222 start = (char*)(pkt + 1);
223 if (this->outpos == 0)
224 { /* first fragment */
225 pkt->flags = EAP_TLS_LENGTH;
226 pkt_len += 4;
227 start += 4;
228 htoun32(pkt + 1, this->output.len);
229 }
230
231 if (this->output.len - this->outpos > EAP_TLS_FRAGMENT_LEN)
232 {
233 pkt->flags |= EAP_TLS_MORE_FRAGS;
234 pkt_len += EAP_TLS_FRAGMENT_LEN;
235 memcpy(start, this->output.ptr + this->outpos, EAP_TLS_FRAGMENT_LEN);
236 this->outpos += EAP_TLS_FRAGMENT_LEN;
237 DBG2(DBG_IKE, "sending EAP-TLS fragment (%u bytes)", pkt_len);
238 }
239 else
240 {
241 pkt_len += this->output.len - this->outpos;
242 memcpy(start, this->output.ptr + this->outpos,
243 this->output.len - this->outpos);
244 chunk_free(&this->output);
245 this->outpos = 0;
246 DBG2(DBG_IKE, "sending EAP-TLS packet (%u bytes)", pkt_len);
247 }
248 }
249 else
250 {
251 DBG2(DBG_IKE, "sending EAP-TLS acknowledgement packet");
252 }
253 htoun16(&pkt->length, pkt_len);
254 return eap_payload_create_data(chunk_create(buf, pkt_len));
255 }
256
257 /**
258 * Pass data in input buffer to upper layers, write result to output buffer
259 */
260 static status_t process_buf(private_eap_tls_t *this)
261 {
262 tls_record_t *in, out;
263 chunk_t data;
264 u_int16_t len;
265 status_t status;
266
267 /* pass input buffer to upper layer, record for record */
268 data = this->input;
269 while (data.len > sizeof(tls_record_t))
270 {
271 in = (tls_record_t*)data.ptr;
272 len = untoh16(&in->length);
273 DBG2(DBG_IKE, "received TLS %N record (%u bytes)",
274 tls_content_type_names, in->type, sizeof(tls_record_t) + len);
275 if (len > data.len - sizeof(tls_record_t))
276 {
277 DBG1(DBG_IKE, "TLS record length invalid");
278 return FAILED;
279 }
280 if (untoh16(&in->version) < TLS_1_0)
281 {
282 DBG1(DBG_IKE, "%N invalid with EAP-TLS",
283 tls_version_names, untoh16(&in->version));
284 return FAILED;
285 }
286
287 status = this->tls->process(this->tls, in->type,
288 chunk_create(in->data, len));
289 if (status != NEED_MORE)
290 {
291 return status;
292 }
293 data = chunk_skip(data, len + sizeof(tls_record_t));
294 }
295 chunk_free(&this->input);
296 this->inpos = 0;
297
298 /* read in records from upper layer, append to output buffer */
299 chunk_free(&this->output);
300 while (TRUE)
301 {
302 tls_content_type_t type;
303 chunk_t header = chunk_from_thing(out);
304
305 status = this->tls->build(this->tls, &type, &data);
306 switch (status)
307 {
308 case NEED_MORE:
309 break;
310 case INVALID_STATE:
311 /* invalid state means we need more input from peer first */
312 return NEED_MORE;
313 case SUCCESS:
314 return SUCCESS;
315 case FAILED:
316 default:
317 return FAILED;
318 }
319 out.type = type;
320 htoun16(&out.version, this->tls->get_version(this->tls));
321 htoun16(&out.length, data.len);
322 this->output = chunk_cat("mcm", this->output, header, data);
323 DBG2(DBG_IKE, "sending TLS %N record (%u bytes)",
324 tls_content_type_names, type, sizeof(tls_record_t) + data.len);
325 }
326 }
327
328 METHOD(eap_method_t, process, status_t,
329 private_eap_tls_t *this, eap_payload_t *in, eap_payload_t **out)
330 {
331 eap_tls_packet_t *pkt;
332 chunk_t data;
333 status_t status;
334
335 if (++this->processed > MAX_EAP_TLS_MESSAGE_COUNT)
336 {
337 DBG1(DBG_IKE, "EAP-TLS packet count exceeded");
338 return FAILED;
339 }
340
341 data = in->get_data(in);
342
343 pkt = (eap_tls_packet_t*)data.ptr;
344 if (data.len < sizeof(eap_tls_packet_t) ||
345 untoh16(&pkt->length) != data.len)
346 {
347 DBG1(DBG_IKE, "invalid EAP-TLS packet length");
348 return FAILED;
349 }
350 if (!(pkt->flags & EAP_TLS_START))
351 {
352 if (data.len == sizeof(eap_tls_packet_t))
353 {
354 if (this->output.len)
355 { /* ACK to our fragment, send next */
356 *out = read_buf(this, pkt->identifier);
357 return NEED_MORE;
358 }
359 if (this->tls->is_complete(this->tls))
360 {
361 return SUCCESS;
362 }
363 return FAILED;
364 }
365 if (!write_buf(this, pkt))
366 {
367 return FAILED;
368 }
369 if (pkt->flags & EAP_TLS_MORE_FRAGS)
370 { /* more fragments follow */
371 *out = create_ack(this, pkt->identifier);
372 return NEED_MORE;
373 }
374 else if (this->input.len != this->inpos)
375 {
376 DBG1(DBG_IKE, "defragmented TLS message has invalid length");
377 return FAILED;
378 }
379 }
380 status = process_buf(this);
381 if (status == NEED_MORE)
382 {
383 *out = read_buf(this, pkt->identifier);
384 }
385 return status;
386 }
387
388 METHOD(eap_method_t, get_type, eap_type_t,
389 private_eap_tls_t *this, u_int32_t *vendor)
390 {
391 *vendor = 0;
392 return EAP_TLS;
393 }
394
395 METHOD(eap_method_t, get_msk, status_t,
396 private_eap_tls_t *this, chunk_t *msk)
397 {
398 *msk = this->tls->get_eap_msk(this->tls);
399 if (msk->len)
400 {
401 return SUCCESS;
402 }
403 return FAILED;
404 }
405
406 METHOD(eap_method_t, is_mutual, bool,
407 private_eap_tls_t *this)
408 {
409 return TRUE;
410 }
411
412 METHOD(eap_method_t, destroy, void,
413 private_eap_tls_t *this)
414 {
415 free(this->input.ptr);
416 free(this->output.ptr);
417
418 this->tls->destroy(this->tls);
419
420 free(this);
421 }
422
423 /**
424 * Generic private constructor
425 */
426 static eap_tls_t *eap_tls_create(identification_t *server,
427 identification_t *peer, bool is_server)
428 {
429 private_eap_tls_t *this;
430
431 INIT(this,
432 .public = {
433 .eap_method = {
434 .initiate = _initiate,
435 .process = _process,
436 .get_type = _get_type,
437 .is_mutual = _is_mutual,
438 .get_msk = _get_msk,
439 .destroy = _destroy,
440 },
441 },
442 .is_server = is_server,
443 );
444 /* MSK PRF ASCII constant label according to EAP-TLS RFC 5216 */
445 this->tls = tls_create(is_server, server, peer, TRUE,
446 "client EAP encryption", NULL);
447
448 return &this->public;
449 }
450
451 eap_tls_t *eap_tls_create_server(identification_t *server,
452 identification_t *peer)
453 {
454 return eap_tls_create(server, peer, TRUE);
455 }
456
457 eap_tls_t *eap_tls_create_peer(identification_t *server,
458 identification_t *peer)
459 {
460 return eap_tls_create(server, peer, FALSE);
461 }