3 * Copyright (C) 2010 Martin Willi
4 * Copyright (C) 2010 revosec AG
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>.
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
21 #include <utils/debug.h>
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
28 #define TLS_MAX_MESSAGE_LEN 4 * (TLS_MAX_FRAGMENT_LEN + 2048)
30 typedef struct private_tls_eap_t private_tls_eap_t
;
33 * Private data of an tls_eap_t object.
35 struct private_tls_eap_t
{
38 * Public tls_eap_t interface.
43 * Type of EAP method, EAP-TLS, EAP-TTLS, or EAP-TNC
48 * Current value of EAP identifier
63 * If FALSE include the total length of an EAP message
64 * in the first fragment of fragmented messages only.
65 * If TRUE also include the length in non-fragmented messages.
70 * First fragment of a multi-fragment record?
75 * Maximum size of an outgoing EAP-TLS fragment
80 * Number of EAP messages/fragments processed so far
85 * Maximum number of processed EAP messages/fragments
91 * Flags of an EAP-TLS/TTLS/TNC message
94 EAP_TLS_LENGTH
= (1<<7), /* shared with EAP-TTLS/TNC/PEAP */
95 EAP_TLS_MORE_FRAGS
= (1<<6), /* shared with EAP-TTLS/TNC/PEAP */
96 EAP_TLS_START
= (1<<5), /* shared with EAP-TTLS/TNC/PEAP */
97 EAP_TTLS_VERSION
= (0x07), /* shared with EAP-TNC/PEAP */
100 #define EAP_TTLS_SUPPORTED_VERSION 0
101 #define EAP_TNC_SUPPORTED_VERSION 1
102 #define EAP_PEAP_SUPPORTED_VERSION 0
105 * EAP-TLS/TTLS packet format
107 typedef struct __attribute__((packed
)) {
115 METHOD(tls_eap_t
, initiate
, status_t
,
116 private_tls_eap_t
*this, chunk_t
*out
)
120 eap_tls_packet_t pkt
= {
123 .flags
= EAP_TLS_START
,
128 pkt
.flags
|= EAP_TTLS_SUPPORTED_VERSION
;
131 pkt
.flags
|= EAP_TNC_SUPPORTED_VERSION
;
134 pkt
.flags
|= EAP_PEAP_SUPPORTED_VERSION
;
139 htoun16(&pkt
.length
, sizeof(eap_tls_packet_t
));
140 pkt
.identifier
= this->identifier
;
142 *out
= chunk_clone(chunk_from_thing(pkt
));
143 DBG2(DBG_TLS
, "sending %N start packet (%u bytes)",
144 eap_type_names
, this->type
, sizeof(eap_tls_packet_t
));
145 DBG3(DBG_TLS
, "%B", out
);
152 * Process a received packet
154 static status_t
process_pkt(private_tls_eap_t
*this, eap_tls_packet_t
*pkt
)
158 size_t msg_len_offset
= 0;
160 pkt_len
= untoh16(&pkt
->length
);
162 if (pkt
->flags
& EAP_TLS_LENGTH
)
164 if (pkt_len
< sizeof(eap_tls_packet_t
) + sizeof(msg_len
))
166 DBG1(DBG_TLS
, "%N packet too short", eap_type_names
, this->type
);
169 msg_len
= untoh32(pkt
+ 1);
170 if (msg_len
< pkt_len
- sizeof(eap_tls_packet_t
) - sizeof(msg_len
) ||
171 msg_len
> TLS_MAX_MESSAGE_LEN
)
173 DBG1(DBG_TLS
, "invalid %N packet length (%u bytes)", eap_type_names
,
174 this->type
, msg_len
);
177 msg_len_offset
= sizeof(msg_len
);
180 return this->tls
->process(this->tls
, (char*)(pkt
+ 1) + msg_len_offset
,
181 pkt_len
- sizeof(eap_tls_packet_t
) - msg_len_offset
);
185 * Build a packet to send
187 static status_t
build_pkt(private_tls_eap_t
*this, chunk_t
*out
)
189 char buf
[this->frag_size
];
190 eap_tls_packet_t
*pkt
;
191 size_t len
, reclen
, msg_len_offset
;
199 pkt
= (eap_tls_packet_t
*)buf
;
200 pkt
->code
= this->is_server
? EAP_REQUEST
: EAP_RESPONSE
;
201 pkt
->identifier
= this->identifier
;
202 pkt
->type
= this->type
;
208 pkt
->flags
|= EAP_TTLS_SUPPORTED_VERSION
;
211 pkt
->flags
|= EAP_TNC_SUPPORTED_VERSION
;
214 pkt
->flags
|= EAP_PEAP_SUPPORTED_VERSION
;
220 if (this->first_fragment
)
222 len
= sizeof(buf
) - sizeof(eap_tls_packet_t
) - sizeof(u_int32_t
);
223 msg_len_offset
= sizeof(u_int32_t
);
227 len
= sizeof(buf
) - sizeof(eap_tls_packet_t
);
230 status
= this->tls
->build(this->tls
, buf
+ sizeof(eap_tls_packet_t
) +
231 msg_len_offset
, &len
, &reclen
);
236 pkt
->flags
|= EAP_TLS_MORE_FRAGS
;
237 kind
= "further fragment";
238 if (this->first_fragment
)
240 pkt
->flags
|= EAP_TLS_LENGTH
;
241 this->first_fragment
= FALSE
;
242 kind
= "first fragment";
246 if (this->first_fragment
)
248 if (this->include_length
)
250 pkt
->flags
|= EAP_TLS_LENGTH
;
254 else if (this->type
!= EAP_TNC
)
256 this->first_fragment
= TRUE
;
257 kind
= "final fragment";
269 if (pkt
->flags
& EAP_TLS_LENGTH
)
271 htoun32(pkt
+ 1, reclen
);
272 len
+= sizeof(u_int32_t
);
273 pkt
->flags
|= EAP_TLS_LENGTH
;
277 /* get rid of the reserved length field */
278 memmove(buf
+ sizeof(eap_tls_packet_t
),
279 buf
+ sizeof(eap_tls_packet_t
) + sizeof(u_int32_t
), len
);
282 len
+= sizeof(eap_tls_packet_t
);
283 htoun16(&pkt
->length
, len
);
284 *out
= chunk_clone(chunk_create(buf
, len
));
285 DBG2(DBG_TLS
, "sending %N %s (%u bytes)",
286 eap_type_names
, this->type
, kind
, len
);
287 DBG3(DBG_TLS
, "%B", out
);
292 * Send an ack to request next fragment
294 static chunk_t
create_ack(private_tls_eap_t
*this)
296 eap_tls_packet_t pkt
= {
297 .code
= this->is_server
? EAP_REQUEST
: EAP_RESPONSE
,
305 pkt
.identifier
= this->identifier
;
306 htoun16(&pkt
.length
, sizeof(pkt
));
311 pkt
.flags
|= EAP_TTLS_SUPPORTED_VERSION
;
314 pkt
.flags
|= EAP_TNC_SUPPORTED_VERSION
;
317 pkt
.flags
|= EAP_PEAP_SUPPORTED_VERSION
;
322 DBG2(DBG_TLS
, "sending %N acknowledgement packet",
323 eap_type_names
, this->type
);
324 return chunk_clone(chunk_from_thing(pkt
));
327 METHOD(tls_eap_t
, process
, status_t
,
328 private_tls_eap_t
*this, chunk_t in
, chunk_t
*out
)
330 eap_tls_packet_t
*pkt
;
333 if (this->max_msg_count
&& ++this->processed
> this->max_msg_count
)
335 DBG1(DBG_TLS
, "%N packet count exceeded (%d > %d)",
336 eap_type_names
, this->type
,
337 this->processed
, this->max_msg_count
);
341 pkt
= (eap_tls_packet_t
*)in
.ptr
;
342 if (in
.len
< sizeof(eap_tls_packet_t
) || untoh16(&pkt
->length
) != in
.len
)
344 DBG1(DBG_TLS
, "invalid %N packet length", eap_type_names
, this->type
);
348 /* update EAP identifier */
349 if (!this->is_server
)
351 this->identifier
= pkt
->identifier
;
353 DBG3(DBG_TLS
, "%N payload %B", eap_type_names
, this->type
, &in
);
355 if (pkt
->flags
& EAP_TLS_START
)
357 if (this->type
== EAP_TTLS
|| this->type
== EAP_TNC
||
358 this->type
== EAP_PEAP
)
360 DBG1(DBG_TLS
, "%N version is v%u", eap_type_names
, this->type
,
361 pkt
->flags
& EAP_TTLS_VERSION
);
366 if (in
.len
== sizeof(eap_tls_packet_t
))
368 DBG2(DBG_TLS
, "received %N acknowledgement packet",
369 eap_type_names
, this->type
);
370 status
= build_pkt(this, out
);
371 if (status
== INVALID_STATE
&& this->tls
->is_complete(this->tls
))
377 status
= process_pkt(this, pkt
);
383 return this->tls
->is_complete(this->tls
) ? SUCCESS
: FAILED
;
388 status
= build_pkt(this, out
);
392 *out
= create_ack(this);
395 if (!this->is_server
)
397 *out
= create_ack(this);
406 METHOD(tls_eap_t
, get_msk
, chunk_t
,
407 private_tls_eap_t
*this)
409 return this->tls
->get_eap_msk(this->tls
);
412 METHOD(tls_eap_t
, get_identifier
, u_int8_t
,
413 private_tls_eap_t
*this)
415 return this->identifier
;
418 METHOD(tls_eap_t
, set_identifier
, void,
419 private_tls_eap_t
*this, u_int8_t identifier
)
421 this->identifier
= identifier
;
424 METHOD(tls_eap_t
, destroy
, void,
425 private_tls_eap_t
*this)
427 this->tls
->destroy(this->tls
);
434 tls_eap_t
*tls_eap_create(eap_type_t type
, tls_t
*tls
, size_t frag_size
,
435 int max_msg_count
, bool include_length
)
437 private_tls_eap_t
*this;
446 .initiate
= _initiate
,
449 .get_identifier
= _get_identifier
,
450 .set_identifier
= _set_identifier
,
454 .is_server
= tls
->is_server(tls
),
455 .first_fragment
= (type
!= EAP_TNC
),
456 .frag_size
= frag_size
,
457 .max_msg_count
= max_msg_count
,
458 .include_length
= include_length
,
465 { /* start with non-zero random identifier */
466 this->identifier
= random();
468 while (!this->identifier
);
471 return &this->public;