1 /* 2 * EAP peer method: LEAP 3 * Copyright (c) 2004-2007, Jouni Malinen <j (at) w1.fi> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15 #include "includes.h" 16 17 #include "common.h" 18 #include "crypto/ms_funcs.h" 19 #include "crypto/crypto.h" 20 #include "crypto/random.h" 21 #include "eap_i.h" 22 23 #define LEAP_VERSION 1 24 #define LEAP_CHALLENGE_LEN 8 25 #define LEAP_RESPONSE_LEN 24 26 #define LEAP_KEY_LEN 16 27 28 29 struct eap_leap_data { 30 enum { 31 LEAP_WAIT_CHALLENGE, 32 LEAP_WAIT_SUCCESS, 33 LEAP_WAIT_RESPONSE, 34 LEAP_DONE 35 } state; 36 37 u8 peer_challenge[LEAP_CHALLENGE_LEN]; 38 u8 peer_response[LEAP_RESPONSE_LEN]; 39 40 u8 ap_challenge[LEAP_CHALLENGE_LEN]; 41 u8 ap_response[LEAP_RESPONSE_LEN]; 42 }; 43 44 45 static void * eap_leap_init(struct eap_sm *sm) 46 { 47 struct eap_leap_data *data; 48 49 data = os_zalloc(sizeof(*data)); 50 if (data == NULL) 51 return NULL; 52 data->state = LEAP_WAIT_CHALLENGE; 53 54 sm->leap_done = FALSE; 55 return data; 56 } 57 58 59 static void eap_leap_deinit(struct eap_sm *sm, void *priv) 60 { 61 os_free(priv); 62 } 63 64 65 static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv, 66 struct eap_method_ret *ret, 67 const struct wpabuf *reqData) 68 { 69 struct eap_leap_data *data = priv; 70 struct wpabuf *resp; 71 const u8 *pos, *challenge, *identity, *password; 72 u8 challenge_len, *rpos; 73 size_t identity_len, password_len, len; 74 int pwhash; 75 76 wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); 77 78 identity = eap_get_config_identity(sm, &identity_len); 79 password = eap_get_config_password2(sm, &password_len, &pwhash); 80 if (identity == NULL || password == NULL) 81 return NULL; 82 83 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); 84 if (pos == NULL || len < 3) { 85 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); 86 ret->ignore = TRUE; 87 return NULL; 88 } 89 90 if (*pos != LEAP_VERSION) { 91 wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " 92 "%d", *pos); 93 ret->ignore = TRUE; 94 return NULL; 95 } 96 pos++; 97 98 pos++; /* skip unused byte */ 99 100 challenge_len = *pos++; 101 if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) { 102 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge " 103 "(challenge_len=%d reqDataLen=%lu)", 104 challenge_len, (unsigned long) wpabuf_len(reqData)); 105 ret->ignore = TRUE; 106 return NULL; 107 } 108 challenge = pos; 109 os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN); 110 wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP", 111 challenge, LEAP_CHALLENGE_LEN); 112 113 wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response"); 114 115 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, 116 3 + LEAP_RESPONSE_LEN + identity_len, 117 EAP_CODE_RESPONSE, eap_get_id(reqData)); 118 if (resp == NULL) 119 return NULL; 120 wpabuf_put_u8(resp, LEAP_VERSION); 121 wpabuf_put_u8(resp, 0); /* unused */ 122 wpabuf_put_u8(resp, LEAP_RESPONSE_LEN); 123 rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN); 124 if (pwhash) 125 challenge_response(challenge, password, rpos); 126 else 127 nt_challenge_response(challenge, password, password_len, rpos); 128 os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); 129 wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", 130 rpos, LEAP_RESPONSE_LEN); 131 wpabuf_put_data(resp, identity, identity_len); 132 133 data->state = LEAP_WAIT_SUCCESS; 134 135 return resp; 136 } 137 138 139 static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, 140 struct eap_method_ret *ret, 141 const struct wpabuf *reqData) 142 { 143 struct eap_leap_data *data = priv; 144 struct wpabuf *resp; 145 u8 *pos; 146 const u8 *identity; 147 size_t identity_len; 148 149 wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); 150 151 identity = eap_get_config_identity(sm, &identity_len); 152 if (identity == NULL) 153 return NULL; 154 155 if (data->state != LEAP_WAIT_SUCCESS) { 156 wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in " 157 "unexpected state (%d) - ignored", data->state); 158 ret->ignore = TRUE; 159 return NULL; 160 } 161 162 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, 163 3 + LEAP_CHALLENGE_LEN + identity_len, 164 EAP_CODE_REQUEST, eap_get_id(reqData)); 165 if (resp == NULL) 166 return NULL; 167 wpabuf_put_u8(resp, LEAP_VERSION); 168 wpabuf_put_u8(resp, 0); /* unused */ 169 wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN); 170 pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN); 171 if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) { 172 wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " 173 "for challenge"); 174 wpabuf_free(resp); 175 ret->ignore = TRUE; 176 return NULL; 177 } 178 os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN); 179 wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos, 180 LEAP_CHALLENGE_LEN); 181 wpabuf_put_data(resp, identity, identity_len); 182 183 data->state = LEAP_WAIT_RESPONSE; 184 185 return resp; 186 } 187 188 189 static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, 190 struct eap_method_ret *ret, 191 const struct wpabuf *reqData) 192 { 193 struct eap_leap_data *data = priv; 194 const u8 *pos, *password; 195 u8 response_len, pw_hash[16], pw_hash_hash[16], 196 expected[LEAP_RESPONSE_LEN]; 197 size_t password_len, len; 198 int pwhash; 199 200 wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); 201 202 password = eap_get_config_password2(sm, &password_len, &pwhash); 203 if (password == NULL) 204 return NULL; 205 206 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); 207 if (pos == NULL || len < 3) { 208 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); 209 ret->ignore = TRUE; 210 return NULL; 211 } 212 213 if (*pos != LEAP_VERSION) { 214 wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " 215 "%d", *pos); 216 ret->ignore = TRUE; 217 return NULL; 218 } 219 pos++; 220 221 pos++; /* skip unused byte */ 222 223 response_len = *pos++; 224 if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) { 225 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " 226 "(response_len=%d reqDataLen=%lu)", 227 response_len, (unsigned long) wpabuf_len(reqData)); 228 ret->ignore = TRUE; 229 return NULL; 230 } 231 232 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", 233 pos, LEAP_RESPONSE_LEN); 234 os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); 235 236 if (pwhash) { 237 if (hash_nt_password_hash(password, pw_hash_hash)) { 238 ret->ignore = TRUE; 239 return NULL; 240 } 241 } else { 242 if (nt_password_hash(password, password_len, pw_hash) || 243 hash_nt_password_hash(pw_hash, pw_hash_hash)) { 244 ret->ignore = TRUE; 245 return NULL; 246 } 247 } 248 challenge_response(data->ap_challenge, pw_hash_hash, expected); 249 250 ret->methodState = METHOD_DONE; 251 ret->allowNotifications = FALSE; 252 253 if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { 254 wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " 255 "response - authentication failed"); 256 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", 257 expected, LEAP_RESPONSE_LEN); 258 ret->decision = DECISION_FAIL; 259 return NULL; 260 } 261 262 ret->decision = DECISION_UNCOND_SUCC; 263 264 /* LEAP is somewhat odd method since it sends EAP-Success in the middle 265 * of the authentication. Use special variable to transit EAP state 266 * machine to SUCCESS state. */ 267 sm->leap_done = TRUE; 268 data->state = LEAP_DONE; 269 270 /* No more authentication messages expected; AP will send EAPOL-Key 271 * frames if encryption is enabled. */ 272 return NULL; 273 } 274 275 276 static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv, 277 struct eap_method_ret *ret, 278 const struct wpabuf *reqData) 279 { 280 const struct eap_hdr *eap; 281 size_t password_len; 282 const u8 *password; 283 284 password = eap_get_config_password(sm, &password_len); 285 if (password == NULL) { 286 wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured"); 287 eap_sm_request_password(sm); 288 ret->ignore = TRUE; 289 return NULL; 290 } 291 292 /* 293 * LEAP needs to be able to handle EAP-Success frame which does not 294 * include Type field. Consequently, eap_hdr_validate() cannot be used 295 * here. This validation will be done separately for EAP-Request and 296 * EAP-Response frames. 297 */ 298 eap = wpabuf_head(reqData); 299 if (wpabuf_len(reqData) < sizeof(*eap) || 300 be_to_host16(eap->length) > wpabuf_len(reqData)) { 301 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame"); 302 ret->ignore = TRUE; 303 return NULL; 304 } 305 306 ret->ignore = FALSE; 307 ret->allowNotifications = TRUE; 308 ret->methodState = METHOD_MAY_CONT; 309 ret->decision = DECISION_FAIL; 310 311 sm->leap_done = FALSE; 312 313 switch (eap->code) { 314 case EAP_CODE_REQUEST: 315 return eap_leap_process_request(sm, priv, ret, reqData); 316 case EAP_CODE_SUCCESS: 317 return eap_leap_process_success(sm, priv, ret, reqData); 318 case EAP_CODE_RESPONSE: 319 return eap_leap_process_response(sm, priv, ret, reqData); 320 default: 321 wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - " 322 "ignored", eap->code); 323 ret->ignore = TRUE; 324 return NULL; 325 } 326 } 327 328 329 static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv) 330 { 331 struct eap_leap_data *data = priv; 332 return data->state == LEAP_DONE; 333 } 334 335 336 static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) 337 { 338 struct eap_leap_data *data = priv; 339 u8 *key, pw_hash_hash[16], pw_hash[16]; 340 const u8 *addr[5], *password; 341 size_t elen[5], password_len; 342 int pwhash; 343 344 if (data->state != LEAP_DONE) 345 return NULL; 346 347 password = eap_get_config_password2(sm, &password_len, &pwhash); 348 if (password == NULL) 349 return NULL; 350 351 key = os_malloc(LEAP_KEY_LEN); 352 if (key == NULL) 353 return NULL; 354 355 if (pwhash) { 356 if (hash_nt_password_hash(password, pw_hash_hash)) { 357 os_free(key); 358 return NULL; 359 } 360 } else { 361 if (nt_password_hash(password, password_len, pw_hash) || 362 hash_nt_password_hash(pw_hash, pw_hash_hash)) { 363 os_free(key); 364 return NULL; 365 } 366 } 367 wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash", 368 pw_hash_hash, 16); 369 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge", 370 data->peer_challenge, LEAP_CHALLENGE_LEN); 371 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response", 372 data->peer_response, LEAP_RESPONSE_LEN); 373 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge", 374 data->ap_challenge, LEAP_CHALLENGE_LEN); 375 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response", 376 data->ap_response, LEAP_RESPONSE_LEN); 377 378 addr[0] = pw_hash_hash; 379 elen[0] = 16; 380 addr[1] = data->ap_challenge; 381 elen[1] = LEAP_CHALLENGE_LEN; 382 addr[2] = data->ap_response; 383 elen[2] = LEAP_RESPONSE_LEN; 384 addr[3] = data->peer_challenge; 385 elen[3] = LEAP_CHALLENGE_LEN; 386 addr[4] = data->peer_response; 387 elen[4] = LEAP_RESPONSE_LEN; 388 md5_vector(5, addr, elen, key); 389 wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); 390 *len = LEAP_KEY_LEN; 391 392 return key; 393 } 394 395 396 int eap_peer_leap_register(void) 397 { 398 struct eap_method *eap; 399 int ret; 400 401 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 402 EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP"); 403 if (eap == NULL) 404 return -1; 405 406 eap->init = eap_leap_init; 407 eap->deinit = eap_leap_deinit; 408 eap->process = eap_leap_process; 409 eap->isKeyAvailable = eap_leap_isKeyAvailable; 410 eap->getKey = eap_leap_getKey; 411 412 ret = eap_peer_method_register(eap); 413 if (ret) 414 eap_peer_method_free(eap); 415 return ret; 416 } 417