Switch to gcc41 as default compiler.
[dragonfly.git] / contrib / wpa_supplicant-0.4.9 / eap_tls_common.c
blob3114d8f78ebb27c51c04a7436f9392c9d6638177
1 /*
2 * WPA Supplicant / EAP-TLS/PEAP/TTLS/FAST common functions
3 * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
19 #include "common.h"
20 #include "eap_i.h"
21 #include "eap_tls_common.h"
22 #include "wpa_supplicant.h"
23 #include "config_ssid.h"
24 #include "md5.h"
25 #include "sha1.h"
26 #include "tls.h"
27 #include "config.h"
30 static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
31 const u8 **data, size_t *data_len)
33 const struct wpa_config_blob *blob;
35 if (*name == NULL || strncmp(*name, "blob://", 7) != 0)
36 return 0;
38 blob = eap_get_config_blob(sm, *name + 7);
39 if (blob == NULL) {
40 wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
41 "found", __func__, *name + 7);
42 return -1;
45 *name = NULL;
46 *data = blob->data;
47 *data_len = blob->len;
49 return 0;
53 int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
54 struct wpa_ssid *config)
56 int ret = -1, res;
57 struct tls_connection_params params;
59 if (config == NULL)
60 return -1;
62 data->eap = sm;
63 data->phase2 = sm->init_phase2;
64 memset(&params, 0, sizeof(params));
65 params.engine = config->engine;
66 if (data->phase2) {
67 params.ca_cert = (char *) config->ca_cert2;
68 params.ca_path = (char *) config->ca_path2;
69 params.client_cert = (char *) config->client_cert2;
70 params.private_key = (char *) config->private_key2;
71 params.private_key_passwd =
72 (char *) config->private_key2_passwd;
73 params.dh_file = (char *) config->dh_file2;
74 params.subject_match = (char *) config->subject_match2;
75 params.altsubject_match = (char *) config->altsubject_match2;
76 } else {
77 params.ca_cert = (char *) config->ca_cert;
78 params.ca_path = (char *) config->ca_path;
79 params.client_cert = (char *) config->client_cert;
80 params.private_key = (char *) config->private_key;
81 params.private_key_passwd =
82 (char *) config->private_key_passwd;
83 params.dh_file = (char *) config->dh_file;
84 params.subject_match = (char *) config->subject_match;
85 params.altsubject_match = (char *) config->altsubject_match;
86 params.engine_id = config->engine_id;
87 params.pin = config->pin;
88 params.key_id = config->key_id;
91 if (eap_tls_check_blob(sm, &params.ca_cert, &params.ca_cert_blob,
92 &params.ca_cert_blob_len) ||
93 eap_tls_check_blob(sm, &params.client_cert,
94 &params.client_cert_blob,
95 &params.client_cert_blob_len) ||
96 eap_tls_check_blob(sm, &params.private_key,
97 &params.private_key_blob,
98 &params.private_key_blob_len) ||
99 eap_tls_check_blob(sm, &params.dh_file, &params.dh_blob,
100 &params.dh_blob_len)) {
101 wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
102 goto done;
105 data->conn = tls_connection_init(sm->ssl_ctx);
106 if (data->conn == NULL) {
107 wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
108 "connection");
109 goto done;
112 res = tls_connection_set_params(sm->ssl_ctx, data->conn, &params);
113 if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
114 /* At this point with the pkcs11 engine the PIN might be wrong.
115 * We reset the PIN in the configuration to be sure to not use
116 * it again and the calling function must request a new one */
117 free(config->pin);
118 config->pin = NULL;
119 } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
120 wpa_printf(MSG_INFO,"TLS: Failed to load private key");
121 /* We don't know exactly but maybe the PIN was wrong,
122 * so ask for a new one. */
123 free(config->pin);
124 config->pin = NULL;
125 eap_sm_request_pin(sm, config);
126 sm->ignore = TRUE;
127 goto done;
128 } else if (res) {
129 wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
130 "parameters");
131 goto done;
134 /* TODO: make this configurable */
135 data->tls_out_limit = 1398;
136 if (data->phase2) {
137 /* Limit the fragment size in the inner TLS authentication
138 * since the outer authentication with EAP-PEAP does not yet
139 * support fragmentation */
140 if (data->tls_out_limit > 100)
141 data->tls_out_limit -= 100;
144 if (config->phase1 &&
145 strstr(config->phase1, "include_tls_length=1")) {
146 wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
147 "unfragmented packets");
148 data->include_tls_length = 1;
151 ret = 0;
153 done:
154 return ret;
158 void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
160 tls_connection_deinit(sm->ssl_ctx, data->conn);
161 free(data->tls_in);
162 free(data->tls_out);
166 u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
167 char *label, size_t len)
169 struct tls_keys keys;
170 u8 *rnd;
171 u8 *out;
173 if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
174 return NULL;
176 if (keys.eap_tls_prf && strcmp(label, "client EAP encryption") == 0) {
177 if (len > keys.eap_tls_prf_len)
178 return NULL;
179 out = malloc(len);
180 if (out == NULL)
181 return NULL;
182 memcpy(out, keys.eap_tls_prf, len);
183 return out;
186 if (keys.client_random == NULL || keys.server_random == NULL ||
187 keys.master_key == NULL)
188 return NULL;
190 out = malloc(len);
191 rnd = malloc(keys.client_random_len + keys.server_random_len);
192 if (out == NULL || rnd == NULL) {
193 free(out);
194 free(rnd);
195 return NULL;
197 memcpy(rnd, keys.client_random, keys.client_random_len);
198 memcpy(rnd + keys.client_random_len, keys.server_random,
199 keys.server_random_len);
201 if (tls_prf(keys.master_key, keys.master_key_len,
202 label, rnd, keys.client_random_len +
203 keys.server_random_len, out, len)) {
204 free(rnd);
205 free(out);
206 return NULL;
208 free(rnd);
209 return out;
214 * eap_tls_data_reassemble - Reassemble TLS data
215 * @sm: Pointer to EAP state machine allocated with eap_sm_init()
216 * @data: Data for TLS processing
217 * @in_data: Next incoming TLS segment
218 * @in_len: Length of in_data
219 * @out_len: Variable for returning output data length
220 * @need_more_input: Variable for returning whether more input data is needed
221 * to reassemble this TLS packet
222 * Returns: Pointer to output data or %NULL on error
224 * This function reassembles TLS fragments.
226 const u8 * eap_tls_data_reassemble(
227 struct eap_sm *sm, struct eap_ssl_data *data, const u8 *in_data,
228 size_t in_len, size_t *out_len, int *need_more_input)
230 u8 *buf;
232 *need_more_input = 0;
234 if (data->tls_in_left > in_len || data->tls_in) {
235 if (data->tls_in_len + in_len == 0) {
236 free(data->tls_in);
237 data->tls_in = NULL;
238 data->tls_in_len = 0;
239 wpa_printf(MSG_WARNING, "SSL: Invalid reassembly "
240 "state: tls_in_left=%lu tls_in_len=%lu "
241 "in_len=%lu",
242 (unsigned long) data->tls_in_left,
243 (unsigned long) data->tls_in_len,
244 (unsigned long) in_len);
245 return NULL;
247 buf = realloc(data->tls_in, data->tls_in_len + in_len);
248 if (buf == NULL) {
249 free(data->tls_in);
250 data->tls_in = NULL;
251 data->tls_in_len = 0;
252 wpa_printf(MSG_INFO, "SSL: Could not allocate memory "
253 "for TLS data");
254 return NULL;
256 memcpy(buf + data->tls_in_len, in_data, in_len);
257 data->tls_in = buf;
258 data->tls_in_len += in_len;
259 if (in_len > data->tls_in_left) {
260 wpa_printf(MSG_INFO, "SSL: more data than TLS message "
261 "length indicated");
262 data->tls_in_left = 0;
263 return NULL;
265 data->tls_in_left -= in_len;
266 if (data->tls_in_left > 0) {
267 wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
268 "data", (unsigned long) data->tls_in_left);
269 *need_more_input = 1;
270 return NULL;
272 } else {
273 data->tls_in_left = 0;
274 data->tls_in = malloc(in_len);
275 if (data->tls_in == NULL)
276 return NULL;
277 memcpy(data->tls_in, in_data, in_len);
278 data->tls_in_len = in_len;
281 *out_len = data->tls_in_len;
282 return data->tls_in;
286 int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
287 int eap_type, int peap_version,
288 u8 id, const u8 *in_data, size_t in_len,
289 u8 **out_data, size_t *out_len)
291 size_t len;
292 u8 *pos, *flags;
293 struct eap_hdr *resp;
294 int ret = 0;
296 WPA_ASSERT(data->tls_out_len == 0 || in_len == 0);
297 *out_len = 0;
299 if (data->tls_out_len == 0) {
300 /* No more data to send out - expect to receive more data from
301 * the AS. */
302 const u8 *msg;
303 size_t msg_len;
304 int need_more_input;
306 msg = eap_tls_data_reassemble(sm, data, in_data, in_len,
307 &msg_len, &need_more_input);
308 if (msg == NULL)
309 return need_more_input ? 1 : -1;
311 /* Full TLS message reassembled - continue handshake processing
313 if (data->tls_out) {
314 /* This should not happen.. */
315 wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - "
316 "pending tls_out data even though "
317 "tls_out_len = 0");
318 free(data->tls_out);
319 WPA_ASSERT(data->tls_out == NULL);
321 data->tls_out = tls_connection_handshake(sm->ssl_ctx,
322 data->conn,
323 msg, msg_len,
324 &data->tls_out_len);
326 /* Clear reassembled input data (if the buffer was needed). */
327 data->tls_in_left = data->tls_in_total = data->tls_in_len = 0;
328 free(data->tls_in);
329 data->tls_in = NULL;
332 if (data->tls_out == NULL) {
333 data->tls_out_len = 0;
334 return -1;
336 if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
337 wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
338 "report error");
339 ret = -1;
340 /* TODO: clean pin if engine used? */
343 if (data->tls_out_len == 0) {
344 /* TLS negotiation should now be complete since all other cases
345 * needing more that should have been catched above based on
346 * the TLS Message Length field. */
347 wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
348 free(data->tls_out);
349 data->tls_out = NULL;
350 return 1;
353 wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
354 "%lu bytes)",
355 (unsigned long) data->tls_out_len - data->tls_out_pos,
356 (unsigned long) data->tls_out_len);
357 resp = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit);
358 if (resp == NULL) {
359 *out_data = NULL;
360 return -1;
362 resp->code = EAP_CODE_RESPONSE;
363 resp->identifier = id;
364 pos = (u8 *) (resp + 1);
365 *pos++ = eap_type;
366 flags = pos++;
367 *flags = peap_version;
368 if (data->tls_out_pos == 0 &&
369 (data->tls_out_len > data->tls_out_limit ||
370 data->include_tls_length)) {
371 *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
372 *pos++ = (data->tls_out_len >> 24) & 0xff;
373 *pos++ = (data->tls_out_len >> 16) & 0xff;
374 *pos++ = (data->tls_out_len >> 8) & 0xff;
375 *pos++ = data->tls_out_len & 0xff;
378 len = data->tls_out_len - data->tls_out_pos;
379 if (len > data->tls_out_limit) {
380 *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
381 len = data->tls_out_limit;
382 wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
383 "will follow", (unsigned long) len);
385 memcpy(pos, &data->tls_out[data->tls_out_pos], len);
386 data->tls_out_pos += len;
387 *out_len = (pos - (u8 *) resp) + len;
388 resp->length = host_to_be16(*out_len);
389 *out_data = (u8 *) resp;
391 if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) {
392 data->tls_out_len = 0;
393 data->tls_out_pos = 0;
394 free(data->tls_out);
395 data->tls_out = NULL;
398 return ret;
402 u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id,
403 int eap_type, int peap_version)
405 struct eap_hdr *resp;
406 u8 *pos;
408 *respDataLen = sizeof(struct eap_hdr) + 2;
409 resp = malloc(*respDataLen);
410 if (resp == NULL)
411 return NULL;
412 wpa_printf(MSG_DEBUG, "SSL: Building ACK");
413 resp->code = EAP_CODE_RESPONSE;
414 resp->identifier = id;
415 resp->length = host_to_be16(*respDataLen);
416 pos = (u8 *) (resp + 1);
417 *pos++ = eap_type; /* Type */
418 *pos = peap_version; /* Flags */
419 return (u8 *) resp;
423 int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
425 free(data->tls_in);
426 data->tls_in = NULL;
427 data->tls_in_len = data->tls_in_left = data->tls_in_total = 0;
428 free(data->tls_out);
429 data->tls_out = NULL;
430 data->tls_out_len = data->tls_out_pos = 0;
432 return tls_connection_shutdown(sm->ssl_ctx, data->conn);
436 int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf,
437 size_t buflen, int verbose)
439 char name[128];
440 int len = 0;
442 if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
443 len += snprintf(buf + len, buflen - len,
444 "EAP TLS cipher=%s\n", name);
447 return len;
451 const u8 * eap_tls_process_init(struct eap_sm *sm, struct eap_ssl_data *data,
452 EapType eap_type, struct eap_method_ret *ret,
453 const u8 *reqData, size_t reqDataLen,
454 size_t *len, u8 *flags)
456 const struct eap_hdr *req;
457 const u8 *pos;
458 size_t left;
459 unsigned int tls_msg_len;
461 if (tls_get_errors(sm->ssl_ctx)) {
462 wpa_printf(MSG_INFO, "SSL: TLS errors detected");
463 ret->ignore = TRUE;
464 return NULL;
467 pos = eap_hdr_validate(eap_type, reqData, reqDataLen, &left);
468 if (pos == NULL) {
469 ret->ignore = TRUE;
470 return NULL;
472 req = (const struct eap_hdr *) reqData;
473 *flags = *pos++;
474 left--;
475 wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
476 "Flags 0x%02x", (unsigned long) reqDataLen, *flags);
477 if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
478 if (left < 4) {
479 wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
480 "length");
481 ret->ignore = TRUE;
482 return NULL;
484 tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) |
485 pos[3];
486 wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
487 tls_msg_len);
488 if (data->tls_in_left == 0) {
489 data->tls_in_total = tls_msg_len;
490 data->tls_in_left = tls_msg_len;
491 free(data->tls_in);
492 data->tls_in = NULL;
493 data->tls_in_len = 0;
495 pos += 4;
496 left -= 4;
499 ret->ignore = FALSE;
500 ret->methodState = METHOD_MAY_CONT;
501 ret->decision = DECISION_FAIL;
502 ret->allowNotifications = TRUE;
504 *len = (size_t) left;
505 return pos;