implemented server side support for EAP-TTLS
[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 #include "eap_ttls_peer.h"
18 #include "eap_ttls_server.h"
19
20 #include <tls.h>
21
22 #include <daemon.h>
23 #include <library.h>
24
25 typedef struct private_eap_ttls_t private_eap_ttls_t;
26
27 /**
28 * Private data of an eap_ttls_t object.
29 */
30 struct private_eap_ttls_t {
31
32 /**
33 * Public interface.
34 */
35 eap_ttls_t public;
36
37 /**
38 * Number of EAP-TLS messages processed so far
39 */
40 int processed;
41
42 /**
43 * Is this method instance acting as server?
44 */
45 bool is_server;
46
47 /**
48 * TLS layers
49 */
50 tls_t *tls;
51
52 /**
53 * Allocated input buffer
54 */
55 chunk_t input;
56
57 /**
58 * Number of bytes read in input buffer
59 */
60 size_t inpos;
61
62 /**
63 * Allocated ouput buffer
64 */
65 chunk_t output;
66
67 /**
68 * Number of bytes sent from output buffer
69 */
70 size_t outpos;
71 };
72
73 /** Size limit for a single TLS message */
74 #define MAX_TLS_MESSAGE_LEN 16384
75 /** Size of a EAP-TLS fragment */
76 #define EAP_TTLS_FRAGMENT_LEN 1014
77 /** Maximum number of EAP-TLS messages/fragments allowed */
78 #define MAX_EAP_TTLS_MESSAGE_COUNT 16
79
80 /**
81 * Flags of an EAP-TLS message
82 */
83 typedef enum {
84 EAP_TTLS_LENGTH = (1<<7),
85 EAP_TTLS_MORE_FRAGS = (1<<6),
86 EAP_TTLS_START = (1<<5),
87 EAP_TTLS_VERSION = 0x07
88 } eap_ttls_flags_t;
89
90 /**
91 * EAP-TLS packet format
92 */
93 typedef struct __attribute__((packed)) {
94 u_int8_t code;
95 u_int8_t identifier;
96 u_int16_t length;
97 u_int8_t type;
98 u_int8_t flags;
99 } eap_ttls_packet_t;
100
101 /**
102 * TLS record
103 */
104 typedef struct __attribute__((packed)) {
105 u_int8_t type;
106 u_int16_t version;
107 u_int16_t length;
108 char data[];
109 } tls_record_t;
110
111 METHOD(eap_method_t, initiate, status_t,
112 private_eap_ttls_t *this, eap_payload_t **out)
113 {
114 if (this->is_server)
115 {
116 eap_ttls_packet_t pkt = {
117 .type = EAP_TTLS,
118 .code = EAP_REQUEST,
119 .flags = EAP_TTLS_START,
120 };
121 htoun16(&pkt.length, sizeof(eap_ttls_packet_t));
122 /* start with non-zero random identifier */
123 do {
124 pkt.identifier = random();
125 } while (!pkt.identifier);
126 DBG2(DBG_IKE, "sending EAP-TLS start packet");
127
128 *out = eap_payload_create_data(chunk_from_thing(pkt));
129 return NEED_MORE;
130 }
131 return FAILED;
132 }
133
134 /**
135 * Write received TLS data to the input buffer
136 */
137 static bool write_buf(private_eap_ttls_t *this, eap_ttls_packet_t *pkt)
138 {
139 u_int32_t msg_len;
140 u_int16_t pkt_len;
141 chunk_t data;
142
143 pkt_len = untoh16(&pkt->length);
144
145 if (pkt->flags & EAP_TTLS_LENGTH)
146 {
147 if (pkt_len < sizeof(eap_ttls_packet_t) + sizeof(msg_len))
148 {
149 DBG1(DBG_IKE, "EAP-TLS packet too short");
150 return FALSE;
151 }
152 msg_len = untoh32(pkt + 1);
153 if (msg_len < pkt_len - sizeof(eap_ttls_packet_t) - sizeof(msg_len) ||
154 msg_len > MAX_TLS_MESSAGE_LEN)
155 {
156 DBG1(DBG_IKE, "invalid EAP-TLS packet length");
157 return FALSE;
158 }
159 if (this->input.ptr)
160 {
161 if (msg_len != this->input.len)
162 {
163 DBG1(DBG_IKE, "received unexpected TLS message length");
164 return FALSE;
165 }
166 }
167 else
168 {
169 this->input = chunk_alloc(msg_len);
170 this->inpos = 0;
171 }
172 data = chunk_create((char*)(pkt + 1) + sizeof(msg_len),
173 pkt_len - sizeof(eap_ttls_packet_t) - sizeof(msg_len));
174 }
175 else
176 {
177 data = chunk_create((char*)(pkt + 1),
178 pkt_len - sizeof(eap_ttls_packet_t));
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_ttls_t *this, u_int8_t identifier)
194 {
195 eap_ttls_packet_t pkt = {
196 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
197 .identifier = this->is_server ? identifier + 1 : identifier,
198 .type = EAP_TTLS,
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_ttls_t *this, u_int8_t identifier)
210 {
211 char buf[EAP_TTLS_FRAGMENT_LEN + sizeof(eap_ttls_packet_t) + 4], *start;
212 eap_ttls_packet_t *pkt = (eap_ttls_packet_t*)buf;
213 u_int16_t pkt_len = sizeof(eap_ttls_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_TTLS;
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_TTLS_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_TTLS_FRAGMENT_LEN)
232 {
233 pkt->flags |= EAP_TTLS_MORE_FRAGS;
234 pkt_len += EAP_TTLS_FRAGMENT_LEN;
235 memcpy(start, this->output.ptr + this->outpos, EAP_TTLS_FRAGMENT_LEN);
236 this->outpos += EAP_TTLS_FRAGMENT_LEN;
237 DBG2(DBG_IKE, "sending EAP-TLS packet fragment");
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");
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_ttls_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 if (len > data.len - sizeof(tls_record_t))
274 {
275 DBG1(DBG_IKE, "TLS record length invalid");
276 return FAILED;
277 }
278 if (untoh16(&in->version) < TLS_1_0)
279 {
280 DBG1(DBG_IKE, "%N invalid with EAP-TLS",
281 tls_version_names, untoh16(&in->version));
282 return FAILED;
283 }
284
285 status = this->tls->process(this->tls, in->type,
286 chunk_create(in->data, len));
287 if (status != NEED_MORE)
288 {
289 return status;
290 }
291 data = chunk_skip(data, len + sizeof(tls_record_t));
292 }
293 chunk_free(&this->input);
294 this->inpos = 0;
295
296 /* read in records from upper layer, append to output buffer */
297 chunk_free(&this->output);
298 while (TRUE)
299 {
300 tls_content_type_t type;
301 chunk_t header = chunk_from_thing(out);
302
303 status = this->tls->build(this->tls, &type, &data);
304 switch (status)
305 {
306 case NEED_MORE:
307 break;
308 case INVALID_STATE:
309 /* invalid state means we need more input from peer first */
310 return NEED_MORE;
311 case SUCCESS:
312 return SUCCESS;
313 case FAILED:
314 default:
315 return FAILED;
316 }
317 out.type = type;
318 htoun16(&out.version, this->tls->get_version(this->tls));
319 htoun16(&out.length, data.len);
320 this->output = chunk_cat("mcm", this->output, header, data);
321 }
322 }
323
324 METHOD(eap_method_t, process, status_t,
325 private_eap_ttls_t *this, eap_payload_t *in, eap_payload_t **out)
326 {
327 eap_ttls_packet_t *pkt;
328 chunk_t data;
329 status_t status;
330
331 if (++this->processed > MAX_EAP_TTLS_MESSAGE_COUNT)
332 {
333 DBG1(DBG_IKE, "EAP-TTLS packet count exceeded");
334 return FAILED;
335 }
336
337 data = in->get_data(in);
338
339 pkt = (eap_ttls_packet_t*)data.ptr;
340 if (data.len < sizeof(eap_ttls_packet_t) ||
341 untoh16(&pkt->length) != data.len)
342 {
343 DBG1(DBG_IKE, "invalid EAP-TLS packet length");
344 return FAILED;
345 }
346 if (pkt->flags & EAP_TTLS_START)
347 {
348 DBG1(DBG_IKE, "EAP-TTLS version is v%u",
349 pkt->flags & EAP_TTLS_VERSION);
350 }
351 else
352 {
353 if (data.len == sizeof(eap_ttls_packet_t))
354 {
355 if (this->output.len)
356 { /* ACK to our fragment, send next */
357 *out = read_buf(this, pkt->identifier);
358 return NEED_MORE;
359 }
360 if (this->tls->is_complete(this->tls))
361 {
362 return SUCCESS;
363 }
364 return FAILED;
365 }
366 if (!write_buf(this, pkt))
367 {
368 return FAILED;
369 }
370 if (pkt->flags & EAP_TTLS_MORE_FRAGS)
371 { /* more fragments follow */
372 *out = create_ack(this, pkt->identifier);
373 return NEED_MORE;
374 }
375 else if (this->input.len != this->inpos)
376 {
377 DBG1(DBG_IKE, "defragmented TLS message has invalid length");
378 return FAILED;
379 }
380 }
381 status = process_buf(this);
382 if (status == NEED_MORE)
383 {
384 *out = read_buf(this, pkt->identifier);
385 }
386 return status;
387 }
388
389 METHOD(eap_method_t, get_type, eap_type_t,
390 private_eap_ttls_t *this, u_int32_t *vendor)
391 {
392 *vendor = 0;
393 return EAP_TTLS;
394 }
395
396 METHOD(eap_method_t, get_msk, status_t,
397 private_eap_ttls_t *this, chunk_t *msk)
398 {
399 *msk = this->tls->get_eap_msk(this->tls);
400 if (msk->len)
401 {
402 return SUCCESS;
403 }
404 return FAILED;
405 }
406
407 METHOD(eap_method_t, is_mutual, bool,
408 private_eap_ttls_t *this)
409 {
410 return TRUE;
411 }
412
413 METHOD(eap_method_t, destroy, void,
414 private_eap_ttls_t *this)
415 {
416 free(this->input.ptr);
417 free(this->output.ptr);
418
419 this->tls->destroy(this->tls);
420
421 free(this);
422 }
423
424 /**
425 * Generic private constructor
426 */
427 static eap_ttls_t *eap_ttls_create(identification_t *server,
428 identification_t *peer, bool is_server,
429 tls_application_t *application)
430 {
431 private_eap_ttls_t *this;
432
433 INIT(this,
434 .public.eap_method = {
435 .initiate = _initiate,
436 .process = _process,
437 .get_type = _get_type,
438 .is_mutual = _is_mutual,
439 .get_msk = _get_msk,
440 .destroy = _destroy,
441 },
442 .is_server = is_server,
443 );
444 /* MSK PRF ASCII constant label according to EAP-TTLS RFC 5281 */
445 this->tls = tls_create(is_server, server, peer, FALSE,
446 "ttls keying material", application);
447 return &this->public;
448 }
449
450 eap_ttls_t *eap_ttls_create_server(identification_t *server,
451 identification_t *peer)
452 {
453 return eap_ttls_create(server, peer, TRUE,
454 &eap_ttls_server_create(server, peer)->application);
455 }
456
457 eap_ttls_t *eap_ttls_create_peer(identification_t *server,
458 identification_t *peer)
459 {
460 return eap_ttls_create(server, peer, FALSE,
461 &eap_ttls_peer_create(server, peer)->application);
462 }