/* * * Wireless daemon for Linux * * Copyright (C) 2016-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 #endif #include #include #include #include #include "src/missing.h" #include "src/crypto.h" #include "src/eap.h" #include "src/eap-private.h" #include "src/wscutil.h" #include "src/util.h" #include "src/eap-wsc.h" #define EAP_WSC_HEADER_LEN 14 #define EAP_WSC_PDU_MAX_LEN 4096 /* WSC v2.0.5, Section 7.7.1 */ enum wsc_op { WSC_OP_START = 0x01, WSC_OP_ACK = 0x02, WSC_OP_NACK = 0x03, WSC_OP_MSG = 0x04, WSC_OP_DONE = 0x05, WSC_OP_FRAG_ACK = 0x06, }; /* WSC v2.0.5, Section 7.7.1 */ enum wsc_flag { WSC_FLAG_MF = 0x01, WSC_FLAG_LF = 0x02, }; enum state { /* Enrollee states */ STATE_EXPECT_START = 0, STATE_EXPECT_M2, STATE_EXPECT_M4, STATE_EXPECT_M6, STATE_EXPECT_M8, STATE_FINISHED, /* Registrar states */ STATE_EXPECT_IDENTITY, STATE_EXPECT_M1, STATE_EXPECT_M3, STATE_EXPECT_M5, STATE_EXPECT_M7, STATE_EXPECT_DONE, }; static struct l_key *dh5_generator; static struct l_key *dh5_prime; struct eap_wsc_state { bool registrar; struct wsc_m1 *m1; struct wsc_m2 *m2; struct wsc_credential wpa2_cred; struct wsc_credential open_cred; uint8_t *sent_pdu; size_t sent_len; struct l_key *private; char *device_password; uint8_t local_snonce1[16]; uint8_t local_snonce2[16]; uint8_t iv1[16]; uint8_t iv2[16]; 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; struct l_cipher *aes_cbc_128; uint8_t *rx_pdu_buf; size_t rx_pdu_buf_len; size_t rx_pdu_buf_offset; size_t tx_frag_offset; size_t tx_last_frag_len; }; static inline void eap_wsc_state_set_sent_pdu(struct eap_wsc_state *wsc, uint8_t *pdu, size_t len) { l_free(wsc->sent_pdu); wsc->sent_pdu = pdu; wsc->sent_len = len; } static inline bool authenticator_check(struct eap_wsc_state *wsc, const uint8_t *pdu, size_t len) { uint8_t authenticator[8]; struct iovec iov[2]; iov[0].iov_base = wsc->sent_pdu; iov[0].iov_len = wsc->sent_len; iov[1].iov_base = (void *) pdu; iov[1].iov_len = len - 12; l_checksum_updatev(wsc->hmac_auth_key, iov, 2); l_checksum_get_digest(wsc->hmac_auth_key, authenticator, 8); /* Authenticator is the last 8 bytes of the message */ if (memcmp(authenticator, pdu + len - 8, 8)) return false; return true; } static inline void authenticator_put(struct eap_wsc_state *wsc, const uint8_t *prev_msg, size_t prev_msg_len, uint8_t *cur_msg, size_t cur_msg_len) { struct iovec iov[2]; iov[0].iov_base = (void *) prev_msg; iov[0].iov_len = prev_msg_len; iov[1].iov_base = cur_msg; iov[1].iov_len = cur_msg_len - 12; l_checksum_updatev(wsc->hmac_auth_key, iov, 2); l_checksum_get_digest(wsc->hmac_auth_key, cur_msg + cur_msg_len - 8, 8); } static inline bool keywrap_authenticator_check(struct eap_wsc_state *wsc, const uint8_t *pdu, size_t len) { uint8_t authenticator[8]; /* We omit the included KeyWrapAuthenticator element from the hash */ l_checksum_update(wsc->hmac_auth_key, pdu, len - 12); l_checksum_get_digest(wsc->hmac_auth_key, authenticator, 8); /* KeyWrapAuthenticator is the last 8 bytes of the message */ if (memcmp(authenticator, pdu + len - 8, 8)) return false; return true; } static inline void keywrap_authenticator_put(struct eap_wsc_state *wsc, uint8_t *pdu, size_t len) { l_checksum_update(wsc->hmac_auth_key, pdu, len - 12); l_checksum_get_digest(wsc->hmac_auth_key, pdu + len - 8, 8); } static inline bool x_hash_check(struct eap_wsc_state *wsc, uint8_t *x_snonce, uint8_t *psk, uint8_t *x_hash_expected) { struct iovec iov[4]; 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 = x_snonce; iov[0].iov_len = 16; iov[1].iov_base = psk; iov[1].iov_len = 16; iov[2].iov_base = wsc->m1->public_key; iov[2].iov_len = sizeof(wsc->m1->public_key); 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, hash, sizeof(hash)); return !memcmp(hash, x_hash_expected, sizeof(hash)); } static uint8_t *encrypted_settings_decrypt(struct eap_wsc_state *wsc, const uint8_t *pdu, size_t len, size_t *out_len) { size_t encrypted_len; uint8_t *decrypted; unsigned int i; uint8_t pad; /* WSC 2.0.5, Section 12, Encrypted Settings: * "The Data field of the Encrypted Settings attribute includes an * initialization vector (IV) followed by a set of encrypted Wi-Fi * Simple Configuration TLV attributes." * * Account for the IV being in the beginning 16 bytes */ if (len < 16 ) return NULL; encrypted_len = len - 16; if (encrypted_len < 16 || encrypted_len % 16) return NULL; decrypted = l_malloc(encrypted_len); l_cipher_set_iv(wsc->aes_cbc_128, pdu, 16); if (!l_cipher_decrypt(wsc->aes_cbc_128, pdu + 16, decrypted, encrypted_len)) goto fail; /* Check that the pad value is sane */ pad = decrypted[encrypted_len - 1]; if (pad > encrypted_len) goto fail; for (i = 0; i < pad; i++) { if (decrypted[encrypted_len - pad + i] == pad) continue; goto fail; } *out_len = encrypted_len - pad; return decrypted; fail: explicit_bzero(decrypted, encrypted_len); l_free(decrypted); return NULL; } static bool encrypted_settings_encrypt(struct eap_wsc_state *wsc, const uint8_t *iv, const uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len) { size_t len = 0; unsigned int i; uint8_t pad; l_cipher_set_iv(wsc->aes_cbc_128, iv, 16); memcpy(out, iv, 16); len += 16; memcpy(out + len, in, in_len); len += in_len; pad = 16 - in_len % 16; for (i = 0; i < pad; i++) out[len++] = pad; if (!l_cipher_encrypt(wsc->aes_cbc_128, out + 16, out + 16, len - 16)) { explicit_bzero(out + 16, len - 16); return false; } *out_len = len; return true; } static void eap_wsc_state_free(struct eap_wsc_state *wsc) { if (wsc->device_password) { explicit_bzero(wsc->device_password, strlen(wsc->device_password)); l_free(wsc->device_password); } l_key_free(wsc->private); l_free(wsc->sent_pdu); wsc->sent_pdu = NULL; wsc->sent_len = 0; if (wsc->rx_pdu_buf) { l_free(wsc->rx_pdu_buf); wsc->rx_pdu_buf = NULL; wsc->rx_pdu_buf_len = 0; wsc->rx_pdu_buf_offset = 0; } l_checksum_free(wsc->hmac_auth_key); l_cipher_free(wsc->aes_cbc_128); l_free(wsc->m1); l_free(wsc->m2); explicit_bzero(wsc->local_snonce1, 16); explicit_bzero(wsc->local_snonce2, 16); explicit_bzero(wsc->psk1, 16); explicit_bzero(wsc->psk2, 16); explicit_bzero(&wsc->wpa2_cred, sizeof(struct wsc_credential)); explicit_bzero(&wsc->open_cred, sizeof(struct wsc_credential)); l_free(wsc); } static void eap_wsc_free(struct eap_state *eap) { struct eap_wsc_state *wsc = eap_get_data(eap); eap_set_data(eap, NULL); 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); size_t mtu = eap_get_mtu(eap); uint8_t buf[mtu]; size_t len = wsc->sent_len - wsc->tx_frag_offset; size_t header_len = EAP_WSC_HEADER_LEN; buf[12] = WSC_OP_MSG; if (len > mtu - EAP_WSC_HEADER_LEN) { len = mtu - EAP_WSC_HEADER_LEN; buf[13] = WSC_FLAG_MF; } else { buf[13] = 0; } if (!wsc->tx_frag_offset) { buf[13] |= WSC_FLAG_LF; l_put_be16(wsc->sent_len, &buf[14]); len -= 2; header_len += 2; } 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_message(struct eap_state *eap, uint8_t *pdu, size_t pdu_len) { struct eap_wsc_state *wsc = eap_get_data(eap); size_t msg_len = pdu_len + EAP_WSC_HEADER_LEN; eap_wsc_state_set_sent_pdu(wsc, pdu, pdu_len); if (msg_len <= eap_get_mtu(eap)) { uint8_t *buf = l_malloc(msg_len); buf[12] = WSC_OP_MSG; 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; } wsc->tx_frag_offset = 0; eap_wsc_send_fragment(eap); } static void eap_wsc_send_nack(struct eap_state *eap, enum wsc_configuration_error error) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_nack nack; uint8_t *pdu; size_t pdu_len; uint8_t buf[256]; /* * WSC 2.0.5, Table 34, Configuration Error 0 states: * "- not valid for WSC_NACK except when a station acts as an External * Registrar (to learn the current AP settings after M7 with * configuration error = 0)" * * However, section 7.7.3 states: * "Once M5 is sent, for example, if anything but M6 is received, * the Enrollee will respond with a NACK message." * * Section 7.1 states: * "If a message is received with either an invalid nonce or an invalid * Authenticator attribute, the recipient shall silently ignore this * message." * * So it is entirely unclear what to do in the situation of an * out-of-order message being sent. To centralize decision making, * callers will call this function with error 0. */ if (error == WSC_CONFIGURATION_ERROR_NO_ERROR) 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, sizeof(nack.registrar_nonce)); else memset(nack.registrar_nonce, 0, sizeof(nack.registrar_nonce)); nack.configuration_error = error; pdu = wsc_build_wsc_nack(&nack, &pdu_len); if (!pdu) return; buf[12] = WSC_OP_NACK; 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); } static void eap_wsc_send_done(struct eap_state *eap) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_done done; uint8_t *pdu; size_t pdu_len; uint8_t buf[256]; done.version2 = true; memcpy(done.enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(done.enrollee_nonce)); memcpy(done.registrar_nonce, wsc->m2->registrar_nonce, sizeof(done.registrar_nonce)); pdu = wsc_build_wsc_done(&done, &pdu_len); if (!pdu) return; buf[12] = WSC_OP_DONE; buf[13] = 0; memcpy(buf + EAP_WSC_HEADER_LEN, pdu, pdu_len); eap_method_respond(eap, buf, pdu_len + EAP_WSC_HEADER_LEN); l_free(pdu); } 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); } static void eap_wsc_handle_m8(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m8 m8; struct iovec encrypted; uint8_t *decrypted; size_t decrypted_len; struct wsc_m8_encrypted_settings m8es; struct iovec creds[3]; size_t n_creds; size_t i; bool nack = true; /* Spec unclear what to do here, see comments in eap_wsc_send_nack */ if (wsc_parse_m8(pdu, len, &m8, &encrypted) != 0) { eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR); return; } if (memcmp(m8.enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(m8.enrollee_nonce))) return; if (!authenticator_check(wsc, pdu, len)) return; decrypted = encrypted_settings_decrypt(wsc, encrypted.iov_base, encrypted.iov_len, &decrypted_len); if (!decrypted) goto send_nack; n_creds = L_ARRAY_SIZE(creds); if (wsc_parse_m8_encrypted_settings(decrypted, decrypted_len, &m8es, creds, &n_creds)) goto cleanup; if (!keywrap_authenticator_check(wsc, decrypted, decrypted_len)) goto cleanup; for (i = 0; i < n_creds; i++) { struct wsc_credential cred; if (wsc_parse_credential(creds[i].iov_base, creds[i].iov_len, &cred) == 0) eap_method_event(eap, EAP_WSC_EVENT_CREDENTIAL_OBTAINED, &cred); explicit_bzero(&cred, sizeof(cred)); } eap_wsc_send_done(eap); wsc->state = STATE_FINISHED; nack = false; cleanup: explicit_bzero(&m8es, sizeof(m8es)); explicit_bzero(decrypted, decrypted_len); l_free(decrypted); if (!nack) return; send_nack: eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE); } static void eap_wsc_send_m7(struct eap_state *eap, const uint8_t *m6_pdu, size_t m6_len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m7_encrypted_settings m7es; struct wsc_m7 m7; uint8_t *pdu; size_t pdu_len; /* 20 for SNonce, 12 for Authenticator, 16 for IV + up to 16 pad */ uint8_t encrypted[64]; size_t encrypted_len; bool r; memcpy(m7es.e_snonce2, wsc->local_snonce2, sizeof(wsc->local_snonce2)); pdu = wsc_build_m7_encrypted_settings(&m7es, &pdu_len); explicit_bzero(m7es.e_snonce2, sizeof(wsc->local_snonce2)); if (!pdu) return; keywrap_authenticator_put(wsc, pdu, pdu_len); r = encrypted_settings_encrypt(wsc, wsc->iv2, pdu, pdu_len, encrypted, &encrypted_len); explicit_bzero(pdu, pdu_len); l_free(pdu); if (!r) return; m7.version2 = true; memcpy(m7.registrar_nonce, wsc->m2->registrar_nonce, sizeof(m7.registrar_nonce)); pdu = wsc_build_m7(&m7, encrypted, encrypted_len, &pdu_len); if (!pdu) return; authenticator_put(wsc, m6_pdu, m6_len, pdu, pdu_len); eap_wsc_send_message(eap, pdu, pdu_len); wsc->state = STATE_EXPECT_M8; } static void eap_wsc_handle_m6(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m6 m6; struct iovec encrypted; uint8_t *decrypted; size_t decrypted_len; struct wsc_m6_encrypted_settings m6es; enum wsc_configuration_error error = WSC_CONFIGURATION_ERROR_NO_ERROR; /* Spec unclear what to do here, see comments in eap_wsc_send_nack */ if (wsc_parse_m6(pdu, len, &m6, &encrypted) != 0) goto send_nack; if (memcmp(m6.enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(m6.enrollee_nonce))) return; if (!authenticator_check(wsc, pdu, len)) return; decrypted = encrypted_settings_decrypt(wsc, encrypted.iov_base, encrypted.iov_len, &decrypted_len); if (!decrypted) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto send_nack; } if (wsc_parse_m6_encrypted_settings(decrypted, decrypted_len, &m6es)) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto cleanup; } if (!keywrap_authenticator_check(wsc, decrypted, decrypted_len)) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto cleanup; } /* We now have R-SNonce2, verify R-Hash2 stored in eap_wsc_handle_m4 */ if (!x_hash_check(wsc, m6es.r_snonce2, wsc->psk2, wsc->r_hash2)) { error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE; goto cleanup; } eap_wsc_send_m7(eap, pdu, len); cleanup: explicit_bzero(&m6es, sizeof(m6es)); explicit_bzero(decrypted, decrypted_len); l_free(decrypted); if (!error) return; send_nack: eap_wsc_send_nack(eap, error); } static void eap_wsc_send_m5(struct eap_state *eap, const uint8_t *m4_pdu, size_t m4_len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m5_encrypted_settings m5es; struct wsc_m5 m5; uint8_t *pdu; size_t pdu_len; /* 20 for SNonce, 12 for Authenticator, 16 for IV + up to 16 pad */ uint8_t encrypted[64]; size_t encrypted_len; bool r; memcpy(m5es.e_snonce1, wsc->local_snonce1, sizeof(wsc->local_snonce1)); pdu = wsc_build_m5_encrypted_settings(&m5es, &pdu_len); explicit_bzero(m5es.e_snonce1, sizeof(wsc->local_snonce1)); if (!pdu) return; keywrap_authenticator_put(wsc, pdu, pdu_len); r = encrypted_settings_encrypt(wsc, wsc->iv1, pdu, pdu_len, encrypted, &encrypted_len); explicit_bzero(pdu, pdu_len); l_free(pdu); if (!r) return; m5.version2 = true; memcpy(m5.registrar_nonce, wsc->m2->registrar_nonce, sizeof(m5.registrar_nonce)); pdu = wsc_build_m5(&m5, encrypted, encrypted_len, &pdu_len); if (!pdu) return; authenticator_put(wsc, m4_pdu, m4_len, pdu, pdu_len); eap_wsc_send_message(eap, pdu, pdu_len); wsc->state = STATE_EXPECT_M6; } static void eap_wsc_handle_m4(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m4 m4; struct iovec encrypted; uint8_t *decrypted; size_t decrypted_len; struct wsc_m4_encrypted_settings m4es; enum wsc_configuration_error error = WSC_CONFIGURATION_ERROR_NO_ERROR; /* Spec unclear what to do here, see comments in eap_wsc_send_nack */ if (wsc_parse_m4(pdu, len, &m4, &encrypted) != 0) goto send_nack; if (memcmp(m4.enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(m4.enrollee_nonce))) return; if (!authenticator_check(wsc, pdu, len)) return; decrypted = encrypted_settings_decrypt(wsc, encrypted.iov_base, encrypted.iov_len, &decrypted_len); if (!decrypted) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto send_nack; } if (wsc_parse_m4_encrypted_settings(decrypted, decrypted_len, &m4es)) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto cleanup; } if (!keywrap_authenticator_check(wsc, decrypted, decrypted_len)) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto cleanup; } /* Since we have obtained R-SNonce1, we can now verify 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; } /* Now store R_Hash2 so we can verify it when we receive M6 */ memcpy(wsc->r_hash2, m4.r_hash2, sizeof(m4.r_hash2)); eap_wsc_send_m5(eap, pdu, len); cleanup: explicit_bzero(&m4es, sizeof(m4es)); explicit_bzero(decrypted, decrypted_len); l_free(decrypted); if (!error) return; send_nack: eap_wsc_send_nack(eap, error); } static void eap_wsc_send_m3(struct eap_state *eap, const uint8_t *m2_pdu, size_t m2_len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m2 *m2 = wsc->m2; size_t len; size_t len_half1; struct wsc_m3 m3; struct iovec iov[4]; uint8_t *pdu; size_t pdu_len; len = strlen(wsc->device_password); /* WSC 2.0.5, Section 7.4: * In case the UTF8 representation of the DevicePassword length is an * odd number (N), the first half of DevicePassword will have length * of N/2+1 and the second half of the DevicePassword will have length * of N/2. */ len_half1 = len / 2; if ((len % 2) == 1) len_half1 += 1; l_checksum_update(wsc->hmac_auth_key, wsc->device_password, len_half1); l_checksum_get_digest(wsc->hmac_auth_key, wsc->psk1, sizeof(wsc->psk1)); l_checksum_update(wsc->hmac_auth_key, wsc->device_password + len_half1, len / 2); l_checksum_get_digest(wsc->hmac_auth_key, wsc->psk2, sizeof(wsc->psk2)); m3.version2 = true; memcpy(m3.registrar_nonce, m2->registrar_nonce, sizeof(m3.registrar_nonce)); /* 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) */ iov[0].iov_base = wsc->local_snonce1; iov[0].iov_len = sizeof(wsc->local_snonce1); iov[1].iov_base = wsc->psk1; iov[1].iov_len = sizeof(wsc->psk1); iov[2].iov_base = wsc->m1->public_key; iov[2].iov_len = sizeof(wsc->m1->public_key); iov[3].iov_base = m2->public_key; iov[3].iov_len = sizeof(m2->public_key); l_checksum_updatev(wsc->hmac_auth_key, iov, 4); l_checksum_get_digest(wsc->hmac_auth_key, m3.e_hash1, sizeof(m3.e_hash1)); iov[0].iov_base = wsc->local_snonce2; iov[0].iov_len = sizeof(wsc->local_snonce2); iov[1].iov_base = wsc->psk2; iov[1].iov_len = sizeof(wsc->psk2); l_checksum_updatev(wsc->hmac_auth_key, iov, 4); l_checksum_get_digest(wsc->hmac_auth_key, m3.e_hash2, sizeof(m3.e_hash2)); pdu = wsc_build_m3(&m3, &pdu_len); if (!pdu) return; authenticator_put(wsc, m2_pdu, m2_len, pdu, pdu_len); eap_wsc_send_message(eap, pdu, pdu_len); wsc->state = STATE_EXPECT_M4; } static void eap_wsc_handle_m2(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct l_key *remote_public; uint8_t shared_secret[192] = { 0 }; size_t shared_secret_len = sizeof(shared_secret); struct l_checksum *sha256; uint8_t dhkey[32]; struct l_checksum *hmac_sha256; struct iovec iov[3]; uint8_t kdk[32]; struct wsc_session_key keys; bool r; /* TODO: Check to see if message is M2D first */ if (!wsc->m2) wsc->m2 = l_new(struct wsc_m2, 1); /* Spec unclear what to do here, see comments in eap_wsc_send_nack */ if (wsc_parse_m2(pdu, len, wsc->m2) != 0) { eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR); return; } if (memcmp(wsc->m2->enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(wsc->m2->enrollee_nonce))) return; if (!l_key_validate_dh_payload(wsc->m2->public_key, sizeof(wsc->m2->public_key), crypto_dh5_prime, crypto_dh5_prime_size)) return; remote_public = l_key_new(L_KEY_RAW, wsc->m2->public_key, sizeof(wsc->m2->public_key)); if (!remote_public) return; r = l_key_compute_dh_secret(remote_public, wsc->private, dh5_prime, shared_secret, &shared_secret_len); l_key_free(remote_public); if (!r) return; sha256 = l_checksum_new(L_CHECKSUM_SHA256); if (!sha256) return; l_checksum_update(sha256, shared_secret, shared_secret_len); l_checksum_get_digest(sha256, dhkey, sizeof(dhkey)); l_checksum_free(sha256); explicit_bzero(shared_secret, shared_secret_len); hmac_sha256 = l_checksum_new_hmac(L_CHECKSUM_SHA256, dhkey, sizeof(dhkey)); explicit_bzero(dhkey, sizeof(dhkey)); if (!hmac_sha256) return; iov[0].iov_base = wsc->m1->enrollee_nonce; iov[0].iov_len = 16; iov[1].iov_base = wsc->m1->addr; iov[1].iov_len = 6; iov[2].iov_base = wsc->m2->registrar_nonce; iov[2].iov_len = 16; l_checksum_updatev(hmac_sha256, iov, 3); l_checksum_get_digest(hmac_sha256, kdk, sizeof(kdk)); l_checksum_free(hmac_sha256); r = wsc_kdf(kdk, &keys, sizeof(keys)); explicit_bzero(kdk, sizeof(kdk)); if (!r) return; wsc->hmac_auth_key = l_checksum_new_hmac(L_CHECKSUM_SHA256, keys.auth_key, sizeof(keys.auth_key)); if (!authenticator_check(wsc, pdu, len)) { l_checksum_free(wsc->hmac_auth_key); wsc->hmac_auth_key = NULL; goto clear_keys; } /* Everything checks out, lets build M3 */ eap_wsc_send_m3(eap, pdu, len); /* * AuthKey is uploaded into the kernel, once we upload KeyWrapKey, * the keys variable is no longer useful. Make sure to wipe it */ wsc->aes_cbc_128 = l_cipher_new(L_CIPHER_AES_CBC, keys.keywrap_key, sizeof(keys.keywrap_key)); clear_keys: explicit_bzero(&keys, sizeof(keys)); } static void eap_wsc_r_send_m8(struct eap_state *eap, const uint8_t *m7_pdu, size_t m7_len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m8_encrypted_settings m8es = {}; struct wsc_m8 m8; uint8_t *pdu; size_t pdu_len; /* At least: 2 * credential, 12 for Authenticator, 16 for IV + up to 16 pad */ uint8_t encrypted[1024]; size_t encrypted_len; bool r; struct wsc_credential creds[2]; unsigned int creds_cnt = 0; unsigned int auth_types = wsc->m1->auth_type_flags & wsc->m2->auth_type_flags; if (auth_types & WSC_AUTHENTICATION_TYPE_OPEN) memcpy(&creds[creds_cnt++], &wsc->open_cred, sizeof(struct wsc_credential)); if (auth_types & WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL) memcpy(&creds[creds_cnt++], &wsc->wpa2_cred, sizeof(struct wsc_credential)); pdu = wsc_build_m8_encrypted_settings(&m8es, creds, creds_cnt, &pdu_len); explicit_bzero(creds, creds_cnt * sizeof(struct wsc_credential)); if (!pdu) return; keywrap_authenticator_put(wsc, pdu, pdu_len); r = encrypted_settings_encrypt(wsc, wsc->iv3, pdu, pdu_len, encrypted, &encrypted_len); explicit_bzero(pdu, pdu_len); l_free(pdu); if (!r) return; m8.version2 = true; memcpy(m8.enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(m8.enrollee_nonce)); pdu = wsc_build_m8(&m8, encrypted, encrypted_len, &pdu_len); if (!pdu) return; authenticator_put(wsc, m7_pdu, m7_len, pdu, pdu_len); eap_wsc_send_message(eap, pdu, pdu_len); wsc->state = STATE_EXPECT_DONE; } static void eap_wsc_r_handle_m7(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m7 m7; struct iovec encrypted; uint8_t *decrypted; size_t decrypted_len; struct wsc_m7_encrypted_settings m7es; enum wsc_configuration_error error = WSC_CONFIGURATION_ERROR_NO_ERROR; /* Spec unclear what to do here, see comments in eap_wsc_send_nack */ if (wsc_parse_m7(pdu, len, &m7, &encrypted) != 0) goto send_nack; if (memcmp(m7.registrar_nonce, wsc->m2->registrar_nonce, sizeof(m7.registrar_nonce))) goto send_nack; if (!authenticator_check(wsc, pdu, len)) return; decrypted = encrypted_settings_decrypt(wsc, encrypted.iov_base, encrypted.iov_len, &decrypted_len); if (!decrypted) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto send_nack; } if (wsc_parse_m7_encrypted_settings(decrypted, decrypted_len, &m7es)) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto cleanup; } if (!keywrap_authenticator_check(wsc, decrypted, decrypted_len)) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto cleanup; } /* We have E-SNonce2, verify E-Hash2 stored in eap_wsc_r_handle_m3 */ if (!x_hash_check(wsc, m7es.e_snonce2, wsc->psk2, wsc->e_hash2)) { error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE; goto cleanup; } eap_wsc_r_send_m8(eap, pdu, len); cleanup: explicit_bzero(&m7es, sizeof(m7es)); explicit_bzero(decrypted, decrypted_len); l_free(decrypted); if (!error) return; send_nack: eap_wsc_send_nack(eap, error); } static void eap_wsc_r_send_m6(struct eap_state *eap, const uint8_t *m5_pdu, size_t m5_len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m6_encrypted_settings m6es; struct wsc_m6 m6; uint8_t *pdu; size_t pdu_len; /* 20 for SNonce, 12 for Authenticator, 16 for IV + up to 16 pad */ uint8_t encrypted[64]; size_t encrypted_len; bool r; memcpy(m6es.r_snonce2, wsc->local_snonce2, sizeof(wsc->local_snonce2)); pdu = wsc_build_m6_encrypted_settings(&m6es, &pdu_len); explicit_bzero(m6es.r_snonce2, sizeof(wsc->local_snonce2)); if (!pdu) return; keywrap_authenticator_put(wsc, pdu, pdu_len); r = encrypted_settings_encrypt(wsc, wsc->iv2, pdu, pdu_len, encrypted, &encrypted_len); explicit_bzero(pdu, pdu_len); l_free(pdu); if (!r) return; m6.version2 = true; memcpy(m6.enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(m6.enrollee_nonce)); pdu = wsc_build_m6(&m6, encrypted, encrypted_len, &pdu_len); if (!pdu) return; authenticator_put(wsc, m5_pdu, m5_len, pdu, pdu_len); eap_wsc_send_message(eap, pdu, pdu_len); wsc->state = STATE_EXPECT_M7; } static void eap_wsc_r_handle_m5(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m5 m5; struct iovec encrypted; uint8_t *decrypted; size_t decrypted_len; struct wsc_m5_encrypted_settings m5es; enum wsc_configuration_error error = WSC_CONFIGURATION_ERROR_NO_ERROR; /* Spec unclear what to do here, see comments in eap_wsc_send_nack */ if (wsc_parse_m5(pdu, len, &m5, &encrypted) != 0) goto send_nack; if (memcmp(m5.registrar_nonce, wsc->m2->registrar_nonce, sizeof(m5.registrar_nonce))) goto send_nack; if (!authenticator_check(wsc, pdu, len)) return; decrypted = encrypted_settings_decrypt(wsc, encrypted.iov_base, encrypted.iov_len, &decrypted_len); if (!decrypted) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto send_nack; } if (wsc_parse_m5_encrypted_settings(decrypted, decrypted_len, &m5es)) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto cleanup; } if (!keywrap_authenticator_check(wsc, decrypted, decrypted_len)) { error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE; goto cleanup; } /* We have E-SNonce1, verify E-Hash1 stored in eap_wsc_r_handle_m3 */ if (!x_hash_check(wsc, m5es.e_snonce1, wsc->psk1, wsc->e_hash1)) { error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE; goto cleanup; } eap_wsc_r_send_m6(eap, pdu, len); cleanup: explicit_bzero(&m5es, sizeof(m5es)); explicit_bzero(decrypted, decrypted_len); l_free(decrypted); if (!error) return; send_nack: eap_wsc_send_nack(eap, error); } static void eap_wsc_r_send_m4(struct eap_state *eap, const uint8_t *m3_pdu, size_t m3_len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m4_encrypted_settings m4es; struct wsc_m4 m4; uint8_t *pdu; size_t pdu_len; /* 20 for SNonce, 12 for Authenticator, 16 for IV + up to 16 pad */ uint8_t encrypted[64]; size_t encrypted_len; bool r; size_t len; size_t len_half1; struct iovec iov[4]; memcpy(m4es.r_snonce1, wsc->local_snonce1, sizeof(wsc->local_snonce1)); pdu = wsc_build_m4_encrypted_settings(&m4es, &pdu_len); explicit_bzero(m4es.r_snonce1, sizeof(wsc->local_snonce1)); if (!pdu) return; keywrap_authenticator_put(wsc, pdu, pdu_len); r = encrypted_settings_encrypt(wsc, wsc->iv1, pdu, pdu_len, encrypted, &encrypted_len); explicit_bzero(pdu, pdu_len); l_free(pdu); if (!r) return; len = strlen(wsc->device_password); /* WSC 2.0.5, Section 7.4: * In case the UTF8 representation of the DevicePassword length is an * odd number (N), the first half of DevicePassword will have length * of N/2+1 and the second half of the DevicePassword will have length * of N/2. */ len_half1 = (len + 1) / 2; l_checksum_update(wsc->hmac_auth_key, wsc->device_password, len_half1); l_checksum_get_digest(wsc->hmac_auth_key, wsc->psk1, sizeof(wsc->psk1)); l_checksum_update(wsc->hmac_auth_key, wsc->device_password + len_half1, len / 2); l_checksum_get_digest(wsc->hmac_auth_key, wsc->psk2, sizeof(wsc->psk2)); m4.version2 = true; memcpy(m4.enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(m4.enrollee_nonce)); /* WSC 2.0.5, Section 7.4: * 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 = wsc->local_snonce1; iov[0].iov_len = sizeof(wsc->local_snonce1); iov[1].iov_base = wsc->psk1; iov[1].iov_len = sizeof(wsc->psk1); iov[2].iov_base = wsc->m1->public_key; iov[2].iov_len = sizeof(wsc->m1->public_key); 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, m4.r_hash1, sizeof(m4.r_hash1)); iov[0].iov_base = wsc->local_snonce2; iov[0].iov_len = sizeof(wsc->local_snonce2); iov[1].iov_base = wsc->psk2; iov[1].iov_len = sizeof(wsc->psk2); l_checksum_updatev(wsc->hmac_auth_key, iov, 4); l_checksum_get_digest(wsc->hmac_auth_key, m4.r_hash2, sizeof(m4.r_hash2)); pdu = wsc_build_m4(&m4, encrypted, encrypted_len, &pdu_len); if (!pdu) return; authenticator_put(wsc, m3_pdu, m3_len, pdu, pdu_len); eap_wsc_send_message(eap, pdu, pdu_len); wsc->state = STATE_EXPECT_M5; } static void eap_wsc_r_handle_m3(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_m3 m3; if (wsc_parse_m3(pdu, len, &m3) != 0) goto send_nack; if (memcmp(m3.registrar_nonce, wsc->m2->registrar_nonce, sizeof(m3.registrar_nonce))) goto send_nack; if (!authenticator_check(wsc, pdu, len)) return; /* We can't verify these until we receive the E-S1/E-S2 in M5/M7 */ memcpy(wsc->e_hash1, m3.e_hash1, sizeof(m3.e_hash1)); memcpy(wsc->e_hash2, m3.e_hash2, sizeof(m3.e_hash2)); eap_wsc_r_send_m4(eap, pdu, len); return; send_nack: /* Spec unclear what to do here, see comments in eap_wsc_send_nack */ eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR); } static void eap_wsc_r_send_m2(struct eap_state *eap, const uint8_t *m1_pdu, size_t m1_len) { struct eap_wsc_state *wsc = eap_get_data(eap); uint8_t *pdu; size_t pdu_len; wsc->m2->version2 = true; memcpy(wsc->m2->enrollee_nonce, wsc->m1->enrollee_nonce, 16); wsc->m2->association_state = WSC_ASSOCIATION_STATE_NOT_ASSOCIATED; wsc->m2->configuration_error = WSC_CONFIGURATION_ERROR_NO_ERROR; pdu = wsc_build_m2(wsc->m2, &pdu_len); if (!pdu) return; authenticator_put(wsc, m1_pdu, m1_len, pdu, pdu_len); eap_wsc_send_message(eap, pdu, pdu_len); wsc->state = STATE_EXPECT_M3; } static void eap_wsc_r_handle_m1(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct l_key *remote_public; uint8_t shared_secret[192] = { 0 }; size_t shared_secret_len = sizeof(shared_secret); struct l_checksum *sha256; uint8_t dhkey[32]; struct l_checksum *hmac_sha256; struct iovec iov[3]; uint8_t kdk[32]; struct wsc_session_key keys; bool r; uint8_t expected_enrollee_mac[6]; uint8_t expected_uuid_e[16]; memcpy(expected_enrollee_mac, wsc->m1->addr, 6); memcpy(expected_uuid_e, wsc->m1->uuid_e, 16); /* Spec unclear what to do here, see comments in eap_wsc_send_nack */ if (wsc_parse_m1(pdu, len, wsc->m1) != 0) { eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR); return; } if (memcmp(expected_enrollee_mac, wsc->m1->addr, 6) || memcmp(expected_uuid_e, wsc->m1->uuid_e, 16)) { eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_MULTIPLE_PBC_SESSIONS_DETECTED); eap_method_error(eap); return; } if (wsc->m1->state != WSC_STATE_NOT_CONFIGURED || !(wsc->m1->auth_type_flags & wsc->m2->auth_type_flags) || !(wsc->m1->encryption_type_flags & wsc->m2->encryption_type_flags) || !(wsc->m1->connection_type_flags & wsc->m2->connection_type_flags) || !(wsc->m1->config_methods & wsc->m2->config_methods) || !(wsc->m1->rf_bands & wsc->m2->rf_bands)) { eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR); return; } if (wsc->m1->configuration_error != WSC_CONFIGURATION_ERROR_NO_ERROR || wsc->m1->device_password_id != wsc->m2->device_password_id) { eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR); return; } /* m1->request_to_enroll not checked */ if (!l_key_validate_dh_payload(wsc->m1->public_key, sizeof(wsc->m1->public_key), crypto_dh5_prime, crypto_dh5_prime_size)) return; /* * Done validating the M1, we can now derive the keys for most of * the rest of the registration protocol. */ remote_public = l_key_new(L_KEY_RAW, wsc->m1->public_key, sizeof(wsc->m1->public_key)); if (!remote_public) return; r = l_key_compute_dh_secret(remote_public, wsc->private, dh5_prime, shared_secret, &shared_secret_len); l_key_free(remote_public); if (!r) return; sha256 = l_checksum_new(L_CHECKSUM_SHA256); if (!sha256) return; l_checksum_update(sha256, shared_secret, shared_secret_len); explicit_bzero(shared_secret, shared_secret_len); l_checksum_get_digest(sha256, dhkey, sizeof(dhkey)); l_checksum_free(sha256); hmac_sha256 = l_checksum_new_hmac(L_CHECKSUM_SHA256, dhkey, sizeof(dhkey)); explicit_bzero(dhkey, sizeof(dhkey)); if (!hmac_sha256) return; iov[0].iov_base = wsc->m1->enrollee_nonce; iov[0].iov_len = 16; iov[1].iov_base = wsc->m1->addr; iov[1].iov_len = 6; iov[2].iov_base = wsc->m2->registrar_nonce; iov[2].iov_len = 16; l_checksum_updatev(hmac_sha256, iov, 3); l_checksum_get_digest(hmac_sha256, kdk, sizeof(kdk)); l_checksum_free(hmac_sha256); r = wsc_kdf(kdk, &keys, sizeof(keys)); explicit_bzero(kdk, sizeof(kdk)); if (!r) return; wsc->hmac_auth_key = l_checksum_new_hmac(L_CHECKSUM_SHA256, keys.auth_key, sizeof(keys.auth_key)); /* * AuthKey is uploaded into the kernel, once we upload KeyWrapKey, * the keys variable is no longer useful. Make sure to wipe it */ wsc->aes_cbc_128 = l_cipher_new(L_CIPHER_AES_CBC, keys.keywrap_key, sizeof(keys.keywrap_key)); explicit_bzero(&keys, sizeof(keys)); eap_wsc_r_send_m2(eap, pdu, len); } static void eap_wsc_handle_nack(struct eap_state *eap, const uint8_t *pdu, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); struct wsc_nack nack; if (wsc_parse_wsc_nack(pdu, len, &nack) != 0) return; if (wsc->state != STATE_EXPECT_M1 && memcmp(nack.enrollee_nonce, wsc->m1->enrollee_nonce, sizeof(nack.enrollee_nonce))) return; if (!wsc->m2) return; if (memcmp(nack.registrar_nonce, wsc->m2->registrar_nonce, sizeof(nack.registrar_nonce))) return; /* * The spec is completely unclear what the NACK error should be set * 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_message(struct eap_state *eap, const uint8_t *pkt, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); uint8_t op; uint8_t flags; uint8_t *pdu; 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; op = pkt[0]; flags = pkt[1]; pkt += 2; len -= 2; switch (op) { case WSC_OP_START: if (len) return; if (wsc->state != STATE_EXPECT_START) return; pdu = wsc_build_m1(wsc->m1, &pdu_len); if (!pdu) return; eap_wsc_send_message(eap, pdu, pdu_len); wsc->state = STATE_EXPECT_M2; return; case WSC_OP_NACK: if (!len) return; 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: 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 && (wsc->tx_frag_offset + wsc->tx_last_frag_len) < wsc->sent_len) { wsc->tx_frag_offset += wsc->tx_last_frag_len; wsc->tx_last_frag_len = 0; eap_wsc_send_fragment(eap); } return; case WSC_OP_MSG: if (flags & WSC_FLAG_LF) { if (wsc->rx_pdu_buf || !(flags & WSC_FLAG_MF) || len < 2) goto invalid_frag; wsc->rx_pdu_buf_len = l_get_be16(pkt); if (!wsc->rx_pdu_buf_len || wsc->rx_pdu_buf_len > EAP_WSC_PDU_MAX_LEN) { l_warn("Fragmented pkt size is outside of " "allowed boundaries [1, %u]", EAP_WSC_PDU_MAX_LEN); return; } if (wsc->rx_pdu_buf_len < len) { l_warn("Fragmented pkt size is smaller than " "the received packet"); return; } wsc->rx_pdu_buf = l_malloc(wsc->rx_pdu_buf_len); wsc->rx_pdu_buf_offset = 0; rx_header_offset = 2; } if (wsc->rx_pdu_buf) { pdu_len = len - rx_header_offset; if (wsc->rx_pdu_buf_len < (wsc->rx_pdu_buf_offset + pdu_len)) { l_error("Request fragment pkt size mismatch"); goto invalid_frag; } memcpy(wsc->rx_pdu_buf + wsc->rx_pdu_buf_offset, pkt + rx_header_offset, pdu_len); wsc->rx_pdu_buf_offset += pdu_len; } if (flags & WSC_FLAG_MF) { 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("Message fragment pkt size mismatch"); goto invalid_frag; } pkt = wsc->rx_pdu_buf; len = wsc->rx_pdu_buf_len; } break; } if (!len) return; switch (wsc->state) { /* Enrollee state machine */ case STATE_EXPECT_START: return; case STATE_EXPECT_M2: eap_wsc_handle_m2(eap, pkt, len); break; case STATE_EXPECT_M4: eap_wsc_handle_m4(eap, pkt, len); break; case STATE_EXPECT_M6: eap_wsc_handle_m6(eap, pkt, len); break; case STATE_EXPECT_M8: eap_wsc_handle_m8(eap, pkt, len); break; case STATE_FINISHED: eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR); return; /* Registrar state machine */ case STATE_EXPECT_M1: eap_wsc_r_handle_m1(eap, pkt, len); break; case STATE_EXPECT_M3: eap_wsc_r_handle_m3(eap, pkt, len); break; case STATE_EXPECT_M5: eap_wsc_r_handle_m5(eap, pkt, len); break; case STATE_EXPECT_M7: eap_wsc_r_handle_m7(eap, pkt, len); break; case STATE_EXPECT_IDENTITY: case STATE_EXPECT_DONE: eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR); return; } if (wsc->rx_pdu_buf) { l_free(wsc->rx_pdu_buf); wsc->rx_pdu_buf = NULL; wsc->rx_pdu_buf_len = 0; wsc->rx_pdu_buf_offset = 0; } return; invalid_frag: eap_method_error(eap); } static void eap_wsc_handle_retransmit(struct eap_state *eap, const uint8_t *pkt, size_t len) { struct eap_wsc_state *wsc = eap_get_data(eap); uint8_t op; uint8_t flags; if (len < 2) return; op = pkt[0]; switch (op) { case WSC_OP_NACK: eap_wsc_handle_nack(eap, pkt + 2, len - 2); return; case WSC_OP_ACK: case WSC_OP_DONE: /* Should never receive these as Enrollee */ return; case WSC_OP_MSG: flags = pkt[1]; if (flags & WSC_FLAG_MF) { if (!wsc->rx_pdu_buf) { eap_method_error(eap); return; } eap_wsc_send_frag_ack(eap); return; } } if (!wsc->sent_pdu || !wsc->sent_len) { eap_method_error(eap); return; } if (wsc->sent_len + EAP_WSC_HEADER_LEN > eap_get_mtu(eap)) { eap_wsc_send_fragment(eap); } else { size_t msg_len = wsc->sent_len + EAP_WSC_HEADER_LEN; uint8_t buf[msg_len]; buf[12] = WSC_OP_MSG; buf[13] = 0; memcpy(buf + EAP_WSC_HEADER_LEN, wsc->sent_pdu, wsc->sent_len); eap_method_respond(eap, buf, msg_len); } } static bool load_hexencoded(struct l_settings *settings, const char *key, uint8_t *to, size_t len) { uint8_t *v; size_t v_len; v = l_settings_get_bytes(settings, "WSC", key, &v_len); if (!v) return false; if (v_len != len) { explicit_bzero(v, v_len); l_free(v); return false; } memcpy(to, v, len); explicit_bzero(v, v_len); l_free(v); return true; } static bool load_primary_device_type(struct l_settings *settings, struct wsc_primary_device_type *pdt) { const char *v; int r; v = l_settings_get_value(settings, "WSC", "PrimaryDeviceType"); if (!v) return false; r = sscanf(v, "%hx-%2hhx%2hhx%2hhx%2hhx-%2hx", &pdt->category, &pdt->oui[0], &pdt->oui[1], &pdt->oui[2], &pdt->oui_type, &pdt->subcategory); if (r != 6) return false; return true; } static bool load_constrained_string(struct l_settings *settings, const char *key, char *out, size_t max) { char *v; size_t tocopy; v = l_settings_get_string(settings, "WSC", key); if (!v) return false; tocopy = strlen(v); if (tocopy >= max) tocopy = max - 1; memcpy(out, v, tocopy); out[max - 1] = '\0'; l_free(v); return true; } static bool load_device_data(struct l_settings *settings, char *manufacturer, char *model_name, char *model_number, char *serial_number, struct wsc_primary_device_type *dev_type, char *device_name, uint8_t *rf_bands, uint32_t *os_version) { struct wsc_m1 *m1; unsigned int u32; if (!load_constrained_string(settings, "Manufacturer", manufacturer, sizeof(m1->manufacturer))) strcpy(manufacturer, " "); if (!load_constrained_string(settings, "ModelName", model_name, sizeof(m1->model_name))) strcpy(model_name, " "); if (!load_constrained_string(settings, "ModelNumber", model_number, sizeof(m1->model_number))) strcpy(model_number, " "); if (!load_constrained_string(settings, "SerialNumber", serial_number, sizeof(m1->serial_number))) strcpy(serial_number, " "); if (!load_primary_device_type(settings, dev_type)) { /* Make ourselves a WFA standard PC by default */ dev_type->category = 1; memcpy(dev_type->oui, wsc_wfa_oui, 3); dev_type->oui_type = 0x04; dev_type->subcategory = 1; } if (!load_constrained_string(settings, "DeviceName", device_name, sizeof(m1->device_name))) strcpy(device_name, " "); if (!l_settings_get_uint(settings, "WSC", "RFBand", &u32)) return false; switch (u32) { case WSC_RF_BAND_2_4_GHZ: case WSC_RF_BAND_5_0_GHZ: case WSC_RF_BAND_60_GHZ: *rf_bands = u32; break; default: return false; } if (!l_settings_get_uint(settings, "WSC", "OSVersion", &u32)) u32 = 0; *os_version = u32 & 0x7fffffff; return true; } static struct eap_wsc_state *eap_wsc_new_common(struct l_settings *settings, bool registrar) { struct eap_wsc_state *wsc; const char *v; uint8_t private_key[192]; size_t len; unsigned int u32; wsc = l_new(struct eap_wsc_state, 1); wsc->registrar = registrar; wsc->m1 = l_new(struct wsc_m1, 1); if (registrar) wsc->m2 = l_new(struct wsc_m2, 1); v = l_settings_get_value(settings, "WSC", "EnrolleeMAC"); if (!v) goto err; if (!util_string_to_address(v, wsc->m1->addr)) goto err; if (!l_settings_get_uint(settings, "WSC", "ConfigurationMethods", &u32)) u32 = WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN; if (!registrar) wsc->m1->config_methods = u32; else wsc->m2->config_methods = u32; if (!l_settings_get_uint(settings, "WSC", "DevicePasswordId", &u32)) u32 = WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON; if (!registrar) wsc->m1->device_password_id = u32; else wsc->m2->device_password_id = u32; wsc->device_password = l_settings_get_string(settings, "WSC", "DevicePassword"); if (wsc->device_password) { int i; for (i = 0; wsc->device_password[i]; i++) { if (!l_ascii_isxdigit(wsc->device_password[i])) goto err; } /* * WSC 2.0.5: Section 7.4: * If an out-of-band mechanism is used as the configuration * method, the device password is expressed in hexadecimal * using ASCII character (two characters per octet, uppercase * letters only). */ for (i = 0; wsc->device_password[i]; i++) { if (wsc->device_password[i] >= 'a' && wsc->device_password[i] <= 'f') wsc->device_password[i] = 'A' + wsc->device_password[i] - 'a'; } } else wsc->device_password = l_strdup("00000000"); /* * The settings we read below probably shouldn't be used except to * eliminate randomness in debugging/tests. */ if (load_hexencoded(settings, "PrivateKey", private_key, 192)) { if (!l_key_validate_dh_payload(private_key, 192, crypto_dh5_prime, crypto_dh5_prime_size)) { explicit_bzero(private_key, 192); goto err; } wsc->private = l_key_new(L_KEY_RAW, private_key, 192); explicit_bzero(private_key, 192); } else wsc->private = l_key_generate_dh_private(crypto_dh5_prime, crypto_dh5_prime_size); if (!wsc->private) goto err; len = sizeof(wsc->m1->public_key); if (!l_key_compute_dh_public(dh5_generator, wsc->private, dh5_prime, registrar ? wsc->m2->public_key : wsc->m1->public_key, &len)) goto err; if (len != sizeof(wsc->m1->public_key)) goto err; if (!load_hexencoded(settings, registrar ? "R-SNonce1" : "E-SNonce1", wsc->local_snonce1, 16)) l_getrandom(wsc->local_snonce1, 16); if (!load_hexencoded(settings, registrar ? "R-SNonce2" : "E-SNonce2", wsc->local_snonce2, 16)) l_getrandom(wsc->local_snonce2, 16); if (!load_hexencoded(settings, "IV1", wsc->iv1, 16)) l_getrandom(wsc->iv1, 16); if (!load_hexencoded(settings, "IV2", wsc->iv2, 16)) l_getrandom(wsc->iv2, 16); return wsc; err: eap_wsc_state_free(wsc); return NULL; } static bool eap_wsc_load_settings(struct eap_state *eap, struct l_settings *settings, const char *prefix) { struct eap_wsc_state *wsc = eap_wsc_new_common(settings, false); if (!wsc) return false; wsc->m1->version2 = true; wsc->m1->state = WSC_STATE_NOT_CONFIGURED; wsc->m1->association_state = WSC_ASSOCIATION_STATE_NOT_ASSOCIATED; wsc->m1->configuration_error = WSC_CONFIGURATION_ERROR_NO_ERROR; wsc->m1->auth_type_flags = WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL | WSC_AUTHENTICATION_TYPE_WPA_PERSONAL | WSC_AUTHENTICATION_TYPE_OPEN; wsc->m1->encryption_type_flags = WSC_ENCRYPTION_TYPE_NONE | WSC_ENCRYPTION_TYPE_AES_TKIP; wsc->m1->connection_type_flags = WSC_CONNECTION_TYPE_ESS; if (!wsc_uuid_from_addr(wsc->m1->addr, wsc->m1->uuid_e)) goto err; if (!load_device_data(settings, wsc->m1->manufacturer, wsc->m1->model_name, wsc->m1->model_number, wsc->m1->serial_number, &wsc->m1->primary_device_type, wsc->m1->device_name, &wsc->m1->rf_bands, &wsc->m1->os_version)) goto err; /* Debug settings */ if (!load_hexencoded(settings, "EnrolleeNonce", wsc->m1->enrollee_nonce, 16)) l_getrandom(wsc->m1->enrollee_nonce, 16); wsc->state = STATE_EXPECT_START; eap_set_data(eap, wsc); return true; err: eap_wsc_state_free(wsc); return false; } static struct eap_method eap_wsc = { .vendor_id = { 0x00, 0x37, 0x2a }, .vendor_type = 0x00000001, .request_type = EAP_TYPE_EXPANDED, .exports_msk = true, .name = "WSC", .free = eap_wsc_free, .handle_request = eap_wsc_handle_message, .handle_retransmit = eap_wsc_handle_retransmit, .load_settings = eap_wsc_load_settings, }; static bool eap_wsc_r_load_settings(struct eap_state *eap, struct l_settings *settings, const char *prefix) { /* * On the Registrar we store some information in wsc->m1 for the * actual M1's validation, and we fill in the elements of M2 * that don't depend on the data received in M1. */ struct eap_wsc_state *wsc = eap_wsc_new_common(settings, true); char *str; if (!wsc) return false; if (!load_hexencoded(settings, "UUID-E", wsc->m1->uuid_e, 16)) goto err; if (!load_hexencoded(settings, "UUID-R", wsc->m2->uuid_r, 16)) goto err; if (!load_device_data(settings, wsc->m2->manufacturer, wsc->m2->model_name, wsc->m2->model_number, wsc->m2->serial_number, &wsc->m2->primary_device_type, wsc->m2->device_name, &wsc->m2->rf_bands, &wsc->m2->os_version)) goto err; wsc->m2->auth_type_flags = 0; str = l_settings_get_string(settings, "WSC", "WPA2-SSID"); if (str) { if (strlen(str) > 32) goto bad_string; wsc->m2->auth_type_flags |= WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL; wsc->m2->encryption_type_flags |= WSC_ENCRYPTION_TYPE_AES_TKIP; wsc->wpa2_cred.auth_type = WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL; wsc->wpa2_cred.encryption_type = WSC_ENCRYPTION_TYPE_AES_TKIP; wsc->wpa2_cred.ssid_len = strlen(str); memcpy(wsc->wpa2_cred.ssid, str, wsc->wpa2_cred.ssid_len); l_free(str); str = l_settings_get_string(settings, "WSC", "WPA2-Passphrase"); if (str) { size_t len = strlen(str); if (len < 8 || len > 63) { explicit_bzero(str, len); goto bad_string; } memcpy(wsc->wpa2_cred.network_key, str, len); wsc->wpa2_cred.network_key_len = len; explicit_bzero(str, len); l_free(str); } else { uint8_t buf[32]; if (!load_hexencoded(settings, "WPA2-PSK", buf, 32)) goto err; str = l_util_hexstring(buf, 32); explicit_bzero(buf, 32); memcpy(wsc->wpa2_cred.network_key, str, 64); explicit_bzero(str, 64); free(str); wsc->wpa2_cred.network_key_len = 64; } memcpy(wsc->wpa2_cred.addr, wsc->m1->addr, 6); } str = l_settings_get_string(settings, "WSC", "Open-SSID"); if (str) { if (strlen(str) > 32) goto bad_string; wsc->m2->auth_type_flags |= WSC_AUTHENTICATION_TYPE_OPEN; wsc->m2->encryption_type_flags |= WSC_ENCRYPTION_TYPE_NONE; wsc->open_cred.auth_type = WSC_AUTHENTICATION_TYPE_OPEN; wsc->open_cred.encryption_type = WSC_ENCRYPTION_TYPE_NONE; wsc->open_cred.ssid_len = strlen(str); memcpy(wsc->open_cred.ssid, str, wsc->open_cred.ssid_len); l_free(str); wsc->open_cred.network_key_len = 0; memcpy(wsc->open_cred.addr, wsc->m1->addr, 6); } if (!wsc->m2->auth_type_flags) goto err; wsc->m2->connection_type_flags = WSC_CONNECTION_TYPE_ESS; /* Debug settings */ if (!load_hexencoded(settings, "RegistrarNonce", wsc->m2->registrar_nonce, 16)) l_getrandom(wsc->m2->registrar_nonce, 16); if (!load_hexencoded(settings, "IV3", wsc->iv3, 16)) l_getrandom(wsc->iv3, 16); wsc->state = STATE_EXPECT_IDENTITY; eap_set_data(eap, wsc); return true; bad_string: l_free(str); err: eap_wsc_state_free(wsc); 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, .vendor_id = { 0x00, 0x37, 0x2a }, .vendor_type = 0x00000001, .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) { int r = -ENOTSUP; l_debug(""); dh5_generator = l_key_new(L_KEY_RAW, crypto_dh5_generator, crypto_dh5_generator_size); if (!dh5_generator) goto fail_generator; dh5_prime = l_key_new(L_KEY_RAW, crypto_dh5_prime, crypto_dh5_prime_size); if (!dh5_prime) goto fail_prime; r = eap_register_method(&eap_wsc); if (r) goto fail_register; r = eap_register_method(&eap_wsc_r); if (!r) return 0; eap_unregister_method(&eap_wsc); fail_register: l_key_free(dh5_prime); dh5_prime = NULL; fail_prime: l_key_free(dh5_generator); dh5_generator = NULL; fail_generator: return r; } static void eap_wsc_exit(void) { l_debug(""); eap_unregister_method(&eap_wsc); eap_unregister_method(&eap_wsc_r); l_key_free(dh5_prime); l_key_free(dh5_generator); } EAP_METHOD_BUILTIN(eap_wsc, eap_wsc_init, eap_wsc_exit)