226422c35f9de590a65d8f7fe5cd5fe6e21a43d4
[strongswan.git] / src / libtls / tls_eap.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 "tls_eap.h"
17
18 #include "tls.h"
19
20 #include <debug.h>
21 #include <library.h>
22
23 /** Size limit for a single TLS message */
24 #define MAX_TLS_MESSAGE_LEN 65536
25
26 typedef struct private_tls_eap_t private_tls_eap_t;
27
28 /**
29 * Private data of an tls_eap_t object.
30 */
31 struct private_tls_eap_t {
32
33 /**
34 * Public tls_eap_t interface.
35 */
36 tls_eap_t public;
37
38 /**
39 * Type of EAP method, EAP-TLS, EAP-TTLS, or EAP-TNC
40 */
41 eap_type_t type;
42
43 /**
44 * Current value of EAP identifier
45 */
46 u_int8_t identifier;
47
48 /**
49 * TLS stack
50 */
51 tls_t *tls;
52
53 /**
54 * Role
55 */
56 bool is_server;
57
58 /**
59 * First fragment of a multi-fragment record?
60 */
61 bool first_fragment;
62
63 /**
64 * Maximum size of an outgoing EAP-TLS fragment
65 */
66 size_t frag_size;
67
68 /**
69 * Number of EAP messages/fragments processed so far
70 */
71 int processed;
72
73 /**
74 * Maximum number of processed EAP messages/fragments
75 */
76 int max_msg_count;
77 };
78
79 /**
80 * Flags of an EAP-TLS/TTLS/TNC message
81 */
82 typedef enum {
83 EAP_TLS_LENGTH = (1<<7), /* shared with EAP-TTLS/TNC/PEAP */
84 EAP_TLS_MORE_FRAGS = (1<<6), /* shared with EAP-TTLS/TNC/PEAP */
85 EAP_TLS_START = (1<<5), /* shared with EAP-TTLS/TNC/PEAP */
86 EAP_TTLS_VERSION = (0x07), /* shared with EAP-TNC/PEAP */
87 } eap_tls_flags_t;
88
89 #define EAP_TTLS_SUPPORTED_VERSION 0
90 #define EAP_TNC_SUPPORTED_VERSION 1
91 #define EAP_PEAP_SUPPORTED_VERSION 0
92
93 /**
94 * EAP-TLS/TTLS packet format
95 */
96 typedef struct __attribute__((packed)) {
97 u_int8_t code;
98 u_int8_t identifier;
99 u_int16_t length;
100 u_int8_t type;
101 u_int8_t flags;
102 } eap_tls_packet_t;
103
104 METHOD(tls_eap_t, initiate, status_t,
105 private_tls_eap_t *this, chunk_t *out)
106 {
107 if (this->is_server)
108 {
109 eap_tls_packet_t pkt = {
110 .type = this->type,
111 .code = EAP_REQUEST,
112 .flags = EAP_TLS_START,
113 };
114 switch (this->type)
115 {
116 case EAP_TTLS:
117 pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
118 break;
119 case EAP_TNC:
120 pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
121 break;
122 case EAP_PEAP:
123 pkt.flags |= EAP_PEAP_SUPPORTED_VERSION;
124 break;
125 default:
126 break;
127 }
128 htoun16(&pkt.length, sizeof(eap_tls_packet_t));
129 pkt.identifier = this->identifier;
130
131 DBG2(DBG_IKE, "sending %N start packet", eap_type_names, this->type);
132 *out = chunk_clone(chunk_from_thing(pkt));
133 return NEED_MORE;
134 }
135 return FAILED;
136 }
137
138 /**
139 * Process a received packet
140 */
141 static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
142 {
143 u_int32_t msg_len;
144 u_int16_t pkt_len;
145
146 pkt_len = untoh16(&pkt->length);
147 if (pkt->flags & EAP_TLS_LENGTH)
148 {
149 if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
150 {
151 DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type);
152 return FAILED;
153 }
154 msg_len = untoh32(pkt + 1);
155 if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
156 msg_len > MAX_TLS_MESSAGE_LEN)
157 {
158 DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type);
159 return FAILED;
160 }
161 return this->tls->process(this->tls, (char*)(pkt + 1) + sizeof(msg_len),
162 pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len));
163 }
164 return this->tls->process(this->tls, (char*)(pkt + 1),
165 pkt_len - sizeof(eap_tls_packet_t));
166 }
167
168 /**
169 * Build a packet to send
170 */
171 static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
172 {
173 char buf[this->frag_size];
174 eap_tls_packet_t *pkt;
175 size_t len, reclen;
176 status_t status;
177 char *kind;
178
179 if (this->is_server)
180 {
181 this->identifier++;
182 }
183 pkt = (eap_tls_packet_t*)buf;
184 pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
185 pkt->identifier = this->identifier;
186 pkt->type = this->type;
187 pkt->flags = 0;
188
189 switch (this->type)
190 {
191 case EAP_TTLS:
192 pkt->flags |= EAP_TTLS_SUPPORTED_VERSION;
193 break;
194 case EAP_TNC:
195 pkt->flags |= EAP_TNC_SUPPORTED_VERSION;
196 break;
197 case EAP_PEAP:
198 pkt->flags |= EAP_PEAP_SUPPORTED_VERSION;
199 break;
200 default:
201 break;
202 }
203
204 if (this->first_fragment)
205 {
206 pkt->flags |= EAP_TLS_LENGTH;
207 len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(u_int32_t);
208 status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +
209 sizeof(u_int32_t), &len, &reclen);
210 }
211 else
212 {
213 len = sizeof(buf) - sizeof(eap_tls_packet_t);
214 status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t),
215 &len, &reclen);
216 }
217 switch (status)
218 {
219 case NEED_MORE:
220 pkt->flags |= EAP_TLS_MORE_FRAGS;
221 kind = "further fragment";
222 if (this->first_fragment)
223 {
224 this->first_fragment = FALSE;
225 kind = "first fragment";
226 }
227 break;
228 case ALREADY_DONE:
229 kind = "packet";
230 if (!this->first_fragment)
231 {
232 this->first_fragment = TRUE;
233 kind = "final fragment";
234 }
235 break;
236 default:
237 return status;
238 }
239 DBG2(DBG_TLS, "sending %N %s (%u bytes)",
240 eap_type_names, this->type, kind, len);
241 if (reclen)
242 {
243 htoun32(pkt + 1, reclen);
244 len += sizeof(u_int32_t);
245 pkt->flags |= EAP_TLS_LENGTH;
246 }
247 len += sizeof(eap_tls_packet_t);
248 htoun16(&pkt->length, len);
249 *out = chunk_clone(chunk_create(buf, len));
250 return NEED_MORE;
251 }
252
253 /**
254 * Send an ack to request next fragment
255 */
256 static chunk_t create_ack(private_tls_eap_t *this)
257 {
258 eap_tls_packet_t pkt = {
259 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
260 .type = this->type,
261 };
262
263 if (this->is_server)
264 {
265 this->identifier++;
266 }
267 pkt.identifier = this->identifier;
268 htoun16(&pkt.length, sizeof(pkt));
269
270 switch (this->type)
271 {
272 case EAP_TTLS:
273 pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
274 break;
275 case EAP_TNC:
276 pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
277 break;
278 case EAP_PEAP:
279 pkt.flags |= EAP_PEAP_SUPPORTED_VERSION;
280 break;
281 default:
282 break;
283 }
284 DBG2(DBG_TLS, "sending %N acknowledgement packet",
285 eap_type_names, this->type);
286 return chunk_clone(chunk_from_thing(pkt));
287 }
288
289 METHOD(tls_eap_t, process, status_t,
290 private_tls_eap_t *this, chunk_t in, chunk_t *out)
291 {
292 eap_tls_packet_t *pkt;
293 status_t status;
294
295 if (++this->processed > this->max_msg_count)
296 {
297 DBG1(DBG_IKE, "%N packet count exceeded (%d > %d)",
298 eap_type_names, this->type,
299 this->processed, this->max_msg_count);
300 return FAILED;
301 }
302
303 pkt = (eap_tls_packet_t*)in.ptr;
304 if (in.len < sizeof(eap_tls_packet_t) || untoh16(&pkt->length) != in.len)
305 {
306 DBG1(DBG_IKE, "invalid %N packet length", eap_type_names, this->type);
307 return FAILED;
308 }
309
310 /* update EAP identifier */
311 this->identifier = pkt->identifier;
312
313 if (pkt->flags & EAP_TLS_START)
314 {
315 if (this->type == EAP_TTLS || this->type == EAP_TNC ||
316 this->type == EAP_PEAP)
317 {
318 DBG1(DBG_TLS, "%N version is v%u", eap_type_names, this->type,
319 pkt->flags & EAP_TTLS_VERSION);
320 }
321 }
322 else
323 {
324 if (in.len == sizeof(eap_tls_packet_t))
325 {
326 DBG2(DBG_TLS, "received %N acknowledgement packet",
327 eap_type_names, this->type);
328 status = build_pkt(this, out);
329 if (status == INVALID_STATE && this->tls->is_complete(this->tls))
330 {
331 return SUCCESS;
332 }
333 return status;
334 }
335 status = process_pkt(this, pkt);
336 switch (status)
337 {
338 case NEED_MORE:
339 break;
340 case SUCCESS:
341 return this->tls->is_complete(this->tls) ? SUCCESS : FAILED;
342 default:
343 return status;
344 }
345 }
346 status = build_pkt(this, out);
347 switch (status)
348 {
349 case INVALID_STATE:
350 *out = create_ack(this);
351 return NEED_MORE;
352 case FAILED:
353 if (!this->is_server)
354 {
355 *out = create_ack(this);
356 return NEED_MORE;
357 }
358 return FAILED;
359 default:
360 return status;
361 }
362 }
363
364 METHOD(tls_eap_t, get_msk, chunk_t,
365 private_tls_eap_t *this)
366 {
367 return this->tls->get_eap_msk(this->tls);
368 }
369
370 METHOD(tls_eap_t, get_identifier, u_int8_t,
371 private_tls_eap_t *this)
372 {
373 return this->identifier;
374 }
375
376 METHOD(tls_eap_t, set_identifier, void,
377 private_tls_eap_t *this, u_int8_t identifier)
378 {
379 this->identifier = identifier;
380 }
381
382 METHOD(tls_eap_t, destroy, void,
383 private_tls_eap_t *this)
384 {
385 this->tls->destroy(this->tls);
386 free(this);
387 }
388
389 /**
390 * See header
391 */
392 tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size,
393 int max_msg_count)
394 {
395 private_tls_eap_t *this;
396
397 if (!tls)
398 {
399 return NULL;
400 }
401
402 INIT(this,
403 .public = {
404 .initiate = _initiate,
405 .process = _process,
406 .get_msk = _get_msk,
407 .get_identifier = _get_identifier,
408 .set_identifier = _set_identifier,
409 .destroy = _destroy,
410 },
411 .type = type,
412 .is_server = tls->is_server(tls),
413 .first_fragment = TRUE,
414 .frag_size = frag_size,
415 .max_msg_count = max_msg_count,
416 .tls = tls,
417 );
418
419 if (this->is_server)
420 {
421 do
422 { /* start with non-zero random identifier */
423 this->identifier = random();
424 }
425 while (!this->identifier);
426 }
427
428 return &this->public;
429 }