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) {
145 eap_peap_deinit(sm, data);
149 methods[num_methods - 1] = method;
155 data->phase2_types = methods;
156 data->num_phase2_types = num_methods;
158 if (data->phase2_types == NULL) {
160 eap_get_phase2_types(config, &data->num_phase2_types);
162 if (data->phase2_types == NULL) {
163 wpa_printf(MSG_ERROR, "EAP-PEAP: No Phase2 method available");
164 eap_peap_deinit(sm, data);
167 wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 EAP types",
168 data->phase2_types, data->num_phase2_types);
169 data->phase2_type = EAP_TYPE_NONE;
171 if (eap_tls_ssl_init(sm, &data->ssl, config)) {
172 wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
173 eap_peap_deinit(sm, data);
181 static void eap_peap_deinit(struct eap_sm *sm, void *priv)
183 struct eap_peap_data *data = priv;
186 if (data->phase2_priv && data->phase2_method)
187 data->phase2_method->deinit(sm, data->phase2_priv);
188 free(data->phase2_types);
189 eap_tls_ssl_deinit(sm, &data->ssl);
190 free(data->key_data);
191 free(data->pending_phase2_req);
196 static int eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data,
197 int id, const u8 *plain, size_t plain_len,
198 u8 **out_data, size_t *out_len)
202 struct eap_hdr *resp;
204 /* TODO: add support for fragmentation, if needed. This will need to
205 * add TLS Message Length field, if the frame is fragmented.
206 * Note: Microsoft IAS did not seem to like TLS Message Length with
208 resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit);
212 resp->code = EAP_CODE_RESPONSE;
213 resp->identifier = id;
215 pos = (u8 *) (resp + 1);
216 *pos++ = EAP_TYPE_PEAP;
217 *pos++ = data->peap_version;
219 res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
221 pos, data->ssl.tls_out_limit);
223 wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 "
229 *out_len = sizeof(struct eap_hdr) + 2 + res;
230 resp->length = host_to_be16(*out_len);
231 *out_data = (u8 *) resp;
236 static int eap_peap_phase2_nak(struct eap_sm *sm,
237 struct eap_peap_data *data,
239 u8 **resp, size_t *resp_len)
241 struct eap_hdr *resp_hdr;
242 u8 *pos = (u8 *) (hdr + 1);
244 wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: Nak type=%d", *pos);
245 wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Allowed Phase2 EAP types",
246 data->phase2_types, data->num_phase2_types);
247 *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types;
248 *resp = malloc(*resp_len);
252 resp_hdr = (struct eap_hdr *) (*resp);
253 resp_hdr->code = EAP_CODE_RESPONSE;
254 resp_hdr->identifier = hdr->identifier;
255 resp_hdr->length = host_to_be16(*resp_len);
256 pos = (u8 *) (resp_hdr + 1);
257 *pos++ = EAP_TYPE_NAK;
258 memcpy(pos, data->phase2_types, data->num_phase2_types);
264 static int eap_peap_phase2_request(struct eap_sm *sm,
265 struct eap_peap_data *data,
266 struct eap_method_ret *ret,
267 const struct eap_hdr *req,
269 u8 **resp, size_t *resp_len)
271 size_t len = be_to_host16(hdr->length);
273 struct eap_method_ret iret;
274 struct wpa_ssid *config = eap_get_config(sm);
276 if (len <= sizeof(struct eap_hdr)) {
277 wpa_printf(MSG_INFO, "EAP-PEAP: too short "
278 "Phase 2 request (len=%lu)", (unsigned long) len);
281 pos = (u8 *) (hdr + 1);
282 wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
284 case EAP_TYPE_IDENTITY:
285 *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1);
288 memset(&iret, 0, sizeof(iret));
289 if (eap_tlv_process(sm, &iret, hdr, resp, resp_len)) {
290 ret->methodState = METHOD_DONE;
291 ret->decision = DECISION_FAIL;
294 if (iret.methodState == METHOD_DONE ||
295 iret.methodState == METHOD_MAY_CONT) {
296 ret->methodState = iret.methodState;
297 ret->decision = iret.decision;
298 data->phase2_success = 1;
302 if (data->phase2_type == EAP_TYPE_NONE) {
304 for (i = 0; i < data->num_phase2_types; i++) {
305 if (data->phase2_types[i] != *pos)
308 data->phase2_type = *pos;
309 wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
310 "Phase 2 EAP method %d",
315 if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) {
316 if (eap_peap_phase2_nak(sm, data, hdr, resp, resp_len))
321 if (data->phase2_priv == NULL) {
322 data->phase2_method = eap_sm_get_eap_methods(*pos);
323 if (data->phase2_method) {
326 data->phase2_method->init(sm);
330 if (data->phase2_priv == NULL || data->phase2_method == NULL) {
331 wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
332 "Phase 2 EAP method %d", *pos);
333 ret->methodState = METHOD_DONE;
334 ret->decision = DECISION_FAIL;
337 memset(&iret, 0, sizeof(iret));
338 *resp = data->phase2_method->process(sm, data->phase2_priv,
339 &iret, (u8 *) hdr, len,
341 if ((iret.methodState == METHOD_DONE ||
342 iret.methodState == METHOD_MAY_CONT) &&
343 (iret.decision == DECISION_UNCOND_SUCC ||
344 iret.decision == DECISION_COND_SUCC)) {
345 data->phase2_success = 1;
351 (config->pending_req_identity || config->pending_req_password ||
352 config->pending_req_otp || config->pending_req_new_password)) {
353 free(data->pending_phase2_req);
354 data->pending_phase2_req = malloc(len);
355 if (data->pending_phase2_req) {
356 memcpy(data->pending_phase2_req, hdr, len);
357 data->pending_phase2_req_len = len;
365 static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
366 struct eap_method_ret *ret,
367 const struct eap_hdr *req,
368 const u8 *in_data, size_t in_len,
369 u8 **out_data, size_t *out_len)
372 int buf_len, len_decrypted, len, skip_change = 0;
373 struct eap_hdr *hdr, *rhdr;
380 wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
381 " Phase 2", (unsigned long) in_len);
383 if (data->pending_phase2_req) {
384 wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
385 "skip decryption and use old data");
386 /* Clear TLS reassembly state. */
387 free(data->ssl.tls_in);
388 data->ssl.tls_in = NULL;
389 data->ssl.tls_in_len = 0;
390 data->ssl.tls_in_left = 0;
391 data->ssl.tls_in_total = 0;
392 in_decrypted = data->pending_phase2_req;
393 data->pending_phase2_req = NULL;
394 len_decrypted = data->pending_phase2_req_len;
399 msg = eap_tls_data_reassemble(sm, &data->ssl, in_data, in_len,
400 &msg_len, &need_more_input);
402 return need_more_input ? 1 : -1;
404 if (in_len == 0 && sm->workaround && data->phase2_success) {
406 * Cisco ACS seems to be using TLS ACK to terminate
407 * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
409 wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
410 "expected data - acknowledge with TLS ACK since "
411 "Phase 2 has been completed");
412 ret->decision = DECISION_COND_SUCC;
413 ret->methodState = METHOD_DONE;
418 if (data->ssl.tls_in_total > buf_len)
419 buf_len = data->ssl.tls_in_total;
420 in_decrypted = malloc(buf_len);
421 if (in_decrypted == NULL) {
422 free(data->ssl.tls_in);
423 data->ssl.tls_in = NULL;
424 data->ssl.tls_in_len = 0;
425 wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory "
430 len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
432 in_decrypted, buf_len);
433 free(data->ssl.tls_in);
434 data->ssl.tls_in = NULL;
435 data->ssl.tls_in_len = 0;
436 if (len_decrypted < 0) {
437 wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 "
444 wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted,
447 hdr = (struct eap_hdr *) in_decrypted;
448 if (len_decrypted == 5 && hdr->code == EAP_CODE_REQUEST &&
449 be_to_host16(hdr->length) == 5 &&
450 in_decrypted[4] == EAP_TYPE_IDENTITY) {
451 /* At least FreeRADIUS seems to send full EAP header with
452 * EAP Request Identity */
455 if (len_decrypted >= 5 && hdr->code == EAP_CODE_REQUEST &&
456 in_decrypted[4] == EAP_TYPE_TLV) {
460 if (data->peap_version == 0 && !skip_change) {
461 struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) +
467 memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted);
469 nhdr->code = req->code;
470 nhdr->identifier = req->identifier;
471 nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
474 len_decrypted += sizeof(struct eap_hdr);
475 in_decrypted = (u8 *) nhdr;
477 hdr = (struct eap_hdr *) in_decrypted;
478 if (len_decrypted < sizeof(*hdr)) {
480 wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
481 "EAP frame (len=%d)", len_decrypted);
484 len = be_to_host16(hdr->length);
485 if (len > len_decrypted) {
487 wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
488 "Phase 2 EAP frame (len=%d hdr->length=%d)",
492 if (len < len_decrypted) {
493 wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
494 "shorter length than full decrypted data (%d < %d)",
496 if (sm->workaround && len == 4 && len_decrypted == 5 &&
497 in_decrypted[4] == EAP_TYPE_IDENTITY) {
498 /* Radiator 3.9 seems to set Phase 2 EAP header to use
499 * incorrect length for the EAP-Request Identity
500 * packet, so fix the inner header to interoperate..
501 * This was fixed in 2004-06-23 patch for Radiator and
502 * this workaround can be removed at some point. */
503 wpa_printf(MSG_INFO, "EAP-PEAP: workaround -> replace "
504 "Phase 2 EAP header len (%d) with real "
505 "decrypted len (%d)", len, len_decrypted);
507 hdr->length = host_to_be16(len);
510 wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
511 "identifier=%d length=%d", hdr->code, hdr->identifier, len);
513 case EAP_CODE_REQUEST:
514 if (eap_peap_phase2_request(sm, data, ret, req, hdr,
517 wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
518 "processing failed");
522 case EAP_CODE_SUCCESS:
523 wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
524 if (data->peap_version == 1) {
525 /* EAP-Success within TLS tunnel is used to indicate
526 * shutdown of the TLS channel. The authentication has
528 wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
529 "EAP-Success within TLS tunnel - "
530 "authentication completed");
531 ret->decision = DECISION_UNCOND_SUCC;
532 ret->methodState = METHOD_DONE;
533 data->phase2_success = 1;
534 if (data->peap_outer_success == 2) {
536 wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
537 "to finish authentication");
539 } else if (data->peap_outer_success == 1) {
540 /* Reply with EAP-Success within the TLS
541 * channel to complete the authentication. */
542 resp_len = sizeof(struct eap_hdr);
543 resp = malloc(resp_len);
545 memset(resp, 0, resp_len);
546 rhdr = (struct eap_hdr *) resp;
547 rhdr->code = EAP_CODE_SUCCESS;
548 rhdr->identifier = hdr->identifier;
549 rhdr->length = host_to_be16(resp_len);
552 /* No EAP-Success expected for Phase 1 (outer,
553 * unencrypted auth), so force EAP state
554 * machine to SUCCESS state. */
555 sm->peap_done = TRUE;
561 case EAP_CODE_FAILURE:
562 wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
563 ret->decision = DECISION_FAIL;
564 ret->methodState = METHOD_MAY_CONT;
565 ret->allowNotifications = FALSE;
566 /* Reply with EAP-Failure within the TLS channel to complete
567 * failure reporting. */
568 resp_len = sizeof(struct eap_hdr);
569 resp = malloc(resp_len);
571 memset(resp, 0, resp_len);
572 rhdr = (struct eap_hdr *) resp;
573 rhdr->code = EAP_CODE_FAILURE;
574 rhdr->identifier = hdr->identifier;
575 rhdr->length = host_to_be16(resp_len);
579 wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
580 "Phase 2 EAP header", hdr->code);
588 size_t resp_send_len;
591 wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
593 /* PEAP version changes */
594 if (resp_len >= 5 && resp[0] == EAP_CODE_RESPONSE &&
595 resp[4] == EAP_TYPE_TLV)
597 if (data->peap_version == 0 && !skip_change) {
598 resp_pos = resp + sizeof(struct eap_hdr);
599 resp_send_len = resp_len - sizeof(struct eap_hdr);
602 resp_send_len = resp_len;
605 if (eap_peap_encrypt(sm, data, req->identifier,
606 resp_pos, resp_send_len,
607 out_data, out_len)) {
608 wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
618 static u8 * eap_peap_process(struct eap_sm *sm, void *priv,
619 struct eap_method_ret *ret,
620 const u8 *reqData, size_t reqDataLen,
623 const struct eap_hdr *req;
628 struct eap_peap_data *data = priv;
630 pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
631 reqData, reqDataLen, &left, &flags);
634 req = (const struct eap_hdr *) reqData;
635 id = req->identifier;
637 if (flags & EAP_TLS_FLAGS_START) {
638 wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
639 "ver=%d)", flags & EAP_PEAP_VERSION_MASK,
641 if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version)
642 data->peap_version = flags & EAP_PEAP_VERSION_MASK;
643 if (data->force_peap_version >= 0 &&
644 data->force_peap_version != data->peap_version) {
645 wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
646 "forced PEAP version %d",
647 data->force_peap_version);
648 ret->methodState = METHOD_DONE;
649 ret->decision = DECISION_FAIL;
650 ret->allowNotifications = FALSE;
653 wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
655 left = 0; /* make sure that this frame is empty, even though it
656 * should always be, anyway */
660 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
662 res = eap_peap_decrypt(sm, data, ret, req, pos, left,
665 res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP,
666 data->peap_version, id, pos, left,
669 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
671 wpa_printf(MSG_DEBUG,
672 "EAP-PEAP: TLS done, proceed to Phase 2");
673 free(data->key_data);
674 /* draft-josefsson-ppext-eap-tls-eap-05.txt
675 * specifies that PEAPv1 would use "client PEAP
676 * encryption" as the label. However, most existing
677 * PEAPv1 implementations seem to be using the old
678 * label, "client EAP encryption", instead. Use the old
679 * label by default, but allow it to be configured with
680 * phase1 parameter peaplabel=1. */
681 if (data->peap_version > 1 || data->force_new_label)
682 label = "client PEAP encryption";
684 label = "client EAP encryption";
685 wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
686 "key derivation", label);
688 eap_tls_derive_key(sm, &data->ssl, label,
690 if (data->key_data) {
691 wpa_hexdump_key(MSG_DEBUG,
692 "EAP-PEAP: Derived key",
696 wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
700 if (sm->workaround && data->resuming) {
702 * At least few RADIUS servers (Aegis v1.1.6;
703 * but not v1.1.4; and Cisco ACS) seem to be
704 * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
705 * ACS) session resumption with outer
706 * EAP-Success. This does not seem to follow
707 * draft-josefsson-pppext-eap-tls-eap-05.txt
708 * section 4.2, so only allow this if EAP
709 * workarounds are enabled.
711 wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
712 "allow outer EAP-Success to "
713 "terminate PEAP resumption");
714 ret->decision = DECISION_COND_SUCC;
715 data->phase2_success = 1;
722 if (ret->methodState == METHOD_DONE) {
723 ret->allowNotifications = FALSE;
727 return eap_tls_build_ack(&data->ssl, respDataLen, id,
728 EAP_TYPE_PEAP, data->peap_version);
735 static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
737 struct eap_peap_data *data = priv;
738 return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
739 data->phase2_success;
743 static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
745 struct eap_peap_data *data = priv;
746 free(data->pending_phase2_req);
747 data->pending_phase2_req = NULL;
751 static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
753 struct eap_peap_data *data = priv;
754 free(data->key_data);
755 data->key_data = NULL;
756 if (eap_tls_reauth_init(sm, &data->ssl)) {
760 data->phase2_success = 0;
762 sm->peap_done = FALSE;
767 static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
768 size_t buflen, int verbose)
770 struct eap_peap_data *data = priv;
773 len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose);
774 if (data->phase2_method) {
775 len += snprintf(buf + len, buflen - len,
776 "EAP-PEAPv%d Phase2 method=%s\n",
777 data->peap_version, data->phase2_method->name);
783 static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
785 struct eap_peap_data *data = priv;
786 return data->key_data != NULL && data->phase2_success;
790 static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
792 struct eap_peap_data *data = priv;
795 if (data->key_data == NULL || !data->phase2_success)
798 key = malloc(EAP_TLS_KEY_LEN);
802 *len = EAP_TLS_KEY_LEN;
803 memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
809 const struct eap_method eap_method_peap =
811 .method = EAP_TYPE_PEAP,
813 .init = eap_peap_init,
814 .deinit = eap_peap_deinit,
815 .process = eap_peap_process,
816 .isKeyAvailable = eap_peap_isKeyAvailable,
817 .getKey = eap_peap_getKey,
818 .get_status = eap_peap_get_status,
819 .has_reauth_data = eap_peap_has_reauth_data,
820 .deinit_for_reauth = eap_peap_deinit_for_reauth,
821 .init_for_reauth = eap_peap_init_for_reauth,