]>
Commit | Line | Data |
---|---|---|
6fc6879b | 1 | /* |
81c85c06 JM |
2 | * EAP-TLS/PEAP/TTLS/FAST server common functions |
3 | * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> | |
6fc6879b JM |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * Alternatively, this software may be distributed under the terms of BSD | |
10 | * license. | |
11 | * | |
12 | * See README and COPYING for more details. | |
13 | */ | |
14 | ||
15 | #include "includes.h" | |
16 | ||
17 | #include "common.h" | |
03da66bd JM |
18 | #include "crypto/sha1.h" |
19 | #include "crypto/tls.h" | |
6fc6879b JM |
20 | #include "eap_i.h" |
21 | #include "eap_tls_common.h" | |
6fc6879b JM |
22 | |
23 | ||
2e06e9dd JM |
24 | static void eap_server_tls_free_in_buf(struct eap_ssl_data *data); |
25 | ||
26 | ||
6fc6879b JM |
27 | int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, |
28 | int verify_peer) | |
29 | { | |
30 | data->eap = sm; | |
31 | data->phase2 = sm->init_phase2; | |
32 | ||
33 | data->conn = tls_connection_init(sm->ssl_ctx); | |
34 | if (data->conn == NULL) { | |
35 | wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " | |
36 | "connection"); | |
37 | return -1; | |
38 | } | |
39 | ||
40 | if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { | |
41 | wpa_printf(MSG_INFO, "SSL: Failed to configure verification " | |
42 | "of TLS peer certificate"); | |
43 | tls_connection_deinit(sm->ssl_ctx, data->conn); | |
44 | data->conn = NULL; | |
45 | return -1; | |
46 | } | |
47 | ||
48 | /* TODO: make this configurable */ | |
49 | data->tls_out_limit = 1398; | |
50 | if (data->phase2) { | |
51 | /* Limit the fragment size in the inner TLS authentication | |
52 | * since the outer authentication with EAP-PEAP does not yet | |
53 | * support fragmentation */ | |
54 | if (data->tls_out_limit > 100) | |
55 | data->tls_out_limit -= 100; | |
56 | } | |
57 | return 0; | |
58 | } | |
59 | ||
60 | ||
61 | void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) | |
62 | { | |
63 | tls_connection_deinit(sm->ssl_ctx, data->conn); | |
2e06e9dd JM |
64 | eap_server_tls_free_in_buf(data); |
65 | wpabuf_free(data->tls_out); | |
66 | data->tls_out = NULL; | |
6fc6879b JM |
67 | } |
68 | ||
69 | ||
70 | u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, | |
71 | char *label, size_t len) | |
72 | { | |
73 | struct tls_keys keys; | |
74 | u8 *rnd = NULL, *out; | |
75 | ||
76 | out = os_malloc(len); | |
77 | if (out == NULL) | |
78 | return NULL; | |
79 | ||
80 | if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == | |
81 | 0) | |
82 | return out; | |
83 | ||
84 | if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) | |
85 | goto fail; | |
86 | ||
87 | if (keys.client_random == NULL || keys.server_random == NULL || | |
88 | keys.master_key == NULL) | |
89 | goto fail; | |
90 | ||
91 | rnd = os_malloc(keys.client_random_len + keys.server_random_len); | |
92 | if (rnd == NULL) | |
93 | goto fail; | |
94 | os_memcpy(rnd, keys.client_random, keys.client_random_len); | |
95 | os_memcpy(rnd + keys.client_random_len, keys.server_random, | |
96 | keys.server_random_len); | |
97 | ||
98 | if (tls_prf(keys.master_key, keys.master_key_len, | |
99 | label, rnd, keys.client_random_len + | |
100 | keys.server_random_len, out, len)) | |
101 | goto fail; | |
102 | ||
103 | os_free(rnd); | |
104 | return out; | |
105 | ||
106 | fail: | |
107 | os_free(out); | |
108 | os_free(rnd); | |
109 | return NULL; | |
110 | } | |
111 | ||
112 | ||
34f564db | 113 | struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, |
3c724cc5 | 114 | int eap_type, int version, u8 id) |
6fc6879b | 115 | { |
34f564db JM |
116 | struct wpabuf *req; |
117 | u8 flags; | |
118 | size_t send_len, plen; | |
119 | ||
120 | wpa_printf(MSG_DEBUG, "SSL: Generating Request"); | |
2a29f0d4 JM |
121 | if (data->tls_out == NULL) { |
122 | wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__); | |
7cdeb81e JM |
123 | return NULL; |
124 | } | |
34f564db | 125 | |
3c724cc5 | 126 | flags = version; |
2a29f0d4 | 127 | send_len = wpabuf_len(data->tls_out) - data->tls_out_pos; |
34f564db JM |
128 | if (1 + send_len > data->tls_out_limit) { |
129 | send_len = data->tls_out_limit - 1; | |
130 | flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; | |
2a29f0d4 | 131 | if (data->tls_out_pos == 0) { |
34f564db JM |
132 | flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; |
133 | send_len -= 4; | |
6fc6879b | 134 | } |
34f564db | 135 | } |
6fc6879b | 136 | |
34f564db JM |
137 | plen = 1 + send_len; |
138 | if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) | |
139 | plen += 4; | |
6fc6879b | 140 | |
34f564db JM |
141 | req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen, |
142 | EAP_CODE_REQUEST, id); | |
143 | if (req == NULL) | |
144 | return NULL; | |
145 | ||
146 | wpabuf_put_u8(req, flags); /* Flags */ | |
147 | if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) | |
2a29f0d4 | 148 | wpabuf_put_be32(req, wpabuf_len(data->tls_out)); |
34f564db | 149 | |
2a29f0d4 | 150 | wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos, |
34f564db | 151 | send_len); |
2a29f0d4 | 152 | data->tls_out_pos += send_len; |
34f564db | 153 | |
2a29f0d4 | 154 | if (data->tls_out_pos == wpabuf_len(data->tls_out)) { |
34f564db JM |
155 | wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " |
156 | "(message sent completely)", | |
157 | (unsigned long) send_len); | |
2a29f0d4 JM |
158 | wpabuf_free(data->tls_out); |
159 | data->tls_out = NULL; | |
160 | data->tls_out_pos = 0; | |
34f564db JM |
161 | data->state = MSG; |
162 | } else { | |
163 | wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " | |
164 | "(%lu more to send)", (unsigned long) send_len, | |
2a29f0d4 JM |
165 | (unsigned long) wpabuf_len(data->tls_out) - |
166 | data->tls_out_pos); | |
34f564db JM |
167 | data->state = WAIT_FRAG_ACK; |
168 | } | |
169 | ||
170 | return req; | |
6fc6879b JM |
171 | } |
172 | ||
173 | ||
3c724cc5 | 174 | struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version) |
6fc6879b | 175 | { |
34f564db JM |
176 | struct wpabuf *req; |
177 | ||
178 | req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST, | |
179 | id); | |
180 | if (req == NULL) | |
181 | return NULL; | |
182 | wpa_printf(MSG_DEBUG, "SSL: Building ACK"); | |
3c724cc5 | 183 | wpabuf_put_u8(req, version); /* Flags */ |
34f564db JM |
184 | return req; |
185 | } | |
186 | ||
187 | ||
188 | static int eap_server_tls_process_cont(struct eap_ssl_data *data, | |
189 | const u8 *buf, size_t len) | |
190 | { | |
191 | /* Process continuation of a pending message */ | |
2a29f0d4 | 192 | if (len > wpabuf_tailroom(data->tls_in)) { |
34f564db JM |
193 | wpa_printf(MSG_DEBUG, "SSL: Fragment overflow"); |
194 | return -1; | |
6fc6879b JM |
195 | } |
196 | ||
2a29f0d4 | 197 | wpabuf_put_data(data->tls_in, buf, len); |
34f564db JM |
198 | wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu " |
199 | "bytes more", (unsigned long) len, | |
2a29f0d4 | 200 | (unsigned long) wpabuf_tailroom(data->tls_in)); |
34f564db JM |
201 | |
202 | return 0; | |
203 | } | |
204 | ||
205 | ||
206 | static int eap_server_tls_process_fragment(struct eap_ssl_data *data, | |
207 | u8 flags, u32 message_length, | |
208 | const u8 *buf, size_t len) | |
209 | { | |
210 | /* Process a fragment that is not the last one of the message */ | |
2a29f0d4 | 211 | if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) { |
34f564db JM |
212 | wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a " |
213 | "fragmented packet"); | |
6fc6879b JM |
214 | return -1; |
215 | } | |
34f564db | 216 | |
2a29f0d4 | 217 | if (data->tls_in == NULL) { |
34f564db JM |
218 | /* First fragment of the message */ |
219 | ||
220 | /* Limit length to avoid rogue peers from causing large | |
221 | * memory allocations. */ | |
222 | if (message_length > 65536) { | |
223 | wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size" | |
224 | " over 64 kB)"); | |
6fc6879b JM |
225 | return -1; |
226 | } | |
227 | ||
2a29f0d4 JM |
228 | data->tls_in = wpabuf_alloc(message_length); |
229 | if (data->tls_in == NULL) { | |
34f564db JM |
230 | wpa_printf(MSG_DEBUG, "SSL: No memory for message"); |
231 | return -1; | |
232 | } | |
2a29f0d4 | 233 | wpabuf_put_data(data->tls_in, buf, len); |
34f564db JM |
234 | wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first " |
235 | "fragment, waiting for %lu bytes more", | |
236 | (unsigned long) len, | |
2a29f0d4 | 237 | (unsigned long) wpabuf_tailroom(data->tls_in)); |
6fc6879b JM |
238 | } |
239 | ||
34f564db JM |
240 | return 0; |
241 | } | |
242 | ||
6fc6879b | 243 | |
34f564db JM |
244 | int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) |
245 | { | |
2a29f0d4 | 246 | if (data->tls_out) { |
34f564db JM |
247 | /* This should not happen.. */ |
248 | wpa_printf(MSG_INFO, "SSL: pending tls_out data when " | |
249 | "processing new message"); | |
2a29f0d4 JM |
250 | wpabuf_free(data->tls_out); |
251 | WPA_ASSERT(data->tls_out == NULL); | |
34f564db | 252 | } |
81c85c06 | 253 | |
2a29f0d4 | 254 | data->tls_out = tls_connection_server_handshake(sm->ssl_ctx, |
81c85c06 | 255 | data->conn, |
2a29f0d4 JM |
256 | data->tls_in, NULL); |
257 | if (data->tls_out == NULL) { | |
81c85c06 | 258 | wpa_printf(MSG_INFO, "SSL: TLS processing failed"); |
34f564db JM |
259 | return -1; |
260 | } | |
2574634b JM |
261 | if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { |
262 | /* TLS processing has failed - return error */ | |
2a29f0d4 | 263 | wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " |
2574634b JM |
264 | "report error"); |
265 | return -1; | |
266 | } | |
81c85c06 | 267 | |
6fc6879b JM |
268 | return 0; |
269 | } | |
270 | ||
271 | ||
cda97d11 JM |
272 | static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, |
273 | const u8 **pos, size_t *left) | |
6fc6879b | 274 | { |
34f564db JM |
275 | unsigned int tls_msg_len = 0; |
276 | const u8 *end = *pos + *left; | |
6fc6879b | 277 | |
34f564db JM |
278 | if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { |
279 | if (*left < 4) { | |
280 | wpa_printf(MSG_INFO, "SSL: Short frame with TLS " | |
281 | "length"); | |
282 | return -1; | |
283 | } | |
284 | tls_msg_len = WPA_GET_BE32(*pos); | |
285 | wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", | |
286 | tls_msg_len); | |
287 | *pos += 4; | |
288 | *left -= 4; | |
6fc6879b | 289 | } |
34f564db JM |
290 | |
291 | wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x " | |
292 | "Message Length %u", flags, tls_msg_len); | |
293 | ||
294 | if (data->state == WAIT_FRAG_ACK) { | |
295 | if (*left != 0) { | |
296 | wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in " | |
297 | "WAIT_FRAG_ACK state"); | |
298 | return -1; | |
299 | } | |
300 | wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged"); | |
301 | return 1; | |
6fc6879b JM |
302 | } |
303 | ||
2a29f0d4 | 304 | if (data->tls_in && |
34f564db JM |
305 | eap_server_tls_process_cont(data, *pos, end - *pos) < 0) |
306 | return -1; | |
307 | ||
308 | if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) { | |
309 | if (eap_server_tls_process_fragment(data, flags, tls_msg_len, | |
310 | *pos, end - *pos) < 0) | |
311 | return -1; | |
312 | ||
313 | data->state = FRAG_ACK; | |
314 | return 1; | |
6fc6879b | 315 | } |
6fc6879b | 316 | |
34f564db JM |
317 | if (data->state == FRAG_ACK) { |
318 | wpa_printf(MSG_DEBUG, "SSL: All fragments received"); | |
319 | data->state = MSG; | |
320 | } | |
6fc6879b | 321 | |
2a29f0d4 | 322 | if (data->tls_in == NULL) { |
34f564db JM |
323 | /* Wrap unfragmented messages as wpabuf without extra copy */ |
324 | wpabuf_set(&data->tmpbuf, *pos, end - *pos); | |
2a29f0d4 | 325 | data->tls_in = &data->tmpbuf; |
6fc6879b JM |
326 | } |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | ||
cda97d11 | 332 | static void eap_server_tls_free_in_buf(struct eap_ssl_data *data) |
6fc6879b | 333 | { |
2a29f0d4 JM |
334 | if (data->tls_in != &data->tmpbuf) |
335 | wpabuf_free(data->tls_in); | |
336 | data->tls_in = NULL; | |
34f564db | 337 | } |
6fc6879b | 338 | |
34f564db JM |
339 | |
340 | struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, | |
341 | struct eap_ssl_data *data, | |
81c85c06 | 342 | const struct wpabuf *plain) |
34f564db | 343 | { |
34f564db | 344 | struct wpabuf *buf; |
34f564db | 345 | |
81c85c06 JM |
346 | buf = tls_connection_encrypt(sm->ssl_ctx, data->conn, |
347 | plain); | |
348 | if (buf == NULL) { | |
34f564db | 349 | wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data"); |
6fc6879b | 350 | return NULL; |
34f564db JM |
351 | } |
352 | ||
34f564db | 353 | return buf; |
6fc6879b | 354 | } |
cda97d11 JM |
355 | |
356 | ||
357 | int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, | |
358 | struct wpabuf *respData, void *priv, int eap_type, | |
359 | int (*proc_version)(struct eap_sm *sm, void *priv, | |
360 | int peer_version), | |
361 | void (*proc_msg)(struct eap_sm *sm, void *priv, | |
362 | const struct wpabuf *respData)) | |
363 | { | |
364 | const u8 *pos; | |
365 | u8 flags; | |
366 | size_t left; | |
367 | int ret, res = 0; | |
368 | ||
369 | pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); | |
370 | if (pos == NULL || left < 1) | |
371 | return 0; /* Should not happen - frame already validated */ | |
372 | flags = *pos++; | |
373 | left--; | |
374 | wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x", | |
375 | (unsigned long) wpabuf_len(respData), flags); | |
376 | ||
377 | if (proc_version && | |
378 | proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0) | |
379 | return -1; | |
380 | ||
381 | ret = eap_server_tls_reassemble(data, flags, &pos, &left); | |
382 | if (ret < 0) { | |
383 | res = -1; | |
384 | goto done; | |
385 | } else if (ret == 1) | |
386 | return 0; | |
387 | ||
388 | if (proc_msg) | |
389 | proc_msg(sm, priv, respData); | |
390 | ||
391 | if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) { | |
392 | wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in " | |
393 | "TLS processing"); | |
394 | res = -1; | |
395 | } | |
396 | ||
397 | done: | |
398 | eap_server_tls_free_in_buf(data); | |
399 | ||
400 | return res; | |
401 | } |