2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
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>.
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
23 typedef struct private_eap_tls_t private_eap_tls_t
;
26 * Private data of an eap_tls_t object.
28 struct private_eap_tls_t
{
36 * Number of EAP-TLS messages processed so far
41 * Is this method instance acting as server?
51 * Allocated input buffer
56 * Number of bytes read in input buffer
61 * Allocated ouput buffer
66 * Number of bytes sent from output buffer
71 /** Size limit for a single TLS message */
72 #define MAX_TLS_MESSAGE_LEN 16384
73 /** Size of a EAP-TLS fragment */
74 #define EAP_TLS_FRAGMENT_LEN 1014
75 /** Maximum number of EAP-TLS messages/fragments allowed */
76 #define MAX_EAP_TLS_MESSAGE_COUNT 16
79 * Flags of an EAP-TLS message
82 EAP_TLS_LENGTH
= (1<<7),
83 EAP_TLS_MORE_FRAGS
= (1<<6),
84 EAP_TLS_START
= (1<<5),
88 * EAP-TLS packet format
90 typedef struct __attribute__((packed
)) {
101 typedef struct __attribute__((packed
)) {
108 METHOD(eap_method_t
, initiate
, status_t
,
109 private_eap_tls_t
*this, eap_payload_t
**out
)
113 eap_tls_packet_t pkt
= {
116 .flags
= EAP_TLS_START
,
118 htoun16(&pkt
.length
, sizeof(eap_tls_packet_t
));
119 /* start with non-zero random identifier */
121 pkt
.identifier
= random();
122 } while (!pkt
.identifier
);
124 *out
= eap_payload_create_data(chunk_from_thing(pkt
));
131 * Write received TLS data to the input buffer
133 static bool write_buf(private_eap_tls_t
*this, eap_tls_packet_t
*pkt
)
139 pkt_len
= untoh16(&pkt
->length
);
141 if (pkt
->flags
& EAP_TLS_LENGTH
)
143 if (pkt_len
< sizeof(eap_tls_packet_t
) + sizeof(msg_len
))
145 DBG1(DBG_IKE
, "EAP-TLS packet too short");
148 msg_len
= untoh32(pkt
+ 1);
149 if (msg_len
< pkt_len
- sizeof(eap_tls_packet_t
) - sizeof(msg_len
) ||
150 msg_len
> MAX_TLS_MESSAGE_LEN
)
152 DBG1(DBG_IKE
, "invalid EAP-TLS packet length");
157 if (msg_len
!= this->input
.len
)
159 DBG1(DBG_IKE
, "received unexpected TLS message length");
165 this->input
= chunk_alloc(msg_len
);
168 data
= chunk_create((char*)(pkt
+ 1) + sizeof(msg_len
),
169 pkt_len
- sizeof(eap_tls_packet_t
) - sizeof(msg_len
));
173 data
= chunk_create((char*)(pkt
+ 1),
174 pkt_len
- sizeof(eap_tls_packet_t
));
176 if (data
.len
> this->input
.len
- this->inpos
)
178 DBG1(DBG_IKE
, "EAP-TLS fragment exceeds TLS message length");
181 memcpy(this->input
.ptr
+ this->inpos
, data
.ptr
, data
.len
);
182 this->inpos
+= data
.len
;
187 * Send an ack to request next fragment
189 static eap_payload_t
*create_ack(private_eap_tls_t
*this, u_int8_t identifier
)
191 eap_tls_packet_t pkt
= {
192 .code
= this->is_server
? EAP_REQUEST
: EAP_RESPONSE
,
193 .identifier
= this->is_server
? identifier
+ 1 : identifier
,
196 htoun16(&pkt
.length
, sizeof(pkt
));
198 return eap_payload_create_data(chunk_from_thing(pkt
));
202 * Create a eap response from data in the TLS output buffer
204 static eap_payload_t
*read_buf(private_eap_tls_t
*this, u_int8_t identifier
)
206 char buf
[EAP_TLS_FRAGMENT_LEN
+ sizeof(eap_tls_packet_t
) + 4], *start
;
207 eap_tls_packet_t
*pkt
= (eap_tls_packet_t
*)buf
;
208 u_int16_t pkt_len
= sizeof(eap_tls_packet_t
);
210 pkt
->code
= this->is_server
? EAP_REQUEST
: EAP_RESPONSE
;
211 pkt
->identifier
= this->is_server
? identifier
+ 1 : identifier
;
215 if (this->output
.len
)
217 start
= (char*)(pkt
+ 1);
218 if (this->outpos
== 0)
219 { /* first fragment */
220 pkt
->flags
= EAP_TLS_LENGTH
;
223 htoun32(pkt
+ 1, this->output
.len
);
226 if (this->output
.len
- this->outpos
> EAP_TLS_FRAGMENT_LEN
)
228 pkt
->flags
|= EAP_TLS_MORE_FRAGS
;
229 pkt_len
+= EAP_TLS_FRAGMENT_LEN
;
230 memcpy(start
, this->output
.ptr
+ this->outpos
, EAP_TLS_FRAGMENT_LEN
);
231 this->outpos
+= EAP_TLS_FRAGMENT_LEN
;
235 pkt_len
+= this->output
.len
- this->outpos
;
236 memcpy(start
, this->output
.ptr
+ this->outpos
,
237 this->output
.len
- this->outpos
);
238 chunk_free(&this->output
);
242 htoun16(&pkt
->length
, pkt_len
);
243 return eap_payload_create_data(chunk_create(buf
, pkt_len
));
247 * Pass data in input buffer to upper layers, write result to output buffer
249 static status_t
process_buf(private_eap_tls_t
*this)
251 tls_record_t
*in
, out
;
256 /* pass input buffer to upper layer, record for record */
258 while (data
.len
> sizeof(tls_record_t
))
260 in
= (tls_record_t
*)data
.ptr
;
261 len
= untoh16(&in
->length
);
262 if (len
> data
.len
- sizeof(tls_record_t
))
264 DBG1(DBG_IKE
, "TLS record length invalid");
267 if (untoh16(&in
->version
) < TLS_1_0
)
269 DBG1(DBG_IKE
, "%N invalid with EAP-TLS",
270 tls_version_names
, untoh16(&in
->version
));
274 status
= this->tls
->process(this->tls
, in
->type
,
275 chunk_create(in
->data
, len
));
276 if (status
!= NEED_MORE
)
280 data
= chunk_skip(data
, len
+ sizeof(tls_record_t
));
282 chunk_free(&this->input
);
285 /* read in records from upper layer, append to output buffer */
286 chunk_free(&this->output
);
289 tls_content_type_t type
;
290 chunk_t header
= chunk_from_thing(out
);
292 status
= this->tls
->build(this->tls
, &type
, &data
);
298 /* invalid state means we need more input from peer first */
307 htoun16(&out
.version
, this->tls
->get_version(this->tls
));
308 htoun16(&out
.length
, data
.len
);
309 this->output
= chunk_cat("mcm", this->output
, header
, data
);
313 METHOD(eap_method_t
, process
, status_t
,
314 private_eap_tls_t
*this, eap_payload_t
*in
, eap_payload_t
**out
)
316 eap_tls_packet_t
*pkt
;
320 if (++this->processed
> MAX_EAP_TLS_MESSAGE_COUNT
)
322 DBG1(DBG_IKE
, "EAP-TLS packet count exceeded");
326 data
= in
->get_data(in
);
328 pkt
= (eap_tls_packet_t
*)data
.ptr
;
329 if (data
.len
< sizeof(eap_tls_packet_t
) ||
330 untoh16(&pkt
->length
) != data
.len
)
332 DBG1(DBG_IKE
, "invalid EAP-TLS packet length");
335 if (!(pkt
->flags
& EAP_TLS_START
))
337 if (data
.len
== sizeof(eap_tls_packet_t
))
339 if (this->output
.len
)
340 { /* ACK to our fragment, send next */
341 *out
= read_buf(this, pkt
->identifier
);
344 if (this->tls
->is_complete(this->tls
))
350 if (!write_buf(this, pkt
))
354 if (pkt
->flags
& EAP_TLS_MORE_FRAGS
)
355 { /* more fragments follow */
356 *out
= create_ack(this, pkt
->identifier
);
359 else if (this->input
.len
!= this->inpos
)
361 DBG1(DBG_IKE
, "defragemented TLS message has invalid length");
365 status
= process_buf(this);
366 if (status
== NEED_MORE
)
368 *out
= read_buf(this, pkt
->identifier
);
373 METHOD(eap_method_t
, get_type
, eap_type_t
,
374 private_eap_tls_t
*this, u_int32_t
*vendor
)
380 METHOD(eap_method_t
, get_msk
, status_t
,
381 private_eap_tls_t
*this, chunk_t
*msk
)
383 *msk
= this->tls
->get_eap_msk(this->tls
);
391 METHOD(eap_method_t
, is_mutual
, bool,
392 private_eap_tls_t
*this)
397 METHOD(eap_method_t
, destroy
, void,
398 private_eap_tls_t
*this)
400 free(this->input
.ptr
);
401 free(this->output
.ptr
);
403 this->tls
->destroy(this->tls
);
409 * Generic private constructor
411 static eap_tls_t
*eap_tls_create(identification_t
*server
,
412 identification_t
*peer
, bool is_server
)
414 private_eap_tls_t
*this;
417 .public.eap_method
= {
418 .initiate
= _initiate
,
420 .get_type
= _get_type
,
421 .is_mutual
= _is_mutual
,
425 .is_server
= is_server
,
427 this->tls
= tls_create(is_server
, server
, peer
);
429 return &this->public;
432 eap_tls_t
*eap_tls_create_server(identification_t
*server
,
433 identification_t
*peer
)
435 return eap_tls_create(server
, peer
, TRUE
);
438 eap_tls_t
*eap_tls_create_peer(identification_t
*server
,
439 identification_t
*peer
)
441 return eap_tls_create(server
, peer
, FALSE
);