LCOV - code coverage report
Current view: top level - src/eap_peer - eap_mschapv2.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1401264779 Lines: 224 380 58.9 %
Date: 2014-05-28 Functions: 14 16 87.5 %

          Line data    Source code
       1             : /*
       2             :  * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
       3             :  * Copyright (c) 2004-2008, 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             :  * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
       9             :  * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
      10             :  * Extensions Protocol, Version 2, for mutual authentication and key
      11             :  * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
      12             :  * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
      13             :  * RFC 3079.
      14             :  */
      15             : 
      16             : #include "includes.h"
      17             : 
      18             : #include "common.h"
      19             : #include "crypto/ms_funcs.h"
      20             : #include "crypto/random.h"
      21             : #include "common/wpa_ctrl.h"
      22             : #include "mschapv2.h"
      23             : #include "eap_i.h"
      24             : #include "eap_config.h"
      25             : 
      26             : 
      27             : #ifdef _MSC_VER
      28             : #pragma pack(push, 1)
      29             : #endif /* _MSC_VER */
      30             : 
      31             : struct eap_mschapv2_hdr {
      32             :         u8 op_code; /* MSCHAPV2_OP_* */
      33             :         u8 mschapv2_id; /* usually same as EAP identifier; must be changed
      34             :                          * for challenges, but not for success/failure */
      35             :         u8 ms_length[2]; /* Note: misaligned; length - 5 */
      36             :         /* followed by data */
      37             : } STRUCT_PACKED;
      38             : 
      39             : /* Response Data field */
      40             : struct ms_response {
      41             :         u8 peer_challenge[MSCHAPV2_CHAL_LEN];
      42             :         u8 reserved[8];
      43             :         u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
      44             :         u8 flags;
      45             : } STRUCT_PACKED;
      46             : 
      47             : /* Change-Password Data field */
      48             : struct ms_change_password {
      49             :         u8 encr_password[516];
      50             :         u8 encr_hash[16];
      51             :         u8 peer_challenge[MSCHAPV2_CHAL_LEN];
      52             :         u8 reserved[8];
      53             :         u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
      54             :         u8 flags[2];
      55             : } STRUCT_PACKED;
      56             : 
      57             : #ifdef _MSC_VER
      58             : #pragma pack(pop)
      59             : #endif /* _MSC_VER */
      60             : 
      61             : #define MSCHAPV2_OP_CHALLENGE 1
      62             : #define MSCHAPV2_OP_RESPONSE 2
      63             : #define MSCHAPV2_OP_SUCCESS 3
      64             : #define MSCHAPV2_OP_FAILURE 4
      65             : #define MSCHAPV2_OP_CHANGE_PASSWORD 7
      66             : 
      67             : #define ERROR_RESTRICTED_LOGON_HOURS 646
      68             : #define ERROR_ACCT_DISABLED 647
      69             : #define ERROR_PASSWD_EXPIRED 648
      70             : #define ERROR_NO_DIALIN_PERMISSION 649
      71             : #define ERROR_AUTHENTICATION_FAILURE 691
      72             : #define ERROR_CHANGING_PASSWORD 709
      73             : 
      74             : #define PASSWD_CHANGE_CHAL_LEN 16
      75             : #define MSCHAPV2_KEY_LEN 16
      76             : 
      77             : 
      78             : struct eap_mschapv2_data {
      79             :         u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
      80             :         int auth_response_valid;
      81             : 
      82             :         int prev_error;
      83             :         u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
      84             :         int passwd_change_challenge_valid;
      85             :         int passwd_change_version;
      86             : 
      87             :         /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
      88             :          */
      89             :         u8 *peer_challenge;
      90             :         u8 *auth_challenge;
      91             : 
      92             :         int phase2;
      93             :         u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
      94             :         int master_key_valid;
      95             :         int success;
      96             : 
      97             :         struct wpabuf *prev_challenge;
      98             : };
      99             : 
     100             : 
     101             : static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
     102             : 
     103             : 
     104          42 : static void * eap_mschapv2_init(struct eap_sm *sm)
     105             : {
     106             :         struct eap_mschapv2_data *data;
     107          42 :         data = os_zalloc(sizeof(*data));
     108          42 :         if (data == NULL)
     109           0 :                 return NULL;
     110             : 
     111          42 :         if (sm->peer_challenge) {
     112           4 :                 data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
     113           4 :                 if (data->peer_challenge == NULL) {
     114           0 :                         eap_mschapv2_deinit(sm, data);
     115           0 :                         return NULL;
     116             :                 }
     117           4 :                 os_memcpy(data->peer_challenge, sm->peer_challenge,
     118             :                           MSCHAPV2_CHAL_LEN);
     119             :         }
     120             : 
     121          42 :         if (sm->auth_challenge) {
     122           4 :                 data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
     123           4 :                 if (data->auth_challenge == NULL) {
     124           0 :                         eap_mschapv2_deinit(sm, data);
     125           0 :                         return NULL;
     126             :                 }
     127           4 :                 os_memcpy(data->auth_challenge, sm->auth_challenge,
     128             :                           MSCHAPV2_CHAL_LEN);
     129             :         }
     130             : 
     131          42 :         data->phase2 = sm->init_phase2;
     132             : 
     133          42 :         return data;
     134             : }
     135             : 
     136             : 
     137          42 : static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
     138             : {
     139          42 :         struct eap_mschapv2_data *data = priv;
     140          42 :         os_free(data->peer_challenge);
     141          42 :         os_free(data->auth_challenge);
     142          42 :         wpabuf_free(data->prev_challenge);
     143          42 :         os_free(data);
     144          42 : }
     145             : 
     146             : 
     147          46 : static struct wpabuf * eap_mschapv2_challenge_reply(
     148             :         struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
     149             :         u8 mschapv2_id, const u8 *auth_challenge)
     150             : {
     151             :         struct wpabuf *resp;
     152             :         struct eap_mschapv2_hdr *ms;
     153             :         u8 *peer_challenge;
     154             :         int ms_len;
     155             :         struct ms_response *r;
     156             :         size_t identity_len, password_len;
     157             :         const u8 *identity, *password;
     158             :         int pwhash;
     159             : 
     160          46 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
     161             : 
     162          46 :         identity = eap_get_config_identity(sm, &identity_len);
     163          46 :         password = eap_get_config_password2(sm, &password_len, &pwhash);
     164          46 :         if (identity == NULL || password == NULL)
     165           0 :                 return NULL;
     166             : 
     167          46 :         ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
     168          46 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
     169             :                              EAP_CODE_RESPONSE, id);
     170          46 :         if (resp == NULL)
     171           0 :                 return NULL;
     172             : 
     173          46 :         ms = wpabuf_put(resp, sizeof(*ms));
     174          46 :         ms->op_code = MSCHAPV2_OP_RESPONSE;
     175          46 :         ms->mschapv2_id = mschapv2_id;
     176          46 :         if (data->prev_error) {
     177             :                 /*
     178             :                  * TODO: this does not seem to be enough when processing two
     179             :                  * or more failure messages. IAS did not increment mschapv2_id
     180             :                  * in its own packets, but it seemed to expect the peer to
     181             :                  * increment this for all packets(?).
     182             :                  */
     183           0 :                 ms->mschapv2_id++;
     184             :         }
     185          46 :         WPA_PUT_BE16(ms->ms_length, ms_len);
     186             : 
     187          46 :         wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
     188             : 
     189             :         /* Response */
     190          46 :         r = wpabuf_put(resp, sizeof(*r));
     191          46 :         peer_challenge = r->peer_challenge;
     192          46 :         if (data->peer_challenge) {
     193           4 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
     194             :                            "in Phase 1");
     195           4 :                 peer_challenge = data->peer_challenge;
     196           4 :                 os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
     197          42 :         } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
     198           0 :                 wpabuf_free(resp);
     199           0 :                 return NULL;
     200             :         }
     201          46 :         os_memset(r->reserved, 0, 8);
     202          46 :         if (data->auth_challenge) {
     203           4 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
     204             :                            "in Phase 1");
     205           4 :                 auth_challenge = data->auth_challenge;
     206             :         }
     207          46 :         if (mschapv2_derive_response(identity, identity_len, password,
     208             :                                      password_len, pwhash, auth_challenge,
     209          46 :                                      peer_challenge, r->nt_response,
     210          46 :                                      data->auth_response, data->master_key)) {
     211           0 :                 wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
     212             :                            "response");
     213           0 :                 wpabuf_free(resp);
     214           0 :                 return NULL;
     215             :         }
     216          46 :         data->auth_response_valid = 1;
     217          46 :         data->master_key_valid = 1;
     218             : 
     219          46 :         r->flags = 0; /* reserved, must be zero */
     220             : 
     221          46 :         wpabuf_put_data(resp, identity, identity_len);
     222          46 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
     223          46 :                    "(response)", id, ms->mschapv2_id);
     224          46 :         return resp;
     225             : }
     226             : 
     227             : 
     228             : /**
     229             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
     230             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     231             :  * @data: Pointer to private EAP method data from eap_mschapv2_init()
     232             :  * @ret: Return values from EAP request validation and processing
     233             :  * @req: Pointer to EAP-MSCHAPv2 header from the request
     234             :  * @req_len: Length of the EAP-MSCHAPv2 data
     235             :  * @id: EAP identifier used in the request
     236             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     237             :  * no reply available
     238             :  */
     239          46 : static struct wpabuf * eap_mschapv2_challenge(
     240             :         struct eap_sm *sm, struct eap_mschapv2_data *data,
     241             :         struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
     242             :         size_t req_len, u8 id)
     243             : {
     244             :         size_t len, challenge_len;
     245             :         const u8 *pos, *challenge;
     246             : 
     247          92 :         if (eap_get_config_identity(sm, &len) == NULL ||
     248          46 :             eap_get_config_password(sm, &len) == NULL)
     249           0 :                 return NULL;
     250             : 
     251          46 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
     252          46 :         if (req_len < sizeof(*req) + 1) {
     253           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
     254             :                            "(len %lu)", (unsigned long) req_len);
     255           0 :                 ret->ignore = TRUE;
     256           0 :                 return NULL;
     257             :         }
     258          46 :         pos = (const u8 *) (req + 1);
     259          46 :         challenge_len = *pos++;
     260          46 :         len = req_len - sizeof(*req) - 1;
     261          46 :         if (challenge_len != MSCHAPV2_CHAL_LEN) {
     262           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
     263             :                            "%lu", (unsigned long) challenge_len);
     264           0 :                 ret->ignore = TRUE;
     265           0 :                 return NULL;
     266             :         }
     267             : 
     268          46 :         if (len < challenge_len) {
     269           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
     270             :                            " packet: len=%lu challenge_len=%lu",
     271             :                            (unsigned long) len, (unsigned long) challenge_len);
     272           0 :                 ret->ignore = TRUE;
     273           0 :                 return NULL;
     274             :         }
     275             : 
     276          46 :         if (data->passwd_change_challenge_valid) {
     277           0 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
     278             :                            "failure message");
     279           0 :                 challenge = data->passwd_change_challenge;
     280             :         } else
     281          46 :                 challenge = pos;
     282          46 :         pos += challenge_len;
     283          46 :         len -= challenge_len;
     284          46 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
     285             :                     pos, len);
     286             : 
     287          46 :         ret->ignore = FALSE;
     288          46 :         ret->methodState = METHOD_MAY_CONT;
     289          46 :         ret->decision = DECISION_FAIL;
     290          46 :         ret->allowNotifications = TRUE;
     291             : 
     292          46 :         return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
     293             :                                             challenge);
     294             : }
     295             : 
     296             : 
     297           0 : static void eap_mschapv2_password_changed(struct eap_sm *sm,
     298             :                                           struct eap_mschapv2_data *data)
     299             : {
     300           0 :         struct eap_peer_config *config = eap_get_config(sm);
     301           0 :         if (config && config->new_password) {
     302           0 :                 wpa_msg(sm->msg_ctx, MSG_INFO,
     303             :                         WPA_EVENT_PASSWORD_CHANGED
     304             :                         "EAP-MSCHAPV2: Password changed successfully");
     305           0 :                 data->prev_error = 0;
     306           0 :                 os_free(config->password);
     307           0 :                 if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
     308             :                         /* TODO: update external storage */
     309           0 :                 } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
     310           0 :                         config->password = os_malloc(16);
     311           0 :                         config->password_len = 16;
     312           0 :                         if (config->password) {
     313           0 :                                 nt_password_hash(config->new_password,
     314             :                                                  config->new_password_len,
     315             :                                                  config->password);
     316             :                         }
     317           0 :                         os_free(config->new_password);
     318             :                 } else {
     319           0 :                         config->password = config->new_password;
     320           0 :                         config->password_len = config->new_password_len;
     321             :                 }
     322           0 :                 config->new_password = NULL;
     323           0 :                 config->new_password_len = 0;
     324             :         }
     325           0 : }
     326             : 
     327             : 
     328             : /**
     329             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
     330             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     331             :  * @data: Pointer to private EAP method data from eap_mschapv2_init()
     332             :  * @ret: Return values from EAP request validation and processing
     333             :  * @req: Pointer to EAP-MSCHAPv2 header from the request
     334             :  * @req_len: Length of the EAP-MSCHAPv2 data
     335             :  * @id: EAP identifier used in th erequest
     336             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     337             :  * no reply available
     338             :  */
     339          44 : static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
     340             :                                             struct eap_mschapv2_data *data,
     341             :                                             struct eap_method_ret *ret,
     342             :                                             const struct eap_mschapv2_hdr *req,
     343             :                                             size_t req_len, u8 id)
     344             : {
     345             :         struct wpabuf *resp;
     346             :         const u8 *pos;
     347             :         size_t len;
     348             : 
     349          44 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
     350          44 :         len = req_len - sizeof(*req);
     351          44 :         pos = (const u8 *) (req + 1);
     352          88 :         if (!data->auth_response_valid ||
     353          44 :             mschapv2_verify_auth_response(data->auth_response, pos, len)) {
     354           0 :                 wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
     355             :                            "response in success request");
     356           0 :                 ret->methodState = METHOD_DONE;
     357           0 :                 ret->decision = DECISION_FAIL;
     358           0 :                 return NULL;
     359             :         }
     360          44 :         pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
     361          44 :         len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
     362         132 :         while (len > 0 && *pos == ' ') {
     363          44 :                 pos++;
     364          44 :                 len--;
     365             :         }
     366          44 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
     367             :                           pos, len);
     368          44 :         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
     369             : 
     370             :         /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
     371             :          * message. */
     372          44 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
     373             :                              EAP_CODE_RESPONSE, id);
     374          44 :         if (resp == NULL) {
     375           0 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
     376             :                            "buffer for success response");
     377           0 :                 ret->ignore = TRUE;
     378           0 :                 return NULL;
     379             :         }
     380             : 
     381          44 :         wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
     382             : 
     383          44 :         ret->methodState = METHOD_DONE;
     384          44 :         ret->decision = DECISION_UNCOND_SUCC;
     385          44 :         ret->allowNotifications = FALSE;
     386          44 :         data->success = 1;
     387             : 
     388          44 :         if (data->prev_error == ERROR_PASSWD_EXPIRED)
     389           0 :                 eap_mschapv2_password_changed(sm, data);
     390             : 
     391          44 :         return resp;
     392             : }
     393             : 
     394             : 
     395           2 : static int eap_mschapv2_failure_txt(struct eap_sm *sm,
     396             :                                     struct eap_mschapv2_data *data, char *txt)
     397             : {
     398           2 :         char *pos, *msg = "";
     399           2 :         int retry = 1;
     400           2 :         struct eap_peer_config *config = eap_get_config(sm);
     401             : 
     402             :         /* For example:
     403             :          * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
     404             :          */
     405             : 
     406           2 :         pos = txt;
     407             : 
     408           2 :         if (pos && os_strncmp(pos, "E=", 2) == 0) {
     409           2 :                 pos += 2;
     410           2 :                 data->prev_error = atoi(pos);
     411           2 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
     412             :                            data->prev_error);
     413           2 :                 pos = os_strchr(pos, ' ');
     414           2 :                 if (pos)
     415           2 :                         pos++;
     416             :         }
     417             : 
     418           2 :         if (pos && os_strncmp(pos, "R=", 2) == 0) {
     419           2 :                 pos += 2;
     420           2 :                 retry = atoi(pos);
     421           2 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
     422             :                            retry == 1 ? "" : "not ");
     423           2 :                 pos = os_strchr(pos, ' ');
     424           2 :                 if (pos)
     425           2 :                         pos++;
     426             :         }
     427             : 
     428           4 :         if (pos && os_strncmp(pos, "C=", 2) == 0) {
     429             :                 int hex_len;
     430           2 :                 pos += 2;
     431           2 :                 hex_len = os_strchr(pos, ' ') - (char *) pos;
     432           2 :                 if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
     433           2 :                         if (hexstr2bin(pos, data->passwd_change_challenge,
     434             :                                        PASSWD_CHANGE_CHAL_LEN)) {
     435           0 :                                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
     436             :                                            "failure challenge");
     437             :                         } else {
     438           2 :                                 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
     439             :                                             "challenge",
     440           2 :                                             data->passwd_change_challenge,
     441             :                                             PASSWD_CHANGE_CHAL_LEN);
     442           2 :                                 data->passwd_change_challenge_valid = 1;
     443             :                         }
     444             :                 } else {
     445           0 :                         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
     446             :                                    "challenge len %d", hex_len);
     447             :                 }
     448           2 :                 pos = os_strchr(pos, ' ');
     449           2 :                 if (pos)
     450           2 :                         pos++;
     451             :         } else {
     452           0 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
     453             :                            "was not present in failure message");
     454             :         }
     455             : 
     456           2 :         if (pos && os_strncmp(pos, "V=", 2) == 0) {
     457           2 :                 pos += 2;
     458           2 :                 data->passwd_change_version = atoi(pos);
     459           2 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
     460             :                            "protocol version %d", data->passwd_change_version);
     461           2 :                 pos = os_strchr(pos, ' ');
     462           2 :                 if (pos)
     463           2 :                         pos++;
     464             :         }
     465             : 
     466           2 :         if (pos && os_strncmp(pos, "M=", 2) == 0) {
     467           2 :                 pos += 2;
     468           2 :                 msg = pos;
     469             :         }
     470           2 :         wpa_msg(sm->msg_ctx, MSG_WARNING,
     471             :                 "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
     472             :                 "%d)",
     473             :                 msg, retry == 1 ? "" : "not ", data->prev_error);
     474           2 :         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
     475           0 :             data->passwd_change_version == 3 && config) {
     476           0 :                 if (config->new_password == NULL) {
     477           0 :                         wpa_msg(sm->msg_ctx, MSG_INFO,
     478             :                                 "EAP-MSCHAPV2: Password expired - password "
     479             :                                 "change required");
     480           0 :                         eap_sm_request_new_password(sm);
     481             :                 }
     482           2 :         } else if (retry == 1 && config) {
     483             :                 /* TODO: could prevent the current password from being used
     484             :                  * again at least for some period of time */
     485           0 :                 if (!config->mschapv2_retry)
     486           0 :                         eap_sm_request_identity(sm);
     487           0 :                 eap_sm_request_password(sm);
     488           0 :                 config->mschapv2_retry = 1;
     489           2 :         } else if (config) {
     490             :                 /* TODO: prevent retries using same username/password */
     491           2 :                 config->mschapv2_retry = 0;
     492             :         }
     493             : 
     494           2 :         return retry == 1;
     495             : }
     496             : 
     497             : 
     498           0 : static struct wpabuf * eap_mschapv2_change_password(
     499             :         struct eap_sm *sm, struct eap_mschapv2_data *data,
     500             :         struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
     501             : {
     502             :         struct wpabuf *resp;
     503             :         int ms_len;
     504             :         const u8 *username, *password, *new_password;
     505             :         size_t username_len, password_len, new_password_len;
     506             :         struct eap_mschapv2_hdr *ms;
     507             :         struct ms_change_password *cp;
     508             :         u8 password_hash[16], password_hash_hash[16];
     509             :         int pwhash;
     510             : 
     511           0 :         username = eap_get_config_identity(sm, &username_len);
     512           0 :         password = eap_get_config_password2(sm, &password_len, &pwhash);
     513           0 :         new_password = eap_get_config_new_password(sm, &new_password_len);
     514           0 :         if (username == NULL || password == NULL || new_password == NULL)
     515           0 :                 return NULL;
     516             : 
     517           0 :         username = mschapv2_remove_domain(username, &username_len);
     518             : 
     519           0 :         ret->ignore = FALSE;
     520           0 :         ret->methodState = METHOD_MAY_CONT;
     521           0 :         ret->decision = DECISION_COND_SUCC;
     522           0 :         ret->allowNotifications = TRUE;
     523             : 
     524           0 :         ms_len = sizeof(*ms) + sizeof(*cp);
     525           0 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
     526             :                              EAP_CODE_RESPONSE, id);
     527           0 :         if (resp == NULL)
     528           0 :                 return NULL;
     529             : 
     530           0 :         ms = wpabuf_put(resp, sizeof(*ms));
     531           0 :         ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
     532           0 :         ms->mschapv2_id = req->mschapv2_id + 1;
     533           0 :         WPA_PUT_BE16(ms->ms_length, ms_len);
     534           0 :         cp = wpabuf_put(resp, sizeof(*cp));
     535             : 
     536             :         /* Encrypted-Password */
     537           0 :         if (pwhash) {
     538           0 :                 if (encrypt_pw_block_with_password_hash(
     539             :                             new_password, new_password_len,
     540           0 :                             password, cp->encr_password))
     541           0 :                         goto fail;
     542             :         } else {
     543           0 :                 if (new_password_encrypted_with_old_nt_password_hash(
     544             :                             new_password, new_password_len,
     545           0 :                             password, password_len, cp->encr_password))
     546           0 :                         goto fail;
     547             :         }
     548             : 
     549             :         /* Encrypted-Hash */
     550           0 :         if (pwhash) {
     551             :                 u8 new_password_hash[16];
     552           0 :                 nt_password_hash(new_password, new_password_len,
     553             :                                  new_password_hash);
     554           0 :                 nt_password_hash_encrypted_with_block(password,
     555             :                                                       new_password_hash,
     556           0 :                                                       cp->encr_hash);
     557             :         } else {
     558           0 :                 old_nt_password_hash_encrypted_with_new_nt_password_hash(
     559             :                         new_password, new_password_len,
     560           0 :                         password, password_len, cp->encr_hash);
     561             :         }
     562             : 
     563             :         /* Peer-Challenge */
     564           0 :         if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
     565           0 :                 goto fail;
     566             : 
     567             :         /* Reserved, must be zero */
     568           0 :         os_memset(cp->reserved, 0, 8);
     569             : 
     570             :         /* NT-Response */
     571           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
     572           0 :                     data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
     573           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
     574           0 :                     cp->peer_challenge, MSCHAPV2_CHAL_LEN);
     575           0 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
     576             :                           username, username_len);
     577           0 :         wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
     578             :                               new_password, new_password_len);
     579           0 :         generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
     580             :                              username, username_len,
     581             :                              new_password, new_password_len,
     582           0 :                              cp->nt_response);
     583           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
     584           0 :                     cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
     585             : 
     586             :         /* Authenticator response is not really needed yet, but calculate it
     587             :          * here so that challenges need not be saved. */
     588           0 :         generate_authenticator_response(new_password, new_password_len,
     589           0 :                                         cp->peer_challenge,
     590           0 :                                         data->passwd_change_challenge,
     591             :                                         username, username_len,
     592           0 :                                         cp->nt_response, data->auth_response);
     593           0 :         data->auth_response_valid = 1;
     594             : 
     595             :         /* Likewise, generate master_key here since we have the needed data
     596             :          * available. */
     597           0 :         nt_password_hash(new_password, new_password_len, password_hash);
     598           0 :         hash_nt_password_hash(password_hash, password_hash_hash);
     599           0 :         get_master_key(password_hash_hash, cp->nt_response, data->master_key);
     600           0 :         data->master_key_valid = 1;
     601             : 
     602             :         /* Flags */
     603           0 :         os_memset(cp->flags, 0, 2);
     604             : 
     605           0 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
     606           0 :                    "(change pw)", id, ms->mschapv2_id);
     607             : 
     608           0 :         return resp;
     609             : 
     610             : fail:
     611           0 :         wpabuf_free(resp);
     612           0 :         return NULL;
     613             : }
     614             : 
     615             : 
     616             : /**
     617             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
     618             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     619             :  * @data: Pointer to private EAP method data from eap_mschapv2_init()
     620             :  * @ret: Return values from EAP request validation and processing
     621             :  * @req: Pointer to EAP-MSCHAPv2 header from the request
     622             :  * @req_len: Length of the EAP-MSCHAPv2 data
     623             :  * @id: EAP identifier used in th erequest
     624             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     625             :  * no reply available
     626             :  */
     627           2 : static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
     628             :                                             struct eap_mschapv2_data *data,
     629             :                                             struct eap_method_ret *ret,
     630             :                                             const struct eap_mschapv2_hdr *req,
     631             :                                             size_t req_len, u8 id)
     632             : {
     633             :         struct wpabuf *resp;
     634           2 :         const u8 *msdata = (const u8 *) (req + 1);
     635             :         char *buf;
     636           2 :         size_t len = req_len - sizeof(*req);
     637           2 :         int retry = 0;
     638             : 
     639           2 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
     640           2 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
     641             :                           msdata, len);
     642             :         /*
     643             :          * eap_mschapv2_failure_txt() expects a nul terminated string, so we
     644             :          * must allocate a large enough temporary buffer to create that since
     645             :          * the received message does not include nul termination.
     646             :          */
     647           2 :         buf = dup_binstr(msdata, len);
     648           2 :         if (buf) {
     649           2 :                 retry = eap_mschapv2_failure_txt(sm, data, buf);
     650           2 :                 os_free(buf);
     651             :         }
     652             : 
     653           2 :         ret->ignore = FALSE;
     654           2 :         ret->methodState = METHOD_DONE;
     655           2 :         ret->decision = DECISION_FAIL;
     656           2 :         ret->allowNotifications = FALSE;
     657             : 
     658           2 :         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
     659           0 :             data->passwd_change_version == 3) {
     660           0 :                 struct eap_peer_config *config = eap_get_config(sm);
     661           0 :                 if (config && config->new_password)
     662           0 :                         return eap_mschapv2_change_password(sm, data, ret, req,
     663             :                                                             id);
     664           0 :                 if (config && config->pending_req_new_password)
     665           0 :                         return NULL;
     666           2 :         } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
     667             :                 /* TODO: could try to retry authentication, e.g, after having
     668             :                  * changed the username/password. In this case, EAP MS-CHAP-v2
     669             :                  * Failure Response would not be sent here. */
     670           0 :                 return NULL;
     671             :         }
     672             : 
     673             :         /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
     674             :          * message. */
     675           2 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
     676             :                              EAP_CODE_RESPONSE, id);
     677           2 :         if (resp == NULL)
     678           0 :                 return NULL;
     679             : 
     680           2 :         wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
     681             : 
     682           2 :         return resp;
     683             : }
     684             : 
     685             : 
     686          94 : static int eap_mschapv2_check_config(struct eap_sm *sm)
     687             : {
     688             :         size_t len;
     689             : 
     690          94 :         if (eap_get_config_identity(sm, &len) == NULL) {
     691           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
     692           0 :                 eap_sm_request_identity(sm);
     693           0 :                 return -1;
     694             :         }
     695             : 
     696          94 :         if (eap_get_config_password(sm, &len) == NULL) {
     697           2 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
     698           2 :                 eap_sm_request_password(sm);
     699           2 :                 return -1;
     700             :         }
     701             : 
     702          92 :         return 0;
     703             : }
     704             : 
     705             : 
     706          92 : static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
     707             :                                     const struct eap_mschapv2_hdr *ms)
     708             : {
     709          92 :         size_t ms_len = WPA_GET_BE16(ms->ms_length);
     710             : 
     711          92 :         if (ms_len == len)
     712          92 :                 return 0;
     713             : 
     714           0 :         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
     715             :                    "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
     716           0 :         if (sm->workaround) {
     717             :                 /* Some authentication servers use invalid ms_len,
     718             :                  * ignore it for interoperability. */
     719           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
     720             :                            " invalid ms_len %lu (len %lu)",
     721             :                            (unsigned long) ms_len,
     722             :                            (unsigned long) len);
     723           0 :                 return 0;
     724             :         }
     725             : 
     726           0 :         return -1;
     727             : }
     728             : 
     729             : 
     730          46 : static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
     731             :                                         const struct wpabuf *reqData)
     732             : {
     733             :         /*
     734             :          * Store a copy of the challenge message, so that it can be processed
     735             :          * again in case retry is allowed after a possible failure.
     736             :          */
     737          46 :         wpabuf_free(data->prev_challenge);
     738          46 :         data->prev_challenge = wpabuf_dup(reqData);
     739          46 : }
     740             : 
     741             : 
     742             : /**
     743             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
     744             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     745             :  * @priv: Pointer to private EAP method data from eap_mschapv2_init()
     746             :  * @ret: Return values from EAP request validation and processing
     747             :  * @reqData: EAP request to be processed (eapReqData)
     748             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     749             :  * no reply available
     750             :  */
     751          94 : static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
     752             :                                             struct eap_method_ret *ret,
     753             :                                             const struct wpabuf *reqData)
     754             : {
     755          94 :         struct eap_mschapv2_data *data = priv;
     756          94 :         struct eap_peer_config *config = eap_get_config(sm);
     757             :         const struct eap_mschapv2_hdr *ms;
     758          94 :         int using_prev_challenge = 0;
     759             :         const u8 *pos;
     760             :         size_t len;
     761             :         u8 id;
     762             : 
     763          94 :         if (eap_mschapv2_check_config(sm)) {
     764           2 :                 ret->ignore = TRUE;
     765           2 :                 return NULL;
     766             :         }
     767             : 
     768          92 :         if (config->mschapv2_retry && data->prev_challenge &&
     769           0 :             data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
     770           0 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
     771             :                            "with the previous challenge");
     772             : 
     773           0 :                 reqData = data->prev_challenge;
     774           0 :                 using_prev_challenge = 1;
     775           0 :                 config->mschapv2_retry = 0;
     776             :         }
     777             : 
     778          92 :         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
     779             :                                &len);
     780          92 :         if (pos == NULL || len < sizeof(*ms) + 1) {
     781           0 :                 ret->ignore = TRUE;
     782           0 :                 return NULL;
     783             :         }
     784             : 
     785          92 :         ms = (const struct eap_mschapv2_hdr *) pos;
     786          92 :         if (eap_mschapv2_check_mslen(sm, len, ms)) {
     787           0 :                 ret->ignore = TRUE;
     788           0 :                 return NULL;
     789             :         }
     790             : 
     791          92 :         id = eap_get_id(reqData);
     792          92 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
     793          92 :                    id, ms->mschapv2_id);
     794             : 
     795          92 :         switch (ms->op_code) {
     796             :         case MSCHAPV2_OP_CHALLENGE:
     797          46 :                 if (!using_prev_challenge)
     798          46 :                         eap_mschapv2_copy_challenge(data, reqData);
     799          46 :                 return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
     800             :         case MSCHAPV2_OP_SUCCESS:
     801          44 :                 return eap_mschapv2_success(sm, data, ret, ms, len, id);
     802             :         case MSCHAPV2_OP_FAILURE:
     803           2 :                 return eap_mschapv2_failure(sm, data, ret, ms, len, id);
     804             :         default:
     805           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
     806           0 :                            ms->op_code);
     807           0 :                 ret->ignore = TRUE;
     808           0 :                 return NULL;
     809             :         }
     810             : }
     811             : 
     812             : 
     813          20 : static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
     814             : {
     815          20 :         struct eap_mschapv2_data *data = priv;
     816          20 :         return data->success && data->master_key_valid;
     817             : }
     818             : 
     819             : 
     820          20 : static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
     821             : {
     822          20 :         struct eap_mschapv2_data *data = priv;
     823             :         u8 *key;
     824             :         int key_len;
     825             : 
     826          20 :         if (!data->master_key_valid || !data->success)
     827           0 :                 return NULL;
     828             : 
     829          20 :         key_len = 2 * MSCHAPV2_KEY_LEN;
     830             : 
     831          20 :         key = os_malloc(key_len);
     832          20 :         if (key == NULL)
     833           0 :                 return NULL;
     834             : 
     835             :         /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
     836             :          *      peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
     837          20 :         get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
     838          20 :         get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
     839             :                                 MSCHAPV2_KEY_LEN, 0, 0);
     840             : 
     841          20 :         wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
     842             :                         key, key_len);
     843             : 
     844          20 :         *len = key_len;
     845          20 :         return key;
     846             : }
     847             : 
     848             : 
     849             : /**
     850             :  * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
     851             :  * Returns: 0 on success, -1 on failure
     852             :  *
     853             :  * This function is used to register EAP-MSCHAPv2 peer method into the EAP
     854             :  * method list.
     855             :  */
     856           4 : int eap_peer_mschapv2_register(void)
     857             : {
     858             :         struct eap_method *eap;
     859             :         int ret;
     860             : 
     861           4 :         eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
     862             :                                     EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
     863             :                                     "MSCHAPV2");
     864           4 :         if (eap == NULL)
     865           0 :                 return -1;
     866             : 
     867           4 :         eap->init = eap_mschapv2_init;
     868           4 :         eap->deinit = eap_mschapv2_deinit;
     869           4 :         eap->process = eap_mschapv2_process;
     870           4 :         eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
     871           4 :         eap->getKey = eap_mschapv2_getKey;
     872             : 
     873           4 :         ret = eap_peer_method_register(eap);
     874           4 :         if (ret)
     875           0 :                 eap_peer_method_free(eap);
     876           4 :         return ret;
     877             : }

Generated by: LCOV version 1.10