set EAP-TTLS/TNC version also in acknowledgement packets
[strongswan.git] / src / libtls / tls_eap.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 "tls_eap.h"
17
18 #include "tls.h"
19
20 #include <debug.h>
21 #include <library.h>
22
23 /** Size limit for a single TLS message */
24 #define MAX_TLS_MESSAGE_LEN 65536
25
26 typedef struct private_tls_eap_t private_tls_eap_t;
27
28 /**
29 * Private data of an tls_eap_t object.
30 */
31 struct private_tls_eap_t {
32
33 /**
34 * Public tls_eap_t interface.
35 */
36 tls_eap_t public;
37
38 /**
39 * Type of EAP method, EAP-TLS, EAP-TTLS, or EAP-TNC
40 */
41 eap_type_t type;
42
43 /**
44 * TLS stack
45 */
46 tls_t *tls;
47
48 /**
49 * Role
50 */
51 bool is_server;
52
53 /**
54 * First fragment of a multi-fragment record?
55 */
56 bool first_fragment;
57
58 /**
59 * Maximum size of an outgoing EAP-TLS fragment
60 */
61 size_t frag_size;
62
63 /**
64 * Number of EAP messages/fragments processed so far
65 */
66 int processed;
67
68 /**
69 * Maximum number of processed EAP messages/fragments
70 */
71 int max_msg_count;
72 };
73
74 /**
75 * Flags of an EAP-TLS/TTLS/TNC message
76 */
77 typedef enum {
78 EAP_TLS_LENGTH = (1<<7), /* shared with EAP-TTLS/TNC */
79 EAP_TLS_MORE_FRAGS = (1<<6), /* shared with EAP-TTLS/TNC */
80 EAP_TLS_START = (1<<5), /* shared with EAP-TTLS/TNC */
81 EAP_TTLS_VERSION = (0x07), /* shared with EAP-TNC */
82 } eap_tls_flags_t;
83
84 #define EAP_TTLS_SUPPORTED_VERSION 0
85 #define EAP_TNC_SUPPORTED_VERSION 1
86
87 /**
88 * EAP-TLS/TTLS packet format
89 */
90 typedef struct __attribute__((packed)) {
91 u_int8_t code;
92 u_int8_t identifier;
93 u_int16_t length;
94 u_int8_t type;
95 u_int8_t flags;
96 } eap_tls_packet_t;
97
98 METHOD(tls_eap_t, initiate, status_t,
99 private_tls_eap_t *this, chunk_t *out)
100 {
101 if (this->is_server)
102 {
103 eap_tls_packet_t pkt = {
104 .type = this->type,
105 .code = EAP_REQUEST,
106 .flags = EAP_TLS_START,
107 };
108 switch (this->type)
109 {
110 case EAP_TTLS:
111 pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
112 break;
113 case EAP_TNC:
114 pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
115 break;
116 default:
117 break;
118 }
119 htoun16(&pkt.length, sizeof(eap_tls_packet_t));
120 do
121 { /* start with non-zero random identifier */
122 pkt.identifier = random();
123 }
124 while (!pkt.identifier);
125
126 DBG2(DBG_IKE, "sending %N start packet", eap_type_names, this->type);
127 *out = chunk_clone(chunk_from_thing(pkt));
128 return NEED_MORE;
129 }
130 return FAILED;
131 }
132
133 /**
134 * Process a received packet
135 */
136 static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
137 {
138 u_int32_t msg_len;
139 u_int16_t pkt_len;
140
141 pkt_len = untoh16(&pkt->length);
142 if (pkt->flags & EAP_TLS_LENGTH)
143 {
144 if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
145 {
146 DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type);
147 return FAILED;
148 }
149 msg_len = untoh32(pkt + 1);
150 if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
151 msg_len > MAX_TLS_MESSAGE_LEN)
152 {
153 DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type);
154 return FAILED;
155 }
156 return this->tls->process(this->tls, (char*)(pkt + 1) + sizeof(msg_len),
157 pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len));
158 }
159 return this->tls->process(this->tls, (char*)(pkt + 1),
160 pkt_len - sizeof(eap_tls_packet_t));
161 }
162
163 /**
164 * Build a packet to send
165 */
166 static status_t build_pkt(private_tls_eap_t *this,
167 u_int8_t identifier, chunk_t *out)
168 {
169 char buf[this->frag_size];
170 eap_tls_packet_t *pkt;
171 size_t len, reclen;
172 status_t status;
173 char *kind;
174
175 pkt = (eap_tls_packet_t*)buf;
176 pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
177 pkt->identifier = this->is_server ? identifier + 1 : identifier;
178 pkt->type = this->type;
179 pkt->flags = 0;
180
181 switch (this->type)
182 {
183 case EAP_TTLS:
184 pkt->flags |= EAP_TTLS_SUPPORTED_VERSION;
185 break;
186 case EAP_TNC:
187 pkt->flags |= EAP_TNC_SUPPORTED_VERSION;
188 break;
189 default:
190 break;
191 }
192
193 if (this->first_fragment)
194 {
195 pkt->flags |= EAP_TLS_LENGTH;
196 len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(u_int32_t);
197 status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +
198 sizeof(u_int32_t), &len, &reclen);
199 }
200 else
201 {
202 len = sizeof(buf) - sizeof(eap_tls_packet_t);
203 status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t),
204 &len, &reclen);
205 }
206 switch (status)
207 {
208 case NEED_MORE:
209 pkt->flags |= EAP_TLS_MORE_FRAGS;
210 kind = "further fragment";
211 if (this->first_fragment)
212 {
213 this->first_fragment = FALSE;
214 kind = "first fragment";
215 }
216 break;
217 case ALREADY_DONE:
218 kind = "packet";
219 if (!this->first_fragment)
220 {
221 this->first_fragment = TRUE;
222 kind = "final fragment";
223 }
224 break;
225 default:
226 return status;
227 }
228 DBG2(DBG_TLS, "sending %N %s (%u bytes)",
229 eap_type_names, this->type, kind, len);
230 if (reclen)
231 {
232 htoun32(pkt + 1, reclen);
233 len += sizeof(u_int32_t);
234 pkt->flags |= EAP_TLS_LENGTH;
235 }
236 len += sizeof(eap_tls_packet_t);
237 htoun16(&pkt->length, len);
238 *out = chunk_clone(chunk_create(buf, len));
239 return NEED_MORE;
240 }
241
242 /**
243 * Send an ack to request next fragment
244 */
245 static chunk_t create_ack(private_tls_eap_t *this, u_int8_t identifier)
246 {
247 eap_tls_packet_t pkt = {
248 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
249 .identifier = this->is_server ? identifier + 1 : identifier,
250 .type = this->type,
251 };
252 htoun16(&pkt.length, sizeof(pkt));
253 switch (this->type)
254 {
255 case EAP_TTLS:
256 pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
257 break;
258 case EAP_TNC:
259 pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
260 break;
261 default:
262 break;
263 }
264 DBG2(DBG_TLS, "sending %N acknowledgement packet",
265 eap_type_names, this->type);
266 return chunk_clone(chunk_from_thing(pkt));
267 }
268
269 METHOD(tls_eap_t, process, status_t,
270 private_tls_eap_t *this, chunk_t in, chunk_t *out)
271 {
272 eap_tls_packet_t *pkt;
273 status_t status;
274
275 if (++this->processed > this->max_msg_count)
276 {
277 DBG1(DBG_IKE, "%N packet count exceeded (%d > %d)",
278 eap_type_names, this->type,
279 this->processed, this->max_msg_count);
280 return FAILED;
281 }
282
283 pkt = (eap_tls_packet_t*)in.ptr;
284 if (in.len < sizeof(eap_tls_packet_t) ||
285 untoh16(&pkt->length) != in.len)
286 {
287 DBG1(DBG_IKE, "invalid %N packet length",
288 eap_type_names, this->type);
289 return FAILED;
290 }
291 if (pkt->flags & EAP_TLS_START)
292 {
293 if (this->type == EAP_TTLS || this->type == EAP_TNC)
294 {
295 DBG1(DBG_TLS, "%N version is v%u", eap_type_names, this->type,
296 pkt->flags & EAP_TTLS_VERSION);
297 }
298 }
299 else
300 {
301 if (in.len == sizeof(eap_tls_packet_t))
302 {
303 DBG2(DBG_TLS, "received %N acknowledgement packet",
304 eap_type_names, this->type);
305 status = build_pkt(this, pkt->identifier, out);
306 if (status == INVALID_STATE &&
307 this->tls->is_complete(this->tls))
308 {
309 return SUCCESS;
310 }
311 return status;
312 }
313 status = process_pkt(this, pkt);
314 if (status != NEED_MORE)
315 {
316 return status;
317 }
318 }
319 status = build_pkt(this, pkt->identifier, out);
320 switch (status)
321 {
322 case INVALID_STATE:
323 *out = create_ack(this, pkt->identifier);
324 return NEED_MORE;
325 case FAILED:
326 if (!this->is_server)
327 {
328 *out = create_ack(this, pkt->identifier);
329 return NEED_MORE;
330 }
331 return FAILED;
332 default:
333 return status;
334 }
335 }
336
337 METHOD(tls_eap_t, get_msk, chunk_t,
338 private_tls_eap_t *this)
339 {
340 return this->tls->get_eap_msk(this->tls);
341 }
342
343 METHOD(tls_eap_t, destroy, void,
344 private_tls_eap_t *this)
345 {
346 this->tls->destroy(this->tls);
347 free(this);
348 }
349
350 /**
351 * See header
352 */
353 tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size,
354 int max_msg_count)
355 {
356 private_tls_eap_t *this;
357
358 if (!tls)
359 {
360 return NULL;
361 }
362
363 INIT(this,
364 .public = {
365 .initiate = _initiate,
366 .process = _process,
367 .get_msk = _get_msk,
368 .destroy = _destroy,
369 },
370 .type = type,
371 .is_server = tls->is_server(tls),
372 .first_fragment = TRUE,
373 .frag_size = frag_size,
374 .max_msg_count = max_msg_count,
375 .tls = tls,
376 );
377
378 return &this->public;
379 }