Annotation of embedaddon/strongswan/src/libtls/tls_eap.c, revision 1.1.1.2
1.1 misho 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: uint8_t identifier;
51:
52: /**
53: * TLS stack
54: */
55: tls_t *tls;
56:
57: /**
58: * Role
59: */
60: bool is_server;
61:
62: /**
63: * Supported version of the EAP tunnel protocol
64: */
65: uint8_t supported_version;
66:
67: /**
68: * If FALSE include the total length of an EAP message
69: * in the first fragment of fragmented messages only.
70: * If TRUE also include the length in non-fragmented messages.
71: */
72: bool include_length;
73:
74: /**
75: * First fragment of a multi-fragment record?
76: */
77: bool first_fragment;
78:
79: /**
80: * Maximum size of an outgoing EAP-TLS fragment
81: */
82: size_t frag_size;
83:
84: /**
85: * Number of EAP messages/fragments processed so far
86: */
87: int processed;
88:
89: /**
90: * Maximum number of processed EAP messages/fragments
91: */
92: int max_msg_count;
93: };
94:
95: /**
96: * Flags of an EAP-TLS/TTLS/TNC message
97: */
98: typedef enum {
99: EAP_TLS_LENGTH = (1<<7), /* shared with EAP-TTLS/TNC/PEAP */
100: EAP_TLS_MORE_FRAGS = (1<<6), /* shared with EAP-TTLS/TNC/PEAP */
101: EAP_TLS_START = (1<<5), /* shared with EAP-TTLS/TNC/PEAP */
102: EAP_TTLS_VERSION = (0x07), /* shared with EAP-TNC/PEAP/PT-EAP */
103: EAP_PT_START = (1<<7) /* PT-EAP only */
104: } eap_tls_flags_t;
105:
106: #define EAP_TTLS_SUPPORTED_VERSION 0
107: #define EAP_TNC_SUPPORTED_VERSION 1
108: #define EAP_PEAP_SUPPORTED_VERSION 0
109: #define EAP_PT_EAP_SUPPORTED_VERSION 1
110:
111: /**
112: * EAP-TLS/TTLS packet format
113: */
114: typedef struct __attribute__((packed)) {
115: uint8_t code;
116: uint8_t identifier;
117: uint16_t length;
118: uint8_t type;
119: uint8_t flags;
120: } eap_tls_packet_t;
121:
122: METHOD(tls_eap_t, initiate, status_t,
123: private_tls_eap_t *this, chunk_t *out)
124: {
125: if (this->is_server)
126: {
127: eap_tls_packet_t pkt = {
128: .type = this->type,
129: .code = EAP_REQUEST,
130: .flags = this->supported_version
131: };
132: switch (this->type)
133: {
134: case EAP_TLS:
135: case EAP_TTLS:
136: case EAP_TNC:
137: case EAP_PEAP:
138: pkt.flags |= EAP_TLS_START;
139: break;
140: case EAP_PT_EAP:
141: pkt.flags |= EAP_PT_START;
142: break;
143: default:
144: break;
145: }
146: htoun16(&pkt.length, sizeof(eap_tls_packet_t));
147: pkt.identifier = this->identifier;
148:
149: *out = chunk_clone(chunk_from_thing(pkt));
150: DBG2(DBG_TLS, "sending %N start packet (%u bytes)",
151: eap_type_names, this->type, sizeof(eap_tls_packet_t));
152: DBG3(DBG_TLS, "%B", out);
153: return NEED_MORE;
154: }
155: return FAILED;
156: }
157:
158: /**
159: * Process a received packet
160: */
161: static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
162: {
163: uint8_t version;
164: uint16_t pkt_len;
165: uint32_t msg_len;
166: size_t msg_len_offset = 0;
167:
168: /* EAP-TLS doesn't have a version field */
169: if (this->type != EAP_TLS)
170: {
171: version = pkt->flags & EAP_TTLS_VERSION;
172: if (version != this->supported_version)
173: {
174: DBG1(DBG_TLS, "received %N packet with unsupported version v%u",
175: eap_type_names, this->type, version);
176: return FAILED;
177: }
178: }
179: pkt_len = untoh16(&pkt->length);
180:
181: if (this->type != EAP_PT_EAP && (pkt->flags & EAP_TLS_LENGTH))
182: {
183: if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
184: {
185: DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type);
186: return FAILED;
187: }
188: msg_len = untoh32(pkt + 1);
189: if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
190: msg_len > TLS_MAX_MESSAGE_LEN)
191: {
192: DBG1(DBG_TLS, "invalid %N packet length (%u bytes)", eap_type_names,
193: this->type, msg_len);
194: return FAILED;
195: }
196: msg_len_offset = sizeof(msg_len);
197: }
198:
199: return this->tls->process(this->tls, (char*)(pkt + 1) + msg_len_offset,
200: pkt_len - sizeof(eap_tls_packet_t) - msg_len_offset);
201: }
202:
203: /**
204: * Build a packet to send
205: */
206: static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
207: {
208: char buf[this->frag_size];
209: eap_tls_packet_t *pkt;
210: size_t len, reclen, msg_len_offset;
211: status_t status;
212: char *kind;
213:
214: if (this->is_server)
215: {
216: this->identifier++;
217: }
218: pkt = (eap_tls_packet_t*)buf;
219: pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
220: pkt->identifier = this->identifier;
221: pkt->type = this->type;
222: pkt->flags = this->supported_version;
223:
224: if (this->first_fragment)
225: {
226: len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(uint32_t);
227: msg_len_offset = sizeof(uint32_t);
228: }
229: else
230: {
231: len = sizeof(buf) - sizeof(eap_tls_packet_t);
232: msg_len_offset = 0;
233: }
234: status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +
235: msg_len_offset, &len, &reclen);
236:
237: switch (status)
238: {
239: case NEED_MORE:
240: pkt->flags |= EAP_TLS_MORE_FRAGS;
241: kind = "further fragment";
242: if (this->first_fragment)
243: {
244: pkt->flags |= EAP_TLS_LENGTH;
245: this->first_fragment = FALSE;
246: kind = "first fragment";
247: }
248: break;
249: case ALREADY_DONE:
250: if (this->first_fragment)
251: {
252: if (this->include_length)
253: {
254: pkt->flags |= EAP_TLS_LENGTH;
255: }
256: kind = "packet";
257: }
258: else if (this->type != EAP_TNC && this->type != EAP_PT_EAP)
259: {
260: this->first_fragment = TRUE;
261: kind = "final fragment";
262: }
263: else
264: {
265: kind = "packet";
266: }
267: break;
268: default:
269: return status;
270: }
271: if (reclen)
272: {
273: if (pkt->flags & EAP_TLS_LENGTH)
274: {
275: htoun32(pkt + 1, reclen);
276: len += sizeof(uint32_t);
277: pkt->flags |= EAP_TLS_LENGTH;
278: }
279: else
280: {
281: /* get rid of the reserved length field */
282: memmove(buf + sizeof(eap_tls_packet_t),
283: buf + sizeof(eap_tls_packet_t) + sizeof(uint32_t), len);
284: }
285: }
286: len += sizeof(eap_tls_packet_t);
287: htoun16(&pkt->length, len);
288: *out = chunk_clone(chunk_create(buf, len));
289: DBG2(DBG_TLS, "sending %N %s (%u bytes)",
290: eap_type_names, this->type, kind, len);
291: DBG3(DBG_TLS, "%B", out);
292: return NEED_MORE;
293: }
294:
295: /**
296: * Send an ack to request next fragment
297: */
298: static chunk_t create_ack(private_tls_eap_t *this)
299: {
300: eap_tls_packet_t pkt = {
301: .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
302: .type = this->type,
303: };
304:
305: if (this->is_server)
306: {
307: this->identifier++;
308: }
309: pkt.identifier = this->identifier;
310: htoun16(&pkt.length, sizeof(pkt));
311:
312: switch (this->type)
313: {
314: case EAP_TTLS:
315: pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
316: break;
317: case EAP_TNC:
318: pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
319: break;
320: case EAP_PEAP:
321: pkt.flags |= EAP_PEAP_SUPPORTED_VERSION;
322: break;
323: default:
324: break;
325: }
326: DBG2(DBG_TLS, "sending %N acknowledgement packet",
327: eap_type_names, this->type);
328: return chunk_clone(chunk_from_thing(pkt));
329: }
330:
331: METHOD(tls_eap_t, process, status_t,
332: private_tls_eap_t *this, chunk_t in, chunk_t *out)
333: {
334: eap_tls_packet_t *pkt;
335: status_t status;
336:
337: if (this->max_msg_count && ++this->processed > this->max_msg_count)
338: {
339: DBG1(DBG_TLS, "%N packet count exceeded (%d > %d)",
340: eap_type_names, this->type,
341: this->processed, this->max_msg_count);
342: return FAILED;
343: }
344:
345: pkt = (eap_tls_packet_t*)in.ptr;
346: if (in.len < sizeof(eap_tls_packet_t) || untoh16(&pkt->length) != in.len)
347: {
348: DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type);
349: return FAILED;
350: }
351:
352: /* update EAP identifier */
353: if (!this->is_server)
354: {
355: this->identifier = pkt->identifier;
356: }
357: DBG3(DBG_TLS, "%N payload %B", eap_type_names, this->type, &in);
358:
359: if ((this->type == EAP_PT_EAP && (pkt->flags & EAP_PT_START)) ||
360: (pkt->flags & EAP_TLS_START))
361: {
362: if (this->type == EAP_TTLS || this->type == EAP_TNC ||
363: this->type == EAP_PEAP || this->type == EAP_PT_EAP)
364: {
365: DBG1(DBG_TLS, "%N version is v%u", eap_type_names, this->type,
366: pkt->flags & EAP_TTLS_VERSION);
367: }
368: }
369: else
370: {
371: if (in.len == sizeof(eap_tls_packet_t))
372: {
373: DBG2(DBG_TLS, "received %N acknowledgement packet",
374: eap_type_names, this->type);
375: status = build_pkt(this, out);
376: if (status == INVALID_STATE && this->tls->is_complete(this->tls))
377: {
378: return SUCCESS;
379: }
380: return status;
381: }
382: status = process_pkt(this, pkt);
383: switch (status)
384: {
385: case NEED_MORE:
386: break;
387: case SUCCESS:
388: return this->tls->is_complete(this->tls) ? SUCCESS : FAILED;
389: default:
390: return status;
391: }
392: }
393: status = build_pkt(this, out);
394: switch (status)
395: {
396: case INVALID_STATE:
1.1.1.2 ! misho 397: if (this->is_server && this->tls->is_complete(this->tls))
! 398: {
! 399: return SUCCESS;
! 400: }
1.1 misho 401: *out = create_ack(this);
402: return NEED_MORE;
403: case FAILED:
404: if (!this->is_server)
405: {
406: *out = create_ack(this);
407: return NEED_MORE;
408: }
409: return FAILED;
410: default:
411: return status;
412: }
413: }
414:
415: METHOD(tls_eap_t, get_msk, chunk_t,
416: private_tls_eap_t *this)
417: {
418: return this->tls->get_eap_msk(this->tls);
419: }
420:
421: METHOD(tls_eap_t, get_identifier, uint8_t,
422: private_tls_eap_t *this)
423: {
424: return this->identifier;
425: }
426:
427: METHOD(tls_eap_t, set_identifier, void,
428: private_tls_eap_t *this, uint8_t identifier)
429: {
430: this->identifier = identifier;
431: }
432:
433: METHOD(tls_eap_t, get_auth, auth_cfg_t*,
434: private_tls_eap_t *this)
435: {
436: return this->tls->get_auth(this->tls);
437: }
438:
439: METHOD(tls_eap_t, destroy, void,
440: private_tls_eap_t *this)
441: {
442: this->tls->destroy(this->tls);
443: free(this);
444: }
445:
446: /**
447: * See header
448: */
449: tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size,
450: int max_msg_count, bool include_length)
451: {
452: private_tls_eap_t *this;
453:
454: if (!tls)
455: {
456: return NULL;
457: }
458:
459: INIT(this,
460: .public = {
461: .initiate = _initiate,
462: .process = _process,
463: .get_msk = _get_msk,
464: .get_identifier = _get_identifier,
465: .set_identifier = _set_identifier,
466: .get_auth = _get_auth,
467: .destroy = _destroy,
468: },
469: .type = type,
470: .is_server = tls->is_server(tls),
471: .first_fragment = (type != EAP_TNC && type != EAP_PT_EAP),
472: .frag_size = frag_size,
473: .max_msg_count = max_msg_count,
474: .include_length = include_length,
475: .tls = tls,
476: );
477:
478: switch (type)
479: {
480: case EAP_TTLS:
481: this->supported_version = EAP_TTLS_SUPPORTED_VERSION;
482: break;
483: case EAP_TNC:
484: this->supported_version = EAP_TNC_SUPPORTED_VERSION;
485: break;
486: case EAP_PEAP:
487: this->supported_version = EAP_PEAP_SUPPORTED_VERSION;
488: break;
489: case EAP_PT_EAP:
490: this->supported_version = EAP_PT_EAP_SUPPORTED_VERSION;
491: break;
492: default:
493: break;
494: }
495:
496: if (this->is_server)
497: {
498: do
499: { /* start with non-zero random identifier */
500: this->identifier = random();
501: }
502: while (!this->identifier);
503: }
504:
505: return &this->public;
506: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>