]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions | |
81c85c06 | 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" | |
22 | #include "eap_config.h" | |
6fc6879b JM |
23 | |
24 | ||
25 | static int eap_tls_check_blob(struct eap_sm *sm, const char **name, | |
26 | const u8 **data, size_t *data_len) | |
27 | { | |
28 | const struct wpa_config_blob *blob; | |
29 | ||
30 | if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0) | |
31 | return 0; | |
32 | ||
33 | blob = eap_get_config_blob(sm, *name + 7); | |
34 | if (blob == NULL) { | |
35 | wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not " | |
36 | "found", __func__, *name + 7); | |
37 | return -1; | |
38 | } | |
39 | ||
40 | *name = NULL; | |
41 | *data = blob->data; | |
42 | *data_len = blob->len; | |
43 | ||
44 | return 0; | |
45 | } | |
46 | ||
47 | ||
48 | static void eap_tls_params_from_conf1(struct tls_connection_params *params, | |
49 | struct eap_peer_config *config) | |
50 | { | |
51 | params->ca_cert = (char *) config->ca_cert; | |
52 | params->ca_path = (char *) config->ca_path; | |
53 | params->client_cert = (char *) config->client_cert; | |
54 | params->private_key = (char *) config->private_key; | |
55 | params->private_key_passwd = (char *) config->private_key_passwd; | |
56 | params->dh_file = (char *) config->dh_file; | |
57 | params->subject_match = (char *) config->subject_match; | |
58 | params->altsubject_match = (char *) config->altsubject_match; | |
98842d51 | 59 | params->engine = config->engine; |
6fc6879b JM |
60 | params->engine_id = config->engine_id; |
61 | params->pin = config->pin; | |
62 | params->key_id = config->key_id; | |
d1f73353 DS |
63 | params->cert_id = config->cert_id; |
64 | params->ca_cert_id = config->ca_cert_id; | |
6fc6879b JM |
65 | } |
66 | ||
67 | ||
68 | static void eap_tls_params_from_conf2(struct tls_connection_params *params, | |
69 | struct eap_peer_config *config) | |
70 | { | |
71 | params->ca_cert = (char *) config->ca_cert2; | |
72 | params->ca_path = (char *) config->ca_path2; | |
73 | params->client_cert = (char *) config->client_cert2; | |
74 | params->private_key = (char *) config->private_key2; | |
75 | params->private_key_passwd = (char *) config->private_key2_passwd; | |
76 | params->dh_file = (char *) config->dh_file2; | |
77 | params->subject_match = (char *) config->subject_match2; | |
78 | params->altsubject_match = (char *) config->altsubject_match2; | |
98842d51 CL |
79 | params->engine = config->engine2; |
80 | params->engine_id = config->engine2_id; | |
81 | params->pin = config->pin2; | |
d1f73353 DS |
82 | params->key_id = config->key2_id; |
83 | params->cert_id = config->cert2_id; | |
84 | params->ca_cert_id = config->ca_cert2_id; | |
6fc6879b JM |
85 | } |
86 | ||
87 | ||
88 | static int eap_tls_params_from_conf(struct eap_sm *sm, | |
89 | struct eap_ssl_data *data, | |
90 | struct tls_connection_params *params, | |
91 | struct eap_peer_config *config, int phase2) | |
92 | { | |
93 | os_memset(params, 0, sizeof(*params)); | |
98842d51 CL |
94 | if (phase2) { |
95 | wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); | |
6fc6879b | 96 | eap_tls_params_from_conf2(params, config); |
98842d51 CL |
97 | } else { |
98 | wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); | |
6fc6879b | 99 | eap_tls_params_from_conf1(params, config); |
98842d51 | 100 | } |
6fc6879b JM |
101 | params->tls_ia = data->tls_ia; |
102 | ||
103 | /* | |
104 | * Use blob data, if available. Otherwise, leave reference to external | |
105 | * file as-is. | |
106 | */ | |
107 | if (eap_tls_check_blob(sm, ¶ms->ca_cert, ¶ms->ca_cert_blob, | |
108 | ¶ms->ca_cert_blob_len) || | |
109 | eap_tls_check_blob(sm, ¶ms->client_cert, | |
110 | ¶ms->client_cert_blob, | |
111 | ¶ms->client_cert_blob_len) || | |
112 | eap_tls_check_blob(sm, ¶ms->private_key, | |
113 | ¶ms->private_key_blob, | |
114 | ¶ms->private_key_blob_len) || | |
115 | eap_tls_check_blob(sm, ¶ms->dh_file, ¶ms->dh_blob, | |
116 | ¶ms->dh_blob_len)) { | |
117 | wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs"); | |
118 | return -1; | |
119 | } | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | ||
125 | static int eap_tls_init_connection(struct eap_sm *sm, | |
126 | struct eap_ssl_data *data, | |
127 | struct eap_peer_config *config, | |
128 | struct tls_connection_params *params) | |
129 | { | |
130 | int res; | |
131 | ||
132 | data->conn = tls_connection_init(sm->ssl_ctx); | |
133 | if (data->conn == NULL) { | |
134 | wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " | |
135 | "connection"); | |
136 | return -1; | |
137 | } | |
138 | ||
139 | res = tls_connection_set_params(sm->ssl_ctx, data->conn, params); | |
140 | if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { | |
141 | /* | |
142 | * At this point with the pkcs11 engine the PIN might be wrong. | |
143 | * We reset the PIN in the configuration to be sure to not use | |
144 | * it again and the calling function must request a new one. | |
145 | */ | |
146 | os_free(config->pin); | |
147 | config->pin = NULL; | |
148 | } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { | |
149 | wpa_printf(MSG_INFO, "TLS: Failed to load private key"); | |
150 | /* | |
151 | * We do not know exactly but maybe the PIN was wrong, | |
152 | * so ask for a new one. | |
153 | */ | |
154 | os_free(config->pin); | |
155 | config->pin = NULL; | |
156 | eap_sm_request_pin(sm); | |
157 | sm->ignore = TRUE; | |
158 | return -1; | |
159 | } else if (res) { | |
160 | wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " | |
161 | "parameters"); | |
162 | return -1; | |
163 | } | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | ||
169 | /** | |
170 | * eap_peer_tls_ssl_init - Initialize shared TLS functionality | |
171 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
172 | * @data: Data for TLS processing | |
173 | * @config: Pointer to the network configuration | |
174 | * Returns: 0 on success, -1 on failure | |
175 | * | |
176 | * This function is used to initialize shared TLS functionality for EAP-TLS, | |
177 | * EAP-PEAP, EAP-TTLS, and EAP-FAST. | |
178 | */ | |
179 | int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, | |
180 | struct eap_peer_config *config) | |
181 | { | |
182 | struct tls_connection_params params; | |
183 | ||
184 | if (config == NULL) | |
185 | return -1; | |
186 | ||
187 | data->eap = sm; | |
188 | data->phase2 = sm->init_phase2; | |
189 | if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) < | |
190 | 0) | |
191 | return -1; | |
192 | ||
193 | if (eap_tls_init_connection(sm, data, config, ¶ms) < 0) | |
194 | return -1; | |
195 | ||
196 | data->tls_out_limit = config->fragment_size; | |
197 | if (data->phase2) { | |
198 | /* Limit the fragment size in the inner TLS authentication | |
199 | * since the outer authentication with EAP-PEAP does not yet | |
200 | * support fragmentation */ | |
201 | if (data->tls_out_limit > 100) | |
202 | data->tls_out_limit -= 100; | |
203 | } | |
204 | ||
205 | if (config->phase1 && | |
206 | os_strstr(config->phase1, "include_tls_length=1")) { | |
207 | wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in " | |
208 | "unfragmented packets"); | |
209 | data->include_tls_length = 1; | |
210 | } | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | ||
216 | /** | |
217 | * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality | |
218 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
219 | * @data: Data for TLS processing | |
220 | * | |
221 | * This function deinitializes shared TLS functionality that was initialized | |
222 | * with eap_peer_tls_ssl_init(). | |
223 | */ | |
224 | void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) | |
225 | { | |
226 | tls_connection_deinit(sm->ssl_ctx, data->conn); | |
227 | eap_peer_tls_reset_input(data); | |
228 | eap_peer_tls_reset_output(data); | |
229 | } | |
230 | ||
231 | ||
232 | /** | |
233 | * eap_peer_tls_derive_key - Derive a key based on TLS session data | |
234 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
235 | * @data: Data for TLS processing | |
236 | * @label: Label string for deriving the keys, e.g., "client EAP encryption" | |
237 | * @len: Length of the key material to generate (usually 64 for MSK) | |
238 | * Returns: Pointer to allocated key on success or %NULL on failure | |
239 | * | |
240 | * This function uses TLS-PRF to generate pseudo-random data based on the TLS | |
241 | * session data (client/server random and master key). Each key type may use a | |
242 | * different label to bind the key usage into the generated material. | |
243 | * | |
244 | * The caller is responsible for freeing the returned buffer. | |
245 | */ | |
246 | u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, | |
247 | const char *label, size_t len) | |
248 | { | |
249 | struct tls_keys keys; | |
250 | u8 *rnd = NULL, *out; | |
251 | ||
252 | out = os_malloc(len); | |
253 | if (out == NULL) | |
254 | return NULL; | |
255 | ||
256 | /* First, try to use TLS library function for PRF, if available. */ | |
257 | if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == | |
258 | 0) | |
259 | return out; | |
260 | ||
261 | /* | |
262 | * TLS library did not support key generation, so get the needed TLS | |
263 | * session parameters and use an internal implementation of TLS PRF to | |
264 | * derive the key. | |
265 | */ | |
266 | if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) | |
267 | goto fail; | |
268 | ||
269 | if (keys.client_random == NULL || keys.server_random == NULL || | |
270 | keys.master_key == NULL) | |
271 | goto fail; | |
272 | ||
273 | rnd = os_malloc(keys.client_random_len + keys.server_random_len); | |
274 | if (rnd == NULL) | |
275 | goto fail; | |
276 | os_memcpy(rnd, keys.client_random, keys.client_random_len); | |
277 | os_memcpy(rnd + keys.client_random_len, keys.server_random, | |
278 | keys.server_random_len); | |
279 | ||
280 | if (tls_prf(keys.master_key, keys.master_key_len, | |
281 | label, rnd, keys.client_random_len + | |
282 | keys.server_random_len, out, len)) | |
283 | goto fail; | |
284 | ||
285 | os_free(rnd); | |
286 | return out; | |
287 | ||
288 | fail: | |
289 | os_free(out); | |
290 | os_free(rnd); | |
291 | return NULL; | |
292 | } | |
293 | ||
294 | ||
295 | /** | |
296 | * eap_peer_tls_reassemble_fragment - Reassemble a received fragment | |
297 | * @data: Data for TLS processing | |
298 | * @in_data: Next incoming TLS segment | |
6fc6879b JM |
299 | * Returns: 0 on success, 1 if more data is needed for the full message, or |
300 | * -1 on error | |
301 | */ | |
302 | static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, | |
81c85c06 | 303 | const struct wpabuf *in_data) |
6fc6879b | 304 | { |
81c85c06 JM |
305 | size_t tls_in_len, in_len; |
306 | ||
307 | tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0; | |
308 | in_len = in_data ? wpabuf_len(in_data) : 0; | |
6fc6879b | 309 | |
81c85c06 | 310 | if (tls_in_len + in_len == 0) { |
6fc6879b JM |
311 | /* No message data received?! */ |
312 | wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: " | |
313 | "tls_in_left=%lu tls_in_len=%lu in_len=%lu", | |
314 | (unsigned long) data->tls_in_left, | |
81c85c06 | 315 | (unsigned long) tls_in_len, |
6fc6879b JM |
316 | (unsigned long) in_len); |
317 | eap_peer_tls_reset_input(data); | |
318 | return -1; | |
319 | } | |
320 | ||
81c85c06 | 321 | if (tls_in_len + in_len > 65536) { |
6fc6879b JM |
322 | /* |
323 | * Limit length to avoid rogue servers from causing large | |
324 | * memory allocations. | |
325 | */ | |
326 | wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over " | |
327 | "64 kB)"); | |
328 | eap_peer_tls_reset_input(data); | |
329 | return -1; | |
330 | } | |
331 | ||
332 | if (in_len > data->tls_in_left) { | |
333 | /* Sender is doing something odd - reject message */ | |
334 | wpa_printf(MSG_INFO, "SSL: more data than TLS message length " | |
335 | "indicated"); | |
336 | eap_peer_tls_reset_input(data); | |
337 | return -1; | |
338 | } | |
339 | ||
81c85c06 | 340 | if (wpabuf_resize(&data->tls_in, in_len) < 0) { |
6fc6879b JM |
341 | wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS " |
342 | "data"); | |
343 | eap_peer_tls_reset_input(data); | |
344 | return -1; | |
345 | } | |
81c85c06 | 346 | wpabuf_put_buf(data->tls_in, in_data); |
6fc6879b JM |
347 | data->tls_in_left -= in_len; |
348 | ||
349 | if (data->tls_in_left > 0) { | |
350 | wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " | |
351 | "data", (unsigned long) data->tls_in_left); | |
352 | return 1; | |
353 | } | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
358 | ||
359 | /** | |
360 | * eap_peer_tls_data_reassemble - Reassemble TLS data | |
361 | * @data: Data for TLS processing | |
362 | * @in_data: Next incoming TLS segment | |
6fc6879b JM |
363 | * @need_more_input: Variable for returning whether more input data is needed |
364 | * to reassemble this TLS packet | |
365 | * Returns: Pointer to output data, %NULL on error or when more data is needed | |
366 | * for the full message (in which case, *need_more_input is also set to 1). | |
367 | * | |
368 | * This function reassembles TLS fragments. Caller must not free the returned | |
369 | * data buffer since an internal pointer to it is maintained. | |
370 | */ | |
81c85c06 JM |
371 | static const struct wpabuf * eap_peer_tls_data_reassemble( |
372 | struct eap_ssl_data *data, const struct wpabuf *in_data, | |
373 | int *need_more_input) | |
6fc6879b JM |
374 | { |
375 | *need_more_input = 0; | |
376 | ||
81c85c06 | 377 | if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) { |
6fc6879b | 378 | /* Message has fragments */ |
81c85c06 | 379 | int res = eap_peer_tls_reassemble_fragment(data, in_data); |
6fc6879b JM |
380 | if (res) { |
381 | if (res == 1) | |
382 | *need_more_input = 1; | |
383 | return NULL; | |
384 | } | |
385 | ||
386 | /* Message is now fully reassembled. */ | |
387 | } else { | |
388 | /* No fragments in this message, so just make a copy of it. */ | |
389 | data->tls_in_left = 0; | |
81c85c06 | 390 | data->tls_in = wpabuf_dup(in_data); |
6fc6879b JM |
391 | if (data->tls_in == NULL) |
392 | return NULL; | |
6fc6879b JM |
393 | } |
394 | ||
6fc6879b JM |
395 | return data->tls_in; |
396 | } | |
397 | ||
398 | ||
399 | /** | |
400 | * eap_tls_process_input - Process incoming TLS message | |
401 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
402 | * @data: Data for TLS processing | |
403 | * @in_data: Message received from the server | |
404 | * @in_len: Length of in_data | |
405 | * @out_data: Buffer for returning a pointer to application data (if available) | |
406 | * Returns: 0 on success, 1 if more input data is needed, 2 if application data | |
407 | * is available, -1 on failure | |
408 | */ | |
409 | static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, | |
410 | const u8 *in_data, size_t in_len, | |
411 | struct wpabuf **out_data) | |
412 | { | |
81c85c06 | 413 | const struct wpabuf *msg; |
6fc6879b | 414 | int need_more_input; |
81c85c06 JM |
415 | struct wpabuf *appl_data; |
416 | struct wpabuf buf; | |
6fc6879b | 417 | |
81c85c06 JM |
418 | wpabuf_set(&buf, in_data, in_len); |
419 | msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input); | |
6fc6879b JM |
420 | if (msg == NULL) |
421 | return need_more_input ? 1 : -1; | |
422 | ||
423 | /* Full TLS message reassembled - continue handshake processing */ | |
424 | if (data->tls_out) { | |
425 | /* This should not happen.. */ | |
426 | wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " | |
427 | "tls_out data even though tls_out_len = 0"); | |
81c85c06 | 428 | wpabuf_free(data->tls_out); |
6fc6879b JM |
429 | WPA_ASSERT(data->tls_out == NULL); |
430 | } | |
431 | appl_data = NULL; | |
432 | data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn, | |
81c85c06 | 433 | msg, &appl_data); |
6fc6879b JM |
434 | |
435 | eap_peer_tls_reset_input(data); | |
436 | ||
437 | if (appl_data && | |
438 | tls_connection_established(sm->ssl_ctx, data->conn) && | |
439 | !tls_connection_get_failed(sm->ssl_ctx, data->conn)) { | |
81c85c06 JM |
440 | wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data", |
441 | appl_data); | |
442 | *out_data = appl_data; | |
6fc6879b JM |
443 | return 2; |
444 | } | |
445 | ||
81c85c06 | 446 | wpabuf_free(appl_data); |
6fc6879b JM |
447 | |
448 | return 0; | |
449 | } | |
450 | ||
451 | ||
452 | /** | |
453 | * eap_tls_process_output - Process outgoing TLS message | |
454 | * @data: Data for TLS processing | |
455 | * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) | |
456 | * @peap_version: Version number for EAP-PEAP/TTLS | |
457 | * @id: EAP identifier for the response | |
458 | * @ret: Return value to use on success | |
459 | * @out_data: Buffer for returning the allocated output buffer | |
460 | * Returns: ret (0 or 1) on success, -1 on failure | |
461 | */ | |
462 | static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, | |
463 | int peap_version, u8 id, int ret, | |
464 | struct wpabuf **out_data) | |
465 | { | |
466 | size_t len; | |
467 | u8 *flags; | |
468 | int more_fragments, length_included; | |
81c85c06 JM |
469 | |
470 | if (data->tls_out == NULL) | |
471 | return -1; | |
472 | len = wpabuf_len(data->tls_out) - data->tls_out_pos; | |
6fc6879b JM |
473 | wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " |
474 | "%lu bytes)", | |
81c85c06 JM |
475 | (unsigned long) len, |
476 | (unsigned long) wpabuf_len(data->tls_out)); | |
6fc6879b JM |
477 | |
478 | /* | |
479 | * Limit outgoing message to the configured maximum size. Fragment | |
480 | * message if needed. | |
481 | */ | |
482 | if (len > data->tls_out_limit) { | |
483 | more_fragments = 1; | |
484 | len = data->tls_out_limit; | |
485 | wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " | |
486 | "will follow", (unsigned long) len); | |
487 | } else | |
488 | more_fragments = 0; | |
489 | ||
490 | length_included = data->tls_out_pos == 0 && | |
81c85c06 | 491 | (wpabuf_len(data->tls_out) > data->tls_out_limit || |
6fc6879b | 492 | data->include_tls_length); |
f4f2774a JM |
493 | if (!length_included && |
494 | eap_type == EAP_TYPE_PEAP && peap_version == 0 && | |
495 | !tls_connection_established(data->eap->ssl_ctx, data->conn)) { | |
496 | /* | |
497 | * Windows Server 2008 NPS really wants to have the TLS Message | |
498 | * length included in phase 0 even for unfragmented frames or | |
499 | * it will get very confused with Compound MAC calculation and | |
500 | * Outer TLVs. | |
501 | */ | |
502 | length_included = 1; | |
503 | } | |
6fc6879b JM |
504 | |
505 | *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, | |
506 | 1 + length_included * 4 + len, | |
507 | EAP_CODE_RESPONSE, id); | |
508 | if (*out_data == NULL) | |
509 | return -1; | |
510 | ||
511 | flags = wpabuf_put(*out_data, 1); | |
512 | *flags = peap_version; | |
513 | if (more_fragments) | |
514 | *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; | |
515 | if (length_included) { | |
516 | *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; | |
81c85c06 | 517 | wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out)); |
6fc6879b JM |
518 | } |
519 | ||
81c85c06 JM |
520 | wpabuf_put_data(*out_data, |
521 | wpabuf_head_u8(data->tls_out) + data->tls_out_pos, | |
522 | len); | |
6fc6879b JM |
523 | data->tls_out_pos += len; |
524 | ||
525 | if (!more_fragments) | |
526 | eap_peer_tls_reset_output(data); | |
527 | ||
528 | return ret; | |
529 | } | |
530 | ||
531 | ||
532 | /** | |
533 | * eap_peer_tls_process_helper - Process TLS handshake message | |
534 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
535 | * @data: Data for TLS processing | |
536 | * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) | |
537 | * @peap_version: Version number for EAP-PEAP/TTLS | |
538 | * @id: EAP identifier for the response | |
539 | * @in_data: Message received from the server | |
540 | * @in_len: Length of in_data | |
541 | * @out_data: Buffer for returning a pointer to the response message | |
542 | * Returns: 0 on success, 1 if more input data is needed, 2 if application data | |
543 | * is available, or -1 on failure | |
544 | * | |
545 | * This function can be used to process TLS handshake messages. It reassembles | |
546 | * the received fragments and uses a TLS library to process the messages. The | |
547 | * response data from the TLS library is fragmented to suitable output messages | |
548 | * that the caller can send out. | |
549 | * | |
550 | * out_data is used to return the response message if the return value of this | |
551 | * function is 0, 2, or -1. In case of failure, the message is likely a TLS | |
552 | * alarm message. The caller is responsible for freeing the allocated buffer if | |
553 | * *out_data is not %NULL. | |
554 | * | |
555 | * This function is called for each received TLS message during the TLS | |
556 | * handshake after eap_peer_tls_process_init() call and possible processing of | |
557 | * TLS Flags field. Once the handshake has been completed, i.e., when | |
558 | * tls_connection_established() returns 1, EAP method specific decrypting of | |
559 | * the tunneled data is used. | |
560 | */ | |
561 | int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, | |
562 | EapType eap_type, int peap_version, | |
563 | u8 id, const u8 *in_data, size_t in_len, | |
564 | struct wpabuf **out_data) | |
565 | { | |
566 | int ret = 0; | |
567 | ||
568 | *out_data = NULL; | |
569 | ||
81c85c06 | 570 | if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) { |
6fc6879b JM |
571 | wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " |
572 | "fragments are waiting to be sent out"); | |
573 | return -1; | |
574 | } | |
575 | ||
81c85c06 | 576 | if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { |
6fc6879b JM |
577 | /* |
578 | * No more data to send out - expect to receive more data from | |
579 | * the AS. | |
580 | */ | |
581 | int res = eap_tls_process_input(sm, data, in_data, in_len, | |
582 | out_data); | |
583 | if (res) { | |
584 | /* | |
585 | * Input processing failed (res = -1) or more data is | |
586 | * needed (res = 1). | |
587 | */ | |
588 | return res; | |
589 | } | |
590 | ||
591 | /* | |
592 | * The incoming message has been reassembled and processed. The | |
593 | * response was allocated into data->tls_out buffer. | |
594 | */ | |
595 | } | |
596 | ||
597 | if (data->tls_out == NULL) { | |
598 | /* | |
599 | * No outgoing fragments remaining from the previous message | |
600 | * and no new message generated. This indicates an error in TLS | |
601 | * processing. | |
602 | */ | |
603 | eap_peer_tls_reset_output(data); | |
604 | return -1; | |
605 | } | |
606 | ||
607 | if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { | |
608 | /* TLS processing has failed - return error */ | |
609 | wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " | |
610 | "report error"); | |
611 | ret = -1; | |
612 | /* TODO: clean pin if engine used? */ | |
613 | } | |
614 | ||
81c85c06 | 615 | if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { |
6fc6879b JM |
616 | /* |
617 | * TLS negotiation should now be complete since all other cases | |
618 | * needing more data should have been caught above based on | |
619 | * the TLS Message Length field. | |
620 | */ | |
621 | wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); | |
81c85c06 | 622 | wpabuf_free(data->tls_out); |
6fc6879b JM |
623 | data->tls_out = NULL; |
624 | return 1; | |
625 | } | |
626 | ||
627 | /* Send the pending message (in fragments, if needed). */ | |
628 | return eap_tls_process_output(data, eap_type, peap_version, id, ret, | |
629 | out_data); | |
630 | } | |
631 | ||
632 | ||
633 | /** | |
634 | * eap_peer_tls_build_ack - Build a TLS ACK frame | |
635 | * @id: EAP identifier for the response | |
636 | * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) | |
637 | * @peap_version: Version number for EAP-PEAP/TTLS | |
638 | * Returns: Pointer to the allocated ACK frame or %NULL on failure | |
639 | */ | |
640 | struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, | |
641 | int peap_version) | |
642 | { | |
643 | struct wpabuf *resp; | |
644 | ||
645 | resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE, | |
646 | id); | |
647 | if (resp == NULL) | |
648 | return NULL; | |
649 | wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)", | |
650 | (int) eap_type, id, peap_version); | |
651 | wpabuf_put_u8(resp, peap_version); /* Flags */ | |
652 | return resp; | |
653 | } | |
654 | ||
655 | ||
656 | /** | |
657 | * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption | |
658 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
659 | * @data: Data for TLS processing | |
660 | * Returns: 0 on success, -1 on failure | |
661 | */ | |
662 | int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) | |
663 | { | |
664 | eap_peer_tls_reset_input(data); | |
665 | eap_peer_tls_reset_output(data); | |
666 | return tls_connection_shutdown(sm->ssl_ctx, data->conn); | |
667 | } | |
668 | ||
669 | ||
670 | /** | |
671 | * eap_peer_tls_status - Get TLS status | |
672 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
673 | * @data: Data for TLS processing | |
674 | * @buf: Buffer for status information | |
675 | * @buflen: Maximum buffer length | |
676 | * @verbose: Whether to include verbose status information | |
677 | * Returns: Number of bytes written to buf. | |
678 | */ | |
679 | int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, | |
680 | char *buf, size_t buflen, int verbose) | |
681 | { | |
682 | char name[128]; | |
683 | int len = 0, ret; | |
684 | ||
685 | if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) { | |
686 | ret = os_snprintf(buf + len, buflen - len, | |
687 | "EAP TLS cipher=%s\n", name); | |
688 | if (ret < 0 || (size_t) ret >= buflen - len) | |
689 | return len; | |
690 | len += ret; | |
691 | } | |
692 | ||
693 | return len; | |
694 | } | |
695 | ||
696 | ||
697 | /** | |
698 | * eap_peer_tls_process_init - Initial validation/processing of EAP requests | |
699 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
700 | * @data: Data for TLS processing | |
701 | * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) | |
702 | * @ret: Return values from EAP request validation and processing | |
703 | * @reqData: EAP request to be processed (eapReqData) | |
704 | * @len: Buffer for returning length of the remaining payload | |
705 | * @flags: Buffer for returning TLS flags | |
706 | * Returns: Pointer to payload after TLS flags and length or %NULL on failure | |
707 | * | |
708 | * This function validates the EAP header and processes the optional TLS | |
709 | * Message Length field. If this is the first fragment of a TLS message, the | |
710 | * TLS reassembly code is initialized to receive the indicated number of bytes. | |
711 | * | |
712 | * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this | |
713 | * function as the first step in processing received messages. They will need | |
714 | * to process the flags (apart from Message Length Included) that are returned | |
715 | * through the flags pointer and the message payload that will be returned (and | |
716 | * the length is returned through the len pointer). Return values (ret) are set | |
717 | * for continuation of EAP method processing. The caller is responsible for | |
718 | * setting these to indicate completion (either success or failure) based on | |
719 | * the authentication result. | |
720 | */ | |
721 | const u8 * eap_peer_tls_process_init(struct eap_sm *sm, | |
722 | struct eap_ssl_data *data, | |
723 | EapType eap_type, | |
724 | struct eap_method_ret *ret, | |
725 | const struct wpabuf *reqData, | |
726 | size_t *len, u8 *flags) | |
727 | { | |
728 | const u8 *pos; | |
729 | size_t left; | |
730 | unsigned int tls_msg_len; | |
731 | ||
732 | if (tls_get_errors(sm->ssl_ctx)) { | |
733 | wpa_printf(MSG_INFO, "SSL: TLS errors detected"); | |
734 | ret->ignore = TRUE; | |
735 | return NULL; | |
736 | } | |
737 | ||
738 | pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left); | |
739 | if (pos == NULL) { | |
740 | ret->ignore = TRUE; | |
741 | return NULL; | |
742 | } | |
3f732d1f JM |
743 | if (left == 0) { |
744 | wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags " | |
745 | "octet included"); | |
746 | if (!sm->workaround) { | |
747 | ret->ignore = TRUE; | |
748 | return NULL; | |
749 | } | |
750 | ||
751 | wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags " | |
752 | "indicates ACK frame"); | |
753 | *flags = 0; | |
754 | } else { | |
755 | *flags = *pos++; | |
756 | left--; | |
757 | } | |
6fc6879b JM |
758 | wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - " |
759 | "Flags 0x%02x", (unsigned long) wpabuf_len(reqData), | |
760 | *flags); | |
761 | if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { | |
762 | if (left < 4) { | |
763 | wpa_printf(MSG_INFO, "SSL: Short frame with TLS " | |
764 | "length"); | |
765 | ret->ignore = TRUE; | |
766 | return NULL; | |
767 | } | |
768 | tls_msg_len = WPA_GET_BE32(pos); | |
769 | wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", | |
770 | tls_msg_len); | |
771 | if (data->tls_in_left == 0) { | |
772 | data->tls_in_total = tls_msg_len; | |
773 | data->tls_in_left = tls_msg_len; | |
81c85c06 | 774 | wpabuf_free(data->tls_in); |
6fc6879b | 775 | data->tls_in = NULL; |
6fc6879b JM |
776 | } |
777 | pos += 4; | |
778 | left -= 4; | |
779 | } | |
780 | ||
781 | ret->ignore = FALSE; | |
782 | ret->methodState = METHOD_MAY_CONT; | |
783 | ret->decision = DECISION_FAIL; | |
784 | ret->allowNotifications = TRUE; | |
785 | ||
786 | *len = left; | |
787 | return pos; | |
788 | } | |
789 | ||
790 | ||
791 | /** | |
792 | * eap_peer_tls_reset_input - Reset input buffers | |
793 | * @data: Data for TLS processing | |
794 | * | |
795 | * This function frees any allocated memory for input buffers and resets input | |
796 | * state. | |
797 | */ | |
798 | void eap_peer_tls_reset_input(struct eap_ssl_data *data) | |
799 | { | |
81c85c06 JM |
800 | data->tls_in_left = data->tls_in_total = 0; |
801 | wpabuf_free(data->tls_in); | |
6fc6879b JM |
802 | data->tls_in = NULL; |
803 | } | |
804 | ||
805 | ||
806 | /** | |
807 | * eap_peer_tls_reset_output - Reset output buffers | |
808 | * @data: Data for TLS processing | |
809 | * | |
810 | * This function frees any allocated memory for output buffers and resets | |
811 | * output state. | |
812 | */ | |
813 | void eap_peer_tls_reset_output(struct eap_ssl_data *data) | |
814 | { | |
6fc6879b | 815 | data->tls_out_pos = 0; |
81c85c06 | 816 | wpabuf_free(data->tls_out); |
6fc6879b JM |
817 | data->tls_out = NULL; |
818 | } | |
819 | ||
820 | ||
821 | /** | |
822 | * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message | |
823 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
824 | * @data: Data for TLS processing | |
825 | * @in_data: Message received from the server | |
826 | * @in_decrypted: Buffer for returning a pointer to the decrypted message | |
827 | * Returns: 0 on success, 1 if more input data is needed, or -1 on failure | |
828 | */ | |
829 | int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, | |
830 | const struct wpabuf *in_data, | |
831 | struct wpabuf **in_decrypted) | |
832 | { | |
81c85c06 | 833 | const struct wpabuf *msg; |
6fc6879b JM |
834 | int need_more_input; |
835 | ||
81c85c06 | 836 | msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); |
6fc6879b JM |
837 | if (msg == NULL) |
838 | return need_more_input ? 1 : -1; | |
839 | ||
81c85c06 | 840 | *in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg); |
6fc6879b | 841 | eap_peer_tls_reset_input(data); |
81c85c06 | 842 | if (*in_decrypted == NULL) { |
6fc6879b JM |
843 | wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); |
844 | return -1; | |
845 | } | |
6fc6879b JM |
846 | return 0; |
847 | } | |
848 | ||
849 | ||
850 | /** | |
851 | * eap_peer_tls_encrypt - Encrypt phase 2 TLS message | |
852 | * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() | |
853 | * @data: Data for TLS processing | |
854 | * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) | |
855 | * @peap_version: Version number for EAP-PEAP/TTLS | |
856 | * @id: EAP identifier for the response | |
857 | * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments | |
858 | * @out_data: Buffer for returning a pointer to the encrypted response message | |
859 | * Returns: 0 on success, -1 on failure | |
860 | */ | |
861 | int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, | |
862 | EapType eap_type, int peap_version, u8 id, | |
863 | const struct wpabuf *in_data, | |
864 | struct wpabuf **out_data) | |
865 | { | |
6fc6879b JM |
866 | if (in_data) { |
867 | eap_peer_tls_reset_output(data); | |
81c85c06 JM |
868 | data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn, |
869 | in_data); | |
870 | if (data->tls_out == NULL) { | |
6fc6879b JM |
871 | wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 " |
872 | "data (in_len=%lu)", | |
873 | (unsigned long) wpabuf_len(in_data)); | |
874 | eap_peer_tls_reset_output(data); | |
875 | return -1; | |
876 | } | |
6fc6879b JM |
877 | } |
878 | ||
879 | return eap_tls_process_output(data, eap_type, peap_version, id, 0, | |
880 | out_data); | |
881 | } | |
882 | ||
883 | ||
884 | /** | |
885 | * eap_peer_select_phase2_methods - Select phase 2 EAP method | |
886 | * @config: Pointer to the network configuration | |
887 | * @prefix: 'phase2' configuration prefix, e.g., "auth=" | |
888 | * @types: Buffer for returning allocated list of allowed EAP methods | |
889 | * @num_types: Buffer for returning number of allocated EAP methods | |
890 | * Returns: 0 on success, -1 on failure | |
891 | * | |
892 | * This function is used to parse EAP method list and select allowed methods | |
893 | * for Phase2 authentication. | |
894 | */ | |
895 | int eap_peer_select_phase2_methods(struct eap_peer_config *config, | |
896 | const char *prefix, | |
897 | struct eap_method_type **types, | |
898 | size_t *num_types) | |
899 | { | |
900 | char *start, *pos, *buf; | |
901 | struct eap_method_type *methods = NULL, *_methods; | |
902 | u8 method; | |
903 | size_t num_methods = 0, prefix_len; | |
904 | ||
905 | if (config == NULL || config->phase2 == NULL) | |
906 | goto get_defaults; | |
907 | ||
908 | start = buf = os_strdup(config->phase2); | |
909 | if (buf == NULL) | |
910 | return -1; | |
911 | ||
912 | prefix_len = os_strlen(prefix); | |
913 | ||
914 | while (start && *start != '\0') { | |
915 | int vendor; | |
916 | pos = os_strstr(start, prefix); | |
917 | if (pos == NULL) | |
918 | break; | |
919 | if (start != pos && *(pos - 1) != ' ') { | |
920 | start = pos + prefix_len; | |
921 | continue; | |
922 | } | |
923 | ||
924 | start = pos + prefix_len; | |
925 | pos = os_strchr(start, ' '); | |
926 | if (pos) | |
927 | *pos++ = '\0'; | |
928 | method = eap_get_phase2_type(start, &vendor); | |
929 | if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { | |
930 | wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP " | |
931 | "method '%s'", start); | |
932 | } else { | |
933 | num_methods++; | |
934 | _methods = os_realloc(methods, | |
935 | num_methods * sizeof(*methods)); | |
936 | if (_methods == NULL) { | |
937 | os_free(methods); | |
938 | os_free(buf); | |
939 | return -1; | |
940 | } | |
941 | methods = _methods; | |
942 | methods[num_methods - 1].vendor = vendor; | |
943 | methods[num_methods - 1].method = method; | |
944 | } | |
945 | ||
946 | start = pos; | |
947 | } | |
948 | ||
949 | os_free(buf); | |
950 | ||
951 | get_defaults: | |
952 | if (methods == NULL) | |
953 | methods = eap_get_phase2_types(config, &num_methods); | |
954 | ||
955 | if (methods == NULL) { | |
956 | wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available"); | |
957 | return -1; | |
958 | } | |
959 | wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types", | |
960 | (u8 *) methods, | |
961 | num_methods * sizeof(struct eap_method_type)); | |
962 | ||
963 | *types = methods; | |
964 | *num_types = num_methods; | |
965 | ||
966 | return 0; | |
967 | } | |
968 | ||
969 | ||
970 | /** | |
971 | * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2 | |
972 | * @types: Buffer for returning allocated list of allowed EAP methods | |
973 | * @num_types: Buffer for returning number of allocated EAP methods | |
974 | * @hdr: EAP-Request header (and the following EAP type octet) | |
975 | * @resp: Buffer for returning the EAP-Nak message | |
976 | * Returns: 0 on success, -1 on failure | |
977 | */ | |
978 | int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, | |
979 | struct eap_hdr *hdr, struct wpabuf **resp) | |
980 | { | |
981 | u8 *pos = (u8 *) (hdr + 1); | |
982 | size_t i; | |
983 | ||
984 | /* TODO: add support for expanded Nak */ | |
985 | wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos); | |
986 | wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types", | |
987 | (u8 *) types, num_types * sizeof(struct eap_method_type)); | |
988 | *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types, | |
989 | EAP_CODE_RESPONSE, hdr->identifier); | |
990 | if (*resp == NULL) | |
991 | return -1; | |
992 | ||
993 | for (i = 0; i < num_types; i++) { | |
994 | if (types[i].vendor == EAP_VENDOR_IETF && | |
995 | types[i].method < 256) | |
996 | wpabuf_put_u8(*resp, types[i].method); | |
997 | } | |
998 | ||
999 | eap_update_len(*resp); | |
1000 | ||
1001 | return 0; | |
1002 | } |