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