Change the kernel dev_t, representing a pointer to a specinfo structure,
[dragonfly.git] / contrib / wpa_supplicant-0.4.9 / 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         if (config == NULL)
60                 return -1;
61
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;
89         }
90
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;
103         }
104
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;
110         }
111
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;
132         }
133
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;
142         }
143
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;
149         }
150
151         ret = 0;
152
153 done:
154         return ret;
155 }
156
157
158 void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
159 {
160         tls_connection_deinit(sm->ssl_ctx, data->conn);
161         free(data->tls_in);
162         free(data->tls_out);
163 }
164
165
166 u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
167                         char *label, size_t len)
168 {
169         struct tls_keys keys;
170         u8 *rnd;
171         u8 *out;
172
173         if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
174                 return NULL;
175
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;
184         }
185
186         if (keys.client_random == NULL || keys.server_random == NULL ||
187             keys.master_key == NULL)
188                 return NULL;
189
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;
196         }
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);
200
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;
207         }
208         free(rnd);
209         return out;
210 }
211
212
213 /**
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
223  *
224  * This function reassembles TLS fragments.
225  */
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)
229 {
230         u8 *buf;
231
232         *need_more_input = 0;
233
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;
246                 }
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;
255                 }
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;
264                 }
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;
271                 }
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;
279         }
280
281         *out_len = data->tls_in_len;
282         return data->tls_in;
283 }
284
285
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)
290 {
291         size_t len;
292         u8 *pos, *flags;
293         struct eap_hdr *resp;
294         int ret = 0;
295
296         WPA_ASSERT(data->tls_out_len == 0 || in_len == 0);
297         *out_len = 0;
298
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;
305
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;
310
311                 /* Full TLS message reassembled - continue handshake processing
312                  */
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);
320                 }
321                 data->tls_out = tls_connection_handshake(sm->ssl_ctx,
322                                                          data->conn,
323                                                          msg, msg_len,
324                                                          &data->tls_out_len);
325
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;
330         }
331
332         if (data->tls_out == NULL) {
333                 data->tls_out_len = 0;
334                 return -1;
335         }
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? */
341         }
342
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;
351         }
352
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;
361         }
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;
376         }
377
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);
384         }
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;
390
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;
396         }
397
398         return ret;
399 }
400
401
402 u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id,
403                        int eap_type, int peap_version)
404 {
405         struct eap_hdr *resp;
406         u8 *pos;
407
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;
420 }
421
422
423 int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
424 {
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;
431
432         return tls_connection_shutdown(sm->ssl_ctx, data->conn);
433 }
434
435
436 int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf,
437                    size_t buflen, int verbose)
438 {
439         char name[128];
440         int len = 0;
441
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);
445         }
446
447         return len;
448 }
449
450
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)
455 {
456         const struct eap_hdr *req;
457         const u8 *pos;
458         size_t left;
459         unsigned int tls_msg_len;
460
461         if (tls_get_errors(sm->ssl_ctx)) {
462                 wpa_printf(MSG_INFO, "SSL: TLS errors detected");
463                 ret->ignore = TRUE;
464                 return NULL;
465         }
466
467         pos = eap_hdr_validate(eap_type, reqData, reqDataLen, &left);
468         if (pos == NULL) {
469                 ret->ignore = TRUE;
470                 return NULL;
471         }
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;
483                 }
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;
494                 }
495                 pos += 4;
496                 left -= 4;
497         }
498
499         ret->ignore = FALSE;
500         ret->methodState = METHOD_MAY_CONT;
501         ret->decision = DECISION_FAIL;
502         ret->allowNotifications = TRUE;
503
504         *len = (size_t) left;
505         return pos;
506 }