68cebb9941672bebeae2f0deb0614a6fef717dfa
[strongswan.git] / src / libtls / tls_eap.c
1
2 /*
3 * Copyright (C) 2010 Martin Willi
4 * Copyright (C) 2010 revosec AG
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "tls_eap.h"
18
19 #include "tls.h"
20
21 #include <utils/debug.h>
22 #include <library.h>
23
24 /**
25 * Size limit for a TLS message allowing for worst-case protection overhead
26 * according to section 6.2.3. "Payload Protection" of RFC 5246 TLS 1.2
27 */
28 #define TLS_MAX_MESSAGE_LEN 4 * (TLS_MAX_FRAGMENT_LEN + 2048)
29
30 typedef struct private_tls_eap_t private_tls_eap_t;
31
32 /**
33 * Private data of an tls_eap_t object.
34 */
35 struct private_tls_eap_t {
36
37 /**
38 * Public tls_eap_t interface.
39 */
40 tls_eap_t public;
41
42 /**
43 * Type of EAP method, EAP-TLS, EAP-TTLS, or EAP-TNC
44 */
45 eap_type_t type;
46
47 /**
48 * Current value of EAP identifier
49 */
50 u_int8_t identifier;
51
52 /**
53 * TLS stack
54 */
55 tls_t *tls;
56
57 /**
58 * Role
59 */
60 bool is_server;
61
62 /**
63 * If FALSE include the total length of an EAP message
64 * in the first fragment of fragmented messages only.
65 * If TRUE also include the length in non-fragmented messages.
66 */
67 bool include_length;
68
69 /**
70 * First fragment of a multi-fragment record?
71 */
72 bool first_fragment;
73
74 /**
75 * Maximum size of an outgoing EAP-TLS fragment
76 */
77 size_t frag_size;
78
79 /**
80 * Number of EAP messages/fragments processed so far
81 */
82 int processed;
83
84 /**
85 * Maximum number of processed EAP messages/fragments
86 */
87 int max_msg_count;
88 };
89
90 /**
91 * Flags of an EAP-TLS/TTLS/TNC message
92 */
93 typedef enum {
94 EAP_TLS_LENGTH = (1<<7), /* shared with EAP-TTLS/TNC/PEAP */
95 EAP_TLS_MORE_FRAGS = (1<<6), /* shared with EAP-TTLS/TNC/PEAP */
96 EAP_TLS_START = (1<<5), /* shared with EAP-TTLS/TNC/PEAP */
97 EAP_TTLS_VERSION = (0x07), /* shared with EAP-TNC/PEAP */
98 } eap_tls_flags_t;
99
100 #define EAP_TTLS_SUPPORTED_VERSION 0
101 #define EAP_TNC_SUPPORTED_VERSION 1
102 #define EAP_PEAP_SUPPORTED_VERSION 0
103
104 /**
105 * EAP-TLS/TTLS packet format
106 */
107 typedef struct __attribute__((packed)) {
108 u_int8_t code;
109 u_int8_t identifier;
110 u_int16_t length;
111 u_int8_t type;
112 u_int8_t flags;
113 } eap_tls_packet_t;
114
115 METHOD(tls_eap_t, initiate, status_t,
116 private_tls_eap_t *this, chunk_t *out)
117 {
118 if (this->is_server)
119 {
120 eap_tls_packet_t pkt = {
121 .type = this->type,
122 .code = EAP_REQUEST,
123 .flags = EAP_TLS_START,
124 };
125 switch (this->type)
126 {
127 case EAP_TTLS:
128 pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
129 break;
130 case EAP_TNC:
131 pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
132 break;
133 case EAP_PEAP:
134 pkt.flags |= EAP_PEAP_SUPPORTED_VERSION;
135 break;
136 default:
137 break;
138 }
139 htoun16(&pkt.length, sizeof(eap_tls_packet_t));
140 pkt.identifier = this->identifier;
141
142 *out = chunk_clone(chunk_from_thing(pkt));
143 DBG2(DBG_TLS, "sending %N start packet (%u bytes)",
144 eap_type_names, this->type, sizeof(eap_tls_packet_t));
145 DBG3(DBG_TLS, "%B", out);
146 return NEED_MORE;
147 }
148 return FAILED;
149 }
150
151 /**
152 * Process a received packet
153 */
154 static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
155 {
156 u_int16_t pkt_len;
157 u_int32_t msg_len;
158 size_t msg_len_offset = 0;
159
160 pkt_len = untoh16(&pkt->length);
161
162 if (pkt->flags & EAP_TLS_LENGTH)
163 {
164 if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
165 {
166 DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type);
167 return FAILED;
168 }
169 msg_len = untoh32(pkt + 1);
170 if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
171 msg_len > TLS_MAX_MESSAGE_LEN)
172 {
173 DBG1(DBG_TLS, "invalid %N packet length (%u bytes)", eap_type_names,
174 this->type, msg_len);
175 return FAILED;
176 }
177 msg_len_offset = sizeof(msg_len);
178 }
179
180 return this->tls->process(this->tls, (char*)(pkt + 1) + msg_len_offset,
181 pkt_len - sizeof(eap_tls_packet_t) - msg_len_offset);
182 }
183
184 /**
185 * Build a packet to send
186 */
187 static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
188 {
189 char buf[this->frag_size];
190 eap_tls_packet_t *pkt;
191 size_t len, reclen, msg_len_offset;
192 status_t status;
193 char *kind;
194
195 if (this->is_server)
196 {
197 this->identifier++;
198 }
199 pkt = (eap_tls_packet_t*)buf;
200 pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
201 pkt->identifier = this->identifier;
202 pkt->type = this->type;
203 pkt->flags = 0;
204
205 switch (this->type)
206 {
207 case EAP_TTLS:
208 pkt->flags |= EAP_TTLS_SUPPORTED_VERSION;
209 break;
210 case EAP_TNC:
211 pkt->flags |= EAP_TNC_SUPPORTED_VERSION;
212 break;
213 case EAP_PEAP:
214 pkt->flags |= EAP_PEAP_SUPPORTED_VERSION;
215 break;
216 default:
217 break;
218 }
219
220 if (this->first_fragment)
221 {
222 len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(u_int32_t);
223 msg_len_offset = sizeof(u_int32_t);
224 }
225 else
226 {
227 len = sizeof(buf) - sizeof(eap_tls_packet_t);
228 msg_len_offset = 0;
229 }
230 status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +
231 msg_len_offset, &len, &reclen);
232
233 switch (status)
234 {
235 case NEED_MORE:
236 pkt->flags |= EAP_TLS_MORE_FRAGS;
237 kind = "further fragment";
238 if (this->first_fragment)
239 {
240 pkt->flags |= EAP_TLS_LENGTH;
241 this->first_fragment = FALSE;
242 kind = "first fragment";
243 }
244 break;
245 case ALREADY_DONE:
246 if (this->first_fragment)
247 {
248 if (this->include_length)
249 {
250 pkt->flags |= EAP_TLS_LENGTH;
251 }
252 kind = "packet";
253 }
254 else if (this->type != EAP_TNC)
255 {
256 this->first_fragment = TRUE;
257 kind = "final fragment";
258 }
259 else
260 {
261 kind = "packet";
262 }
263 break;
264 default:
265 return status;
266 }
267 if (reclen)
268 {
269 if (pkt->flags & EAP_TLS_LENGTH)
270 {
271 htoun32(pkt + 1, reclen);
272 len += sizeof(u_int32_t);
273 pkt->flags |= EAP_TLS_LENGTH;
274 }
275 else
276 {
277 /* get rid of the reserved length field */
278 memmove(buf + sizeof(eap_tls_packet_t),
279 buf + sizeof(eap_tls_packet_t) + sizeof(u_int32_t), len);
280 }
281 }
282 len += sizeof(eap_tls_packet_t);
283 htoun16(&pkt->length, len);
284 *out = chunk_clone(chunk_create(buf, len));
285 DBG2(DBG_TLS, "sending %N %s (%u bytes)",
286 eap_type_names, this->type, kind, len);
287 DBG3(DBG_TLS, "%B", out);
288 return NEED_MORE;
289 }
290
291 /**
292 * Send an ack to request next fragment
293 */
294 static chunk_t create_ack(private_tls_eap_t *this)
295 {
296 eap_tls_packet_t pkt = {
297 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
298 .type = this->type,
299 };
300
301 if (this->is_server)
302 {
303 this->identifier++;
304 }
305 pkt.identifier = this->identifier;
306 htoun16(&pkt.length, sizeof(pkt));
307
308 switch (this->type)
309 {
310 case EAP_TTLS:
311 pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
312 break;
313 case EAP_TNC:
314 pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
315 break;
316 case EAP_PEAP:
317 pkt.flags |= EAP_PEAP_SUPPORTED_VERSION;
318 break;
319 default:
320 break;
321 }
322 DBG2(DBG_TLS, "sending %N acknowledgement packet",
323 eap_type_names, this->type);
324 return chunk_clone(chunk_from_thing(pkt));
325 }
326
327 METHOD(tls_eap_t, process, status_t,
328 private_tls_eap_t *this, chunk_t in, chunk_t *out)
329 {
330 eap_tls_packet_t *pkt;
331 status_t status;
332
333 if (this->max_msg_count && ++this->processed > this->max_msg_count)
334 {
335 DBG1(DBG_TLS, "%N packet count exceeded (%d > %d)",
336 eap_type_names, this->type,
337 this->processed, this->max_msg_count);
338 return FAILED;
339 }
340
341 pkt = (eap_tls_packet_t*)in.ptr;
342 if (in.len < sizeof(eap_tls_packet_t) || untoh16(&pkt->length) != in.len)
343 {
344 DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type);
345 return FAILED;
346 }
347
348 /* update EAP identifier */
349 if (!this->is_server)
350 {
351 this->identifier = pkt->identifier;
352 }
353 DBG3(DBG_TLS, "%N payload %B", eap_type_names, this->type, &in);
354
355 if (pkt->flags & EAP_TLS_START)
356 {
357 if (this->type == EAP_TTLS || this->type == EAP_TNC ||
358 this->type == EAP_PEAP)
359 {
360 DBG1(DBG_TLS, "%N version is v%u", eap_type_names, this->type,
361 pkt->flags & EAP_TTLS_VERSION);
362 }
363 }
364 else
365 {
366 if (in.len == sizeof(eap_tls_packet_t))
367 {
368 DBG2(DBG_TLS, "received %N acknowledgement packet",
369 eap_type_names, this->type);
370 status = build_pkt(this, out);
371 if (status == INVALID_STATE && this->tls->is_complete(this->tls))
372 {
373 return SUCCESS;
374 }
375 return status;
376 }
377 status = process_pkt(this, pkt);
378 switch (status)
379 {
380 case NEED_MORE:
381 break;
382 case SUCCESS:
383 return this->tls->is_complete(this->tls) ? SUCCESS : FAILED;
384 default:
385 return status;
386 }
387 }
388 status = build_pkt(this, out);
389 switch (status)
390 {
391 case INVALID_STATE:
392 *out = create_ack(this);
393 return NEED_MORE;
394 case FAILED:
395 if (!this->is_server)
396 {
397 *out = create_ack(this);
398 return NEED_MORE;
399 }
400 return FAILED;
401 default:
402 return status;
403 }
404 }
405
406 METHOD(tls_eap_t, get_msk, chunk_t,
407 private_tls_eap_t *this)
408 {
409 return this->tls->get_eap_msk(this->tls);
410 }
411
412 METHOD(tls_eap_t, get_identifier, u_int8_t,
413 private_tls_eap_t *this)
414 {
415 return this->identifier;
416 }
417
418 METHOD(tls_eap_t, set_identifier, void,
419 private_tls_eap_t *this, u_int8_t identifier)
420 {
421 this->identifier = identifier;
422 }
423
424 METHOD(tls_eap_t, destroy, void,
425 private_tls_eap_t *this)
426 {
427 this->tls->destroy(this->tls);
428 free(this);
429 }
430
431 /**
432 * See header
433 */
434 tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size,
435 int max_msg_count, bool include_length)
436 {
437 private_tls_eap_t *this;
438
439 if (!tls)
440 {
441 return NULL;
442 }
443
444 INIT(this,
445 .public = {
446 .initiate = _initiate,
447 .process = _process,
448 .get_msk = _get_msk,
449 .get_identifier = _get_identifier,
450 .set_identifier = _set_identifier,
451 .destroy = _destroy,
452 },
453 .type = type,
454 .is_server = tls->is_server(tls),
455 .first_fragment = (type != EAP_TNC),
456 .frag_size = frag_size,
457 .max_msg_count = max_msg_count,
458 .include_length = include_length,
459 .tls = tls,
460 );
461
462 if (this->is_server)
463 {
464 do
465 { /* start with non-zero random identifier */
466 this->identifier = random();
467 }
468 while (!this->identifier);
469 }
470
471 return &this->public;
472 }