3 * Copyright (C) 2010 Martin Willi
5 * Copyright (C) secunet Security Networks AG
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 #include <utils/debug.h>
26 * Size limit for a TLS message allowing for worst-case protection overhead
27 * according to section 6.2.3. "Payload Protection" of RFC 5246 TLS 1.2
29 #define TLS_MAX_MESSAGE_LEN 4 * (TLS_MAX_FRAGMENT_LEN + 2048)
31 typedef struct private_tls_eap_t private_tls_eap_t
;
34 * Private data of an tls_eap_t object.
36 struct private_tls_eap_t
{
39 * Public tls_eap_t interface.
44 * Type of EAP method, EAP-TLS, EAP-TTLS, or EAP-TNC
49 * Current value of EAP identifier
64 * Supported version of the EAP tunnel protocol
66 uint8_t supported_version
;
69 * If FALSE include the total length of an EAP message
70 * in the first fragment of fragmented messages only.
71 * If TRUE also include the length in non-fragmented messages.
76 * First fragment of a multi-fragment record?
81 * Maximum size of an outgoing EAP-TLS fragment
86 * Number of EAP messages/fragments processed so far
91 * Maximum number of processed EAP messages/fragments
97 * Flags of an EAP-TLS/TTLS/TNC message
100 EAP_TLS_LENGTH
= (1<<7), /* shared with EAP-TTLS/TNC/PEAP */
101 EAP_TLS_MORE_FRAGS
= (1<<6), /* shared with EAP-TTLS/TNC/PEAP */
102 EAP_TLS_START
= (1<<5), /* shared with EAP-TTLS/TNC/PEAP */
103 EAP_TTLS_VERSION
= (0x07), /* shared with EAP-TNC/PEAP/PT-EAP */
104 EAP_PT_START
= (1<<7) /* PT-EAP only */
107 #define EAP_TTLS_SUPPORTED_VERSION 0
108 #define EAP_TNC_SUPPORTED_VERSION 1
109 #define EAP_PEAP_SUPPORTED_VERSION 0
110 #define EAP_PT_EAP_SUPPORTED_VERSION 1
113 * EAP-TLS/TTLS packet format
115 typedef struct __attribute__((packed
)) {
123 METHOD(tls_eap_t
, initiate
, status_t
,
124 private_tls_eap_t
*this, chunk_t
*out
)
128 eap_tls_packet_t pkt
= {
131 .flags
= this->supported_version
139 pkt
.flags
|= EAP_TLS_START
;
142 pkt
.flags
|= EAP_PT_START
;
147 htoun16(&pkt
.length
, sizeof(eap_tls_packet_t
));
148 pkt
.identifier
= this->identifier
;
150 *out
= chunk_clone(chunk_from_thing(pkt
));
151 DBG2(DBG_TLS
, "sending %N start packet (%u bytes)",
152 eap_type_names
, this->type
, sizeof(eap_tls_packet_t
));
153 DBG3(DBG_TLS
, "%B", out
);
160 * Process a received packet
162 static status_t
process_pkt(private_tls_eap_t
*this, eap_tls_packet_t
*pkt
)
167 size_t msg_len_offset
= 0;
169 /* EAP-TLS doesn't have a version field */
170 if (this->type
!= EAP_TLS
)
172 version
= pkt
->flags
& EAP_TTLS_VERSION
;
173 if (version
!= this->supported_version
)
175 DBG1(DBG_TLS
, "received %N packet with unsupported version v%u",
176 eap_type_names
, this->type
, version
);
180 pkt_len
= untoh16(&pkt
->length
);
182 if (this->type
!= EAP_PT_EAP
&& (pkt
->flags
& EAP_TLS_LENGTH
))
184 if (pkt_len
< sizeof(eap_tls_packet_t
) + sizeof(msg_len
))
186 DBG1(DBG_TLS
, "%N packet too short", eap_type_names
, this->type
);
189 msg_len
= untoh32(pkt
+ 1);
190 if (msg_len
< pkt_len
- sizeof(eap_tls_packet_t
) - sizeof(msg_len
) ||
191 msg_len
> TLS_MAX_MESSAGE_LEN
)
193 DBG1(DBG_TLS
, "invalid %N packet length (%u bytes)", eap_type_names
,
194 this->type
, msg_len
);
197 msg_len_offset
= sizeof(msg_len
);
200 return this->tls
->process(this->tls
, (char*)(pkt
+ 1) + msg_len_offset
,
201 pkt_len
- sizeof(eap_tls_packet_t
) - msg_len_offset
);
205 * Build a packet to send
207 static status_t
build_pkt(private_tls_eap_t
*this, chunk_t
*out
)
209 char buf
[this->frag_size
];
210 eap_tls_packet_t
*pkt
;
211 size_t len
, reclen
, msg_len_offset
;
219 pkt
= (eap_tls_packet_t
*)buf
;
220 pkt
->code
= this->is_server
? EAP_REQUEST
: EAP_RESPONSE
;
221 pkt
->identifier
= this->identifier
;
222 pkt
->type
= this->type
;
223 pkt
->flags
= this->supported_version
;
225 if (this->first_fragment
)
227 len
= sizeof(buf
) - sizeof(eap_tls_packet_t
) - sizeof(uint32_t);
228 msg_len_offset
= sizeof(uint32_t);
232 len
= sizeof(buf
) - sizeof(eap_tls_packet_t
);
235 status
= this->tls
->build(this->tls
, buf
+ sizeof(eap_tls_packet_t
) +
236 msg_len_offset
, &len
, &reclen
);
241 pkt
->flags
|= EAP_TLS_MORE_FRAGS
;
242 kind
= "further fragment";
243 if (this->first_fragment
)
245 pkt
->flags
|= EAP_TLS_LENGTH
;
246 this->first_fragment
= FALSE
;
247 kind
= "first fragment";
251 if (this->first_fragment
)
253 if (this->include_length
)
255 pkt
->flags
|= EAP_TLS_LENGTH
;
259 else if (this->type
!= EAP_TNC
&& this->type
!= EAP_PT_EAP
)
261 this->first_fragment
= TRUE
;
262 kind
= "final fragment";
274 if (pkt
->flags
& EAP_TLS_LENGTH
)
276 htoun32(pkt
+ 1, reclen
);
277 len
+= sizeof(uint32_t);
278 pkt
->flags
|= EAP_TLS_LENGTH
;
282 /* get rid of the reserved length field */
283 memmove(buf
+ sizeof(eap_tls_packet_t
),
284 buf
+ sizeof(eap_tls_packet_t
) + sizeof(uint32_t), len
);
287 len
+= sizeof(eap_tls_packet_t
);
288 htoun16(&pkt
->length
, len
);
289 *out
= chunk_clone(chunk_create(buf
, len
));
290 DBG2(DBG_TLS
, "sending %N %s (%u bytes)",
291 eap_type_names
, this->type
, kind
, len
);
292 DBG3(DBG_TLS
, "%B", out
);
297 * Send an ack to request next fragment
299 static chunk_t
create_ack(private_tls_eap_t
*this)
301 eap_tls_packet_t pkt
= {
302 .code
= this->is_server
? EAP_REQUEST
: EAP_RESPONSE
,
310 pkt
.identifier
= this->identifier
;
311 htoun16(&pkt
.length
, sizeof(pkt
));
316 pkt
.flags
|= EAP_TTLS_SUPPORTED_VERSION
;
319 pkt
.flags
|= EAP_TNC_SUPPORTED_VERSION
;
322 pkt
.flags
|= EAP_PEAP_SUPPORTED_VERSION
;
327 DBG2(DBG_TLS
, "sending %N acknowledgment packet",
328 eap_type_names
, this->type
);
329 return chunk_clone(chunk_from_thing(pkt
));
332 METHOD(tls_eap_t
, process
, status_t
,
333 private_tls_eap_t
*this, chunk_t in
, chunk_t
*out
)
335 eap_tls_packet_t
*pkt
;
338 if (this->max_msg_count
&& ++this->processed
> this->max_msg_count
)
340 DBG1(DBG_TLS
, "%N packet count exceeded (%d > %d)",
341 eap_type_names
, this->type
,
342 this->processed
, this->max_msg_count
);
346 pkt
= (eap_tls_packet_t
*)in
.ptr
;
347 if (in
.len
< sizeof(eap_tls_packet_t
) || untoh16(&pkt
->length
) != in
.len
)
349 DBG1(DBG_TLS
, "invalid %N packet length", eap_type_names
, this->type
);
353 /* update EAP identifier */
354 if (!this->is_server
)
356 this->identifier
= pkt
->identifier
;
358 DBG3(DBG_TLS
, "%N payload %B", eap_type_names
, this->type
, &in
);
360 if ((this->type
== EAP_PT_EAP
&& (pkt
->flags
& EAP_PT_START
)) ||
361 (pkt
->flags
& EAP_TLS_START
))
363 if (this->type
== EAP_TTLS
|| this->type
== EAP_TNC
||
364 this->type
== EAP_PEAP
|| this->type
== EAP_PT_EAP
)
366 DBG1(DBG_TLS
, "%N version is v%u", eap_type_names
, this->type
,
367 pkt
->flags
& EAP_TTLS_VERSION
);
372 if (in
.len
== sizeof(eap_tls_packet_t
))
374 DBG2(DBG_TLS
, "received %N acknowledgment packet",
375 eap_type_names
, this->type
);
376 status
= build_pkt(this, out
);
377 if (status
== INVALID_STATE
&& this->tls
->is_complete(this->tls
))
383 status
= process_pkt(this, pkt
);
389 return this->tls
->is_complete(this->tls
) ? SUCCESS
: FAILED
;
394 status
= build_pkt(this, out
);
398 if (this->is_server
&& this->tls
->is_complete(this->tls
))
402 *out
= create_ack(this);
405 if (!this->is_server
)
407 *out
= create_ack(this);
416 METHOD(tls_eap_t
, get_msk
, chunk_t
,
417 private_tls_eap_t
*this)
419 return this->tls
->get_eap_msk(this->tls
);
422 METHOD(tls_eap_t
, get_identifier
, uint8_t,
423 private_tls_eap_t
*this)
425 return this->identifier
;
428 METHOD(tls_eap_t
, set_identifier
, void,
429 private_tls_eap_t
*this, uint8_t identifier
)
431 this->identifier
= identifier
;
434 METHOD(tls_eap_t
, get_auth
, auth_cfg_t
*,
435 private_tls_eap_t
*this)
437 return this->tls
->get_auth(this->tls
);
440 METHOD(tls_eap_t
, destroy
, void,
441 private_tls_eap_t
*this)
443 this->tls
->destroy(this->tls
);
450 tls_eap_t
*tls_eap_create(eap_type_t type
, tls_t
*tls
, size_t frag_size
,
451 int max_msg_count
, bool include_length
)
453 private_tls_eap_t
*this;
462 .initiate
= _initiate
,
465 .get_identifier
= _get_identifier
,
466 .set_identifier
= _set_identifier
,
467 .get_auth
= _get_auth
,
471 .is_server
= tls
->is_server(tls
),
472 .first_fragment
= (type
!= EAP_TNC
&& type
!= EAP_PT_EAP
),
473 .frag_size
= frag_size
,
474 .max_msg_count
= max_msg_count
,
475 .include_length
= include_length
,
482 this->supported_version
= EAP_TTLS_SUPPORTED_VERSION
;
485 this->supported_version
= EAP_TNC_SUPPORTED_VERSION
;
488 this->supported_version
= EAP_PEAP_SUPPORTED_VERSION
;
491 this->supported_version
= EAP_PT_EAP_SUPPORTED_VERSION
;
500 { /* start with non-zero random identifier */
501 this->identifier
= random();
503 while (!this->identifier
);
506 return &this->public;