mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-25 17:59:25 +01:00
eap-wsc: Registrar mode message processing
This commit has all the changes to extend and generalise the current eap-wsc.c code to handle both the Enrollee and Registrar side of the protocol, reusing existing functions and structures.
This commit is contained in:
parent
50f2b3d0ca
commit
e768f034a3
122
src/eap-wsc.c
122
src/eap-wsc.c
@ -93,6 +93,8 @@ struct eap_wsc_state {
|
||||
uint8_t iv3[16];
|
||||
uint8_t psk1[16];
|
||||
uint8_t psk2[16];
|
||||
uint8_t e_hash1[32];
|
||||
uint8_t e_hash2[32];
|
||||
uint8_t r_hash2[32];
|
||||
enum state state;
|
||||
struct l_checksum *hmac_auth_key;
|
||||
@ -171,22 +173,26 @@ static inline void keywrap_authenticator_put(struct eap_wsc_state *wsc,
|
||||
l_checksum_get_digest(wsc->hmac_auth_key, pdu + len - 8, 8);
|
||||
}
|
||||
|
||||
static inline bool r_hash_check(struct eap_wsc_state *wsc,
|
||||
uint8_t *r_snonce,
|
||||
static inline bool x_hash_check(struct eap_wsc_state *wsc,
|
||||
uint8_t *x_snonce,
|
||||
uint8_t *psk,
|
||||
uint8_t *r_hash_expected)
|
||||
uint8_t *x_hash_expected)
|
||||
{
|
||||
struct iovec iov[4];
|
||||
uint8_t r_hash[32];
|
||||
uint8_t hash[32];
|
||||
|
||||
/*
|
||||
* WSC 2.0.5, Section 7.4:
|
||||
* The Enrollee creates two 128-bit secret nonces, E-S1, E-S2 and
|
||||
* then computes
|
||||
* E-Hash1 = HMACAuthKey(E-S1 || PSK1 || PKE || PKR)
|
||||
* E-Hash2 = HMACAuthKey(E-S2 || PSK2 || PKE || PKR)
|
||||
* The Registrar creates two 128-bit secret nonces, R-S1, R-S2 and
|
||||
* then computes
|
||||
* R-Hash1 = HMACAuthKey(R-S1 || PSK1 || PKE || PKR)
|
||||
* R-Hash2 = HMACAuthKey(R-S2 || PSK2 || PKE || PKR)
|
||||
*/
|
||||
iov[0].iov_base = r_snonce;
|
||||
iov[0].iov_base = x_snonce;
|
||||
iov[0].iov_len = 16;
|
||||
iov[1].iov_base = psk;
|
||||
iov[1].iov_len = 16;
|
||||
@ -195,9 +201,9 @@ static inline bool r_hash_check(struct eap_wsc_state *wsc,
|
||||
iov[3].iov_base = wsc->m2->public_key;
|
||||
iov[3].iov_len = sizeof(wsc->m2->public_key);
|
||||
l_checksum_updatev(wsc->hmac_auth_key, iov, 4);
|
||||
l_checksum_get_digest(wsc->hmac_auth_key, r_hash, sizeof(r_hash));
|
||||
l_checksum_get_digest(wsc->hmac_auth_key, hash, sizeof(hash));
|
||||
|
||||
return !memcmp(r_hash, r_hash_expected, sizeof(r_hash));
|
||||
return !memcmp(hash, x_hash_expected, sizeof(hash));
|
||||
}
|
||||
|
||||
static uint8_t *encrypted_settings_decrypt(struct eap_wsc_state *wsc,
|
||||
@ -331,6 +337,16 @@ static void eap_wsc_free(struct eap_state *eap)
|
||||
eap_wsc_state_free(wsc);
|
||||
}
|
||||
|
||||
static void eap_wsc_r_send_start(struct eap_state *eap)
|
||||
{
|
||||
uint8_t buf[EAP_WSC_HEADER_LEN];
|
||||
|
||||
buf[12] = WSC_OP_START;
|
||||
buf[13] = 0;
|
||||
|
||||
eap_method_new_request(eap, buf, EAP_WSC_HEADER_LEN);
|
||||
}
|
||||
|
||||
static void eap_wsc_send_fragment(struct eap_state *eap)
|
||||
{
|
||||
struct eap_wsc_state *wsc = eap_get_data(eap);
|
||||
@ -357,12 +373,16 @@ static void eap_wsc_send_fragment(struct eap_state *eap)
|
||||
}
|
||||
|
||||
memcpy(buf + header_len, wsc->sent_pdu + wsc->tx_frag_offset, len);
|
||||
|
||||
if (wsc->registrar)
|
||||
eap_method_new_request(eap, buf, header_len + len);
|
||||
else
|
||||
eap_method_respond(eap, buf, header_len + len);
|
||||
|
||||
wsc->tx_last_frag_len = len;
|
||||
}
|
||||
|
||||
static void eap_wsc_send_response(struct eap_state *eap,
|
||||
static void eap_wsc_send_message(struct eap_state *eap,
|
||||
uint8_t *pdu, size_t pdu_len)
|
||||
{
|
||||
struct eap_wsc_state *wsc = eap_get_data(eap);
|
||||
@ -377,7 +397,11 @@ static void eap_wsc_send_response(struct eap_state *eap,
|
||||
buf[13] = 0;
|
||||
memcpy(buf + EAP_WSC_HEADER_LEN, pdu, pdu_len);
|
||||
|
||||
if (wsc->registrar)
|
||||
eap_method_new_request(eap, buf, msg_len);
|
||||
else
|
||||
eap_method_respond(eap, buf, msg_len);
|
||||
|
||||
l_free(buf);
|
||||
return;
|
||||
}
|
||||
@ -418,8 +442,11 @@ static void eap_wsc_send_nack(struct eap_state *eap,
|
||||
return;
|
||||
|
||||
nack.version2 = true;
|
||||
if (wsc->state != STATE_EXPECT_M1)
|
||||
memcpy(nack.enrollee_nonce, wsc->m1->enrollee_nonce,
|
||||
sizeof(nack.enrollee_nonce));
|
||||
else
|
||||
memset(nack.enrollee_nonce, 0, sizeof(nack.enrollee_nonce));
|
||||
|
||||
if (wsc->m2)
|
||||
memcpy(nack.registrar_nonce, wsc->m2->registrar_nonce,
|
||||
@ -437,7 +464,11 @@ static void eap_wsc_send_nack(struct eap_state *eap,
|
||||
buf[13] = 0;
|
||||
memcpy(buf + EAP_WSC_HEADER_LEN, pdu, pdu_len);
|
||||
|
||||
if (wsc->registrar)
|
||||
eap_method_new_request(eap, buf, pdu_len + EAP_WSC_HEADER_LEN);
|
||||
else
|
||||
eap_method_respond(eap, buf, pdu_len + EAP_WSC_HEADER_LEN);
|
||||
|
||||
l_free(pdu);
|
||||
}
|
||||
|
||||
@ -469,11 +500,15 @@ static void eap_wsc_send_done(struct eap_state *eap)
|
||||
|
||||
static void eap_wsc_send_frag_ack(struct eap_state *eap)
|
||||
{
|
||||
struct eap_wsc_state *wsc = eap_get_data(eap);
|
||||
uint8_t buf[EAP_WSC_HEADER_LEN];
|
||||
|
||||
buf[12] = WSC_OP_FRAG_ACK;
|
||||
buf[13] = 0;
|
||||
|
||||
if (wsc->registrar)
|
||||
eap_method_new_request(eap, buf, EAP_WSC_HEADER_LEN);
|
||||
else
|
||||
eap_method_respond(eap, buf, EAP_WSC_HEADER_LEN);
|
||||
}
|
||||
|
||||
@ -583,7 +618,7 @@ static void eap_wsc_send_m7(struct eap_state *eap,
|
||||
return;
|
||||
|
||||
authenticator_put(wsc, m6_pdu, m6_len, pdu, pdu_len);
|
||||
eap_wsc_send_response(eap, pdu, pdu_len);
|
||||
eap_wsc_send_message(eap, pdu, pdu_len);
|
||||
wsc->state = STATE_EXPECT_M8;
|
||||
}
|
||||
|
||||
@ -628,7 +663,7 @@ static void eap_wsc_handle_m6(struct eap_state *eap,
|
||||
}
|
||||
|
||||
/* We now have R-SNonce2, verify R-Hash2 stored in eap_wsc_handle_m4 */
|
||||
if (!r_hash_check(wsc, m6es.r_snonce2, wsc->psk2, wsc->r_hash2)) {
|
||||
if (!x_hash_check(wsc, m6es.r_snonce2, wsc->psk2, wsc->r_hash2)) {
|
||||
error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
@ -684,7 +719,7 @@ static void eap_wsc_send_m5(struct eap_state *eap,
|
||||
return;
|
||||
|
||||
authenticator_put(wsc, m4_pdu, m4_len, pdu, pdu_len);
|
||||
eap_wsc_send_response(eap, pdu, pdu_len);
|
||||
eap_wsc_send_message(eap, pdu, pdu_len);
|
||||
wsc->state = STATE_EXPECT_M6;
|
||||
}
|
||||
|
||||
@ -729,7 +764,7 @@ static void eap_wsc_handle_m4(struct eap_state *eap,
|
||||
}
|
||||
|
||||
/* Since we have obtained R-SNonce1, we can now verify R-Hash1. */
|
||||
if (!r_hash_check(wsc, m4es.r_snonce1, wsc->psk1, m4.r_hash1)) {
|
||||
if (!x_hash_check(wsc, m4es.r_snonce1, wsc->psk1, m4.r_hash1)) {
|
||||
error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
@ -816,7 +851,7 @@ static void eap_wsc_send_m3(struct eap_state *eap,
|
||||
return;
|
||||
|
||||
authenticator_put(wsc, m2_pdu, m2_len, pdu, pdu_len);
|
||||
eap_wsc_send_response(eap, pdu, pdu_len);
|
||||
eap_wsc_send_message(eap, pdu, pdu_len);
|
||||
wsc->state = STATE_EXPECT_M4;
|
||||
}
|
||||
|
||||
@ -932,7 +967,8 @@ static void eap_wsc_handle_nack(struct eap_state *eap,
|
||||
if (wsc_parse_wsc_nack(pdu, len, &nack) != 0)
|
||||
return;
|
||||
|
||||
if (memcmp(nack.enrollee_nonce, wsc->m1->enrollee_nonce,
|
||||
if (wsc->state != STATE_EXPECT_M1 && memcmp(nack.enrollee_nonce,
|
||||
wsc->m1->enrollee_nonce,
|
||||
sizeof(nack.enrollee_nonce)))
|
||||
return;
|
||||
|
||||
@ -948,10 +984,13 @@ static void eap_wsc_handle_nack(struct eap_state *eap,
|
||||
* to. Our choice is to reflect back what the request error code is
|
||||
* in the response.
|
||||
*/
|
||||
if (!wsc->registrar)
|
||||
eap_wsc_send_nack(eap, nack.configuration_error);
|
||||
|
||||
eap_method_error(eap);
|
||||
}
|
||||
|
||||
static void eap_wsc_handle_request(struct eap_state *eap,
|
||||
static void eap_wsc_handle_message(struct eap_state *eap,
|
||||
const uint8_t *pkt, size_t len)
|
||||
{
|
||||
struct eap_wsc_state *wsc = eap_get_data(eap);
|
||||
@ -961,6 +1000,16 @@ static void eap_wsc_handle_request(struct eap_state *eap,
|
||||
size_t pdu_len;
|
||||
size_t rx_header_offset = 0;
|
||||
|
||||
/*
|
||||
* pkt[0:len] is an EAP-Request packet contents if !wsc->registrar
|
||||
* and an EAP-Response otherwise. Most of the parsing is going to
|
||||
* be identical though.
|
||||
*
|
||||
* Since we have disjoint sets of enum state values between
|
||||
* Enrollee and Registrar, most of the time we don't need to look
|
||||
* at wsc->registrar.
|
||||
*/
|
||||
|
||||
if (len < 2)
|
||||
return;
|
||||
|
||||
@ -982,7 +1031,7 @@ static void eap_wsc_handle_request(struct eap_state *eap,
|
||||
if (!pdu)
|
||||
return;
|
||||
|
||||
eap_wsc_send_response(eap, pdu, pdu_len);
|
||||
eap_wsc_send_message(eap, pdu, pdu_len);
|
||||
wsc->state = STATE_EXPECT_M2;
|
||||
return;
|
||||
case WSC_OP_NACK:
|
||||
@ -992,8 +1041,21 @@ static void eap_wsc_handle_request(struct eap_state *eap,
|
||||
eap_wsc_handle_nack(eap, pkt, len);
|
||||
return;
|
||||
case WSC_OP_ACK:
|
||||
/* Not used in the in-band Enrollee Registration scenario */
|
||||
return;
|
||||
case WSC_OP_DONE:
|
||||
/* Should never receive these as Enrollee */
|
||||
if (wsc->state != STATE_EXPECT_DONE)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Notify higher layers that the registration protocol has
|
||||
* finished successfully as the EAP result is going to
|
||||
* always be the same. May not be strictly needed as the
|
||||
* higher layers get the real confirmation when the enrollee
|
||||
* uses the credentials on their next connection.
|
||||
*/
|
||||
eap_method_event(eap, EAP_WSC_EVENT_CREDENTIAL_SENT, NULL);
|
||||
eap_method_error(eap);
|
||||
return;
|
||||
case WSC_OP_FRAG_ACK:
|
||||
if (wsc->tx_last_frag_len &&
|
||||
@ -1050,16 +1112,14 @@ static void eap_wsc_handle_request(struct eap_state *eap,
|
||||
}
|
||||
|
||||
if (flags & WSC_FLAG_MF) {
|
||||
if (!wsc->rx_pdu_buf) {
|
||||
eap_method_error(eap);
|
||||
return;
|
||||
}
|
||||
if (!wsc->rx_pdu_buf)
|
||||
goto invalid_frag;
|
||||
|
||||
eap_wsc_send_frag_ack(eap);
|
||||
return;
|
||||
} else if (wsc->rx_pdu_buf) {
|
||||
if (wsc->rx_pdu_buf_len != wsc->rx_pdu_buf_offset) {
|
||||
l_error("Request fragment pkt size mismatch");
|
||||
l_error("Message fragment pkt size mismatch");
|
||||
goto invalid_frag;
|
||||
}
|
||||
|
||||
@ -1465,7 +1525,7 @@ static struct eap_method eap_wsc = {
|
||||
.exports_msk = true,
|
||||
.name = "WSC",
|
||||
.free = eap_wsc_free,
|
||||
.handle_request = eap_wsc_handle_request,
|
||||
.handle_request = eap_wsc_handle_message,
|
||||
.handle_retransmit = eap_wsc_handle_retransmit,
|
||||
.load_settings = eap_wsc_load_settings,
|
||||
};
|
||||
@ -1588,6 +1648,20 @@ err:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool eap_wsc_r_validate_identity(struct eap_state *eap,
|
||||
const char *identity)
|
||||
{
|
||||
struct eap_wsc_state *wsc = eap_get_data(eap);
|
||||
|
||||
if (strcmp(identity, "WFA-SimpleConfig-Enrollee-1-0"))
|
||||
return false;
|
||||
|
||||
/* Identity Request/Response done, directly send the WSC_Start */
|
||||
eap_wsc_r_send_start(eap);
|
||||
wsc->state = STATE_EXPECT_M1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct eap_method eap_wsc_r = {
|
||||
.name = "WSC-R",
|
||||
.request_type = EAP_TYPE_EXPANDED,
|
||||
@ -1596,6 +1670,8 @@ static struct eap_method eap_wsc_r = {
|
||||
.exports_msk = true,
|
||||
.free = eap_wsc_free,
|
||||
.load_settings = eap_wsc_r_load_settings,
|
||||
.validate_identity = eap_wsc_r_validate_identity,
|
||||
.handle_response = eap_wsc_handle_message,
|
||||
};
|
||||
|
||||
static int eap_wsc_init(void)
|
||||
|
@ -22,4 +22,5 @@
|
||||
|
||||
enum EAP_WSC_EVENT {
|
||||
EAP_WSC_EVENT_CREDENTIAL_OBTAINED = 0x0050f200,
|
||||
EAP_WSC_EVENT_CREDENTIAL_SENT = 0x0050f201,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user