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