2 * WPA Supplicant / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt)
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
12 * See README and COPYING for more details.
21 #include "eap_tls_common.h"
22 #include "wpa_supplicant.h"
23 #include "config_ssid.h"
28 /* Maximum supported PEAP version
29 * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
30 * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
31 * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt
33 #define EAP_PEAP_VERSION 1
36 static void eap_peap_deinit(struct eap_sm *sm, void *priv);
39 struct eap_peap_data {
40 struct eap_ssl_data ssl;
42 int peap_version, force_peap_version, force_new_label;
44 const struct eap_method *phase2_method;
50 size_t num_phase2_types;
52 int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
54 * 1 = reply with tunneled EAP-Success to inner
55 * EAP-Success and expect AS to send outer
56 * (unencrypted) EAP-Success after this
57 * 2 = reply with PEAP/TLS ACK to inner
58 * EAP-Success and expect AS to send outer
59 * (unencrypted) EAP-Success after this */
60 int resuming; /* starting a resumed session */
63 u8 *pending_phase2_req;
64 size_t pending_phase2_req_len;
68 static void * eap_peap_init(struct eap_sm *sm)
70 struct eap_peap_data *data;
71 struct wpa_ssid *config = eap_get_config(sm);
73 data = malloc(sizeof(*data));
76 sm->peap_done = FALSE;
77 memset(data, 0, sizeof(*data));
78 data->peap_version = EAP_PEAP_VERSION;
79 data->force_peap_version = -1;
80 data->peap_outer_success = 2;
82 if (config && config->phase1) {
83 char *pos = strstr(config->phase1, "peapver=");
85 data->force_peap_version = atoi(pos + 8);
86 data->peap_version = data->force_peap_version;
87 wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version "
88 "%d", data->force_peap_version);
91 if (strstr(config->phase1, "peaplabel=1")) {
92 data->force_new_label = 1;
93 wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for "
97 if (strstr(config->phase1, "peap_outer_success=0")) {
98 data->peap_outer_success = 0;
99 wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate "
100 "authentication on tunneled EAP-Success");
101 } else if (strstr(config->phase1, "peap_outer_success=1")) {
102 data->peap_outer_success = 1;
103 wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled "
104 "EAP-Success after receiving tunneled "
106 } else if (strstr(config->phase1, "peap_outer_success=2")) {
107 data->peap_outer_success = 2;
108 wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK "
109 "after receiving tunneled EAP-Success");
113 if (config && config->phase2) {
114 char *start, *pos, *buf;
115 u8 method, *methods = NULL, *_methods;
116 size_t num_methods = 0;
117 start = buf = strdup(config->phase2);
119 eap_peap_deinit(sm, data);
122 while (start && *start != '\0') {
123 pos = strstr(start, "auth=");
126 if (start != pos && *(pos - 1) != ' ') {
132 pos = strchr(start, ' ');
135 method = eap_get_phase2_type(start);
136 if (method == EAP_TYPE_NONE) {
137 wpa_printf(MSG_ERROR, "EAP-PEAP: Unsupported "
138 "Phase2 method '%s'", start);
141 _methods = realloc(methods, num_methods);
142 if (_methods == NULL) {
144 eap_peap_deinit(sm, data);
148 methods[num_methods - 1] = method;
154 data->phase2_types = methods;
155 data->num_phase2_types = num_methods;
157 if (data->phase2_types == NULL) {
159 eap_get_phase2_types(config, &data->num_phase2_types);
161 if (data->phase2_types == NULL) {
162 wpa_printf(MSG_ERROR, "EAP-PEAP: No Phase2 method available");
163 eap_peap_deinit(sm, data);
166 wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 EAP types",
167 data->phase2_types, data->num_phase2_types);
168 data->phase2_type = EAP_TYPE_NONE;
170 if (eap_tls_ssl_init(sm, &data->ssl, config)) {
171 wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
172 eap_peap_deinit(sm, data);
180 static void eap_peap_deinit(struct eap_sm *sm, void *priv)
182 struct eap_peap_data *data = priv;
185 if (data->phase2_priv && data->phase2_method)
186 data->phase2_method->deinit(sm, data->phase2_priv);
187 free(data->phase2_types);
188 eap_tls_ssl_deinit(sm, &data->ssl);
189 free(data->key_data);
190 free(data->pending_phase2_req);
195 static int eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data,
196 int id, const u8 *plain, size_t plain_len,
197 u8 **out_data, size_t *out_len)
201 struct eap_hdr *resp;
203 /* TODO: add support for fragmentation, if needed. This will need to
204 * add TLS Message Length field, if the frame is fragmented.
205 * Note: Microsoft IAS did not seem to like TLS Message Length with
207 resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit);
211 resp->code = EAP_CODE_RESPONSE;
212 resp->identifier = id;
214 pos = (u8 *) (resp + 1);
215 *pos++ = EAP_TYPE_PEAP;
216 *pos++ = data->peap_version;
218 res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
220 pos, data->ssl.tls_out_limit);
222 wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 "
228 *out_len = sizeof(struct eap_hdr) + 2 + res;
229 resp->length = host_to_be16(*out_len);
230 *out_data = (u8 *) resp;
235 static int eap_peap_phase2_nak(struct eap_sm *sm,
236 struct eap_peap_data *data,
238 u8 **resp, size_t *resp_len)
240 struct eap_hdr *resp_hdr;
241 u8 *pos = (u8 *) (hdr + 1);
243 wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: Nak type=%d", *pos);
244 wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Allowed Phase2 EAP types",
245 data->phase2_types, data->num_phase2_types);
246 *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types;
247 *resp = malloc(*resp_len);
251 resp_hdr = (struct eap_hdr *) (*resp);
252 resp_hdr->code = EAP_CODE_RESPONSE;
253 resp_hdr->identifier = hdr->identifier;
254 resp_hdr->length = host_to_be16(*resp_len);
255 pos = (u8 *) (resp_hdr + 1);
256 *pos++ = EAP_TYPE_NAK;
257 memcpy(pos, data->phase2_types, data->num_phase2_types);
263 static int eap_peap_phase2_request(struct eap_sm *sm,
264 struct eap_peap_data *data,
265 struct eap_method_ret *ret,
266 const struct eap_hdr *req,
268 u8 **resp, size_t *resp_len)
270 size_t len = be_to_host16(hdr->length);
272 struct eap_method_ret iret;
273 struct wpa_ssid *config = eap_get_config(sm);
275 if (len <= sizeof(struct eap_hdr)) {
276 wpa_printf(MSG_INFO, "EAP-PEAP: too short "
277 "Phase 2 request (len=%lu)", (unsigned long) len);
280 pos = (u8 *) (hdr + 1);
281 wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
283 case EAP_TYPE_IDENTITY:
284 *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1);
287 memset(&iret, 0, sizeof(iret));
288 if (eap_tlv_process(sm, &iret, hdr, resp, resp_len)) {
289 ret->methodState = METHOD_DONE;
290 ret->decision = DECISION_FAIL;
293 if (iret.methodState == METHOD_DONE ||
294 iret.methodState == METHOD_MAY_CONT) {
295 ret->methodState = iret.methodState;
296 ret->decision = iret.decision;
297 data->phase2_success = 1;
301 if (data->phase2_type == EAP_TYPE_NONE) {
303 for (i = 0; i < data->num_phase2_types; i++) {
304 if (data->phase2_types[i] != *pos)
307 data->phase2_type = *pos;
308 wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
309 "Phase 2 EAP method %d",
314 if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) {
315 if (eap_peap_phase2_nak(sm, data, hdr, resp, resp_len))
320 if (data->phase2_priv == NULL) {
321 data->phase2_method = eap_sm_get_eap_methods(*pos);
322 if (data->phase2_method) {
325 data->phase2_method->init(sm);
329 if (data->phase2_priv == NULL || data->phase2_method == NULL) {
330 wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
331 "Phase 2 EAP method %d", *pos);
332 ret->methodState = METHOD_DONE;
333 ret->decision = DECISION_FAIL;
336 memset(&iret, 0, sizeof(iret));
337 *resp = data->phase2_method->process(sm, data->phase2_priv,
338 &iret, (u8 *) hdr, len,
340 if ((iret.methodState == METHOD_DONE ||
341 iret.methodState == METHOD_MAY_CONT) &&
342 (iret.decision == DECISION_UNCOND_SUCC ||
343 iret.decision == DECISION_COND_SUCC)) {
344 data->phase2_success = 1;
350 (config->pending_req_identity || config->pending_req_password ||
351 config->pending_req_otp || config->pending_req_new_password)) {
352 free(data->pending_phase2_req);
353 data->pending_phase2_req = malloc(len);
354 if (data->pending_phase2_req) {
355 memcpy(data->pending_phase2_req, hdr, len);
356 data->pending_phase2_req_len = len;
364 static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
365 struct eap_method_ret *ret,
366 const struct eap_hdr *req,
367 const u8 *in_data, size_t in_len,
368 u8 **out_data, size_t *out_len)
371 int buf_len, len_decrypted, len, skip_change = 0;
372 struct eap_hdr *hdr, *rhdr;
379 wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
380 " Phase 2", (unsigned long) in_len);
382 if (data->pending_phase2_req) {
383 wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
384 "skip decryption and use old data");
385 /* Clear TLS reassembly state. */
386 free(data->ssl.tls_in);
387 data->ssl.tls_in = NULL;
388 data->ssl.tls_in_len = 0;
389 data->ssl.tls_in_left = 0;
390 data->ssl.tls_in_total = 0;
391 in_decrypted = data->pending_phase2_req;
392 data->pending_phase2_req = NULL;
393 len_decrypted = data->pending_phase2_req_len;
398 msg = eap_tls_data_reassemble(sm, &data->ssl, in_data, in_len,
399 &msg_len, &need_more_input);
401 return need_more_input ? 1 : -1;
403 if (in_len == 0 && sm->workaround && data->phase2_success) {
405 * Cisco ACS seems to be using TLS ACK to terminate
406 * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
408 wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
409 "expected data - acknowledge with TLS ACK since "
410 "Phase 2 has been completed");
411 ret->decision = DECISION_COND_SUCC;
412 ret->methodState = METHOD_DONE;
417 if (data->ssl.tls_in_total > buf_len)
418 buf_len = data->ssl.tls_in_total;
419 in_decrypted = malloc(buf_len);
420 if (in_decrypted == NULL) {
421 free(data->ssl.tls_in);
422 data->ssl.tls_in = NULL;
423 data->ssl.tls_in_len = 0;
424 wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory "
429 len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
431 in_decrypted, buf_len);
432 free(data->ssl.tls_in);
433 data->ssl.tls_in = NULL;
434 data->ssl.tls_in_len = 0;
435 if (len_decrypted < 0) {
436 wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 "
443 wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted,
446 hdr = (struct eap_hdr *) in_decrypted;
447 if (len_decrypted == 5 && hdr->code == EAP_CODE_REQUEST &&
448 be_to_host16(hdr->length) == 5 &&
449 in_decrypted[4] == EAP_TYPE_IDENTITY) {
450 /* At least FreeRADIUS seems to send full EAP header with
451 * EAP Request Identity */
454 if (len_decrypted >= 5 && hdr->code == EAP_CODE_REQUEST &&
455 in_decrypted[4] == EAP_TYPE_TLV) {
459 if (data->peap_version == 0 && !skip_change) {
460 struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) +
466 memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted);
468 nhdr->code = req->code;
469 nhdr->identifier = req->identifier;
470 nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
473 len_decrypted += sizeof(struct eap_hdr);
474 in_decrypted = (u8 *) nhdr;
476 hdr = (struct eap_hdr *) in_decrypted;
477 if (len_decrypted < sizeof(*hdr)) {
479 wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
480 "EAP frame (len=%d)", len_decrypted);
483 len = be_to_host16(hdr->length);
484 if (len > len_decrypted) {
486 wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
487 "Phase 2 EAP frame (len=%d hdr->length=%d)",
491 if (len < len_decrypted) {
492 wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
493 "shorter length than full decrypted data (%d < %d)",
495 if (sm->workaround && len == 4 && len_decrypted == 5 &&
496 in_decrypted[4] == EAP_TYPE_IDENTITY) {
497 /* Radiator 3.9 seems to set Phase 2 EAP header to use
498 * incorrect length for the EAP-Request Identity
499 * packet, so fix the inner header to interoperate..
500 * This was fixed in 2004-06-23 patch for Radiator and
501 * this workaround can be removed at some point. */
502 wpa_printf(MSG_INFO, "EAP-PEAP: workaround -> replace "
503 "Phase 2 EAP header len (%d) with real "
504 "decrypted len (%d)", len, len_decrypted);
506 hdr->length = host_to_be16(len);
509 wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
510 "identifier=%d length=%d", hdr->code, hdr->identifier, len);
512 case EAP_CODE_REQUEST:
513 if (eap_peap_phase2_request(sm, data, ret, req, hdr,
516 wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
517 "processing failed");
521 case EAP_CODE_SUCCESS:
522 wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
523 if (data->peap_version == 1) {
524 /* EAP-Success within TLS tunnel is used to indicate
525 * shutdown of the TLS channel. The authentication has
527 wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
528 "EAP-Success within TLS tunnel - "
529 "authentication completed");
530 ret->decision = DECISION_UNCOND_SUCC;
531 ret->methodState = METHOD_DONE;
532 data->phase2_success = 1;
533 if (data->peap_outer_success == 2) {
535 wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
536 "to finish authentication");
538 } else if (data->peap_outer_success == 1) {
539 /* Reply with EAP-Success within the TLS
540 * channel to complete the authentication. */
541 resp_len = sizeof(struct eap_hdr);
542 resp = malloc(resp_len);
544 memset(resp, 0, resp_len);
545 rhdr = (struct eap_hdr *) resp;
546 rhdr->code = EAP_CODE_SUCCESS;
547 rhdr->identifier = hdr->identifier;
548 rhdr->length = host_to_be16(resp_len);
551 /* No EAP-Success expected for Phase 1 (outer,
552 * unencrypted auth), so force EAP state
553 * machine to SUCCESS state. */
554 sm->peap_done = TRUE;
560 case EAP_CODE_FAILURE:
561 wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
562 ret->decision = DECISION_FAIL;
563 ret->methodState = METHOD_MAY_CONT;
564 ret->allowNotifications = FALSE;
565 /* Reply with EAP-Failure within the TLS channel to complete
566 * failure reporting. */
567 resp_len = sizeof(struct eap_hdr);
568 resp = malloc(resp_len);
570 memset(resp, 0, resp_len);
571 rhdr = (struct eap_hdr *) resp;
572 rhdr->code = EAP_CODE_FAILURE;
573 rhdr->identifier = hdr->identifier;
574 rhdr->length = host_to_be16(resp_len);
578 wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
579 "Phase 2 EAP header", hdr->code);
587 size_t resp_send_len;
590 wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
592 /* PEAP version changes */
593 if (resp_len >= 5 && resp[0] == EAP_CODE_RESPONSE &&
594 resp[4] == EAP_TYPE_TLV)
596 if (data->peap_version == 0 && !skip_change) {
597 resp_pos = resp + sizeof(struct eap_hdr);
598 resp_send_len = resp_len - sizeof(struct eap_hdr);
601 resp_send_len = resp_len;
604 if (eap_peap_encrypt(sm, data, req->identifier,
605 resp_pos, resp_send_len,
606 out_data, out_len)) {
607 wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
617 static u8 * eap_peap_process(struct eap_sm *sm, void *priv,
618 struct eap_method_ret *ret,
619 const u8 *reqData, size_t reqDataLen,
622 const struct eap_hdr *req;
627 struct eap_peap_data *data = priv;
629 pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
630 reqData, reqDataLen, &left, &flags);
633 req = (const struct eap_hdr *) reqData;
634 id = req->identifier;
636 if (flags & EAP_TLS_FLAGS_START) {
637 wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
638 "ver=%d)", flags & EAP_PEAP_VERSION_MASK,
640 if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version)
641 data->peap_version = flags & EAP_PEAP_VERSION_MASK;
642 if (data->force_peap_version >= 0 &&
643 data->force_peap_version != data->peap_version) {
644 wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
645 "forced PEAP version %d",
646 data->force_peap_version);
647 ret->methodState = METHOD_DONE;
648 ret->decision = DECISION_FAIL;
649 ret->allowNotifications = FALSE;
652 wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
654 left = 0; /* make sure that this frame is empty, even though it
655 * should always be, anyway */
659 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
661 res = eap_peap_decrypt(sm, data, ret, req, pos, left,
664 res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP,
665 data->peap_version, id, pos, left,
668 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
670 wpa_printf(MSG_DEBUG,
671 "EAP-PEAP: TLS done, proceed to Phase 2");
672 free(data->key_data);
673 /* draft-josefsson-ppext-eap-tls-eap-05.txt
674 * specifies that PEAPv1 would use "client PEAP
675 * encryption" as the label. However, most existing
676 * PEAPv1 implementations seem to be using the old
677 * label, "client EAP encryption", instead. Use the old
678 * label by default, but allow it to be configured with
679 * phase1 parameter peaplabel=1. */
680 if (data->peap_version > 1 || data->force_new_label)
681 label = "client PEAP encryption";
683 label = "client EAP encryption";
684 wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
685 "key derivation", label);
687 eap_tls_derive_key(sm, &data->ssl, label,
689 if (data->key_data) {
690 wpa_hexdump_key(MSG_DEBUG,
691 "EAP-PEAP: Derived key",
695 wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
699 if (sm->workaround && data->resuming) {
701 * At least few RADIUS servers (Aegis v1.1.6;
702 * but not v1.1.4; and Cisco ACS) seem to be
703 * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
704 * ACS) session resumption with outer
705 * EAP-Success. This does not seem to follow
706 * draft-josefsson-pppext-eap-tls-eap-05.txt
707 * section 4.2, so only allow this if EAP
708 * workarounds are enabled.
710 wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
711 "allow outer EAP-Success to "
712 "terminate PEAP resumption");
713 ret->decision = DECISION_COND_SUCC;
714 data->phase2_success = 1;
721 if (ret->methodState == METHOD_DONE) {
722 ret->allowNotifications = FALSE;
726 return eap_tls_build_ack(&data->ssl, respDataLen, id,
727 EAP_TYPE_PEAP, data->peap_version);
734 static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
736 struct eap_peap_data *data = priv;
737 return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
738 data->phase2_success;
742 static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
744 struct eap_peap_data *data = priv;
745 free(data->pending_phase2_req);
746 data->pending_phase2_req = NULL;
750 static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
752 struct eap_peap_data *data = priv;
753 free(data->key_data);
754 data->key_data = NULL;
755 if (eap_tls_reauth_init(sm, &data->ssl)) {
759 data->phase2_success = 0;
761 sm->peap_done = FALSE;
766 static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
767 size_t buflen, int verbose)
769 struct eap_peap_data *data = priv;
772 len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose);
773 if (data->phase2_method) {
774 len += snprintf(buf + len, buflen - len,
775 "EAP-PEAPv%d Phase2 method=%s\n",
776 data->peap_version, data->phase2_method->name);
782 static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
784 struct eap_peap_data *data = priv;
785 return data->key_data != NULL && data->phase2_success;
789 static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
791 struct eap_peap_data *data = priv;
794 if (data->key_data == NULL || !data->phase2_success)
797 key = malloc(EAP_TLS_KEY_LEN);
801 *len = EAP_TLS_KEY_LEN;
802 memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
808 const struct eap_method eap_method_peap =
810 .method = EAP_TYPE_PEAP,
812 .init = eap_peap_init,
813 .deinit = eap_peap_deinit,
814 .process = eap_peap_process,
815 .isKeyAvailable = eap_peap_isKeyAvailable,
816 .getKey = eap_peap_getKey,
817 .get_status = eap_peap_get_status,
818 .has_reauth_data = eap_peap_has_reauth_data,
819 .deinit_for_reauth = eap_peap_deinit_for_reauth,
820 .init_for_reauth = eap_peap_init_for_reauth,