1 /* 2 * EAP peer method: LEAP 3 * Copyright (c) 2004-2007, Jouni Malinen <j (at) 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) 119 challenge_response(challenge, password, rpos); 120 else 121 nt_challenge_response(challenge, password, password_len, rpos); 122 os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); 123 wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", 124 rpos, LEAP_RESPONSE_LEN); 125 wpabuf_put_data(resp, identity, identity_len); 126 127 data->state = LEAP_WAIT_SUCCESS; 128 129 return resp; 130 } 131 132 133 static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, 134 struct eap_method_ret *ret, 135 const struct wpabuf *reqData) 136 { 137 struct eap_leap_data *data = priv; 138 struct wpabuf *resp; 139 u8 *pos; 140 const u8 *identity; 141 size_t identity_len; 142 143 wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); 144 145 identity = eap_get_config_identity(sm, &identity_len); 146 if (identity == NULL) 147 return NULL; 148 149 if (data->state != LEAP_WAIT_SUCCESS) { 150 wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in " 151 "unexpected state (%d) - ignored", data->state); 152 ret->ignore = TRUE; 153 return NULL; 154 } 155 156 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, 157 3 + LEAP_CHALLENGE_LEN + identity_len, 158 EAP_CODE_REQUEST, eap_get_id(reqData)); 159 if (resp == NULL) 160 return NULL; 161 wpabuf_put_u8(resp, LEAP_VERSION); 162 wpabuf_put_u8(resp, 0); /* unused */ 163 wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN); 164 pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN); 165 if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) { 166 wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " 167 "for challenge"); 168 wpabuf_free(resp); 169 ret->ignore = TRUE; 170 return NULL; 171 } 172 os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN); 173 wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos, 174 LEAP_CHALLENGE_LEN); 175 wpabuf_put_data(resp, identity, identity_len); 176 177 data->state = LEAP_WAIT_RESPONSE; 178 179 return resp; 180 } 181 182 183 static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, 184 struct eap_method_ret *ret, 185 const struct wpabuf *reqData) 186 { 187 struct eap_leap_data *data = priv; 188 const u8 *pos, *password; 189 u8 response_len, pw_hash[16], pw_hash_hash[16], 190 expected[LEAP_RESPONSE_LEN]; 191 size_t password_len, len; 192 int pwhash; 193 194 wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); 195 196 password = eap_get_config_password2(sm, &password_len, &pwhash); 197 if (password == NULL) 198 return NULL; 199 200 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); 201 if (pos == NULL || len < 3) { 202 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); 203 ret->ignore = TRUE; 204 return NULL; 205 } 206 207 if (*pos != LEAP_VERSION) { 208 wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " 209 "%d", *pos); 210 ret->ignore = TRUE; 211 return NULL; 212 } 213 pos++; 214 215 pos++; /* skip unused byte */ 216 217 response_len = *pos++; 218 if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) { 219 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " 220 "(response_len=%d reqDataLen=%lu)", 221 response_len, (unsigned long) wpabuf_len(reqData)); 222 ret->ignore = TRUE; 223 return NULL; 224 } 225 226 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", 227 pos, LEAP_RESPONSE_LEN); 228 os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); 229 230 if (pwhash) { 231 if (hash_nt_password_hash(password, pw_hash_hash)) { 232 ret->ignore = TRUE; 233 return NULL; 234 } 235 } else { 236 if (nt_password_hash(password, password_len, pw_hash) || 237 hash_nt_password_hash(pw_hash, pw_hash_hash)) { 238 ret->ignore = TRUE; 239 return NULL; 240 } 241 } 242 challenge_response(data->ap_challenge, pw_hash_hash, expected); 243 244 ret->methodState = METHOD_DONE; 245 ret->allowNotifications = FALSE; 246 247 if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { 248 wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " 249 "response - authentication failed"); 250 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", 251 expected, LEAP_RESPONSE_LEN); 252 ret->decision = DECISION_FAIL; 253 return NULL; 254 } 255 256 ret->decision = DECISION_UNCOND_SUCC; 257 258 /* LEAP is somewhat odd method since it sends EAP-Success in the middle 259 * of the authentication. Use special variable to transit EAP state 260 * machine to SUCCESS state. */ 261 sm->leap_done = TRUE; 262 data->state = LEAP_DONE; 263 264 /* No more authentication messages expected; AP will send EAPOL-Key 265 * frames if encryption is enabled. */ 266 return NULL; 267 } 268 269 270 static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv, 271 struct eap_method_ret *ret, 272 const struct wpabuf *reqData) 273 { 274 const struct eap_hdr *eap; 275 size_t password_len; 276 const u8 *password; 277 278 password = eap_get_config_password(sm, &password_len); 279 if (password == NULL) { 280 wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured"); 281 eap_sm_request_password(sm); 282 ret->ignore = TRUE; 283 return NULL; 284 } 285 286 /* 287 * LEAP needs to be able to handle EAP-Success frame which does not 288 * include Type field. Consequently, eap_hdr_validate() cannot be used 289 * here. This validation will be done separately for EAP-Request and 290 * EAP-Response frames. 291 */ 292 eap = wpabuf_head(reqData); 293 if (wpabuf_len(reqData) < sizeof(*eap) || 294 be_to_host16(eap->length) > wpabuf_len(reqData)) { 295 wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame"); 296 ret->ignore = TRUE; 297 return NULL; 298 } 299 300 ret->ignore = FALSE; 301 ret->allowNotifications = TRUE; 302 ret->methodState = METHOD_MAY_CONT; 303 ret->decision = DECISION_FAIL; 304 305 sm->leap_done = FALSE; 306 307 switch (eap->code) { 308 case EAP_CODE_REQUEST: 309 return eap_leap_process_request(sm, priv, ret, reqData); 310 case EAP_CODE_SUCCESS: 311 return eap_leap_process_success(sm, priv, ret, reqData); 312 case EAP_CODE_RESPONSE: 313 return eap_leap_process_response(sm, priv, ret, reqData); 314 default: 315 wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - " 316 "ignored", eap->code); 317 ret->ignore = TRUE; 318 return NULL; 319 } 320 } 321 322 323 static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv) 324 { 325 struct eap_leap_data *data = priv; 326 return data->state == LEAP_DONE; 327 } 328 329 330 static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) 331 { 332 struct eap_leap_data *data = priv; 333 u8 *key, pw_hash_hash[16], pw_hash[16]; 334 const u8 *addr[5], *password; 335 size_t elen[5], password_len; 336 int pwhash; 337 338 if (data->state != LEAP_DONE) 339 return NULL; 340 341 password = eap_get_config_password2(sm, &password_len, &pwhash); 342 if (password == NULL) 343 return NULL; 344 345 key = os_malloc(LEAP_KEY_LEN); 346 if (key == NULL) 347 return NULL; 348 349 if (pwhash) { 350 if (hash_nt_password_hash(password, pw_hash_hash)) { 351 os_free(key); 352 return NULL; 353 } 354 } else { 355 if (nt_password_hash(password, password_len, pw_hash) || 356 hash_nt_password_hash(pw_hash, pw_hash_hash)) { 357 os_free(key); 358 return NULL; 359 } 360 } 361 wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash", 362 pw_hash_hash, 16); 363 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge", 364 data->peer_challenge, LEAP_CHALLENGE_LEN); 365 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response", 366 data->peer_response, LEAP_RESPONSE_LEN); 367 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge", 368 data->ap_challenge, LEAP_CHALLENGE_LEN); 369 wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response", 370 data->ap_response, LEAP_RESPONSE_LEN); 371 372 addr[0] = pw_hash_hash; 373 elen[0] = 16; 374 addr[1] = data->ap_challenge; 375 elen[1] = LEAP_CHALLENGE_LEN; 376 addr[2] = data->ap_response; 377 elen[2] = LEAP_RESPONSE_LEN; 378 addr[3] = data->peer_challenge; 379 elen[3] = LEAP_CHALLENGE_LEN; 380 addr[4] = data->peer_response; 381 elen[4] = LEAP_RESPONSE_LEN; 382 md5_vector(5, addr, elen, key); 383 wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); 384 *len = LEAP_KEY_LEN; 385 386 return key; 387 } 388 389 390 int eap_peer_leap_register(void) 391 { 392 struct eap_method *eap; 393 int ret; 394 395 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 396 EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP"); 397 if (eap == NULL) 398 return -1; 399 400 eap->init = eap_leap_init; 401 eap->deinit = eap_leap_deinit; 402 eap->process = eap_leap_process; 403 eap->isKeyAvailable = eap_leap_isKeyAvailable; 404 eap->getKey = eap_leap_getKey; 405 406 ret = eap_peer_method_register(eap); 407 if (ret) 408 eap_peer_method_free(eap); 409 return ret; 410 } 411