mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 02:19:26 +01:00
eap-tls: Drop EAP-{TTLS,PEAP}-Client{Cert,Key}
As requested move the client certificate and private key loading from eap-tls-common.c to eap-tls.c. No man page change needed because those two settings weren't documented in it in the first place.
This commit is contained in:
parent
5e631b4e38
commit
b0aa1e4e88
@ -867,8 +867,8 @@ static struct l_queue *eap_tls_load_ca_cert(struct l_settings *settings,
|
||||
return l_pem_load_certificate_list_from_data(pem, strlen(pem));
|
||||
}
|
||||
|
||||
static struct l_certchain *eap_tls_load_client_cert(struct l_settings *settings,
|
||||
const char *value)
|
||||
struct l_certchain *eap_tls_load_client_cert(struct l_settings *settings,
|
||||
const char *value)
|
||||
{
|
||||
const char *pem;
|
||||
|
||||
@ -882,7 +882,7 @@ static struct l_certchain *eap_tls_load_client_cert(struct l_settings *settings,
|
||||
return l_pem_load_certificate_chain_from_data(pem, strlen(pem));
|
||||
}
|
||||
|
||||
static struct l_key *eap_tls_load_priv_key(struct l_settings *settings,
|
||||
struct l_key *eap_tls_load_priv_key(struct l_settings *settings,
|
||||
const char *value, const char *passphrase,
|
||||
bool *is_encrypted)
|
||||
{
|
||||
@ -905,186 +905,23 @@ int eap_tls_common_settings_check(struct l_settings *settings,
|
||||
struct l_queue **out_missing)
|
||||
{
|
||||
char setting_key[72];
|
||||
char client_cert_setting[72];
|
||||
char passphrase_setting[72];
|
||||
struct l_queue *cacerts = NULL;
|
||||
struct l_certchain *cert = NULL;
|
||||
struct l_key *priv_key = NULL;
|
||||
bool is_encrypted, is_public;
|
||||
int ret;
|
||||
const char *error_str;
|
||||
size_t size;
|
||||
ssize_t result;
|
||||
uint8_t *encrypted, *decrypted;
|
||||
struct l_key *pub_key;
|
||||
bool have_cacerts = false;
|
||||
const char *domain_mask_str;
|
||||
|
||||
L_AUTO_FREE_VAR(char *, value);
|
||||
L_AUTO_FREE_VAR(char *, client_cert) = NULL;
|
||||
L_AUTO_FREE_VAR(char *, passphrase) = NULL;
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sCACert", prefix);
|
||||
value = l_settings_get_string(settings, "Security", setting_key);
|
||||
if (value) {
|
||||
cacerts = eap_tls_load_ca_cert(settings, value);
|
||||
struct l_queue *cacerts = eap_tls_load_ca_cert(settings, value);
|
||||
|
||||
if (!cacerts) {
|
||||
l_error("Failed to load %s", value);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(client_cert_setting, sizeof(client_cert_setting),
|
||||
"%sClientCert", prefix);
|
||||
client_cert = l_settings_get_string(settings, "Security",
|
||||
client_cert_setting);
|
||||
if (client_cert) {
|
||||
cert = eap_tls_load_client_cert(settings, client_cert);
|
||||
|
||||
if (!cert) {
|
||||
l_error("Failed to load %s", client_cert);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check that certchain provided is valid. We do not
|
||||
* verify the certchain against the provided CA, since the
|
||||
* CA that issued user certificates might be different from
|
||||
* the one that is used to verify the peer
|
||||
*/
|
||||
if (!l_certchain_verify(cert, NULL, &error_str)) {
|
||||
l_error("Certificate chain %s fails verification: %s",
|
||||
client_cert, error_str);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
l_free(value);
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
|
||||
value = l_settings_get_string(settings, "Security", setting_key);
|
||||
|
||||
if (value && !client_cert) {
|
||||
l_error("%s present but no client certificate (%s)",
|
||||
setting_key, client_cert_setting);
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
} else if (!value && client_cert) {
|
||||
l_error("%s present but no client private key (%s)",
|
||||
client_cert_setting, setting_key);
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
snprintf(passphrase_setting, sizeof(passphrase_setting),
|
||||
"%sClientKeyPassphrase", prefix);
|
||||
passphrase = l_settings_get_string(settings, "Security",
|
||||
passphrase_setting);
|
||||
|
||||
if (!passphrase) {
|
||||
const struct eap_secret_info *secret;
|
||||
|
||||
secret = l_queue_find(secrets, eap_secret_info_match,
|
||||
passphrase_setting);
|
||||
if (secret)
|
||||
passphrase = l_strdup(secret->value);
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
if (passphrase) {
|
||||
l_error("%s present but no client private key"
|
||||
" value set (%s)", passphrase_setting,
|
||||
setting_key);
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
priv_key = eap_tls_load_priv_key(settings, value, passphrase,
|
||||
&is_encrypted);
|
||||
|
||||
if (!priv_key) {
|
||||
if (!is_encrypted) {
|
||||
l_error("Error loading client private key %s", value);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (passphrase) {
|
||||
l_error("Error loading encrypted client private key %s",
|
||||
value);
|
||||
ret = -EACCES;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've got an encrypted key and passphrase was not saved
|
||||
* in the network settings, need to request the passphrase.
|
||||
*/
|
||||
eap_append_secret(out_missing,
|
||||
EAP_SECRET_LOCAL_PKEY_PASSPHRASE,
|
||||
passphrase_setting, NULL, value,
|
||||
EAP_CACHE_TEMPORARY);
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (passphrase && !is_encrypted) {
|
||||
l_error("%s present but client private key %s is not encrypted",
|
||||
passphrase_setting, value);
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!l_key_get_info(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
|
||||
&size, &is_public) || is_public) {
|
||||
l_error("%s is not a private key or l_key_get_info fails",
|
||||
value);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
size /= 8;
|
||||
encrypted = alloca(size);
|
||||
decrypted = alloca(size);
|
||||
|
||||
pub_key = l_cert_get_pubkey(l_certchain_get_leaf(cert));
|
||||
if (!pub_key) {
|
||||
l_error("l_cert_get_pubkey fails for %s", client_cert);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = l_key_encrypt(pub_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
|
||||
"", encrypted, 1, size);
|
||||
l_key_free(pub_key);
|
||||
|
||||
if (result != (ssize_t) size) {
|
||||
l_error("l_key_encrypt fails with %s: %s", client_cert,
|
||||
strerror(-result));
|
||||
ret = result;
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = l_key_decrypt(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
|
||||
encrypted, decrypted, size, size);
|
||||
if (result < 0) {
|
||||
l_error("l_key_decrypt fails with %s: %s", value,
|
||||
strerror(-result));
|
||||
ret = result;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (result != 1 || decrypted[0] != 0) {
|
||||
l_error("Private key %s does not match certificate %s", value,
|
||||
client_cert);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
l_queue_destroy(cacerts,
|
||||
(l_queue_destroy_func_t) l_cert_free);
|
||||
have_cacerts = true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1097,23 +934,12 @@ int eap_tls_common_settings_check(struct l_settings *settings,
|
||||
prefix);
|
||||
domain_mask_str = l_settings_get_value(settings, "Security",
|
||||
setting_key);
|
||||
if (domain_mask_str && !cacerts) {
|
||||
if (domain_mask_str && !have_cacerts) {
|
||||
l_error("%s was set but no CA Certificates given", setting_key);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
done:
|
||||
l_queue_destroy(cacerts,
|
||||
(l_queue_destroy_func_t) l_cert_free);
|
||||
l_certchain_free(cert);
|
||||
l_key_free(priv_key);
|
||||
|
||||
if (passphrase)
|
||||
explicit_bzero(passphrase, strlen(passphrase));
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool eap_tls_common_settings_load(struct eap_state *eap,
|
||||
@ -1124,8 +950,8 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
|
||||
struct eap_tls_state *eap_tls;
|
||||
char setting_key[72];
|
||||
char *domain_mask_str;
|
||||
|
||||
L_AUTO_FREE_VAR(char *, value) = NULL;
|
||||
L_AUTO_FREE_VAR(char *, passphrase) = NULL;
|
||||
|
||||
eap_tls = l_new(struct eap_tls_state, 1);
|
||||
|
||||
@ -1143,37 +969,6 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
|
||||
}
|
||||
}
|
||||
|
||||
l_free(value);
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sClientCert", prefix);
|
||||
value = l_settings_get_string(settings, "Security", setting_key);
|
||||
if (value) {
|
||||
eap_tls->client_cert = eap_tls_load_client_cert(settings,
|
||||
value);
|
||||
if (!eap_tls->client_cert) {
|
||||
l_error("Could not load ClientCert %s", value);
|
||||
goto load_error;
|
||||
}
|
||||
}
|
||||
|
||||
l_free(value);
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sClientKeyPassphrase",
|
||||
prefix);
|
||||
passphrase = l_settings_get_string(settings, "Security", setting_key);
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sClientKey", prefix);
|
||||
value = l_settings_get_string(settings, "Security", setting_key);
|
||||
if (value) {
|
||||
eap_tls->client_key = eap_tls_load_priv_key(settings, value,
|
||||
passphrase,
|
||||
NULL);
|
||||
if (!eap_tls->client_key) {
|
||||
l_error("Could not load ClientKey %s", value);
|
||||
goto load_error;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sServerDomainMask",
|
||||
prefix);
|
||||
domain_mask_str = l_settings_get_string(settings, "Security",
|
||||
@ -1194,6 +989,16 @@ load_error:
|
||||
return false;
|
||||
}
|
||||
|
||||
void eap_tls_common_set_keys(struct eap_state *eap,
|
||||
struct l_certchain *client_cert,
|
||||
struct l_key *client_key)
|
||||
{
|
||||
struct eap_tls_state *eap_tls = eap_get_data(eap);
|
||||
|
||||
eap_tls->client_cert = client_cert;
|
||||
eap_tls->client_key = client_key;
|
||||
}
|
||||
|
||||
void eap_tls_common_set_completed(struct eap_state *eap)
|
||||
{
|
||||
struct eap_tls_state *eap_tls = eap_get_data(eap);
|
||||
|
@ -48,6 +48,12 @@ void eap_tls_common_handle_request(struct eap_state *eap,
|
||||
void eap_tls_common_handle_retransmit(struct eap_state *eap,
|
||||
const uint8_t *pkt, size_t len);
|
||||
|
||||
struct l_certchain *eap_tls_load_client_cert(struct l_settings *settings,
|
||||
const char *value);
|
||||
struct l_key *eap_tls_load_priv_key(struct l_settings *settings,
|
||||
const char *value, const char *passphrase,
|
||||
bool *is_encrypted);
|
||||
|
||||
int eap_tls_common_settings_check(struct l_settings *settings,
|
||||
struct l_queue *secrets,
|
||||
const char *prefix,
|
||||
@ -56,6 +62,9 @@ bool eap_tls_common_settings_load(struct eap_state *eap,
|
||||
struct l_settings *settings, const char *prefix,
|
||||
const struct eap_tls_variant_ops *variant_ops,
|
||||
void *variant_data);
|
||||
void eap_tls_common_set_keys(struct eap_state *eap,
|
||||
struct l_certchain *client_cert,
|
||||
struct l_key *client_key);
|
||||
|
||||
void eap_tls_common_send_empty_response(struct eap_state *eap);
|
||||
enum eap_tls_version eap_tls_common_get_negotiated_version(
|
||||
|
237
src/eap-tls.c
237
src/eap-tls.c
@ -60,17 +60,205 @@ static bool eap_tls_tunnel_ready(struct eap_state *eap,
|
||||
return true;
|
||||
}
|
||||
|
||||
static int eap_tls_check_keys_match(struct l_key *priv_key, struct l_cert *cert,
|
||||
const char *key_name,
|
||||
const char *cert_name)
|
||||
{
|
||||
bool is_public;
|
||||
size_t size;
|
||||
ssize_t result;
|
||||
uint8_t *encrypted, *decrypted;
|
||||
struct l_key *pub_key;
|
||||
|
||||
if (!l_key_get_info(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
|
||||
&size, &is_public) || is_public) {
|
||||
l_error("%s is not a private key or l_key_get_info fails",
|
||||
key_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size /= 8;
|
||||
encrypted = alloca(size);
|
||||
decrypted = alloca(size);
|
||||
|
||||
pub_key = l_cert_get_pubkey(cert);
|
||||
if (!pub_key) {
|
||||
l_error("l_cert_get_pubkey fails for %s", cert_name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
result = l_key_encrypt(pub_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
|
||||
"", encrypted, 1, size);
|
||||
l_key_free(pub_key);
|
||||
|
||||
if (result != (ssize_t) size) {
|
||||
l_error("l_key_encrypt fails with %s: %s", cert_name,
|
||||
strerror(-result));
|
||||
return result;
|
||||
}
|
||||
|
||||
result = l_key_decrypt(priv_key, L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE,
|
||||
encrypted, decrypted, size, size);
|
||||
if (result < 0) {
|
||||
l_error("l_key_decrypt fails with %s: %s", key_name,
|
||||
strerror(-result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result != 1 || decrypted[0] != 0) {
|
||||
l_error("Private key %s does not match certificate %s", key_name,
|
||||
cert_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eap_tls_settings_check(struct l_settings *settings,
|
||||
struct l_queue *secrets,
|
||||
const char *prefix,
|
||||
struct l_queue **out_missing)
|
||||
{
|
||||
char tls_prefix[72];
|
||||
char tls_prefix[32];
|
||||
char passphrase_setting[72];
|
||||
char client_cert_setting[72];
|
||||
char priv_key_setting[72];
|
||||
L_AUTO_FREE_VAR(char *, passphrase) = NULL;
|
||||
L_AUTO_FREE_VAR(char *, client_cert_value) = NULL;
|
||||
L_AUTO_FREE_VAR(char *, priv_key_value) = NULL;
|
||||
struct l_certchain *client_cert = NULL;
|
||||
struct l_key *priv_key = NULL;
|
||||
const char *error_str;
|
||||
bool is_encrypted;
|
||||
int ret;
|
||||
|
||||
snprintf(tls_prefix, sizeof(tls_prefix), "%sTLS-", prefix);
|
||||
|
||||
return eap_tls_common_settings_check(settings, secrets, tls_prefix,
|
||||
out_missing);
|
||||
ret = eap_tls_common_settings_check(settings, secrets, tls_prefix,
|
||||
out_missing);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
snprintf(client_cert_setting, sizeof(client_cert_setting),
|
||||
"%sClientCert", tls_prefix);
|
||||
client_cert_value = l_settings_get_string(settings, "Security",
|
||||
client_cert_setting);
|
||||
|
||||
snprintf(priv_key_setting, sizeof(priv_key_setting), "%sClientKey",
|
||||
tls_prefix);
|
||||
priv_key_value = l_settings_get_string(settings, "Security",
|
||||
priv_key_setting);
|
||||
|
||||
snprintf(passphrase_setting, sizeof(passphrase_setting),
|
||||
"%sClientKeyPassphrase", tls_prefix);
|
||||
passphrase = l_settings_get_string(settings, "Security",
|
||||
passphrase_setting);
|
||||
|
||||
if (!passphrase) {
|
||||
const struct eap_secret_info *secret;
|
||||
|
||||
secret = l_queue_find(secrets, eap_secret_info_match,
|
||||
passphrase_setting);
|
||||
if (secret)
|
||||
passphrase = l_strdup(secret->value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the combination of settings that are present/missing
|
||||
* makes sense before validating each setting.
|
||||
*/
|
||||
if (priv_key_value && !client_cert_value) {
|
||||
l_error("%s present but no client certificate (%s)",
|
||||
priv_key_setting, client_cert_setting);
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
} else if (!priv_key_value && client_cert) {
|
||||
l_error("%s present but no client private key (%s)",
|
||||
client_cert_setting, priv_key_setting);
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!priv_key_value) {
|
||||
if (passphrase) {
|
||||
l_error("%s present but no client private key set (%s)",
|
||||
passphrase_setting, priv_key_setting);
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
client_cert = eap_tls_load_client_cert(settings, client_cert_value);
|
||||
if (!client_cert) {
|
||||
l_error("Failed to load %s", client_cert_value);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check that certchain provided is valid. We do not verify
|
||||
* the certchain against the provided CA since the CA that issued
|
||||
* user certificates might be different from the one that is used
|
||||
* to verify the peer.
|
||||
*/
|
||||
if (!l_certchain_verify(client_cert, NULL, &error_str)) {
|
||||
l_error("Certificate chain %s fails verification: %s",
|
||||
client_cert_value, error_str);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
priv_key = eap_tls_load_priv_key(settings, priv_key_value, passphrase,
|
||||
&is_encrypted);
|
||||
if (!priv_key) {
|
||||
if (!is_encrypted) {
|
||||
l_error("Error loading client private key %s",
|
||||
priv_key_value);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (passphrase) {
|
||||
l_error("Error loading encrypted client private key %s",
|
||||
priv_key_value);
|
||||
ret = -EACCES;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've got an encrypted key and passphrase was not saved
|
||||
* in the network settings, need to request the passphrase.
|
||||
*/
|
||||
eap_append_secret(out_missing,
|
||||
EAP_SECRET_LOCAL_PKEY_PASSPHRASE,
|
||||
passphrase_setting, NULL,
|
||||
priv_key_value, EAP_CACHE_TEMPORARY);
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (passphrase && !is_encrypted) {
|
||||
l_error("%s present but client private key %s is not encrypted",
|
||||
passphrase_setting, priv_key_value);
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = eap_tls_check_keys_match(priv_key,
|
||||
l_certchain_get_leaf(client_cert),
|
||||
priv_key_value, client_cert_value);
|
||||
|
||||
done:
|
||||
l_certchain_free(client_cert);
|
||||
l_key_free(priv_key);
|
||||
|
||||
if (passphrase)
|
||||
explicit_bzero(passphrase, strlen(passphrase));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct eap_tls_variant_ops eap_tls_ops = {
|
||||
@ -81,16 +269,49 @@ static bool eap_tls_settings_load(struct eap_state *eap,
|
||||
struct l_settings *settings,
|
||||
const char *prefix)
|
||||
{
|
||||
char setting_key_prefix[72];
|
||||
char tls_prefix[32];
|
||||
char setting_key[72];
|
||||
struct l_certchain *client_cert = NULL;
|
||||
struct l_key *client_key = NULL;
|
||||
|
||||
snprintf(setting_key_prefix, sizeof(setting_key_prefix), "%sTLS-",
|
||||
prefix);
|
||||
L_AUTO_FREE_VAR(char *, value) = NULL;
|
||||
L_AUTO_FREE_VAR(char *, passphrase) = NULL;
|
||||
|
||||
if (!eap_tls_common_settings_load(eap, settings, setting_key_prefix,
|
||||
&eap_tls_ops, NULL))
|
||||
snprintf(tls_prefix, sizeof(tls_prefix), "%sTLS-", prefix);
|
||||
|
||||
if (!eap_tls_common_settings_load(eap, settings, tls_prefix,
|
||||
&eap_tls_ops, NULL))
|
||||
return false;
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sClientKeyPassphrase",
|
||||
tls_prefix);
|
||||
passphrase = l_settings_get_string(settings, "Security", setting_key);
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sClientCert", tls_prefix);
|
||||
value = l_settings_get_string(settings, "Security", setting_key);
|
||||
if (value) {
|
||||
client_cert = eap_tls_load_client_cert(settings, value);
|
||||
if (!client_cert)
|
||||
goto load_error;
|
||||
}
|
||||
|
||||
l_free(value);
|
||||
|
||||
snprintf(setting_key, sizeof(setting_key), "%sClientKey", tls_prefix);
|
||||
value = l_settings_get_string(settings, "Security", setting_key);
|
||||
if (value) {
|
||||
client_key = eap_tls_load_priv_key(settings, value,
|
||||
passphrase, NULL);
|
||||
if (!client_key)
|
||||
goto load_error;
|
||||
}
|
||||
|
||||
eap_tls_common_set_keys(eap, client_cert, client_key);
|
||||
return true;
|
||||
|
||||
load_error:
|
||||
eap_tls_common_state_free(eap);
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct eap_method eap_tls = {
|
||||
|
Loading…
Reference in New Issue
Block a user