3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-12-19 18:22:32 +01:00
iwd/src/eap-pwd.c
James Prestwood 06ad1ace00 eap-pwd: fix usage of compressed points (after ELL is fixed)
EAP-PWD was incorrectly computing the PWE but due to the also
incorrect logic in ELL the point converted correctly. This is
being fixed, so both places need the reverse logic.

Also added a big comment explaining why this is, and how
l_ecc_point_from_data behaves since its somewhat confusing since
EAP-PWD expects the pwd-seed to be compared to the actual Y
coordinate (which is handled automatically by ELL).
2023-10-11 10:19:34 -05:00

874 lines
21 KiB
C

/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2018-2019 Intel Corporation. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ell/ell.h>
#include "ell/useful.h"
#include "src/missing.h"
#include "src/eap.h"
#include "src/eap-private.h"
#include "src/crypto.h"
#include "src/util.h"
#define EAP_PWD_GROUP_DESC 19
#define EAP_PWD_RAND_FN 0x01
#define EAP_PWD_PRF 0x01
/* EAP header + PWD-Exch */
#define EAP_PWD_HDR_LEN 6
#define EAP_PWD_L_BIT (1 << 7)
#define EAP_PWD_M_BIT (1 << 6)
enum eap_pwd_prep {
EAP_PWD_PREP_NONE = 0x00,
EAP_PWD_PREP_MS = 0x01,
EAP_PWD_PREP_SASL = 0x02
};
enum eap_pwd_exch {
EAP_PWD_EXCH_RESERVED = 0,
EAP_PWD_EXCH_ID,
EAP_PWD_EXCH_COMMIT,
EAP_PWD_EXCH_CONFIRM
};
enum eap_pwd_state {
EAP_PWD_STATE_INIT = 0,
EAP_PWD_STATE_ID,
EAP_PWD_STATE_COMMIT,
EAP_PWD_STATE_CONFIRM
};
struct eap_pwd_handle {
enum eap_pwd_state state;
enum eap_pwd_prep prep;
char *identity;
char *password;
const struct l_ecc_curve *curve;
struct l_ecc_point *pwe;
struct l_ecc_point *element_s;
struct l_ecc_point *element_p;
uint32_t ciphersuite;
struct l_ecc_scalar *scalar_s;
struct l_ecc_scalar *scalar_p;
struct l_ecc_scalar *p_rand;
uint8_t *rx_frag_buf;
uint16_t rx_frag_total;
uint16_t rx_frag_count;
uint8_t *tx_frag_buf;
uint8_t *tx_frag_pos;
uint16_t tx_frag_remaining;
};
/* RFC 5931, Section 2.5 - Key Derivation Function */
static bool kdf(uint8_t *key, size_t key_len, const char *label,
size_t label_len, void *out, size_t olen)
{
struct l_checksum *hmac;
struct iovec iov[4];
uint16_t ibuf, i = 1;
uint16_t L = L_CPU_TO_BE16(olen * 8);
size_t len = 0;
while (len < olen) {
int iov_pos = 0;
hmac = l_checksum_new_hmac(L_CHECKSUM_SHA256, key, key_len);
if (!hmac)
return false;
/* PRF(key, K(i - 1) | i | label | L) */
if (i > 1) {
iov[iov_pos].iov_base = out + len - 32;
iov[iov_pos++].iov_len = 32;
}
ibuf = L_CPU_TO_BE16(i);
iov[iov_pos].iov_base = (void *) &ibuf;
iov[iov_pos++].iov_len = 2;
iov[iov_pos].iov_base = (void *)label;
iov[iov_pos++].iov_len = label_len;
iov[iov_pos].iov_base = &L;
iov[iov_pos++].iov_len = 2;
if (!l_checksum_updatev(hmac, iov, iov_pos)) {
l_checksum_free(hmac);
return false;
}
l_checksum_get_digest(hmac, out + len, minsize(olen - len, 32));
l_checksum_free(hmac);
len += 32;
i++;
}
return true;
}
static bool eap_pwd_reset_state(struct eap_state *eap)
{
struct eap_pwd_handle *pwd = eap_get_data(eap);
pwd->state = EAP_PWD_STATE_INIT;
l_free(pwd->tx_frag_buf);
pwd->tx_frag_buf = NULL;
pwd->tx_frag_pos = NULL;
pwd->tx_frag_remaining = 0;
l_free(pwd->rx_frag_buf);
pwd->rx_frag_buf = NULL;
pwd->rx_frag_count = 0;
pwd->rx_frag_total = 0;
pwd->prep = EAP_PWD_PREP_NONE;
pwd->ciphersuite = 0;
l_ecc_point_free(pwd->pwe);
pwd->pwe = NULL;
l_ecc_point_free(pwd->element_p);
pwd->element_p = NULL;
l_ecc_point_free(pwd->element_s);
pwd->element_s = NULL;
l_ecc_scalar_free(pwd->scalar_p);
pwd->scalar_p = NULL;
l_ecc_scalar_free(pwd->scalar_s);
pwd->scalar_s = NULL;
l_ecc_scalar_free(pwd->p_rand);
pwd->p_rand = NULL;
return true;
}
static void eap_pwd_free(struct eap_state *eap)
{
struct eap_pwd_handle *pwd = eap_get_data(eap);
eap_pwd_reset_state(eap);
l_free(pwd->identity);
if (pwd->password) {
explicit_bzero(pwd->password, strlen(pwd->password));
l_free(pwd->password);
}
l_free(pwd);
eap_set_data(eap, NULL);
}
static void eap_pwd_send_response(struct eap_state *eap,
uint8_t *pkt, size_t len)
{
struct eap_pwd_handle *pwd = eap_get_data(eap);
size_t mtu = eap_get_mtu(eap);
uint8_t frag[mtu];
uint8_t *pos = frag;
/* first fragment data bytes (mtu - header - Total-Length) */
uint16_t send_bytes = mtu - EAP_PWD_HDR_LEN - 2;
/* packet will fit within mtu */
if (len <= mtu) {
eap_method_respond(eap, pkt, len);
return;
}
if (pwd->tx_frag_buf) {
l_error("already processing fragment, cannot send response");
return;
}
/* header */
memcpy(pos, pkt, 5);
pos += 5;
/* PWD-Exch, first frag, so L and M are both set */
*pos++ = pwd->state | EAP_PWD_L_BIT | EAP_PWD_M_BIT;
/* Total-Length */
l_put_be16((uint16_t)len, pos);
pos += 2;
/* copy packet data bytes */
memcpy(pos, pkt + EAP_PWD_HDR_LEN, send_bytes);
pwd->tx_frag_remaining = len - EAP_PWD_HDR_LEN - send_bytes;
l_info("sending initial fragment, %zu bytes", mtu);
eap_method_respond(eap, frag, mtu);
/* alloc/copy remainder of packet to frag buf */
pwd->tx_frag_buf = l_malloc(pwd->tx_frag_remaining);
memcpy(pwd->tx_frag_buf, pkt + EAP_PWD_HDR_LEN + send_bytes,
pwd->tx_frag_remaining);
pwd->tx_frag_pos = pwd->tx_frag_buf;
}
static void eap_pwd_handle_id(struct eap_state *eap,
const uint8_t *pkt, size_t len)
{
struct eap_pwd_handle *pwd = eap_get_data(eap);
uint16_t group;
uint8_t rand_fn;
uint8_t prf;
uint32_t token;
uint8_t counter = 0;
uint8_t resp[15 + strlen(pwd->identity)];
uint8_t *pos;
uint8_t pwd_seed[32];
uint8_t pwd_value[L_ECC_SCALAR_MAX_BYTES]; /* used as X value */
size_t nbytes;
bool found = false;
/*
* Group desc (2) + Random func (1) + prf (1) + token (4) + prep (1) +
* Identity (at least 1 byte)
*/
if (len < 9) {
l_error("bad packet length");
goto error;
}
if (pwd->state != EAP_PWD_STATE_INIT) {
l_error("received ID request in invalid state");
goto error;
}
pwd->state = EAP_PWD_STATE_ID;
group = l_get_be16(pkt);
pwd->curve = l_ecc_curve_from_ike_group(group);
if (!pwd->curve) {
l_error("group %d not supported", group);
goto error;
}
rand_fn = pkt[2];
if (rand_fn != EAP_PWD_RAND_FN) {
l_error("rand_fn %d not supported", rand_fn);
goto error;
}
prf = pkt[3];
if (prf != EAP_PWD_PRF) {
l_error("PRF function %d not supported", prf);
goto error;
}
/*
* RFC 5931 Section 3.2.1
* The Group Description, Random Function, and PRF together, and in that
* order, comprise the Ciphersuite...
*/
pwd->ciphersuite = l_get_u32(pkt);
token = l_get_u32(pkt + 4);
pwd->prep = pkt[8];
if (pwd->prep != EAP_PWD_PREP_NONE) {
/*
* TODO: Support other PW prep types
*/
l_error("prep type %d not currently supported", pwd->prep);
goto error;
}
nbytes = l_ecc_curve_get_scalar_bytes(pwd->curve);
while (counter < 20) {
struct l_ecc_point *pwe = NULL;
counter++;
/* pwd-seed = H(token|peer-ID|server-ID|password|counter) */
hkdf_extract(L_CHECKSUM_SHA256, NULL, 0, 5, pwd_seed, &token, 4,
pwd->identity, strlen(pwd->identity), pkt + 9,
len - 9, pwd->password, strlen(pwd->password),
&counter, (size_t) 1);
/*
* pwd-value = KDF(pwd-seed, "EAP-pwd Hunting And Pecking",
* len(p))
*/
kdf(pwd_seed, 32, "EAP-pwd Hunting And Pecking",
strlen("EAP-pwd Hunting And Pecking"),
pwd_value, nbytes);
/*
* The RFC requires the point be solved unambiguously (since
* solving for Y results in two solutions). The correct Y value
* is chosen based on the LSB of the pwd-seed:
*
* if (LSB(y) == LSB(pwd-seed))
* then
* PWE = (x, y)
* else
* PWE = (x, p-y)
*
* The ELL API (somewhat hidden from view here) automatically
* performs a subtraction (P - Y) when:
* - Y is even and BIT1
* - Y is odd and BIT0
*
* So we choose the point type which matches the parity of
* pwd-seed. This means a subtraction will be performed (P - Y)
* if the parity of pwd-seed and the computed Y do not match.
*/
if (pwd_seed[31] & 1)
pwe = l_ecc_point_from_data(pwd->curve,
L_ECC_POINT_TYPE_COMPRESSED_BIT1,
pwd_value, nbytes);
else
pwe = l_ecc_point_from_data(pwd->curve,
L_ECC_POINT_TYPE_COMPRESSED_BIT0,
pwd_value, nbytes);
if (!pwe)
continue;
if (!found) {
found = true;
pwd->pwe = pwe;
} else
l_ecc_point_free(pwe);
}
explicit_bzero(pwd_seed, sizeof(pwd_seed));
explicit_bzero(pwd_value, sizeof(pwd_value));
pos = resp + 5; /* header */
*pos++ = EAP_PWD_EXCH_ID;
l_put_be16(group, pos);
pos += 2;
*pos++ = rand_fn;
*pos++ = prf;
l_put_u32(token, pos);
pos += 4;
*pos++ = pwd->prep;
memcpy(pos, pwd->identity, strlen(pwd->identity));
pos += strlen(pwd->identity);
eap_pwd_send_response(eap, resp, pos - resp);
return;
error:
eap_method_error(eap);
}
static void eap_pwd_handle_commit(struct eap_state *eap,
const uint8_t *pkt, size_t len)
{
struct eap_pwd_handle *pwd = eap_get_data(eap);
uint8_t resp[L_ECC_POINT_MAX_BYTES + L_ECC_SCALAR_MAX_BYTES + 6];
uint8_t *pos;
struct l_ecc_scalar *p_mask;
struct l_ecc_scalar *order;
size_t nbytes = l_ecc_curve_get_scalar_bytes(pwd->curve);
/* [Element (nbytes * 2)][Scalar (nbytes)] */
if (len != nbytes + nbytes * 2) {
l_error("bad packet length, expected %zu, got %zu",
nbytes + nbytes * 2, len);
goto error;
}
if (pwd->state != EAP_PWD_STATE_ID) {
l_error("received commit request in invalid state");
goto error;
}
pwd->state = EAP_PWD_STATE_COMMIT;
/*
* Commit contains Element_S (nbytes * 2) then Scalar_s (nbytes)
*/
pwd->element_s = l_ecc_point_from_data(pwd->curve,
L_ECC_POINT_TYPE_FULL,
pkt, nbytes * 2);
if (!pwd->element_s) {
l_error("Server sent invalid Element_S during commit");
goto error;
}
pwd->scalar_s = l_ecc_scalar_new(pwd->curve, pkt + nbytes * 2, nbytes);
if (!pwd->scalar_s) {
l_error("Server sent invalid Scalar_S during commit");
goto error;
}
pwd->p_rand = l_ecc_scalar_new_random(pwd->curve);
p_mask = l_ecc_scalar_new_random(pwd->curve);
pwd->scalar_p = l_ecc_scalar_new(pwd->curve, NULL, 0);
order = l_ecc_curve_get_order(pwd->curve);
l_ecc_scalar_add(pwd->scalar_p, pwd->p_rand, p_mask, order);
l_ecc_scalar_free(order);
pwd->element_p = l_ecc_point_new(pwd->curve);
/* p_mask * PWE */
l_ecc_point_multiply(pwd->element_p, p_mask, pwd->pwe);
l_ecc_scalar_free(p_mask);
/* inv(p_mask * PWE) */
l_ecc_point_inverse(pwd->element_p);
/* send element_p and scalar_p */
pos = resp + 5; /* header */
*pos++ = EAP_PWD_EXCH_COMMIT;
pos += l_ecc_point_get_data(pwd->element_p, pos, nbytes * 2);
pos += l_ecc_scalar_get_data(pwd->scalar_p, pos, nbytes);
eap_pwd_send_response(eap, resp, pos - resp);
return;
error:
eap_method_error(eap);
}
static void eap_pwd_handle_confirm(struct eap_state *eap,
const uint8_t *pkt, size_t len)
{
struct eap_pwd_handle *pwd = eap_get_data(eap);
struct l_ecc_point *kp;
uint8_t resp[38];
uint8_t *pos;
uint8_t confirm_s[32];
uint8_t confirm_p[32];
uint8_t expected_confirm_s[32];
uint8_t mk[32];
uint8_t msk_emsk[128], session_id[33];
/* buffers used for the final hash */
uint8_t kpx[L_ECC_SCALAR_MAX_BYTES];
uint8_t scalar_s[L_ECC_SCALAR_MAX_BYTES];
uint8_t scalar_p[L_ECC_SCALAR_MAX_BYTES];
uint8_t element_s[L_ECC_POINT_MAX_BYTES];
uint8_t element_p[L_ECC_POINT_MAX_BYTES];
ssize_t plen, clen;
if (len != 32) {
l_error("bad packet length");
goto error;
}
if (pwd->state != EAP_PWD_STATE_COMMIT) {
l_error("received confirm request in invalid state");
goto error;
}
pwd->state = EAP_PWD_STATE_CONFIRM;
memcpy(confirm_s, pkt, 32);
kp = l_ecc_point_new(pwd->curve);
/* compute KP = (p_rand * (Scalar_S * PWE + Element_S)) */
l_ecc_point_multiply(kp, pwd->scalar_s, pwd->pwe);
l_ecc_point_add(kp, kp, pwd->element_s);
l_ecc_point_multiply(kp, pwd->p_rand, kp);
/*
* We just need to store clen/plen once. Since all these buffers are
* created with enough bytes in mind we know these won't fail. Also, all
* scalar/point objects were created with the same curve, so it can be
* safe to assume the return values will not change from what clen/plen
* already are.
*/
clen = l_ecc_point_get_x(kp, kpx, sizeof(kpx));
if (clen < 0)
goto invalid_point;
plen = l_ecc_point_get_data(pwd->element_s, element_s,
sizeof(element_s));
if (plen < 0)
goto invalid_point;
if (l_ecc_point_get_data(pwd->element_p, element_p,
sizeof(element_p)) < 0)
goto invalid_point;
if (l_ecc_scalar_get_data(pwd->scalar_s, scalar_s,
sizeof(scalar_s)) < 0)
goto invalid_point;
if (l_ecc_scalar_get_data(pwd->scalar_p, scalar_p,
sizeof(scalar_p)) < 0)
goto invalid_point;
l_ecc_point_free(kp);
/*
* compute Confirm_P = H(kp | Element_P | Scalar_P |
* Element_S | Scalar_S | Ciphersuite)
*/
hkdf_extract(L_CHECKSUM_SHA256, NULL, 0, 6, confirm_p, kpx, clen,
element_p, plen, scalar_p, clen, element_s,
plen, scalar_s, clen, &pwd->ciphersuite,
(size_t) 4);
hkdf_extract(L_CHECKSUM_SHA256, NULL, 0, 6, expected_confirm_s, kpx,
clen, element_s, plen, scalar_s, clen,
element_p, plen, scalar_p, clen,
&pwd->ciphersuite, (size_t) 4);
if (memcmp(confirm_s, expected_confirm_s, 32)) {
l_error("Confirm_S did not verify");
goto error;
}
pos = resp + 5; /* header */
*pos++ = EAP_PWD_EXCH_CONFIRM;
memcpy(pos, confirm_p, 32);
pos += 32;
/* derive MK = H(kp | Confirm_P | Confirm_S ) */
hkdf_extract(L_CHECKSUM_SHA256, NULL, 0, 3, mk, kpx, clen, confirm_p,
(size_t) 32, confirm_s, (size_t) 32);
eap_pwd_send_response(eap, resp, pos - resp);
eap_method_success(eap);
session_id[0] = 52;
hkdf_extract(L_CHECKSUM_SHA256, NULL, 0, 3, session_id + 1,
&pwd->ciphersuite, (size_t) 4, scalar_p, clen,
scalar_s, clen);
kdf(mk, 32, (const char *) session_id, 33, msk_emsk, 128);
eap_set_key_material(eap, msk_emsk, 64, msk_emsk + 64, 64, NULL, 0,
session_id, sizeof(session_id));
explicit_bzero(mk, sizeof(mk));
explicit_bzero(msk_emsk, sizeof(msk_emsk));
explicit_bzero(kpx, sizeof(kpx));
return;
invalid_point:
l_ecc_point_free(kp);
l_error("invalid point during confirm exchange");
error:
explicit_bzero(kpx, sizeof(kpx));
eap_method_error(eap);
}
static void eap_pwd_process(struct eap_state *eap,
const uint8_t *pkt, size_t len)
{
uint8_t pwd_exch = bit_field(pkt[0], 0, 6);
if (len < 1)
return;
switch (pwd_exch) {
case EAP_PWD_EXCH_ID:
eap_pwd_handle_id(eap, pkt + 1, len - 1);
break;
case EAP_PWD_EXCH_COMMIT:
eap_pwd_handle_commit(eap, pkt + 1, len - 1);
break;
case EAP_PWD_EXCH_CONFIRM:
eap_pwd_handle_confirm(eap, pkt + 1, len - 1);
break;
}
}
static void eap_pwd_send_ack(struct eap_state *eap)
{
struct eap_pwd_handle *pwd = eap_get_data(eap);
uint8_t buf[6];
buf[5] = pwd->state + 1;
eap_method_respond(eap, buf, 6);
}
#define FRAG_BYTES(mtu, remaining) \
(((mtu - EAP_PWD_HDR_LEN) < remaining) ? (mtu - EAP_PWD_HDR_LEN) : \
remaining)
static void eap_pwd_handle_request(struct eap_state *eap,
const uint8_t *pkt, size_t len)
{
struct eap_pwd_handle *pwd = eap_get_data(eap);
uint8_t len_bit = false;
uint8_t more_bit = false;
/* ACK from tx fragment, send next fragment */
if (len == 1 && pwd->tx_frag_buf) {
size_t mtu = eap_get_mtu(eap);
uint8_t frag[mtu];
uint8_t *pos = frag;
uint16_t frag_bytes = FRAG_BYTES(mtu, pwd->tx_frag_remaining);
pos += 5; /* header */
*pos = pwd->state;
/* more fragments coming, set M bit */
if (frag_bytes < pwd->tx_frag_remaining)
*pos |= EAP_PWD_M_BIT;
pos++;
memcpy(pos, pwd->tx_frag_pos, frag_bytes);
pwd->tx_frag_pos += frag_bytes;
pwd->tx_frag_remaining -= frag_bytes;
l_info("sending fragment, %d bytes",
frag_bytes + EAP_PWD_HDR_LEN);
eap_method_respond(eap, frag, frag_bytes + EAP_PWD_HDR_LEN);
if (!pwd->tx_frag_remaining) {
/* done sending fragments, free */
l_free(pwd->tx_frag_buf);
pwd->tx_frag_buf = NULL;
pwd->tx_frag_pos = NULL;
pwd->tx_frag_remaining = 0;
}
return;
}
if (pwd->tx_frag_buf) {
l_error("received packet while waiting for ACK!");
return;
}
if (len < 1) {
l_error("packet is too small");
return;
}
/* set if Total-Length parameter is include (i.e. first fragment) */
len_bit = test_bit(pkt, 7);
/* set on all but the last fragment */
more_bit = test_bit(pkt, 6);
/* first rx fragment */
if (len_bit) {
if (len < 3) {
l_error("malformed packet");
return;
}
/* remove length of Total-Length parameter (2) */
pwd->rx_frag_total = l_get_be16(pkt + 1) - 2;
if (pwd->rx_frag_total < len - 2) {
l_error("Total-Length too small for remaining length");
pwd->rx_frag_total = 0;
return;
}
pwd->rx_frag_buf = l_malloc(pwd->rx_frag_total);
/* skip copying Total-Length for easier processing later */
pwd->rx_frag_buf[0] = pkt[0];
memcpy(pwd->rx_frag_buf + 1, pkt + 3, len - 2);
pwd->rx_frag_count = len - 2;
l_info("received first fragment, %d total bytes",
pwd->rx_frag_total);
eap_pwd_send_ack(eap);
return;
}
/* more rx fragments */
if (pwd->rx_frag_buf) {
if (pwd->rx_frag_total - pwd->rx_frag_count <
(uint16_t) len - 1) {
l_error("Not enough room for fragment (%zu)", len - 1);
return;
}
/* continue building packet (not including PWD-Exch byte) */
memcpy(pwd->rx_frag_buf + pwd->rx_frag_count, pkt + 1, len - 1);
pwd->rx_frag_count += (len - 1);
l_info("received another fragment, %zu bytes", len);
/* more fragments coming */
if (more_bit) {
eap_pwd_send_ack(eap);
return;
}
if (pwd->rx_frag_count != pwd->rx_frag_total) {
l_error("fragment length mismatch");
return;
}
/* this was the last fragment, process */
eap_pwd_process(eap, pwd->rx_frag_buf, pwd->rx_frag_total);
l_free(pwd->rx_frag_buf);
pwd->rx_frag_buf = NULL;
pwd->rx_frag_count = 0;
pwd->rx_frag_total = 0;
return;
}
/* no fragmentation, process normally */
eap_pwd_process(eap, pkt, len);
}
static int eap_pwd_check_settings(struct l_settings *settings,
struct l_queue *secrets,
const char *prefix,
struct l_queue **out_missing)
{
const struct eap_secret_info *secret;
char identity_key[72];
char password_key[72];
L_AUTO_FREE_VAR(char *, identity);
L_AUTO_FREE_VAR(char *, password) = NULL;
snprintf(identity_key, sizeof(identity_key), "%sIdentity", prefix);
snprintf(password_key, sizeof(password_key), "%sPassword", prefix);
identity = l_settings_get_string(settings, "Security", identity_key);
if (!identity) {
secret = l_queue_find(secrets, eap_secret_info_match,
identity_key);
if (secret)
return 0;
eap_append_secret(out_missing, EAP_SECRET_REMOTE_USER_PASSWORD,
identity_key, password_key, NULL,
EAP_CACHE_TEMPORARY);
return 0;
}
password = l_settings_get_string(settings, "Security", password_key);
if (!password) {
secret = l_queue_find(secrets, eap_secret_info_match,
password_key);
if (secret)
return 0;
eap_append_secret(out_missing, EAP_SECRET_REMOTE_PASSWORD,
password_key, NULL, identity,
EAP_CACHE_TEMPORARY);
} else
explicit_bzero(password, strlen(password));
return 0;
}
static bool eap_pwd_load_settings(struct eap_state *eap,
struct l_settings *settings,
const char *prefix)
{
struct eap_pwd_handle *pwd;
char setting_key[72];
pwd = l_new(struct eap_pwd_handle, 1);
pwd->state = EAP_PWD_STATE_INIT;
snprintf(setting_key, sizeof(setting_key), "%sIdentity", prefix);
pwd->identity = l_settings_get_string(settings, "Security",
setting_key);
if (!pwd->identity) {
l_error("'%s' setting is missing", setting_key);
goto error;
}
snprintf(setting_key, sizeof(setting_key), "%sPassword", prefix);
pwd->password = l_settings_get_string(settings, "Security",
setting_key);
if (!pwd->password) {
snprintf(setting_key, sizeof(setting_key), "%sPassword",
prefix);
l_error("'%s' setting is missing", setting_key);
goto error;
}
eap_set_data(eap, pwd);
return true;
error:
if (pwd->password) {
explicit_bzero(pwd->password, strlen(pwd->password));
l_free(pwd->password);
}
l_free(pwd->identity);
l_free(pwd);
return false;
}
static struct eap_method eap_pwd = {
.request_type = EAP_TYPE_PWD,
.exports_msk = true,
.name = "PWD",
.free = eap_pwd_free,
.handle_request = eap_pwd_handle_request,
.check_settings = eap_pwd_check_settings,
.load_settings = eap_pwd_load_settings,
.reset_state = eap_pwd_reset_state,
};
static int eap_pwd_init(void)
{
l_debug("");
return eap_register_method(&eap_pwd);
}
static void eap_pwd_exit(void)
{
l_debug("");
eap_unregister_method(&eap_pwd);
}
EAP_METHOD_BUILTIN(eap_pwd, eap_pwd_init, eap_pwd_exit)