fix(wpa_supplicant): (PEAP client) Update Phase 2 auth requirements

The previous PEAP client behavior allowed the server to skip Phase 2
authentication with the expectation that the server was authenticated
during Phase 1 through TLS server certificate validation. Various PEAP
specifications are not exactly clear on what the behavior on this front
is supposed to be and as such, this ended up being more flexible than
the TTLS/FAST/TEAP cases. However, this is not really ideal when
unfortunately common misconfiguration of PEAP is used in deployed
devices where the server trust root (ca_cert) is not configured or the
user has an easy option for allowing this validation step to be skipped.

Change the default PEAP client behavior to be to require Phase 2
authentication to be successfully completed for cases where TLS session
resumption is not used and the client certificate has not been
configured. Those two exceptions are the main cases where a deployed
authentication server might skip Phase 2 and as such, where a more
strict default behavior could result in undesired interoperability
issues. Requiring Phase 2 authentication will end up disabling TLS
session resumption automatically to avoid interoperability issues.

Allow Phase 2 authentication behavior to be configured with a new phase1
configuration parameter option:
'phase2_auth' option can be used to control Phase 2 (i.e., within TLS
tunnel) behavior for PEAP:
 * 0 = do not require Phase 2 authentication
 * 1 = require Phase 2 authentication when client certificate
   (private_key/client_cert) is no used and TLS session resumption was
   not used (default)
 * 2 = require Phase 2 authentication in all cases
This commit is contained in:
Kapil Gupta 2024-02-26 10:23:43 +05:30 committed by Shreyas Sheth
parent 0bef269ac0
commit 6f9cc06b30
3 changed files with 49 additions and 5 deletions

View File

@ -66,6 +66,7 @@ struct eap_peap_data {
u8 cmk[20]; u8 cmk[20];
int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP) int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
* is enabled. */ * is enabled. */
enum { NO_AUTH, FOR_INITIAL, ALWAYS } phase2_auth;
}; };
@ -114,6 +115,19 @@ eap_peap_parse_phase1(struct eap_peap_data *data,
wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding"); wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
} }
if (os_strstr(phase1, "phase2_auth=0")) {
data->phase2_auth = NO_AUTH;
wpa_printf(MSG_DEBUG,
"EAP-PEAP: Do not require Phase 2 authentication");
} else if (os_strstr(phase1, "phase2_auth=1")) {
data->phase2_auth = FOR_INITIAL;
wpa_printf(MSG_DEBUG,
"EAP-PEAP: Require Phase 2 authentication for initial connection");
} else if (os_strstr(phase1, "phase2_auth=2")) {
data->phase2_auth = ALWAYS;
wpa_printf(MSG_DEBUG,
"EAP-PEAP: Require Phase 2 authentication for all cases");
}
#ifdef EAP_TNC #ifdef EAP_TNC
if (os_strstr(phase1, "tnc=soh2")) { if (os_strstr(phase1, "tnc=soh2")) {
data->soh = 2; data->soh = 2;
@ -145,6 +159,7 @@ eap_peap_init(struct eap_sm *sm)
data->force_peap_version = -1; data->force_peap_version = -1;
data->peap_outer_success = 2; data->peap_outer_success = 2;
data->crypto_binding = OPTIONAL_BINDING; data->crypto_binding = OPTIONAL_BINDING;
data->phase2_auth = FOR_INITIAL;
if (config && config->phase1 && if (config && config->phase1 &&
eap_peap_parse_phase1(data, config->phase1) < 0) { eap_peap_parse_phase1(data, config->phase1) < 0) {
@ -449,6 +464,19 @@ eap_tlv_validate_cryptobinding(struct eap_sm *sm,
return 0; return 0;
} }
static bool peap_phase2_sufficient(struct eap_sm *sm,
struct eap_peap_data *data)
{
if ((data->phase2_auth == ALWAYS ||
(data->phase2_auth == FOR_INITIAL &&
!tls_connection_resumed(sm->ssl_ctx, data->ssl.conn) &&
!data->ssl.client_cert_conf) ||
data->phase2_eap_started) &&
!data->phase2_eap_success)
return false;
return true;
}
/** /**
* eap_tlv_process - Process a received EAP-TLV message and generate a response * eap_tlv_process - Process a received EAP-TLV message and generate a response
@ -565,6 +593,11 @@ eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
" - force failed Phase 2"); " - force failed Phase 2");
resp_status = EAP_TLV_RESULT_FAILURE; resp_status = EAP_TLV_RESULT_FAILURE;
ret->decision = DECISION_FAIL; ret->decision = DECISION_FAIL;
} else if (!peap_phase2_sufficient(sm, data)) {
wpa_printf(MSG_INFO,
"EAP-PEAP: Server indicated Phase 2 success, but sufficient Phase 2 authentication has not been completed");
resp_status = EAP_TLV_RESULT_FAILURE;
ret->decision = DECISION_FAIL;
} else { } else {
resp_status = EAP_TLV_RESULT_SUCCESS; resp_status = EAP_TLV_RESULT_SUCCESS;
ret->decision = DECISION_UNCOND_SUCC; ret->decision = DECISION_UNCOND_SUCC;
@ -939,8 +972,7 @@ continue_req:
/* EAP-Success within TLS tunnel is used to indicate /* EAP-Success within TLS tunnel is used to indicate
* shutdown of the TLS channel. The authentication has * shutdown of the TLS channel. The authentication has
* been completed. */ * been completed. */
if (data->phase2_eap_started && if (!peap_phase2_sufficient(sm, data)) {
!data->phase2_eap_success) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 " wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
"Success used to indicate success, " "Success used to indicate success, "
"but Phase 2 EAP was not yet " "but Phase 2 EAP was not yet "
@ -1200,8 +1232,9 @@ static bool
eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
{ {
struct eap_peap_data *data = priv; struct eap_peap_data *data = priv;
return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
data->phase2_success; data->phase2_success && data->phase2_auth != ALWAYS;
} }

View File

@ -94,7 +94,7 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params,
static int eap_tls_params_from_conf(struct eap_sm *sm, static int eap_tls_params_from_conf(struct eap_sm *sm,
struct eap_ssl_data *data, struct eap_ssl_data *data,
struct tls_connection_params *params, struct tls_connection_params *params,
struct eap_peer_config *config) struct eap_peer_config *config, int phase2)
{ {
os_memset(params, 0, sizeof(*params)); os_memset(params, 0, sizeof(*params));
if (sm->workaround && data->eap_type != EAP_TYPE_FAST) { if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
@ -133,6 +133,12 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
return -1; return -1;
} }
if (!phase2)
data->client_cert_conf = params->client_cert ||
params->client_cert_blob ||
params->private_key ||
params->private_key_blob;
return 0; return 0;
} }
@ -210,7 +216,7 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
data->eap = sm; data->eap = sm;
data->eap_type = eap_type; data->eap_type = eap_type;
data->ssl_ctx = sm->ssl_ctx; data->ssl_ctx = sm->ssl_ctx;
if (eap_tls_params_from_conf(sm, data, &params, config) < 0) /* no phase2 */ if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) < 0) /* no phase2 */
return -1; return -1;
if (eap_tls_init_connection(sm, data, config, &params) < 0) if (eap_tls_init_connection(sm, data, config, &params) < 0)

View File

@ -73,6 +73,11 @@ struct eap_ssl_data {
* eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
*/ */
u8 eap_type; u8 eap_type;
/**
* client_cert_conf: Whether client certificate has been configured
*/
bool client_cert_conf;
}; };