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