25c909ac51b0e04c9743aa633b01207cb84c7594
[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 or EAP-TTLS
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 /**
65 * Flags of an EAP-TLS/TTLS message
66 */
67 typedef enum {
68 EAP_TLS_LENGTH = (1<<7),
69 EAP_TLS_MORE_FRAGS = (1<<6),
70 EAP_TLS_START = (1<<5),
71 EAP_TTLS_VERSION = (0x07),
72 } eap_tls_flags_t;
73
74 /**
75 * EAP-TLS/TTLS packet format
76 */
77 typedef struct __attribute__((packed)) {
78 u_int8_t code;
79 u_int8_t identifier;
80 u_int16_t length;
81 u_int8_t type;
82 u_int8_t flags;
83 } eap_tls_packet_t;
84
85 METHOD(tls_eap_t, initiate, status_t,
86 private_tls_eap_t *this, chunk_t *out)
87 {
88 if (this->is_server)
89 {
90 eap_tls_packet_t pkt = {
91 .type = this->type,
92 .code = EAP_REQUEST,
93 .flags = EAP_TLS_START,
94 };
95 htoun16(&pkt.length, sizeof(eap_tls_packet_t));
96 do
97 { /* start with non-zero random identifier */
98 pkt.identifier = random();
99 }
100 while (!pkt.identifier);
101
102 DBG2(DBG_IKE, "sending %N start packet", eap_type_names, this->type);
103 *out = chunk_clone(chunk_from_thing(pkt));
104 return NEED_MORE;
105 }
106 return FAILED;
107 }
108
109 /**
110 * Process a received packet
111 */
112 static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
113 {
114 u_int32_t msg_len;
115 u_int16_t pkt_len;
116
117 pkt_len = untoh16(&pkt->length);
118 if (pkt->flags & EAP_TLS_LENGTH)
119 {
120 if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
121 {
122 DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type);
123 return FAILED;
124 }
125 msg_len = untoh32(pkt + 1);
126 if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
127 msg_len > MAX_TLS_MESSAGE_LEN)
128 {
129 DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type);
130 return FAILED;
131 }
132 return this->tls->process(this->tls, (char*)(pkt + 1) + sizeof(msg_len),
133 pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len));
134 }
135 return this->tls->process(this->tls, (char*)(pkt + 1),
136 pkt_len - sizeof(eap_tls_packet_t));
137 }
138
139 /**
140 * Build a packet to send
141 */
142 static status_t build_pkt(private_tls_eap_t *this,
143 u_int8_t identifier, chunk_t *out)
144 {
145 char buf[this->frag_size];
146 eap_tls_packet_t *pkt;
147 size_t len, reclen;
148 status_t status;
149 char *kind;
150
151 pkt = (eap_tls_packet_t*)buf;
152 pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
153 pkt->identifier = this->is_server ? identifier + 1 : identifier;
154 pkt->type = this->type;
155 pkt->flags = 0;
156
157 if (this->first_fragment)
158 {
159 pkt->flags = EAP_TLS_LENGTH;
160 len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(u_int32_t);
161 status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +
162 sizeof(u_int32_t), &len, &reclen);
163 }
164 else
165 {
166 len = sizeof(buf) - sizeof(eap_tls_packet_t);
167 status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t),
168 &len, &reclen);
169 }
170 switch (status)
171 {
172 case NEED_MORE:
173 pkt->flags |= EAP_TLS_MORE_FRAGS;
174 kind = "further fragment";
175 if (this->first_fragment)
176 {
177 this->first_fragment = FALSE;
178 kind = "first fragment";
179 }
180 break;
181 case ALREADY_DONE:
182 kind = "packet";
183 if (!this->first_fragment)
184 {
185 this->first_fragment = TRUE;
186 kind = "final fragment";
187 }
188 break;
189 default:
190 return status;
191 }
192 DBG2(DBG_TLS, "sending %N %s (%u bytes)",
193 eap_type_names, this->type, kind, len);
194 if (reclen)
195 {
196 htoun32(pkt + 1, reclen);
197 len += sizeof(u_int32_t);
198 pkt->flags |= EAP_TLS_LENGTH;
199 }
200 len += sizeof(eap_tls_packet_t);
201 htoun16(&pkt->length, len);
202 *out = chunk_clone(chunk_create(buf, len));
203 return NEED_MORE;
204 }
205
206 /**
207 * Send an ack to request next fragment
208 */
209 static chunk_t create_ack(private_tls_eap_t *this, u_int8_t identifier)
210 {
211 eap_tls_packet_t pkt = {
212 .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
213 .identifier = this->is_server ? identifier + 1 : identifier,
214 .type = this->type,
215 };
216 htoun16(&pkt.length, sizeof(pkt));
217 DBG2(DBG_TLS, "sending %N acknowledgement packet",
218 eap_type_names, this->type);
219 return chunk_clone(chunk_from_thing(pkt));
220 }
221
222 METHOD(tls_eap_t, process, status_t,
223 private_tls_eap_t *this, chunk_t in, chunk_t *out)
224 {
225 eap_tls_packet_t *pkt;
226 status_t status;
227
228 pkt = (eap_tls_packet_t*)in.ptr;
229 if (in.len < sizeof(eap_tls_packet_t) ||
230 untoh16(&pkt->length) != in.len)
231 {
232 DBG1(DBG_IKE, "invalid %N packet length",
233 eap_type_names, this->type);
234 return FAILED;
235 }
236 if (pkt->flags & EAP_TLS_START)
237 {
238 if (this->type == EAP_TTLS)
239 {
240 DBG1(DBG_TLS, "EAP-TTLS version is v%u",
241 pkt->flags & EAP_TTLS_VERSION);
242 }
243 }
244 else
245 {
246 if (in.len == sizeof(eap_tls_packet_t))
247 {
248 DBG2(DBG_TLS, "received %N acknowledgement packet",
249 eap_type_names, this->type);
250 status = build_pkt(this, pkt->identifier, out);
251 if (status == INVALID_STATE &&
252 this->tls->is_complete(this->tls))
253 {
254 return SUCCESS;
255 }
256 return status;
257 }
258 status = process_pkt(this, pkt);
259 if (status != NEED_MORE)
260 {
261 return status;
262 }
263 }
264 status = build_pkt(this, pkt->identifier, out);
265 switch (status)
266 {
267 case INVALID_STATE:
268 *out = create_ack(this, pkt->identifier);
269 return NEED_MORE;
270 case FAILED:
271 if (!this->is_server)
272 {
273 *out = create_ack(this, pkt->identifier);
274 return NEED_MORE;
275 }
276 return FAILED;
277 default:
278 return status;
279 }
280 }
281
282 METHOD(tls_eap_t, get_msk, chunk_t,
283 private_tls_eap_t *this)
284 {
285 return this->tls->get_eap_msk(this->tls);
286 }
287
288 METHOD(tls_eap_t, destroy, void,
289 private_tls_eap_t *this)
290 {
291 this->tls->destroy(this->tls);
292 free(this);
293 }
294
295 /**
296 * See header
297 */
298 tls_eap_t *tls_eap_create(eap_type_t type, bool is_server,
299 identification_t *server, identification_t *peer,
300 tls_application_t *application, size_t frag_size)
301 {
302 private_tls_eap_t *this;
303 tls_purpose_t purpose;
304
305 switch (type)
306 {
307 case EAP_TLS:
308 purpose = TLS_PURPOSE_EAP_TLS;
309 break;
310 case EAP_TTLS:
311 purpose = TLS_PURPOSE_EAP_TTLS;
312 break;
313 default:
314 return NULL;
315 };
316
317 INIT(this,
318 .public = {
319 .initiate = _initiate,
320 .process = _process,
321 .get_msk = _get_msk,
322 .destroy = _destroy,
323 },
324 .type = type,
325 .is_server = is_server,
326 .first_fragment = TRUE,
327 .frag_size = frag_size,
328 .tls = tls_create(is_server, server, peer, purpose, application),
329 );
330 if (!this->tls)
331 {
332 free(this);
333 return NULL;
334 }
335 return &this->public;
336 }