eap_server_mschapv2.c
Go to the documentation of this file.
00001 /*
00002  * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
00003  * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 2 as
00007  * published by the Free Software Foundation.
00008  *
00009  * Alternatively, this software may be distributed under the terms of BSD
00010  * license.
00011  *
00012  * See README and COPYING for more details.
00013  */
00014 
00015 #include "includes.h"
00016 
00017 #include "common.h"
00018 #include "crypto/ms_funcs.h"
00019 #include "eap_i.h"
00020 
00021 
00022 struct eap_mschapv2_hdr {
00023         u8 op_code; /* MSCHAPV2_OP_* */
00024         u8 mschapv2_id; /* must be changed for challenges, but not for
00025                          * success/failure */
00026         u8 ms_length[2]; /* Note: misaligned; length - 5 */
00027         /* followed by data */
00028 } STRUCT_PACKED;
00029 
00030 #define MSCHAPV2_OP_CHALLENGE 1
00031 #define MSCHAPV2_OP_RESPONSE 2
00032 #define MSCHAPV2_OP_SUCCESS 3
00033 #define MSCHAPV2_OP_FAILURE 4
00034 #define MSCHAPV2_OP_CHANGE_PASSWORD 7
00035 
00036 #define MSCHAPV2_RESP_LEN 49
00037 
00038 #define ERROR_RESTRICTED_LOGON_HOURS 646
00039 #define ERROR_ACCT_DISABLED 647
00040 #define ERROR_PASSWD_EXPIRED 648
00041 #define ERROR_NO_DIALIN_PERMISSION 649
00042 #define ERROR_AUTHENTICATION_FAILURE 691
00043 #define ERROR_CHANGING_PASSWORD 709
00044 
00045 #define PASSWD_CHANGE_CHAL_LEN 16
00046 #define MSCHAPV2_KEY_LEN 16
00047 
00048 
00049 #define CHALLENGE_LEN 16
00050 
00051 struct eap_mschapv2_data {
00052         u8 auth_challenge[CHALLENGE_LEN];
00053         int auth_challenge_from_tls;
00054         u8 *peer_challenge;
00055         u8 auth_response[20];
00056         enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
00057         u8 resp_mschapv2_id;
00058         u8 master_key[16];
00059         int master_key_valid;
00060 };
00061 
00062 
00063 static void * eap_mschapv2_init(struct eap_sm *sm)
00064 {
00065         struct eap_mschapv2_data *data;
00066 
00067         data = os_zalloc(sizeof(*data));
00068         if (data == NULL)
00069                 return NULL;
00070         data->state = CHALLENGE;
00071 
00072         if (sm->auth_challenge) {
00073                 os_memcpy(data->auth_challenge, sm->auth_challenge,
00074                           CHALLENGE_LEN);
00075                 data->auth_challenge_from_tls = 1;
00076         }
00077 
00078         if (sm->peer_challenge) {
00079                 data->peer_challenge = os_malloc(CHALLENGE_LEN);
00080                 if (data->peer_challenge == NULL) {
00081                         os_free(data);
00082                         return NULL;
00083                 }
00084                 os_memcpy(data->peer_challenge, sm->peer_challenge,
00085                           CHALLENGE_LEN);
00086         }
00087 
00088         return data;
00089 }
00090 
00091 
00092 static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
00093 {
00094         struct eap_mschapv2_data *data = priv;
00095         if (data == NULL)
00096                 return;
00097 
00098         os_free(data->peer_challenge);
00099         os_free(data);
00100 }
00101 
00102 
00103 static struct wpabuf * eap_mschapv2_build_challenge(
00104         struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
00105 {
00106         struct wpabuf *req;
00107         struct eap_mschapv2_hdr *ms;
00108         char *name = "hostapd"; /* TODO: make this configurable */
00109         size_t ms_len;
00110 
00111         if (!data->auth_challenge_from_tls &&
00112             os_get_random(data->auth_challenge, CHALLENGE_LEN)) {
00113                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
00114                            "data");
00115                 data->state = FAILURE;
00116                 return NULL;
00117         }
00118 
00119         ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name);
00120         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
00121                             EAP_CODE_REQUEST, id);
00122         if (req == NULL) {
00123                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
00124                            " for request");
00125                 data->state = FAILURE;
00126                 return NULL;
00127         }
00128 
00129         ms = wpabuf_put(req, sizeof(*ms));
00130         ms->op_code = MSCHAPV2_OP_CHALLENGE;
00131         ms->mschapv2_id = id;
00132         WPA_PUT_BE16(ms->ms_length, ms_len);
00133 
00134         wpabuf_put_u8(req, CHALLENGE_LEN);
00135         if (!data->auth_challenge_from_tls)
00136                 wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
00137         else
00138                 wpabuf_put(req, CHALLENGE_LEN);
00139         wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
00140                     data->auth_challenge, CHALLENGE_LEN);
00141         wpabuf_put_data(req, name, os_strlen(name));
00142 
00143         return req;
00144 }
00145 
00146 
00147 static struct wpabuf * eap_mschapv2_build_success_req(
00148         struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
00149 {
00150         struct wpabuf *req;
00151         struct eap_mschapv2_hdr *ms;
00152         u8 *msg;
00153         char *message = "OK";
00154         size_t ms_len;
00155 
00156         ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
00157                 os_strlen(message);
00158         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
00159                             EAP_CODE_REQUEST, id);
00160         if (req == NULL) {
00161                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
00162                            " for request");
00163                 data->state = FAILURE;
00164                 return NULL;
00165         }
00166 
00167         ms = wpabuf_put(req, sizeof(*ms));
00168         ms->op_code = MSCHAPV2_OP_SUCCESS;
00169         ms->mschapv2_id = data->resp_mschapv2_id;
00170         WPA_PUT_BE16(ms->ms_length, ms_len);
00171         msg = (u8 *) (ms + 1);
00172 
00173         wpabuf_put_u8(req, 'S');
00174         wpabuf_put_u8(req, '=');
00175         wpa_snprintf_hex_uppercase(
00176                 wpabuf_put(req, sizeof(data->auth_response) * 2),
00177                 sizeof(data->auth_response) * 2 + 1,
00178                 data->auth_response, sizeof(data->auth_response));
00179         wpabuf_put_u8(req, ' ');
00180         wpabuf_put_u8(req, 'M');
00181         wpabuf_put_u8(req, '=');
00182         wpabuf_put_data(req, message, os_strlen(message));
00183 
00184         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
00185                           msg, ms_len - sizeof(*ms));
00186 
00187         return req;
00188 }
00189 
00190 
00191 static struct wpabuf * eap_mschapv2_build_failure_req(
00192         struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
00193 {
00194         struct wpabuf *req;
00195         struct eap_mschapv2_hdr *ms;
00196         char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
00197                 "M=FAILED";
00198         size_t ms_len;
00199 
00200         ms_len = sizeof(*ms) + os_strlen(message);
00201         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
00202                             EAP_CODE_REQUEST, id);
00203         if (req == NULL) {
00204                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
00205                            " for request");
00206                 data->state = FAILURE;
00207                 return NULL;
00208         }
00209 
00210         ms = wpabuf_put(req, sizeof(*ms));
00211         ms->op_code = MSCHAPV2_OP_FAILURE;
00212         ms->mschapv2_id = data->resp_mschapv2_id;
00213         WPA_PUT_BE16(ms->ms_length, ms_len);
00214 
00215         wpabuf_put_data(req, message, os_strlen(message));
00216 
00217         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
00218                           (u8 *) message, os_strlen(message));
00219 
00220         return req;
00221 }
00222 
00223 
00224 static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
00225                                              u8 id)
00226 {
00227         struct eap_mschapv2_data *data = priv;
00228 
00229         switch (data->state) {
00230         case CHALLENGE:
00231                 return eap_mschapv2_build_challenge(sm, data, id);
00232         case SUCCESS_REQ:
00233                 return eap_mschapv2_build_success_req(sm, data, id);
00234         case FAILURE_REQ:
00235                 return eap_mschapv2_build_failure_req(sm, data, id);
00236         default:
00237                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
00238                            "buildReq", data->state);
00239                 break;
00240         }
00241         return NULL;
00242 }
00243 
00244 
00245 static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
00246                                   struct wpabuf *respData)
00247 {
00248         struct eap_mschapv2_data *data = priv;
00249         struct eap_mschapv2_hdr *resp;
00250         const u8 *pos;
00251         size_t len;
00252 
00253         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
00254                                &len);
00255         if (pos == NULL || len < 1) {
00256                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
00257                 return TRUE;
00258         }
00259 
00260         resp = (struct eap_mschapv2_hdr *) pos;
00261         if (data->state == CHALLENGE &&
00262             resp->op_code != MSCHAPV2_OP_RESPONSE) {
00263                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
00264                            "ignore op %d", resp->op_code);
00265                 return TRUE;
00266         }
00267 
00268         if (data->state == SUCCESS_REQ &&
00269             resp->op_code != MSCHAPV2_OP_SUCCESS &&
00270             resp->op_code != MSCHAPV2_OP_FAILURE) {
00271                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
00272                            "Failure - ignore op %d", resp->op_code);
00273                 return TRUE;
00274         }
00275 
00276         if (data->state == FAILURE_REQ &&
00277             resp->op_code != MSCHAPV2_OP_FAILURE) {
00278                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
00279                            "- ignore op %d", resp->op_code);
00280                 return TRUE;
00281         }
00282 
00283         return FALSE;
00284 }
00285 
00286 
00287 static void eap_mschapv2_process_response(struct eap_sm *sm,
00288                                           struct eap_mschapv2_data *data,
00289                                           struct wpabuf *respData)
00290 {
00291         struct eap_mschapv2_hdr *resp;
00292         const u8 *pos, *end, *peer_challenge, *nt_response, *name;
00293         u8 flags;
00294         size_t len, name_len, i;
00295         u8 expected[24];
00296         const u8 *username, *user;
00297         size_t username_len, user_len;
00298         int res;
00299 
00300         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
00301                                &len);
00302         if (pos == NULL || len < 1)
00303                 return; /* Should not happen - frame already validated */
00304 
00305         end = pos + len;
00306         resp = (struct eap_mschapv2_hdr *) pos;
00307         pos = (u8 *) (resp + 1);
00308 
00309         if (len < sizeof(*resp) + 1 + 49 ||
00310             resp->op_code != MSCHAPV2_OP_RESPONSE ||
00311             pos[0] != 49) {
00312                 wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
00313                                 respData);
00314                 data->state = FAILURE;
00315                 return;
00316         }
00317         data->resp_mschapv2_id = resp->mschapv2_id;
00318         pos++;
00319         peer_challenge = pos;
00320         pos += 16 + 8;
00321         nt_response = pos;
00322         pos += 24;
00323         flags = *pos++;
00324         name = pos;
00325         name_len = end - name;
00326 
00327         if (data->peer_challenge) {
00328                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
00329                            "Peer-Challenge");
00330                 peer_challenge = data->peer_challenge;
00331         }
00332         wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
00333                     peer_challenge, 16);
00334         wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
00335         wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
00336         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
00337 
00338         /* MSCHAPv2 does not include optional domain name in the
00339          * challenge-response calculation, so remove domain prefix
00340          * (if present). */
00341         username = sm->identity;
00342         username_len = sm->identity_len;
00343         for (i = 0; i < username_len; i++) {
00344                 if (username[i] == '\\') {
00345                         username_len -= i + 1;
00346                         username += i + 1;
00347                         break;
00348                 }
00349         }
00350 
00351         user = name;
00352         user_len = name_len;
00353         for (i = 0; i < user_len; i++) {
00354                 if (user[i] == '\\') {
00355                         user_len -= i + 1;
00356                         user += i + 1;
00357                         break;
00358                 }
00359         }
00360 
00361         if (username_len != user_len ||
00362             os_memcmp(username, user, username_len) != 0) {
00363                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
00364                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
00365                                   "name", username, username_len);
00366                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
00367                                   "name", user, user_len);
00368                 data->state = FAILURE;
00369                 return;
00370         }
00371 
00372         wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
00373                           username, username_len);
00374 
00375         if (sm->user->password_hash) {
00376                 res = generate_nt_response_pwhash(data->auth_challenge,
00377                                                   peer_challenge,
00378                                                   username, username_len,
00379                                                   sm->user->password,
00380                                                   expected);
00381         } else {
00382                 res = generate_nt_response(data->auth_challenge,
00383                                            peer_challenge,
00384                                            username, username_len,
00385                                            sm->user->password,
00386                                            sm->user->password_len,
00387                                            expected);
00388         }
00389         if (res) {
00390                 data->state = FAILURE;
00391                 return;
00392         }
00393 
00394         if (os_memcmp(nt_response, expected, 24) == 0) {
00395                 const u8 *pw_hash;
00396                 u8 pw_hash_buf[16], pw_hash_hash[16];
00397 
00398                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
00399                 data->state = SUCCESS_REQ;
00400 
00401                 /* Authenticator response is not really needed yet, but
00402                  * calculate it here so that peer_challenge and username need
00403                  * not be saved. */
00404                 if (sm->user->password_hash) {
00405                         pw_hash = sm->user->password;
00406                 } else {
00407                         nt_password_hash(sm->user->password,
00408                                          sm->user->password_len,
00409                                          pw_hash_buf);
00410                         pw_hash = pw_hash_buf;
00411                 }
00412                 generate_authenticator_response_pwhash(
00413                         pw_hash, peer_challenge, data->auth_challenge,
00414                         username, username_len, nt_response,
00415                         data->auth_response);
00416 
00417                 hash_nt_password_hash(pw_hash, pw_hash_hash);
00418                 get_master_key(pw_hash_hash, nt_response, data->master_key);
00419                 data->master_key_valid = 1;
00420                 wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
00421                                 data->master_key, MSCHAPV2_KEY_LEN);
00422         } else {
00423                 wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
00424                             expected, 24);
00425                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
00426                 data->state = FAILURE_REQ;
00427         }
00428 }
00429 
00430 
00431 static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
00432                                               struct eap_mschapv2_data *data,
00433                                               struct wpabuf *respData)
00434 {
00435         struct eap_mschapv2_hdr *resp;
00436         const u8 *pos;
00437         size_t len;
00438 
00439         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
00440                                &len);
00441         if (pos == NULL || len < 1)
00442                 return; /* Should not happen - frame already validated */
00443 
00444         resp = (struct eap_mschapv2_hdr *) pos;
00445 
00446         if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
00447                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
00448                            " - authentication completed successfully");
00449                 data->state = SUCCESS;
00450         } else {
00451                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
00452                            "Response - peer rejected authentication");
00453                 data->state = FAILURE;
00454         }
00455 }
00456 
00457 
00458 static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
00459                                               struct eap_mschapv2_data *data,
00460                                               struct wpabuf *respData)
00461 {
00462         struct eap_mschapv2_hdr *resp;
00463         const u8 *pos;
00464         size_t len;
00465 
00466         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
00467                                &len);
00468         if (pos == NULL || len < 1)
00469                 return; /* Should not happen - frame already validated */
00470 
00471         resp = (struct eap_mschapv2_hdr *) pos;
00472 
00473         if (resp->op_code == MSCHAPV2_OP_FAILURE) {
00474                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
00475                            " - authentication failed");
00476         } else {
00477                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
00478                            "Response - authentication failed");
00479         }
00480 
00481         data->state = FAILURE;
00482 }
00483 
00484 
00485 static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
00486                                  struct wpabuf *respData)
00487 {
00488         struct eap_mschapv2_data *data = priv;
00489 
00490         if (sm->user == NULL || sm->user->password == NULL) {
00491                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
00492                 data->state = FAILURE;
00493                 return;
00494         }
00495 
00496         switch (data->state) {
00497         case CHALLENGE:
00498                 eap_mschapv2_process_response(sm, data, respData);
00499                 break;
00500         case SUCCESS_REQ:
00501                 eap_mschapv2_process_success_resp(sm, data, respData);
00502                 break;
00503         case FAILURE_REQ:
00504                 eap_mschapv2_process_failure_resp(sm, data, respData);
00505                 break;
00506         default:
00507                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
00508                            "process", data->state);
00509                 break;
00510         }
00511 }
00512 
00513 
00514 static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
00515 {
00516         struct eap_mschapv2_data *data = priv;
00517         return data->state == SUCCESS || data->state == FAILURE;
00518 }
00519 
00520 
00521 static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
00522 {
00523         struct eap_mschapv2_data *data = priv;
00524         u8 *key;
00525 
00526         if (data->state != SUCCESS || !data->master_key_valid)
00527                 return NULL;
00528 
00529         *len = 2 * MSCHAPV2_KEY_LEN;
00530         key = os_malloc(*len);
00531         if (key == NULL)
00532                 return NULL;
00533         /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
00534         get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
00535         get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
00536                                 MSCHAPV2_KEY_LEN, 1, 1);
00537         wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
00538 
00539         return key;
00540 }
00541 
00542 
00543 static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
00544 {
00545         struct eap_mschapv2_data *data = priv;
00546         return data->state == SUCCESS;
00547 }
00548 
00549 
00550 int eap_server_mschapv2_register(void)
00551 {
00552         struct eap_method *eap;
00553         int ret;
00554 
00555         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
00556                                       EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
00557                                       "MSCHAPV2");
00558         if (eap == NULL)
00559                 return -1;
00560 
00561         eap->init = eap_mschapv2_init;
00562         eap->reset = eap_mschapv2_reset;
00563         eap->buildReq = eap_mschapv2_buildReq;
00564         eap->check = eap_mschapv2_check;
00565         eap->process = eap_mschapv2_process;
00566         eap->isDone = eap_mschapv2_isDone;
00567         eap->getKey = eap_mschapv2_getKey;
00568         eap->isSuccess = eap_mschapv2_isSuccess;
00569 
00570         ret = eap_server_method_register(eap);
00571         if (ret)
00572                 eap_server_method_free(eap);
00573         return ret;
00574 }


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Apr 24 2014 15:34:34