1252190Srpaulo/* 2252190Srpaulo * EAP peer method: EAP-pwd (RFC 5931) 3252190Srpaulo * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org> 4252190Srpaulo * 5252190Srpaulo * This software may be distributed under the terms of the BSD license. 6252190Srpaulo * See README for more details. 7252190Srpaulo */ 8252190Srpaulo 9252190Srpaulo#include "includes.h" 10252190Srpaulo 11252190Srpaulo#include "common.h" 12346981Scy#include "crypto/sha1.h" 13252190Srpaulo#include "crypto/sha256.h" 14346981Scy#include "crypto/sha512.h" 15289549Srpaulo#include "crypto/ms_funcs.h" 16346981Scy#include "crypto/crypto.h" 17252190Srpaulo#include "eap_peer/eap_i.h" 18252190Srpaulo#include "eap_common/eap_pwd_common.h" 19252190Srpaulo 20252190Srpaulo 21252190Srpaulostruct eap_pwd_data { 22252190Srpaulo enum { 23281806Srpaulo PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, 24281806Srpaulo SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE 25252190Srpaulo } state; 26252190Srpaulo u8 *id_peer; 27252190Srpaulo size_t id_peer_len; 28252190Srpaulo u8 *id_server; 29252190Srpaulo size_t id_server_len; 30252190Srpaulo u8 *password; 31252190Srpaulo size_t password_len; 32289549Srpaulo int password_hash; 33351611Scy struct wpa_freq_range_list allowed_groups; 34252190Srpaulo u16 group_num; 35346981Scy u8 prep; 36346981Scy u8 token[4]; 37252190Srpaulo EAP_PWD_group *grp; 38252190Srpaulo 39252190Srpaulo struct wpabuf *inbuf; 40252190Srpaulo size_t in_frag_pos; 41252190Srpaulo struct wpabuf *outbuf; 42252190Srpaulo size_t out_frag_pos; 43252190Srpaulo size_t mtu; 44252190Srpaulo 45346981Scy struct crypto_bignum *k; 46346981Scy struct crypto_bignum *private_value; 47346981Scy struct crypto_bignum *server_scalar; 48346981Scy struct crypto_bignum *my_scalar; 49346981Scy struct crypto_ec_point *my_element; 50346981Scy struct crypto_ec_point *server_element; 51252190Srpaulo 52252190Srpaulo u8 msk[EAP_MSK_LEN]; 53252190Srpaulo u8 emsk[EAP_EMSK_LEN]; 54281806Srpaulo u8 session_id[1 + SHA256_MAC_LEN]; 55252190Srpaulo}; 56252190Srpaulo 57252190Srpaulo 58351611Scystatic void eap_pwd_deinit(struct eap_sm *sm, void *priv); 59351611Scy 60351611Scy 61252190Srpaulo#ifndef CONFIG_NO_STDOUT_DEBUG 62252190Srpaulostatic const char * eap_pwd_state_txt(int state) 63252190Srpaulo{ 64252190Srpaulo switch (state) { 65252190Srpaulo case PWD_ID_Req: 66252190Srpaulo return "PWD-ID-Req"; 67252190Srpaulo case PWD_Commit_Req: 68252190Srpaulo return "PWD-Commit-Req"; 69252190Srpaulo case PWD_Confirm_Req: 70252190Srpaulo return "PWD-Confirm-Req"; 71281806Srpaulo case SUCCESS_ON_FRAG_COMPLETION: 72281806Srpaulo return "SUCCESS_ON_FRAG_COMPLETION"; 73252190Srpaulo case SUCCESS: 74252190Srpaulo return "SUCCESS"; 75252190Srpaulo case FAILURE: 76252190Srpaulo return "FAILURE"; 77252190Srpaulo default: 78252190Srpaulo return "PWD-UNK"; 79252190Srpaulo } 80252190Srpaulo} 81252190Srpaulo#endif /* CONFIG_NO_STDOUT_DEBUG */ 82252190Srpaulo 83252190Srpaulo 84252190Srpaulostatic void eap_pwd_state(struct eap_pwd_data *data, int state) 85252190Srpaulo{ 86252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s", 87252190Srpaulo eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); 88252190Srpaulo data->state = state; 89252190Srpaulo} 90252190Srpaulo 91252190Srpaulo 92252190Srpaulostatic void * eap_pwd_init(struct eap_sm *sm) 93252190Srpaulo{ 94252190Srpaulo struct eap_pwd_data *data; 95252190Srpaulo const u8 *identity, *password; 96252190Srpaulo size_t identity_len, password_len; 97281806Srpaulo int fragment_size; 98289549Srpaulo int pwhash; 99351611Scy const char *phase1; 100252190Srpaulo 101289549Srpaulo password = eap_get_config_password2(sm, &password_len, &pwhash); 102252190Srpaulo if (password == NULL) { 103252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); 104252190Srpaulo return NULL; 105252190Srpaulo } 106252190Srpaulo 107252190Srpaulo identity = eap_get_config_identity(sm, &identity_len); 108252190Srpaulo if (identity == NULL) { 109252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!"); 110252190Srpaulo return NULL; 111252190Srpaulo } 112252190Srpaulo 113252190Srpaulo if ((data = os_zalloc(sizeof(*data))) == NULL) { 114252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail"); 115252190Srpaulo return NULL; 116252190Srpaulo } 117252190Srpaulo 118252190Srpaulo if ((data->id_peer = os_malloc(identity_len)) == NULL) { 119252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); 120252190Srpaulo os_free(data); 121252190Srpaulo return NULL; 122252190Srpaulo } 123252190Srpaulo 124252190Srpaulo os_memcpy(data->id_peer, identity, identity_len); 125252190Srpaulo data->id_peer_len = identity_len; 126252190Srpaulo 127252190Srpaulo if ((data->password = os_malloc(password_len)) == NULL) { 128252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); 129281806Srpaulo bin_clear_free(data->id_peer, data->id_peer_len); 130252190Srpaulo os_free(data); 131252190Srpaulo return NULL; 132252190Srpaulo } 133252190Srpaulo os_memcpy(data->password, password, password_len); 134252190Srpaulo data->password_len = password_len; 135289549Srpaulo data->password_hash = pwhash; 136252190Srpaulo 137351611Scy phase1 = eap_get_config_phase1(sm); 138351611Scy if (phase1) { 139351611Scy const char *pos, *end; 140351611Scy char *copy = NULL; 141351611Scy int res; 142351611Scy 143351611Scy pos = os_strstr(phase1, "eap_pwd_groups="); 144351611Scy if (pos) { 145351611Scy pos += 15; 146351611Scy end = os_strchr(pos, ' '); 147351611Scy if (end) { 148351611Scy copy = os_zalloc(end - pos + 1); 149351611Scy if (!copy) 150351611Scy goto fail; 151351611Scy os_memcpy(copy, pos, end - pos); 152351611Scy pos = copy; 153351611Scy } 154351611Scy res = freq_range_list_parse(&data->allowed_groups, pos); 155351611Scy os_free(copy); 156351611Scy if (res) 157351611Scy goto fail; 158351611Scy } 159351611Scy } 160351611Scy 161252190Srpaulo data->out_frag_pos = data->in_frag_pos = 0; 162252190Srpaulo data->inbuf = data->outbuf = NULL; 163281806Srpaulo fragment_size = eap_get_config_fragment_size(sm); 164281806Srpaulo if (fragment_size <= 0) 165281806Srpaulo data->mtu = 1020; /* default from RFC 5931 */ 166281806Srpaulo else 167281806Srpaulo data->mtu = fragment_size; 168252190Srpaulo 169252190Srpaulo data->state = PWD_ID_Req; 170252190Srpaulo 171252190Srpaulo return data; 172351611Scyfail: 173351611Scy eap_pwd_deinit(sm, data); 174351611Scy return NULL; 175252190Srpaulo} 176252190Srpaulo 177252190Srpaulo 178252190Srpaulostatic void eap_pwd_deinit(struct eap_sm *sm, void *priv) 179252190Srpaulo{ 180252190Srpaulo struct eap_pwd_data *data = priv; 181252190Srpaulo 182346981Scy crypto_bignum_deinit(data->private_value, 1); 183346981Scy crypto_bignum_deinit(data->server_scalar, 1); 184346981Scy crypto_bignum_deinit(data->my_scalar, 1); 185346981Scy crypto_bignum_deinit(data->k, 1); 186346981Scy crypto_ec_point_deinit(data->my_element, 1); 187346981Scy crypto_ec_point_deinit(data->server_element, 1); 188281806Srpaulo bin_clear_free(data->id_peer, data->id_peer_len); 189281806Srpaulo bin_clear_free(data->id_server, data->id_server_len); 190281806Srpaulo bin_clear_free(data->password, data->password_len); 191252190Srpaulo if (data->grp) { 192346981Scy crypto_ec_deinit(data->grp->group); 193346981Scy crypto_ec_point_deinit(data->grp->pwe, 1); 194252190Srpaulo os_free(data->grp); 195252190Srpaulo } 196281806Srpaulo wpabuf_free(data->inbuf); 197281806Srpaulo wpabuf_free(data->outbuf); 198351611Scy os_free(data->allowed_groups.range); 199281806Srpaulo bin_clear_free(data, sizeof(*data)); 200252190Srpaulo} 201252190Srpaulo 202252190Srpaulo 203252190Srpaulostatic u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) 204252190Srpaulo{ 205252190Srpaulo struct eap_pwd_data *data = priv; 206252190Srpaulo u8 *key; 207252190Srpaulo 208252190Srpaulo if (data->state != SUCCESS) 209252190Srpaulo return NULL; 210252190Srpaulo 211346981Scy key = os_memdup(data->msk, EAP_MSK_LEN); 212252190Srpaulo if (key == NULL) 213252190Srpaulo return NULL; 214252190Srpaulo 215252190Srpaulo *len = EAP_MSK_LEN; 216252190Srpaulo 217252190Srpaulo return key; 218252190Srpaulo} 219252190Srpaulo 220252190Srpaulo 221281806Srpaulostatic u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 222281806Srpaulo{ 223281806Srpaulo struct eap_pwd_data *data = priv; 224281806Srpaulo u8 *id; 225281806Srpaulo 226281806Srpaulo if (data->state != SUCCESS) 227281806Srpaulo return NULL; 228281806Srpaulo 229346981Scy id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN); 230281806Srpaulo if (id == NULL) 231281806Srpaulo return NULL; 232281806Srpaulo 233281806Srpaulo *len = 1 + SHA256_MAC_LEN; 234281806Srpaulo 235281806Srpaulo return id; 236281806Srpaulo} 237281806Srpaulo 238281806Srpaulo 239351611Scystatic int eap_pwd_allowed_group(struct eap_pwd_data *data, u16 group) 240351611Scy{ 241351611Scy if (!data->allowed_groups.range) { 242351611Scy /* By default, allow the groups using NIST curves P-256, P-384, 243351611Scy * and P-521. */ 244351611Scy return group == 19 || group == 20 || group == 21; 245351611Scy } 246351611Scy 247351611Scy return freq_range_list_includes(&data->allowed_groups, group); 248351611Scy} 249351611Scy 250351611Scy 251252190Srpaulostatic void 252252190Srpauloeap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, 253252190Srpaulo struct eap_method_ret *ret, 254252190Srpaulo const struct wpabuf *reqData, 255252190Srpaulo const u8 *payload, size_t payload_len) 256252190Srpaulo{ 257252190Srpaulo struct eap_pwd_id *id; 258252190Srpaulo 259252190Srpaulo if (data->state != PWD_ID_Req) { 260252190Srpaulo ret->ignore = TRUE; 261252190Srpaulo eap_pwd_state(data, FAILURE); 262252190Srpaulo return; 263252190Srpaulo } 264252190Srpaulo 265252190Srpaulo if (payload_len < sizeof(struct eap_pwd_id)) { 266252190Srpaulo ret->ignore = TRUE; 267252190Srpaulo eap_pwd_state(data, FAILURE); 268252190Srpaulo return; 269252190Srpaulo } 270252190Srpaulo 271252190Srpaulo id = (struct eap_pwd_id *) payload; 272252190Srpaulo data->group_num = be_to_host16(id->group_num); 273289549Srpaulo wpa_printf(MSG_DEBUG, 274289549Srpaulo "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u", 275289549Srpaulo data->group_num, id->random_function, id->prf, id->prep); 276351611Scy if (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC || 277351611Scy id->prf != EAP_PWD_DEFAULT_PRF || 278351611Scy !eap_pwd_allowed_group(data, data->group_num)) { 279351611Scy wpa_printf(MSG_INFO, 280351611Scy "EAP-pwd: Unsupported or disabled proposal"); 281252190Srpaulo eap_pwd_state(data, FAILURE); 282252190Srpaulo return; 283252190Srpaulo } 284252190Srpaulo 285289549Srpaulo if (id->prep != EAP_PWD_PREP_NONE && 286346981Scy id->prep != EAP_PWD_PREP_MS && 287346981Scy id->prep != EAP_PWD_PREP_SSHA1 && 288346981Scy id->prep != EAP_PWD_PREP_SSHA256 && 289346981Scy id->prep != EAP_PWD_PREP_SSHA512) { 290289549Srpaulo wpa_printf(MSG_DEBUG, 291289549Srpaulo "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)", 292289549Srpaulo id->prep); 293289549Srpaulo eap_pwd_state(data, FAILURE); 294289549Srpaulo return; 295289549Srpaulo } 296289549Srpaulo 297289549Srpaulo if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) { 298289549Srpaulo wpa_printf(MSG_DEBUG, 299289549Srpaulo "EAP-PWD: Unhashed password not available"); 300289549Srpaulo eap_pwd_state(data, FAILURE); 301289549Srpaulo return; 302289549Srpaulo } 303289549Srpaulo 304252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", 305252190Srpaulo data->group_num); 306252190Srpaulo 307346981Scy data->prep = id->prep; 308346981Scy os_memcpy(data->token, id->token, sizeof(id->token)); 309346981Scy 310346981Scy if (data->id_server || data->grp) { 311346981Scy wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated"); 312346981Scy eap_pwd_state(data, FAILURE); 313346981Scy return; 314346981Scy } 315346981Scy 316252190Srpaulo data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); 317252190Srpaulo if (data->id_server == NULL) { 318252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); 319252190Srpaulo eap_pwd_state(data, FAILURE); 320252190Srpaulo return; 321252190Srpaulo } 322252190Srpaulo data->id_server_len = payload_len - sizeof(struct eap_pwd_id); 323252190Srpaulo os_memcpy(data->id_server, id->identity, data->id_server_len); 324252190Srpaulo wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", 325252190Srpaulo data->id_server, data->id_server_len); 326252190Srpaulo 327346981Scy data->grp = get_eap_pwd_group(data->group_num); 328281806Srpaulo if (data->grp == NULL) { 329252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " 330252190Srpaulo "group"); 331252190Srpaulo eap_pwd_state(data, FAILURE); 332252190Srpaulo return; 333252190Srpaulo } 334252190Srpaulo 335346981Scy data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + 336346981Scy data->id_peer_len); 337346981Scy if (data->outbuf == NULL) { 338346981Scy eap_pwd_state(data, FAILURE); 339346981Scy return; 340346981Scy } 341346981Scy wpabuf_put_be16(data->outbuf, data->group_num); 342346981Scy wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); 343346981Scy wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); 344346981Scy wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); 345346981Scy wpabuf_put_u8(data->outbuf, id->prep); 346346981Scy wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); 347346981Scy 348346981Scy eap_pwd_state(data, PWD_Commit_Req); 349346981Scy} 350346981Scy 351346981Scy 352346981Scystatic void 353346981Scyeap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, 354346981Scy struct eap_method_ret *ret, 355346981Scy const struct wpabuf *reqData, 356346981Scy const u8 *payload, size_t payload_len) 357346981Scy{ 358346981Scy struct crypto_ec_point *K = NULL; 359346981Scy struct crypto_bignum *mask = NULL; 360346981Scy const u8 *ptr = payload; 361346981Scy u8 *scalar, *element; 362346981Scy size_t prime_len, order_len; 363346981Scy const u8 *password; 364346981Scy size_t password_len; 365346981Scy u8 pwhashhash[16]; 366346981Scy const u8 *salt_pwd[2]; 367346981Scy size_t salt_pwd_len[2], exp_len; 368346981Scy u8 salt_len, salthashpwd[64]; /* 64 = SHA512_DIGEST_LENGTH */ 369346981Scy int res; 370346981Scy 371346981Scy if (data->state != PWD_Commit_Req) { 372346981Scy ret->ignore = TRUE; 373346981Scy goto fin; 374346981Scy } 375346981Scy 376346981Scy if (!data->grp) { 377346981Scy wpa_printf(MSG_DEBUG, 378346981Scy "EAP-PWD (client): uninitialized EAP-pwd group"); 379346981Scy ret->ignore = TRUE; 380346981Scy goto fin; 381346981Scy } 382346981Scy 383346981Scy prime_len = crypto_ec_prime_len(data->grp->group); 384346981Scy order_len = crypto_ec_order_len(data->grp->group); 385346981Scy 386346981Scy switch (data->prep) { 387346981Scy case EAP_PWD_PREP_MS: 388346981Scy wpa_printf(MSG_DEBUG, 389346981Scy "EAP-pwd commit request, password prep is MS"); 390289549Srpaulo#ifdef CONFIG_FIPS 391289549Srpaulo wpa_printf(MSG_ERROR, 392289549Srpaulo "EAP-PWD (peer): MS password hash not supported in FIPS mode"); 393289549Srpaulo eap_pwd_state(data, FAILURE); 394289549Srpaulo return; 395289549Srpaulo#else /* CONFIG_FIPS */ 396346981Scy if (payload_len != 2 * prime_len + order_len) { 397346981Scy wpa_printf(MSG_INFO, 398346981Scy "EAP-pwd: Unexpected Commit payload length %u (expected %u)", 399346981Scy (unsigned int) payload_len, 400346981Scy (unsigned int) (2 * prime_len + order_len)); 401346981Scy goto fin; 402346981Scy } 403289549Srpaulo if (data->password_hash) { 404289549Srpaulo res = hash_nt_password_hash(data->password, pwhashhash); 405289549Srpaulo } else { 406289549Srpaulo u8 pwhash[16]; 407289549Srpaulo 408289549Srpaulo res = nt_password_hash(data->password, 409289549Srpaulo data->password_len, pwhash); 410289549Srpaulo if (res == 0) 411289549Srpaulo res = hash_nt_password_hash(pwhash, pwhashhash); 412351611Scy forced_memzero(pwhash, sizeof(pwhash)); 413289549Srpaulo } 414289549Srpaulo 415289549Srpaulo if (res) { 416289549Srpaulo eap_pwd_state(data, FAILURE); 417289549Srpaulo return; 418289549Srpaulo } 419289549Srpaulo 420289549Srpaulo password = pwhashhash; 421289549Srpaulo password_len = sizeof(pwhashhash); 422289549Srpaulo#endif /* CONFIG_FIPS */ 423346981Scy break; 424346981Scy case EAP_PWD_PREP_SSHA1: 425346981Scy wpa_printf(MSG_DEBUG, 426346981Scy "EAP-pwd commit request, password prep is salted sha1"); 427346981Scy if (payload_len < 1 || *ptr == 0) { 428346981Scy wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); 429346981Scy goto fin; 430346981Scy } 431346981Scy salt_len = *ptr++; 432346981Scy exp_len = 1 + salt_len + 2 * prime_len + order_len; 433346981Scy if (payload_len != exp_len) { 434346981Scy wpa_printf(MSG_INFO, 435346981Scy "EAP-pwd: Unexpected Commit payload length %u (expected %u)", 436346981Scy (unsigned int) payload_len, 437346981Scy (unsigned int) exp_len); 438346981Scy goto fin; 439346981Scy } 440346981Scy 441346981Scy /* salted-password = Hash(password | salt) */ 442346981Scy wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", 443346981Scy data->password, data->password_len); 444346981Scy wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); 445346981Scy salt_pwd[0] = data->password; 446346981Scy salt_pwd[1] = ptr; 447346981Scy salt_pwd_len[0] = data->password_len; 448346981Scy salt_pwd_len[1] = salt_len; 449346981Scy if (sha1_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) 450346981Scy goto fin; 451346981Scy 452346981Scy wpa_printf(MSG_DEBUG, 453346981Scy "EAP-pwd: sha1 hashed %d byte salt with password", 454346981Scy (int) salt_len); 455346981Scy ptr += salt_len; 456346981Scy password = salthashpwd; 457346981Scy password_len = SHA1_MAC_LEN; 458346981Scy wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", 459346981Scy password, password_len); 460346981Scy break; 461346981Scy case EAP_PWD_PREP_SSHA256: 462346981Scy wpa_printf(MSG_DEBUG, 463346981Scy "EAP-pwd commit request, password prep is salted sha256"); 464346981Scy if (payload_len < 1 || *ptr == 0) { 465346981Scy wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); 466346981Scy goto fin; 467346981Scy } 468346981Scy salt_len = *ptr++; 469346981Scy exp_len = 1 + salt_len + 2 * prime_len + order_len; 470346981Scy if (payload_len != exp_len) { 471346981Scy wpa_printf(MSG_INFO, 472346981Scy "EAP-pwd: Unexpected Commit payload length %u (expected %u)", 473346981Scy (unsigned int) payload_len, 474346981Scy (unsigned int) exp_len); 475346981Scy goto fin; 476346981Scy } 477346981Scy 478346981Scy /* salted-password = Hash(password | salt) */ 479346981Scy wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", 480346981Scy data->password, data->password_len); 481346981Scy wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); 482346981Scy salt_pwd[0] = data->password; 483346981Scy salt_pwd[1] = ptr; 484346981Scy salt_pwd_len[0] = data->password_len; 485346981Scy salt_pwd_len[1] = salt_len; 486346981Scy if (sha256_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) 487346981Scy goto fin; 488346981Scy 489346981Scy ptr += salt_len; 490346981Scy password = salthashpwd; 491346981Scy password_len = SHA256_MAC_LEN; 492346981Scy wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", 493346981Scy password, password_len); 494346981Scy break; 495346981Scy#ifdef CONFIG_SHA512 496346981Scy case EAP_PWD_PREP_SSHA512: 497346981Scy wpa_printf(MSG_DEBUG, 498346981Scy "EAP-pwd commit request, password prep is salted sha512"); 499346981Scy if (payload_len < 1 || *ptr == 0) { 500346981Scy wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); 501346981Scy goto fin; 502346981Scy } 503346981Scy salt_len = *ptr++; 504346981Scy exp_len = 1 + salt_len + 2 * prime_len + order_len; 505346981Scy if (payload_len != exp_len) { 506346981Scy wpa_printf(MSG_INFO, 507346981Scy "EAP-pwd: Unexpected Commit payload length %u (expected %u)", 508346981Scy (unsigned int) payload_len, 509346981Scy (unsigned int) exp_len); 510346981Scy goto fin; 511346981Scy } 512346981Scy 513346981Scy /* salted-password = Hash(password | salt) */ 514346981Scy wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", 515346981Scy data->password, data->password_len); 516346981Scy wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); 517346981Scy salt_pwd[0] = data->password; 518346981Scy salt_pwd[1] = ptr; 519346981Scy salt_pwd_len[0] = data->password_len; 520346981Scy salt_pwd_len[1] = salt_len; 521346981Scy if (sha512_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) 522346981Scy goto fin; 523346981Scy 524346981Scy ptr += salt_len; 525346981Scy password = salthashpwd; 526346981Scy password_len = SHA512_MAC_LEN; 527346981Scy wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", 528346981Scy password, password_len); 529346981Scy break; 530346981Scy#endif /* CONFIG_SHA512 */ 531346981Scy case EAP_PWD_PREP_NONE: 532346981Scy wpa_printf(MSG_DEBUG, 533346981Scy "EAP-pwd commit request, password prep is NONE"); 534346981Scy if (data->password_hash) { 535346981Scy wpa_printf(MSG_DEBUG, 536346981Scy "EAP-PWD: Unhashed password not available"); 537346981Scy eap_pwd_state(data, FAILURE); 538346981Scy return; 539346981Scy } 540346981Scy if (payload_len != 2 * prime_len + order_len) { 541346981Scy wpa_printf(MSG_INFO, 542346981Scy "EAP-pwd: Unexpected Commit payload length %u (expected %u)", 543346981Scy (unsigned int) payload_len, 544346981Scy (unsigned int) (2 * prime_len + order_len)); 545346981Scy goto fin; 546346981Scy } 547289549Srpaulo password = data->password; 548289549Srpaulo password_len = data->password_len; 549346981Scy break; 550346981Scy default: 551346981Scy wpa_printf(MSG_DEBUG, 552346981Scy "EAP-pwd: Unsupported password pre-processing technique (Prep=%u)", 553346981Scy data->prep); 554346981Scy eap_pwd_state(data, FAILURE); 555346981Scy return; 556289549Srpaulo } 557289549Srpaulo 558252190Srpaulo /* compute PWE */ 559289549Srpaulo res = compute_password_element(data->grp, data->group_num, 560289549Srpaulo password, password_len, 561289549Srpaulo data->id_server, data->id_server_len, 562289549Srpaulo data->id_peer, data->id_peer_len, 563346981Scy data->token); 564351611Scy forced_memzero(pwhashhash, sizeof(pwhashhash)); 565351611Scy forced_memzero(salthashpwd, sizeof(salthashpwd)); 566289549Srpaulo if (res) { 567252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); 568252190Srpaulo eap_pwd_state(data, FAILURE); 569252190Srpaulo return; 570252190Srpaulo } 571252190Srpaulo 572252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...", 573346981Scy (int) crypto_ec_prime_len_bits(data->grp->group)); 574252190Srpaulo 575346981Scy data->private_value = crypto_bignum_init(); 576346981Scy data->my_element = crypto_ec_point_init(data->grp->group); 577346981Scy data->my_scalar = crypto_bignum_init(); 578346981Scy mask = crypto_bignum_init(); 579346981Scy if (!data->private_value || !data->my_element || 580346981Scy !data->my_scalar || !mask) { 581252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); 582252190Srpaulo goto fin; 583252190Srpaulo } 584252190Srpaulo 585346981Scy if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask, 586346981Scy data->my_scalar) < 0) 587252190Srpaulo goto fin; 588252190Srpaulo 589346981Scy if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, 590346981Scy data->my_element) < 0) { 591252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " 592252190Srpaulo "fail"); 593252190Srpaulo eap_pwd_state(data, FAILURE); 594252190Srpaulo goto fin; 595252190Srpaulo } 596252190Srpaulo 597346981Scy if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) { 598252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); 599252190Srpaulo goto fin; 600252190Srpaulo } 601252190Srpaulo 602252190Srpaulo /* process the request */ 603346981Scy data->k = crypto_bignum_init(); 604346981Scy K = crypto_ec_point_init(data->grp->group); 605346981Scy if (!data->k || !K) { 606252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " 607252190Srpaulo "fail"); 608252190Srpaulo goto fin; 609252190Srpaulo } 610252190Srpaulo 611252190Srpaulo /* element, x then y, followed by scalar */ 612346981Scy data->server_element = eap_pwd_get_element(data->grp, ptr); 613346981Scy if (!data->server_element) { 614252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " 615252190Srpaulo "fail"); 616252190Srpaulo goto fin; 617252190Srpaulo } 618346981Scy ptr += prime_len * 2; 619346981Scy data->server_scalar = eap_pwd_get_scalar(data->grp, ptr); 620346981Scy if (!data->server_scalar) { 621346981Scy wpa_printf(MSG_INFO, 622346981Scy "EAP-PWD (peer): setting peer scalar fail"); 623346981Scy goto fin; 624252190Srpaulo } 625252190Srpaulo 626252190Srpaulo /* compute the shared key, k */ 627346981Scy if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, 628346981Scy data->server_scalar, K) < 0 || 629346981Scy crypto_ec_point_add(data->grp->group, K, data->server_element, 630346981Scy K) < 0 || 631346981Scy crypto_ec_point_mul(data->grp->group, K, data->private_value, 632346981Scy K) < 0) { 633252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " 634252190Srpaulo "fail"); 635252190Srpaulo goto fin; 636252190Srpaulo } 637252190Srpaulo 638252190Srpaulo /* 639346981Scy * This check is strictly speaking just for the case where 640252190Srpaulo * co-factor > 1 but it was suggested that even though this is probably 641252190Srpaulo * never going to happen it is a simple and safe check "just to be 642252190Srpaulo * sure" so let's be safe. 643252190Srpaulo */ 644346981Scy if (crypto_ec_point_is_at_infinity(data->grp->group, K)) { 645252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " 646252190Srpaulo "infinity!\n"); 647252190Srpaulo goto fin; 648252190Srpaulo } 649252190Srpaulo 650346981Scy if (crypto_ec_point_x(data->grp->group, K, data->k) < 0) { 651252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " 652252190Srpaulo "shared secret from point"); 653252190Srpaulo goto fin; 654252190Srpaulo } 655252190Srpaulo 656252190Srpaulo /* now do the response */ 657346981Scy data->outbuf = wpabuf_alloc(2 * prime_len + order_len); 658346981Scy if (data->outbuf == NULL) 659252190Srpaulo goto fin; 660346981Scy /* We send the element as (x,y) followed by the scalar */ 661346981Scy element = wpabuf_put(data->outbuf, 2 * prime_len); 662346981Scy scalar = wpabuf_put(data->outbuf, order_len); 663252190Srpaulo 664252190Srpaulo /* 665252190Srpaulo * bignums occupy as little memory as possible so one that is 666252190Srpaulo * sufficiently smaller than the prime or order might need pre-pending 667252190Srpaulo * with zeros. 668252190Srpaulo */ 669346981Scy crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); 670346981Scy if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, 671346981Scy element + prime_len) != 0) { 672346981Scy wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); 673252190Srpaulo goto fin; 674346981Scy } 675252190Srpaulo 676252190Srpaulofin: 677346981Scy crypto_bignum_deinit(mask, 1); 678346981Scy crypto_ec_point_deinit(K, 1); 679252190Srpaulo if (data->outbuf == NULL) 680252190Srpaulo eap_pwd_state(data, FAILURE); 681252190Srpaulo else 682252190Srpaulo eap_pwd_state(data, PWD_Confirm_Req); 683252190Srpaulo} 684252190Srpaulo 685252190Srpaulo 686252190Srpaulostatic void 687252190Srpauloeap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, 688252190Srpaulo struct eap_method_ret *ret, 689252190Srpaulo const struct wpabuf *reqData, 690252190Srpaulo const u8 *payload, size_t payload_len) 691252190Srpaulo{ 692346981Scy struct crypto_hash *hash = NULL; 693252190Srpaulo u32 cs; 694252190Srpaulo u16 grp; 695252190Srpaulo u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; 696346981Scy size_t prime_len = 0, order_len = 0; 697252190Srpaulo 698289549Srpaulo if (data->state != PWD_Confirm_Req) { 699289549Srpaulo ret->ignore = TRUE; 700289549Srpaulo goto fin; 701289549Srpaulo } 702289549Srpaulo 703289549Srpaulo if (payload_len != SHA256_MAC_LEN) { 704289549Srpaulo wpa_printf(MSG_INFO, 705289549Srpaulo "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", 706289549Srpaulo (unsigned int) payload_len, SHA256_MAC_LEN); 707289549Srpaulo goto fin; 708289549Srpaulo } 709289549Srpaulo 710346981Scy prime_len = crypto_ec_prime_len(data->grp->group); 711346981Scy order_len = crypto_ec_order_len(data->grp->group); 712346981Scy 713252190Srpaulo /* 714252190Srpaulo * first build up the ciphersuite which is group | random_function | 715252190Srpaulo * prf 716252190Srpaulo */ 717252190Srpaulo grp = htons(data->group_num); 718252190Srpaulo ptr = (u8 *) &cs; 719252190Srpaulo os_memcpy(ptr, &grp, sizeof(u16)); 720252190Srpaulo ptr += sizeof(u16); 721252190Srpaulo *ptr = EAP_PWD_DEFAULT_RAND_FUNC; 722252190Srpaulo ptr += sizeof(u8); 723252190Srpaulo *ptr = EAP_PWD_DEFAULT_PRF; 724252190Srpaulo 725346981Scy /* each component of the point will be at most as big as the prime */ 726346981Scy cruft = os_malloc(prime_len * 2); 727346981Scy if (!cruft) { 728252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " 729252190Srpaulo "fail"); 730252190Srpaulo goto fin; 731252190Srpaulo } 732252190Srpaulo 733252190Srpaulo /* 734252190Srpaulo * server's commit is H(k | server_element | server_scalar | 735252190Srpaulo * peer_element | peer_scalar | ciphersuite) 736252190Srpaulo */ 737252190Srpaulo hash = eap_pwd_h_init(); 738252190Srpaulo if (hash == NULL) 739252190Srpaulo goto fin; 740252190Srpaulo 741252190Srpaulo /* 742252190Srpaulo * zero the memory each time because this is mod prime math and some 743252190Srpaulo * value may start with a few zeros and the previous one did not. 744252190Srpaulo */ 745346981Scy crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); 746346981Scy eap_pwd_h_update(hash, cruft, prime_len); 747252190Srpaulo 748252190Srpaulo /* server element: x, y */ 749346981Scy if (crypto_ec_point_to_bin(data->grp->group, data->server_element, 750346981Scy cruft, cruft + prime_len) != 0) { 751252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " 752252190Srpaulo "assignment fail"); 753252190Srpaulo goto fin; 754252190Srpaulo } 755346981Scy eap_pwd_h_update(hash, cruft, prime_len * 2); 756252190Srpaulo 757252190Srpaulo /* server scalar */ 758346981Scy crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len); 759346981Scy eap_pwd_h_update(hash, cruft, order_len); 760252190Srpaulo 761252190Srpaulo /* my element: x, y */ 762346981Scy if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, 763346981Scy cruft + prime_len) != 0) { 764252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " 765252190Srpaulo "assignment fail"); 766252190Srpaulo goto fin; 767252190Srpaulo } 768346981Scy eap_pwd_h_update(hash, cruft, prime_len * 2); 769252190Srpaulo 770252190Srpaulo /* my scalar */ 771346981Scy crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); 772346981Scy eap_pwd_h_update(hash, cruft, order_len); 773252190Srpaulo 774252190Srpaulo /* the ciphersuite */ 775252190Srpaulo eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); 776252190Srpaulo 777252190Srpaulo /* random function fin */ 778252190Srpaulo eap_pwd_h_final(hash, conf); 779346981Scy hash = NULL; 780252190Srpaulo 781252190Srpaulo ptr = (u8 *) payload; 782281806Srpaulo if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { 783252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); 784252190Srpaulo goto fin; 785252190Srpaulo } 786252190Srpaulo 787252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified"); 788252190Srpaulo 789252190Srpaulo /* 790252190Srpaulo * compute confirm: 791252190Srpaulo * H(k | peer_element | peer_scalar | server_element | server_scalar | 792252190Srpaulo * ciphersuite) 793252190Srpaulo */ 794252190Srpaulo hash = eap_pwd_h_init(); 795252190Srpaulo if (hash == NULL) 796252190Srpaulo goto fin; 797252190Srpaulo 798252190Srpaulo /* k */ 799346981Scy crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); 800346981Scy eap_pwd_h_update(hash, cruft, prime_len); 801252190Srpaulo 802252190Srpaulo /* my element */ 803346981Scy if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, 804346981Scy cruft + prime_len) != 0) { 805252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " 806252190Srpaulo "assignment fail"); 807252190Srpaulo goto fin; 808252190Srpaulo } 809346981Scy eap_pwd_h_update(hash, cruft, prime_len * 2); 810252190Srpaulo 811252190Srpaulo /* my scalar */ 812346981Scy crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); 813346981Scy eap_pwd_h_update(hash, cruft, order_len); 814252190Srpaulo 815252190Srpaulo /* server element: x, y */ 816346981Scy if (crypto_ec_point_to_bin(data->grp->group, data->server_element, 817346981Scy cruft, cruft + prime_len) != 0) { 818252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " 819252190Srpaulo "assignment fail"); 820252190Srpaulo goto fin; 821252190Srpaulo } 822346981Scy eap_pwd_h_update(hash, cruft, prime_len * 2); 823252190Srpaulo 824252190Srpaulo /* server scalar */ 825346981Scy crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len); 826346981Scy eap_pwd_h_update(hash, cruft, order_len); 827252190Srpaulo 828252190Srpaulo /* the ciphersuite */ 829252190Srpaulo eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); 830252190Srpaulo 831252190Srpaulo /* all done */ 832252190Srpaulo eap_pwd_h_final(hash, conf); 833346981Scy hash = NULL; 834252190Srpaulo 835346981Scy if (compute_keys(data->grp, data->k, 836252190Srpaulo data->my_scalar, data->server_scalar, conf, ptr, 837281806Srpaulo &cs, data->msk, data->emsk, data->session_id) < 0) { 838252190Srpaulo wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " 839252190Srpaulo "EMSK"); 840252190Srpaulo goto fin; 841252190Srpaulo } 842252190Srpaulo 843252190Srpaulo data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); 844252190Srpaulo if (data->outbuf == NULL) 845252190Srpaulo goto fin; 846252190Srpaulo 847252190Srpaulo wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); 848252190Srpaulo 849252190Srpaulofin: 850346981Scy bin_clear_free(cruft, prime_len * 2); 851252190Srpaulo if (data->outbuf == NULL) { 852281806Srpaulo ret->methodState = METHOD_DONE; 853252190Srpaulo ret->decision = DECISION_FAIL; 854252190Srpaulo eap_pwd_state(data, FAILURE); 855252190Srpaulo } else { 856281806Srpaulo eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION); 857252190Srpaulo } 858346981Scy 859346981Scy /* clean allocated memory */ 860346981Scy if (hash) 861346981Scy eap_pwd_h_final(hash, conf); 862252190Srpaulo} 863252190Srpaulo 864252190Srpaulo 865252190Srpaulostatic struct wpabuf * 866252190Srpauloeap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, 867252190Srpaulo const struct wpabuf *reqData) 868252190Srpaulo{ 869252190Srpaulo struct eap_pwd_data *data = priv; 870252190Srpaulo struct wpabuf *resp = NULL; 871252190Srpaulo const u8 *pos, *buf; 872252190Srpaulo size_t len; 873252190Srpaulo u16 tot_len = 0; 874252190Srpaulo u8 lm_exch; 875252190Srpaulo 876252190Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); 877252190Srpaulo if ((pos == NULL) || (len < 1)) { 878252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " 879252190Srpaulo "len is %d", 880252190Srpaulo pos == NULL ? "NULL" : "not NULL", (int) len); 881252190Srpaulo ret->ignore = TRUE; 882252190Srpaulo return NULL; 883252190Srpaulo } 884252190Srpaulo 885252190Srpaulo ret->ignore = FALSE; 886252190Srpaulo ret->methodState = METHOD_MAY_CONT; 887252190Srpaulo ret->decision = DECISION_FAIL; 888252190Srpaulo ret->allowNotifications = FALSE; 889252190Srpaulo 890252190Srpaulo lm_exch = *pos; 891252190Srpaulo pos++; /* skip over the bits and the exch */ 892252190Srpaulo len--; 893252190Srpaulo 894252190Srpaulo /* 895252190Srpaulo * we're fragmenting so send out the next fragment 896252190Srpaulo */ 897252190Srpaulo if (data->out_frag_pos) { 898252190Srpaulo /* 899252190Srpaulo * this should be an ACK 900252190Srpaulo */ 901252190Srpaulo if (len) 902252190Srpaulo wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " 903252190Srpaulo "not an ACK"); 904252190Srpaulo 905252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); 906252190Srpaulo /* 907252190Srpaulo * check if there are going to be more fragments 908252190Srpaulo */ 909252190Srpaulo len = wpabuf_len(data->outbuf) - data->out_frag_pos; 910252190Srpaulo if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { 911252190Srpaulo len = data->mtu - EAP_PWD_HDR_SIZE; 912252190Srpaulo EAP_PWD_SET_MORE_BIT(lm_exch); 913252190Srpaulo } 914252190Srpaulo resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 915252190Srpaulo EAP_PWD_HDR_SIZE + len, 916252190Srpaulo EAP_CODE_RESPONSE, eap_get_id(reqData)); 917252190Srpaulo if (resp == NULL) { 918252190Srpaulo wpa_printf(MSG_INFO, "Unable to allocate memory for " 919252190Srpaulo "next fragment!"); 920252190Srpaulo return NULL; 921252190Srpaulo } 922252190Srpaulo wpabuf_put_u8(resp, lm_exch); 923252190Srpaulo buf = wpabuf_head_u8(data->outbuf); 924252190Srpaulo wpabuf_put_data(resp, buf + data->out_frag_pos, len); 925252190Srpaulo data->out_frag_pos += len; 926252190Srpaulo /* 927252190Srpaulo * this is the last fragment so get rid of the out buffer 928252190Srpaulo */ 929252190Srpaulo if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { 930252190Srpaulo wpabuf_free(data->outbuf); 931252190Srpaulo data->outbuf = NULL; 932252190Srpaulo data->out_frag_pos = 0; 933252190Srpaulo } 934252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", 935252190Srpaulo data->out_frag_pos == 0 ? "last" : "next", 936252190Srpaulo (int) len); 937281806Srpaulo if (data->state == SUCCESS_ON_FRAG_COMPLETION) { 938281806Srpaulo ret->methodState = METHOD_DONE; 939281806Srpaulo ret->decision = DECISION_UNCOND_SUCC; 940281806Srpaulo eap_pwd_state(data, SUCCESS); 941281806Srpaulo } 942252190Srpaulo return resp; 943252190Srpaulo } 944252190Srpaulo 945252190Srpaulo /* 946252190Srpaulo * see if this is a fragment that needs buffering 947252190Srpaulo * 948252190Srpaulo * if it's the first fragment there'll be a length field 949252190Srpaulo */ 950252190Srpaulo if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { 951289549Srpaulo if (len < 2) { 952289549Srpaulo wpa_printf(MSG_DEBUG, 953289549Srpaulo "EAP-pwd: Frame too short to contain Total-Length field"); 954289549Srpaulo ret->ignore = TRUE; 955289549Srpaulo return NULL; 956289549Srpaulo } 957252190Srpaulo tot_len = WPA_GET_BE16(pos); 958252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " 959252190Srpaulo "total length = %d", tot_len); 960281806Srpaulo if (tot_len > 15000) 961281806Srpaulo return NULL; 962289549Srpaulo if (data->inbuf) { 963289549Srpaulo wpa_printf(MSG_DEBUG, 964289549Srpaulo "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); 965289549Srpaulo ret->ignore = TRUE; 966289549Srpaulo return NULL; 967289549Srpaulo } 968252190Srpaulo data->inbuf = wpabuf_alloc(tot_len); 969252190Srpaulo if (data->inbuf == NULL) { 970252190Srpaulo wpa_printf(MSG_INFO, "Out of memory to buffer " 971252190Srpaulo "fragments!"); 972252190Srpaulo return NULL; 973252190Srpaulo } 974289549Srpaulo data->in_frag_pos = 0; 975252190Srpaulo pos += sizeof(u16); 976252190Srpaulo len -= sizeof(u16); 977252190Srpaulo } 978252190Srpaulo /* 979252190Srpaulo * buffer and ACK the fragment 980252190Srpaulo */ 981337817Scy if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { 982346981Scy if (!data->inbuf) { 983346981Scy wpa_printf(MSG_DEBUG, 984346981Scy "EAP-pwd: No buffer for reassembly"); 985346981Scy ret->methodState = METHOD_DONE; 986346981Scy ret->decision = DECISION_FAIL; 987346981Scy return NULL; 988346981Scy } 989252190Srpaulo data->in_frag_pos += len; 990252190Srpaulo if (data->in_frag_pos > wpabuf_size(data->inbuf)) { 991252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " 992252190Srpaulo "detected (%d vs. %d)!", 993252190Srpaulo (int) data->in_frag_pos, 994252190Srpaulo (int) wpabuf_len(data->inbuf)); 995252190Srpaulo wpabuf_free(data->inbuf); 996281806Srpaulo data->inbuf = NULL; 997252190Srpaulo data->in_frag_pos = 0; 998252190Srpaulo return NULL; 999252190Srpaulo } 1000252190Srpaulo wpabuf_put_data(data->inbuf, pos, len); 1001337817Scy } 1002337817Scy if (EAP_PWD_GET_MORE_BIT(lm_exch)) { 1003252190Srpaulo resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 1004252190Srpaulo EAP_PWD_HDR_SIZE, 1005252190Srpaulo EAP_CODE_RESPONSE, eap_get_id(reqData)); 1006252190Srpaulo if (resp != NULL) 1007252190Srpaulo wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); 1008252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", 1009252190Srpaulo (int) len); 1010252190Srpaulo return resp; 1011252190Srpaulo } 1012252190Srpaulo /* 1013252190Srpaulo * we're buffering and this is the last fragment 1014252190Srpaulo */ 1015346981Scy if (data->in_frag_pos && data->inbuf) { 1016252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", 1017252190Srpaulo (int) len); 1018252190Srpaulo pos = wpabuf_head_u8(data->inbuf); 1019252190Srpaulo len = data->in_frag_pos; 1020252190Srpaulo } 1021252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d", 1022252190Srpaulo EAP_PWD_GET_EXCHANGE(lm_exch), (int) len); 1023252190Srpaulo 1024252190Srpaulo switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { 1025252190Srpaulo case EAP_PWD_OPCODE_ID_EXCH: 1026252190Srpaulo eap_pwd_perform_id_exchange(sm, data, ret, reqData, 1027252190Srpaulo pos, len); 1028252190Srpaulo break; 1029252190Srpaulo case EAP_PWD_OPCODE_COMMIT_EXCH: 1030252190Srpaulo eap_pwd_perform_commit_exchange(sm, data, ret, reqData, 1031252190Srpaulo pos, len); 1032252190Srpaulo break; 1033252190Srpaulo case EAP_PWD_OPCODE_CONFIRM_EXCH: 1034252190Srpaulo eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, 1035252190Srpaulo pos, len); 1036252190Srpaulo break; 1037252190Srpaulo default: 1038252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " 1039252190Srpaulo "opcode %d", lm_exch); 1040252190Srpaulo break; 1041252190Srpaulo } 1042252190Srpaulo /* 1043252190Srpaulo * if we buffered the just processed input now's the time to free it 1044252190Srpaulo */ 1045252190Srpaulo if (data->in_frag_pos) { 1046252190Srpaulo wpabuf_free(data->inbuf); 1047281806Srpaulo data->inbuf = NULL; 1048252190Srpaulo data->in_frag_pos = 0; 1049252190Srpaulo } 1050252190Srpaulo 1051281806Srpaulo if (data->outbuf == NULL) { 1052281806Srpaulo ret->methodState = METHOD_DONE; 1053281806Srpaulo ret->decision = DECISION_FAIL; 1054252190Srpaulo return NULL; /* generic failure */ 1055281806Srpaulo } 1056252190Srpaulo 1057252190Srpaulo /* 1058252190Srpaulo * we have output! Do we need to fragment it? 1059252190Srpaulo */ 1060289549Srpaulo lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch); 1061252190Srpaulo len = wpabuf_len(data->outbuf); 1062252190Srpaulo if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { 1063252190Srpaulo resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, 1064252190Srpaulo EAP_CODE_RESPONSE, eap_get_id(reqData)); 1065252190Srpaulo /* 1066252190Srpaulo * if so it's the first so include a length field 1067252190Srpaulo */ 1068252190Srpaulo EAP_PWD_SET_LENGTH_BIT(lm_exch); 1069252190Srpaulo EAP_PWD_SET_MORE_BIT(lm_exch); 1070252190Srpaulo tot_len = len; 1071252190Srpaulo /* 1072252190Srpaulo * keep the packet at the MTU 1073252190Srpaulo */ 1074252190Srpaulo len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16); 1075252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total " 1076252190Srpaulo "length = %d", tot_len); 1077252190Srpaulo } else { 1078252190Srpaulo resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 1079252190Srpaulo EAP_PWD_HDR_SIZE + len, 1080252190Srpaulo EAP_CODE_RESPONSE, eap_get_id(reqData)); 1081252190Srpaulo } 1082252190Srpaulo if (resp == NULL) 1083252190Srpaulo return NULL; 1084252190Srpaulo 1085252190Srpaulo wpabuf_put_u8(resp, lm_exch); 1086252190Srpaulo if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { 1087252190Srpaulo wpabuf_put_be16(resp, tot_len); 1088252190Srpaulo data->out_frag_pos += len; 1089252190Srpaulo } 1090252190Srpaulo buf = wpabuf_head_u8(data->outbuf); 1091252190Srpaulo wpabuf_put_data(resp, buf, len); 1092252190Srpaulo /* 1093252190Srpaulo * if we're not fragmenting then there's no need to carry this around 1094252190Srpaulo */ 1095252190Srpaulo if (data->out_frag_pos == 0) { 1096252190Srpaulo wpabuf_free(data->outbuf); 1097252190Srpaulo data->outbuf = NULL; 1098252190Srpaulo data->out_frag_pos = 0; 1099281806Srpaulo if (data->state == SUCCESS_ON_FRAG_COMPLETION) { 1100281806Srpaulo ret->methodState = METHOD_DONE; 1101281806Srpaulo ret->decision = DECISION_UNCOND_SUCC; 1102281806Srpaulo eap_pwd_state(data, SUCCESS); 1103281806Srpaulo } 1104252190Srpaulo } 1105252190Srpaulo 1106252190Srpaulo return resp; 1107252190Srpaulo} 1108252190Srpaulo 1109252190Srpaulo 1110252190Srpaulostatic Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv) 1111252190Srpaulo{ 1112252190Srpaulo struct eap_pwd_data *data = priv; 1113252190Srpaulo return data->state == SUCCESS; 1114252190Srpaulo} 1115252190Srpaulo 1116252190Srpaulo 1117252190Srpaulostatic u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 1118252190Srpaulo{ 1119252190Srpaulo struct eap_pwd_data *data = priv; 1120252190Srpaulo u8 *key; 1121252190Srpaulo 1122252190Srpaulo if (data->state != SUCCESS) 1123252190Srpaulo return NULL; 1124252190Srpaulo 1125252190Srpaulo if ((key = os_malloc(EAP_EMSK_LEN)) == NULL) 1126252190Srpaulo return NULL; 1127252190Srpaulo 1128252190Srpaulo os_memcpy(key, data->emsk, EAP_EMSK_LEN); 1129252190Srpaulo *len = EAP_EMSK_LEN; 1130252190Srpaulo 1131252190Srpaulo return key; 1132252190Srpaulo} 1133252190Srpaulo 1134252190Srpaulo 1135252190Srpauloint eap_peer_pwd_register(void) 1136252190Srpaulo{ 1137252190Srpaulo struct eap_method *eap; 1138252190Srpaulo 1139252190Srpaulo eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1140252190Srpaulo EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); 1141252190Srpaulo if (eap == NULL) 1142252190Srpaulo return -1; 1143252190Srpaulo 1144252190Srpaulo eap->init = eap_pwd_init; 1145252190Srpaulo eap->deinit = eap_pwd_deinit; 1146252190Srpaulo eap->process = eap_pwd_process; 1147252190Srpaulo eap->isKeyAvailable = eap_pwd_key_available; 1148252190Srpaulo eap->getKey = eap_pwd_getkey; 1149281806Srpaulo eap->getSessionId = eap_pwd_get_session_id; 1150252190Srpaulo eap->get_emsk = eap_pwd_get_emsk; 1151252190Srpaulo 1152337817Scy return eap_peer_method_register(eap); 1153252190Srpaulo} 1154