Properly send empty EAP-TLS messages
[strongswan.git] / src / charon / plugins / eap_tls / eap_tls.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 "eap_tls.h"
17
18 #include "tls/tls.h"
19
20 #include <daemon.h>
21 #include <library.h>
22
23 typedef struct private_eap_tls_t private_eap_tls_t;
24
25 /**
26 * Private data of an eap_tls_t object.
27 */
28 struct private_eap_tls_t {
29
30 /**
31 * Public interface.
32 */
33 eap_tls_t public;
34
35 /**
36 * ID of the server
37 */
38 identification_t *server;
39
40 /**
41 * ID of the peer
42 */
43 identification_t *peer;
44
45 /**
46 * Is this method instance acting as server?
47 */
48 bool is_server;
49
50 /**
51 * TLS layers
52 */
53 tls_t *tls;
54
55 /**
56 * Allocated input buffer
57 */
58 chunk_t input;
59
60 /**
61 * Number of bytes read in input buffer
62 */
63 size_t inpos;
64
65 /**
66 * Allocated ouput buffer
67 */
68 chunk_t output;
69
70 /**
71 * Number of bytes sent from output buffer
72 */
73 size_t outpos;
74 };
75
76 /** Size limit for a single TLS message */
77 #define MAX_TLS_MESSAGE_LEN 16384
78 /** Size of a EAP-TLS fragment */
79 #define EAP_TLS_FRAGMENT_LEN 1014
80
81 /**
82 * Flags of an EAP-TLS message
83 */
84 typedef enum {
85 EAP_TLS_LENGTH = (1<<7),
86 EAP_TLS_MORE_FRAGS = (1<<6),
87 EAP_TLS_START = (1<<5),
88 } eap_tls_flags_t;
89
90 /**
91 * EAP-TLS packet format
92 */
93 typedef struct __attribute__((packed)) {
94 u_int8_t code;
95 u_int8_t identifier;
96 u_int16_t length;
97 u_int8_t type;
98 u_int8_t flags;
99 } eap_tls_packet_t;
100
101 /**
102 * TLS record
103 */
104 typedef struct __attribute__((packed)) {
105 u_int8_t type;
106 u_int16_t version;
107 u_int16_t length;
108 char data[];
109 } tls_record_t;
110
111 METHOD(eap_method_t, initiate, status_t,
112 private_eap_tls_t *this, eap_payload_t **out)
113 {
114 if (this->is_server)
115 {
116 eap_tls_packet_t pkt = {
117 .type = EAP_TLS,
118 .code = EAP_REQUEST,
119 .flags = EAP_TLS_START,
120 };
121 htoun16(&pkt.length, sizeof(eap_tls_packet_t));
122 /* start with non-zero random identifier */
123 do {
124 pkt.identifier = random();
125 } while (!pkt.identifier);
126
127 *out = eap_payload_create_data(chunk_from_thing(pkt));
128 return NEED_MORE;
129 }
130 return FAILED;
131 }
132
133 /**
134 * Write received TLS data to the input buffer
135 */
136 static bool write_buf(private_eap_tls_t *this, eap_tls_packet_t *pkt)
137 {
138 u_int32_t msg_len;
139 u_int16_t pkt_len;
140 chunk_t data;
141
142 pkt_len = untoh16(&pkt->length);
143
144 if (pkt->flags & EAP_TLS_LENGTH)
145 {
146 if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
147 {
148 DBG1(DBG_IKE, "EAP-TLS packet too short");
149 return FALSE;
150 }
151 msg_len = untoh32(pkt + 1);
152 if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
153 msg_len > MAX_TLS_MESSAGE_LEN)
154 {
155 DBG1(DBG_IKE, "invalid EAP-TLS packet length");
156 return FALSE;
157 }
158 if (this->input.ptr)
159 {
160 if (msg_len != this->input.len)
161 {
162 DBG1(DBG_IKE, "received unexpected TLS message length");
163 return FALSE;
164 }
165 }
166 else
167 {
168 this->input = chunk_alloc(msg_len);
169 this->inpos = 0;
170 }
171 data = chunk_create((char*)(pkt + 1) + sizeof(msg_len),
172 pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len));
173 }
174 else
175 {
176 data = chunk_create((char*)(pkt + 1),
177 pkt_len - sizeof(eap_tls_packet_t));
178 }
179 if (data.len > this->input.len - this->inpos)
180 {
181 DBG1(DBG_IKE, "EAP-TLS fragment exceeds TLS message length");
182 return FALSE;
183 }
184 memcpy(this->input.ptr + this->inpos, data.ptr, data.len);
185 this->inpos += data.len;
186 return TRUE;
187 }
188
189 /**
190 * Send an ack to request next fragment
191 */
192 static eap_payload_t *create_ack(private_eap_tls_t *this, u_int8_t identifier)
193 {
194 eap_tls_packet_t pkt = {
195 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
196 .identifier = this->is_server ? identifier + 1 : identifier,
197 .type = EAP_TLS,
198 };
199 htoun16(&pkt.length, sizeof(pkt));
200
201 return eap_payload_create_data(chunk_from_thing(pkt));
202 }
203
204 /**
205 * Create a eap response from data in the TLS output buffer
206 */
207 static eap_payload_t *read_buf(private_eap_tls_t *this, u_int8_t identifier)
208 {
209 char buf[EAP_TLS_FRAGMENT_LEN + sizeof(eap_tls_packet_t) + 4], *start;
210 eap_tls_packet_t *pkt = (eap_tls_packet_t*)buf;
211 u_int16_t pkt_len = sizeof(eap_tls_packet_t);
212
213 pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
214 pkt->identifier = this->is_server ? identifier + 1 : identifier;
215 pkt->type = EAP_TLS;
216 pkt->flags = 0;
217
218 if (this->output.len)
219 {
220 start = (char*)(pkt + 1);
221 if (this->outpos == 0)
222 { /* first fragment */
223 pkt->flags = EAP_TLS_LENGTH;
224 pkt_len += 4;
225 start += 4;
226 htoun32(pkt + 1, this->output.len);
227 }
228
229 if (this->output.len - this->outpos > EAP_TLS_FRAGMENT_LEN)
230 {
231 pkt->flags |= EAP_TLS_MORE_FRAGS;
232 pkt_len += EAP_TLS_FRAGMENT_LEN;
233 memcpy(start, this->output.ptr + this->outpos, EAP_TLS_FRAGMENT_LEN);
234 this->outpos += EAP_TLS_FRAGMENT_LEN;
235 }
236 else
237 {
238 pkt_len += this->output.len - this->outpos;
239 memcpy(start, this->output.ptr + this->outpos,
240 this->output.len - this->outpos);
241 chunk_free(&this->output);
242 this->outpos = 0;
243 }
244 }
245 htoun16(&pkt->length, pkt_len);
246 return eap_payload_create_data(chunk_create(buf, pkt_len));
247 }
248
249 /**
250 * Pass data in input buffer to upper layers, write result to output buffer
251 */
252 static status_t process_buf(private_eap_tls_t *this)
253 {
254 tls_record_t *in, out;
255 chunk_t data;
256 u_int16_t len;
257 status_t status;
258
259 /* pass input buffer to upper layer, record for record */
260 data = this->input;
261 while (data.len > sizeof(tls_record_t))
262 {
263 in = (tls_record_t*)data.ptr;
264 len = untoh16(&in->length);
265 if (len > data.len - sizeof(tls_record_t))
266 {
267 DBG1(DBG_IKE, "TLS record length invalid");
268 return FAILED;
269 }
270 if (untoh16(&in->version) < TLS_1_0)
271 {
272 DBG1(DBG_IKE, "%N invalid with EAP-TLS",
273 tls_version_names, untoh16(&in->version));
274 return FAILED;
275 }
276
277 status = this->tls->process(this->tls, in->type,
278 chunk_create(in->data, len));
279 if (status != NEED_MORE)
280 {
281 return status;
282 }
283 data = chunk_skip(data, len + sizeof(tls_record_t));
284 }
285 chunk_free(&this->input);
286 this->inpos = 0;
287
288 /* read in records from upper layer, append to output buffer */
289 chunk_free(&this->output);
290 while (TRUE)
291 {
292 tls_content_type_t type;
293 chunk_t header = chunk_from_thing(out);
294
295 status = this->tls->build(this->tls, &type, &data);
296 switch (status)
297 {
298 case NEED_MORE:
299 break;
300 case INVALID_STATE:
301 /* invalid state means we need more input from peer first */
302 return NEED_MORE;
303 case SUCCESS:
304 return SUCCESS;
305 case FAILED:
306 default:
307 return FAILED;
308 }
309 out.type = type;
310 htoun16(&out.version, this->tls->get_version(this->tls));
311 htoun16(&out.length, data.len);
312 this->output = chunk_cat("mcm", this->output, header, data);
313 }
314 }
315
316 METHOD(eap_method_t, process, status_t,
317 private_eap_tls_t *this, eap_payload_t *in, eap_payload_t **out)
318 {
319 eap_tls_packet_t *pkt;
320 chunk_t data;
321 status_t status;
322
323 data = in->get_data(in);
324
325 pkt = (eap_tls_packet_t*)data.ptr;
326 if (data.len < sizeof(eap_tls_packet_t) ||
327 untoh16(&pkt->length) != data.len)
328 {
329 DBG1(DBG_IKE, "invalid EAP-TLS packet length");
330 return FAILED;
331 }
332 if (!(pkt->flags & EAP_TLS_START))
333 {
334 if (data.len == sizeof(eap_tls_packet_t))
335 { /* ACK to our fragment, send next */
336 *out = read_buf(this, pkt->identifier);
337 return NEED_MORE;
338 }
339 if (!write_buf(this, pkt))
340 {
341 return FAILED;
342 }
343 if (pkt->flags & EAP_TLS_MORE_FRAGS)
344 { /* more fragments follow */
345 *out = create_ack(this, pkt->identifier);
346 return NEED_MORE;
347 }
348 else if (this->input.len != this->inpos)
349 {
350 DBG1(DBG_IKE, "defragemented TLS message has invalid length");
351 return FAILED;
352 }
353 }
354 status = process_buf(this);
355 if (status == NEED_MORE)
356 {
357 *out = read_buf(this, pkt->identifier);
358 }
359 return status;
360 }
361
362 METHOD(eap_method_t, get_type, eap_type_t,
363 private_eap_tls_t *this, u_int32_t *vendor)
364 {
365 *vendor = 0;
366 return EAP_TLS;
367 }
368
369 METHOD(eap_method_t, get_msk, status_t,
370 private_eap_tls_t *this, chunk_t *msk)
371 {
372 *msk = this->tls->get_eap_msk(this->tls);
373 if (msk->len)
374 {
375 return SUCCESS;
376 }
377 return FAILED;
378 }
379
380 METHOD(eap_method_t, is_mutual, bool,
381 private_eap_tls_t *this)
382 {
383 return TRUE;
384 }
385
386 METHOD(eap_method_t, destroy, void,
387 private_eap_tls_t *this)
388 {
389 this->peer->destroy(this->peer);
390 this->server->destroy(this->server);
391
392 free(this->input.ptr);
393 free(this->output.ptr);
394
395 this->tls->destroy(this->tls);
396
397 free(this);
398 }
399
400 /**
401 * Generic private constructor
402 */
403 static eap_tls_t *eap_tls_create(identification_t *server,
404 identification_t *peer, bool is_server)
405 {
406 private_eap_tls_t *this;
407
408 INIT(this,
409 .public.eap_method = {
410 .initiate = _initiate,
411 .process = _process,
412 .get_type = _get_type,
413 .is_mutual = _is_mutual,
414 .get_msk = _get_msk,
415 .destroy = _destroy,
416 },
417 .peer = peer->clone(peer),
418 .server = server->clone(server),
419 .is_server = is_server,
420 .tls = tls_create(is_server, server, peer),
421 );
422
423 return &this->public;
424 }
425
426 eap_tls_t *eap_tls_create_server(identification_t *server,
427 identification_t *peer)
428 {
429 return eap_tls_create(server, peer, TRUE);
430 }
431
432 eap_tls_t *eap_tls_create_peer(identification_t *server,
433 identification_t *peer)
434 {
435 return eap_tls_create(server, peer, FALSE);
436 }