log EAP-TTLS version
[strongswan.git] / src / libcharon / plugins / eap_ttls / eap_ttls.c
1 /*
2 * Copyright (C) 2010 Martin Willi, revosec AG
3 * Copyright (C) 2010 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil
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_ttls.h"
17
18 #include <tls.h>
19
20 #include <daemon.h>
21 #include <library.h>
22
23 typedef struct private_eap_ttls_t private_eap_ttls_t;
24
25 /**
26 * Private data of an eap_ttls_t object.
27 */
28 struct private_eap_ttls_t {
29
30 /**
31 * Public interface.
32 */
33 eap_ttls_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 16384
73 /** Size of a EAP-TLS fragment */
74 #define EAP_TTLS_FRAGMENT_LEN 1014
75 /** Maximum number of EAP-TLS messages/fragments allowed */
76 #define MAX_EAP_TTLS_MESSAGE_COUNT 16
77
78 /**
79 * Flags of an EAP-TLS message
80 */
81 typedef enum {
82 EAP_TTLS_LENGTH = (1<<7),
83 EAP_TTLS_MORE_FRAGS = (1<<6),
84 EAP_TTLS_START = (1<<5),
85 EAP_TTLS_VERSION = 0x07
86 } eap_ttls_flags_t;
87
88 /**
89 * EAP-TLS packet format
90 */
91 typedef struct __attribute__((packed)) {
92 u_int8_t code;
93 u_int8_t identifier;
94 u_int16_t length;
95 u_int8_t type;
96 u_int8_t flags;
97 } eap_ttls_packet_t;
98
99 /**
100 * TLS record
101 */
102 typedef struct __attribute__((packed)) {
103 u_int8_t type;
104 u_int16_t version;
105 u_int16_t length;
106 char data[];
107 } tls_record_t;
108
109 METHOD(eap_method_t, initiate, status_t,
110 private_eap_ttls_t *this, eap_payload_t **out)
111 {
112 if (this->is_server)
113 {
114 eap_ttls_packet_t pkt = {
115 .type = EAP_TTLS,
116 .code = EAP_REQUEST,
117 .flags = EAP_TTLS_START,
118 };
119 htoun16(&pkt.length, sizeof(eap_ttls_packet_t));
120 /* start with non-zero random identifier */
121 do {
122 pkt.identifier = random();
123 } while (!pkt.identifier);
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_ttls_t *this, eap_ttls_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_TTLS_LENGTH)
143 {
144 if (pkt_len < sizeof(eap_ttls_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_ttls_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_ttls_packet_t) - sizeof(msg_len));
171 }
172 else
173 {
174 data = chunk_create((char*)(pkt + 1),
175 pkt_len - sizeof(eap_ttls_packet_t));
176 }
177 if (data.len > this->input.len - this->inpos)
178 {
179 DBG1(DBG_IKE, "EAP-TLS fragment exceeds TLS message length");
180 return FALSE;
181 }
182 memcpy(this->input.ptr + this->inpos, data.ptr, data.len);
183 this->inpos += data.len;
184 return TRUE;
185 }
186
187 /**
188 * Send an ack to request next fragment
189 */
190 static eap_payload_t *create_ack(private_eap_ttls_t *this, u_int8_t identifier)
191 {
192 eap_ttls_packet_t pkt = {
193 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
194 .identifier = this->is_server ? identifier + 1 : identifier,
195 .type = EAP_TTLS,
196 };
197 htoun16(&pkt.length, sizeof(pkt));
198
199 return eap_payload_create_data(chunk_from_thing(pkt));
200 }
201
202 /**
203 * Create a eap response from data in the TLS output buffer
204 */
205 static eap_payload_t *read_buf(private_eap_ttls_t *this, u_int8_t identifier)
206 {
207 char buf[EAP_TTLS_FRAGMENT_LEN + sizeof(eap_ttls_packet_t) + 4], *start;
208 eap_ttls_packet_t *pkt = (eap_ttls_packet_t*)buf;
209 u_int16_t pkt_len = sizeof(eap_ttls_packet_t);
210
211 pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
212 pkt->identifier = this->is_server ? identifier + 1 : identifier;
213 pkt->type = EAP_TTLS;
214 pkt->flags = 0;
215
216 if (this->output.len)
217 {
218 start = (char*)(pkt + 1);
219 if (this->outpos == 0)
220 { /* first fragment */
221 pkt->flags = EAP_TTLS_LENGTH;
222 pkt_len += 4;
223 start += 4;
224 htoun32(pkt + 1, this->output.len);
225 }
226
227 if (this->output.len - this->outpos > EAP_TTLS_FRAGMENT_LEN)
228 {
229 pkt->flags |= EAP_TTLS_MORE_FRAGS;
230 pkt_len += EAP_TTLS_FRAGMENT_LEN;
231 memcpy(start, this->output.ptr + this->outpos, EAP_TTLS_FRAGMENT_LEN);
232 this->outpos += EAP_TTLS_FRAGMENT_LEN;
233 }
234 else
235 {
236 pkt_len += this->output.len - this->outpos;
237 memcpy(start, this->output.ptr + this->outpos,
238 this->output.len - this->outpos);
239 chunk_free(&this->output);
240 this->outpos = 0;
241 }
242 }
243 htoun16(&pkt->length, pkt_len);
244 return eap_payload_create_data(chunk_create(buf, pkt_len));
245 }
246
247 /**
248 * Pass data in input buffer to upper layers, write result to output buffer
249 */
250 static status_t process_buf(private_eap_ttls_t *this)
251 {
252 tls_record_t *in, out;
253 chunk_t data;
254 u_int16_t len;
255 status_t status;
256
257 /* pass input buffer to upper layer, record for record */
258 data = this->input;
259 while (data.len > sizeof(tls_record_t))
260 {
261 in = (tls_record_t*)data.ptr;
262 len = untoh16(&in->length);
263 if (len > data.len - sizeof(tls_record_t))
264 {
265 DBG1(DBG_IKE, "TLS record length invalid");
266 return FAILED;
267 }
268 if (untoh16(&in->version) < TLS_1_0)
269 {
270 DBG1(DBG_IKE, "%N invalid with EAP-TLS",
271 tls_version_names, untoh16(&in->version));
272 return FAILED;
273 }
274
275 status = this->tls->process(this->tls, in->type,
276 chunk_create(in->data, len));
277 if (status != NEED_MORE)
278 {
279 return status;
280 }
281 data = chunk_skip(data, len + sizeof(tls_record_t));
282 }
283 chunk_free(&this->input);
284 this->inpos = 0;
285
286 /* read in records from upper layer, append to output buffer */
287 chunk_free(&this->output);
288 while (TRUE)
289 {
290 tls_content_type_t type;
291 chunk_t header = chunk_from_thing(out);
292
293 status = this->tls->build(this->tls, &type, &data);
294 switch (status)
295 {
296 case NEED_MORE:
297 break;
298 case INVALID_STATE:
299 /* invalid state means we need more input from peer first */
300 return NEED_MORE;
301 case SUCCESS:
302 return SUCCESS;
303 case FAILED:
304 default:
305 return FAILED;
306 }
307 out.type = type;
308 htoun16(&out.version, this->tls->get_version(this->tls));
309 htoun16(&out.length, data.len);
310 this->output = chunk_cat("mcm", this->output, header, data);
311 }
312 }
313
314 METHOD(eap_method_t, process, status_t,
315 private_eap_ttls_t *this, eap_payload_t *in, eap_payload_t **out)
316 {
317 eap_ttls_packet_t *pkt;
318 chunk_t data;
319 status_t status;
320
321 if (++this->processed > MAX_EAP_TTLS_MESSAGE_COUNT)
322 {
323 DBG1(DBG_IKE, "EAP-TTLS packet count exceeded");
324 return FAILED;
325 }
326
327 data = in->get_data(in);
328
329 pkt = (eap_ttls_packet_t*)data.ptr;
330 if (data.len < sizeof(eap_ttls_packet_t) ||
331 untoh16(&pkt->length) != data.len)
332 {
333 DBG1(DBG_IKE, "invalid EAP-TTLS packet length");
334 return FAILED;
335 }
336 if (pkt->flags & EAP_TTLS_START)
337 {
338 DBG1(DBG_IKE, "EAP-TTLS version is v%u",
339 pkt->flags & EAP_TTLS_VERSION);
340 }
341 else
342 {
343 if (data.len == sizeof(eap_ttls_packet_t))
344 {
345 if (this->output.len)
346 { /* ACK to our fragment, send next */
347 *out = read_buf(this, pkt->identifier);
348 return NEED_MORE;
349 }
350 if (this->tls->is_complete(this->tls))
351 {
352 return SUCCESS;
353 }
354 return FAILED;
355 }
356 if (!write_buf(this, pkt))
357 {
358 return FAILED;
359 }
360 if (pkt->flags & EAP_TTLS_MORE_FRAGS)
361 { /* more fragments follow */
362 *out = create_ack(this, pkt->identifier);
363 return NEED_MORE;
364 }
365 else if (this->input.len != this->inpos)
366 {
367 DBG1(DBG_IKE, "defragmented TLS message has invalid length");
368 return FAILED;
369 }
370 }
371 status = process_buf(this);
372 if (status == NEED_MORE)
373 {
374 *out = read_buf(this, pkt->identifier);
375 }
376 return status;
377 }
378
379 METHOD(eap_method_t, get_type, eap_type_t,
380 private_eap_ttls_t *this, u_int32_t *vendor)
381 {
382 *vendor = 0;
383 return EAP_TTLS;
384 }
385
386 METHOD(eap_method_t, get_msk, status_t,
387 private_eap_ttls_t *this, chunk_t *msk)
388 {
389 *msk = this->tls->get_eap_msk(this->tls);
390 if (msk->len)
391 {
392 return SUCCESS;
393 }
394 return FAILED;
395 }
396
397 METHOD(eap_method_t, is_mutual, bool,
398 private_eap_ttls_t *this)
399 {
400 return TRUE;
401 }
402
403 METHOD(eap_method_t, destroy, void,
404 private_eap_ttls_t *this)
405 {
406 free(this->input.ptr);
407 free(this->output.ptr);
408
409 this->tls->destroy(this->tls);
410
411 free(this);
412 }
413
414 /**
415 * Generic private constructor
416 */
417 static eap_ttls_t *eap_ttls_create(identification_t *server,
418 identification_t *peer, bool is_server)
419 {
420 private_eap_ttls_t *this;
421
422 INIT(this,
423 .public.eap_method = {
424 .initiate = _initiate,
425 .process = _process,
426 .get_type = _get_type,
427 .is_mutual = _is_mutual,
428 .get_msk = _get_msk,
429 .destroy = _destroy,
430 },
431 .is_server = is_server,
432 );
433 /* MSK PRF ASCII constant label according to EAP-TTLS RFC 5281 */
434 this->tls = tls_create(is_server, server, peer, "ttls keying material");
435
436 return &this->public;
437 }
438
439 eap_ttls_t *eap_ttls_create_server(identification_t *server,
440 identification_t *peer)
441 {
442 return eap_ttls_create(server, peer, TRUE);
443 }
444
445 eap_ttls_t *eap_ttls_create_peer(identification_t *server,
446 identification_t *peer)
447 {
448 return eap_ttls_create(server, peer, FALSE);
449 }