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 1443382998 Lines: 225 393 57.3 %
Date: 2015-09-27 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          55 : static void * eap_mschapv2_init(struct eap_sm *sm)
     105             : {
     106             :         struct eap_mschapv2_data *data;
     107          55 :         data = os_zalloc(sizeof(*data));
     108          55 :         if (data == NULL)
     109           0 :                 return NULL;
     110             : 
     111          55 :         if (sm->peer_challenge) {
     112           5 :                 data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
     113           5 :                 if (data->peer_challenge == NULL) {
     114           0 :                         eap_mschapv2_deinit(sm, data);
     115           0 :                         return NULL;
     116             :                 }
     117           5 :                 os_memcpy(data->peer_challenge, sm->peer_challenge,
     118             :                           MSCHAPV2_CHAL_LEN);
     119             :         }
     120             : 
     121          55 :         if (sm->auth_challenge) {
     122           5 :                 data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
     123           5 :                 if (data->auth_challenge == NULL) {
     124           0 :                         eap_mschapv2_deinit(sm, data);
     125           0 :                         return NULL;
     126             :                 }
     127           5 :                 os_memcpy(data->auth_challenge, sm->auth_challenge,
     128             :                           MSCHAPV2_CHAL_LEN);
     129             :         }
     130             : 
     131          55 :         data->phase2 = sm->init_phase2;
     132             : 
     133          55 :         return data;
     134             : }
     135             : 
     136             : 
     137          55 : static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
     138             : {
     139          55 :         struct eap_mschapv2_data *data = priv;
     140          55 :         os_free(data->peer_challenge);
     141          55 :         os_free(data->auth_challenge);
     142          55 :         wpabuf_free(data->prev_challenge);
     143          55 :         bin_clear_free(data, sizeof(*data));
     144          55 : }
     145             : 
     146             : 
     147          63 : 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          63 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
     161             : 
     162          63 :         identity = eap_get_config_identity(sm, &identity_len);
     163          63 :         password = eap_get_config_password2(sm, &password_len, &pwhash);
     164          63 :         if (identity == NULL || password == NULL)
     165           0 :                 return NULL;
     166             : 
     167          63 :         ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
     168          63 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
     169             :                              EAP_CODE_RESPONSE, id);
     170          63 :         if (resp == NULL)
     171           0 :                 return NULL;
     172             : 
     173          63 :         ms = wpabuf_put(resp, sizeof(*ms));
     174          63 :         ms->op_code = MSCHAPV2_OP_RESPONSE;
     175          63 :         ms->mschapv2_id = mschapv2_id;
     176          63 :         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          63 :         WPA_PUT_BE16(ms->ms_length, ms_len);
     186             : 
     187          63 :         wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
     188             : 
     189             :         /* Response */
     190          63 :         r = wpabuf_put(resp, sizeof(*r));
     191          63 :         peer_challenge = r->peer_challenge;
     192          63 :         if (data->peer_challenge) {
     193           5 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
     194             :                            "in Phase 1");
     195           5 :                 peer_challenge = data->peer_challenge;
     196           5 :                 os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
     197          58 :         } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
     198           0 :                 wpabuf_free(resp);
     199           0 :                 return NULL;
     200             :         }
     201          63 :         os_memset(r->reserved, 0, 8);
     202          63 :         if (data->auth_challenge) {
     203           5 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
     204             :                            "in Phase 1");
     205           5 :                 auth_challenge = data->auth_challenge;
     206             :         }
     207          63 :         if (mschapv2_derive_response(identity, identity_len, password,
     208             :                                      password_len, pwhash, auth_challenge,
     209          63 :                                      peer_challenge, r->nt_response,
     210          63 :                                      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          63 :         data->auth_response_valid = 1;
     217          63 :         data->master_key_valid = 1;
     218             : 
     219          63 :         r->flags = 0; /* reserved, must be zero */
     220             : 
     221          63 :         wpabuf_put_data(resp, identity, identity_len);
     222          63 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
     223          63 :                    "(response)", id, ms->mschapv2_id);
     224          63 :         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          63 : 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         126 :         if (eap_get_config_identity(sm, &len) == NULL ||
     248          63 :             eap_get_config_password(sm, &len) == NULL)
     249           0 :                 return NULL;
     250             : 
     251          63 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
     252          63 :         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          63 :         pos = (const u8 *) (req + 1);
     259          63 :         challenge_len = *pos++;
     260          63 :         len = req_len - sizeof(*req) - 1;
     261          63 :         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          63 :         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          63 :         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          63 :                 challenge = pos;
     282          63 :         pos += challenge_len;
     283          63 :         len -= challenge_len;
     284          63 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
     285             :                     pos, len);
     286             : 
     287          63 :         ret->ignore = FALSE;
     288          63 :         ret->methodState = METHOD_MAY_CONT;
     289          63 :         ret->decision = DECISION_FAIL;
     290          63 :         ret->allowNotifications = TRUE;
     291             : 
     292          63 :         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 :                 bin_clear_free(config->password, config->password_len);
     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           0 :                                 bin_clear_free(config->password,
     317             :                                                config->password_len);
     318           0 :                                 config->password = NULL;
     319           0 :                                 config->password_len = 0;
     320             :                         }
     321           0 :                         bin_clear_free(config->new_password,
     322             :                                        config->new_password_len);
     323             :                 } else {
     324           0 :                         config->password = config->new_password;
     325           0 :                         config->password_len = config->new_password_len;
     326             :                 }
     327           0 :                 config->new_password = NULL;
     328           0 :                 config->new_password_len = 0;
     329             :         }
     330           0 : }
     331             : 
     332             : 
     333             : /**
     334             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
     335             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     336             :  * @data: Pointer to private EAP method data from eap_mschapv2_init()
     337             :  * @ret: Return values from EAP request validation and processing
     338             :  * @req: Pointer to EAP-MSCHAPv2 header from the request
     339             :  * @req_len: Length of the EAP-MSCHAPv2 data
     340             :  * @id: EAP identifier used in th erequest
     341             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     342             :  * no reply available
     343             :  */
     344          57 : static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
     345             :                                             struct eap_mschapv2_data *data,
     346             :                                             struct eap_method_ret *ret,
     347             :                                             const struct eap_mschapv2_hdr *req,
     348             :                                             size_t req_len, u8 id)
     349             : {
     350             :         struct wpabuf *resp;
     351             :         const u8 *pos;
     352             :         size_t len;
     353             : 
     354          57 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
     355          57 :         len = req_len - sizeof(*req);
     356          57 :         pos = (const u8 *) (req + 1);
     357         114 :         if (!data->auth_response_valid ||
     358          57 :             mschapv2_verify_auth_response(data->auth_response, pos, len)) {
     359           0 :                 wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
     360             :                            "response in success request");
     361           0 :                 ret->methodState = METHOD_DONE;
     362           0 :                 ret->decision = DECISION_FAIL;
     363           0 :                 return NULL;
     364             :         }
     365          57 :         pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
     366          57 :         len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
     367         171 :         while (len > 0 && *pos == ' ') {
     368          57 :                 pos++;
     369          57 :                 len--;
     370             :         }
     371          57 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
     372             :                           pos, len);
     373          57 :         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
     374             : 
     375             :         /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
     376             :          * message. */
     377          57 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
     378             :                              EAP_CODE_RESPONSE, id);
     379          57 :         if (resp == NULL) {
     380           0 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
     381             :                            "buffer for success response");
     382           0 :                 ret->ignore = TRUE;
     383           0 :                 return NULL;
     384             :         }
     385             : 
     386          57 :         wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
     387             : 
     388          57 :         ret->methodState = METHOD_DONE;
     389          57 :         ret->decision = DECISION_UNCOND_SUCC;
     390          57 :         ret->allowNotifications = FALSE;
     391          57 :         data->success = 1;
     392             : 
     393          57 :         if (data->prev_error == ERROR_PASSWD_EXPIRED)
     394           0 :                 eap_mschapv2_password_changed(sm, data);
     395             : 
     396          57 :         return resp;
     397             : }
     398             : 
     399             : 
     400           3 : static int eap_mschapv2_failure_txt(struct eap_sm *sm,
     401             :                                     struct eap_mschapv2_data *data, char *txt)
     402             : {
     403           3 :         char *pos, *msg = "";
     404           3 :         int retry = 1;
     405           3 :         struct eap_peer_config *config = eap_get_config(sm);
     406             : 
     407             :         /* For example:
     408             :          * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
     409             :          */
     410             : 
     411           3 :         pos = txt;
     412             : 
     413           3 :         if (pos && os_strncmp(pos, "E=", 2) == 0) {
     414           3 :                 pos += 2;
     415           3 :                 data->prev_error = atoi(pos);
     416           3 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
     417             :                            data->prev_error);
     418           3 :                 pos = os_strchr(pos, ' ');
     419           3 :                 if (pos)
     420           3 :                         pos++;
     421             :         }
     422             : 
     423           3 :         if (pos && os_strncmp(pos, "R=", 2) == 0) {
     424           3 :                 pos += 2;
     425           3 :                 retry = atoi(pos);
     426           3 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
     427             :                            retry == 1 ? "" : "not ");
     428           3 :                 pos = os_strchr(pos, ' ');
     429           3 :                 if (pos)
     430           3 :                         pos++;
     431             :         }
     432             : 
     433           6 :         if (pos && os_strncmp(pos, "C=", 2) == 0) {
     434             :                 int hex_len;
     435           3 :                 pos += 2;
     436           3 :                 hex_len = os_strchr(pos, ' ') - (char *) pos;
     437           3 :                 if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
     438           3 :                         if (hexstr2bin(pos, data->passwd_change_challenge,
     439             :                                        PASSWD_CHANGE_CHAL_LEN)) {
     440           0 :                                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
     441             :                                            "failure challenge");
     442             :                         } else {
     443           3 :                                 wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
     444             :                                             "challenge",
     445           3 :                                             data->passwd_change_challenge,
     446             :                                             PASSWD_CHANGE_CHAL_LEN);
     447           3 :                                 data->passwd_change_challenge_valid = 1;
     448             :                         }
     449             :                 } else {
     450           0 :                         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
     451             :                                    "challenge len %d", hex_len);
     452             :                 }
     453           3 :                 pos = os_strchr(pos, ' ');
     454           3 :                 if (pos)
     455           3 :                         pos++;
     456             :         } else {
     457           0 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
     458             :                            "was not present in failure message");
     459             :         }
     460             : 
     461           3 :         if (pos && os_strncmp(pos, "V=", 2) == 0) {
     462           3 :                 pos += 2;
     463           3 :                 data->passwd_change_version = atoi(pos);
     464           3 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
     465             :                            "protocol version %d", data->passwd_change_version);
     466           3 :                 pos = os_strchr(pos, ' ');
     467           3 :                 if (pos)
     468           3 :                         pos++;
     469             :         }
     470             : 
     471           3 :         if (pos && os_strncmp(pos, "M=", 2) == 0) {
     472           3 :                 pos += 2;
     473           3 :                 msg = pos;
     474             :         }
     475           3 :         if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
     476           0 :             config && config->phase2 &&
     477           0 :             os_strstr(config->phase2, "mschapv2_retry=0")) {
     478           0 :                 wpa_printf(MSG_DEBUG,
     479             :                            "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
     480           0 :                 retry = 0;
     481             :         }
     482           3 :         wpa_msg(sm->msg_ctx, MSG_WARNING,
     483             :                 "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
     484             :                 "%d)",
     485             :                 msg, retry == 1 ? "" : "not ", data->prev_error);
     486           3 :         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
     487           0 :             data->passwd_change_version == 3 && config) {
     488           0 :                 if (config->new_password == NULL) {
     489           0 :                         wpa_msg(sm->msg_ctx, MSG_INFO,
     490             :                                 "EAP-MSCHAPV2: Password expired - password "
     491             :                                 "change required");
     492           0 :                         eap_sm_request_new_password(sm);
     493             :                 }
     494           3 :         } else if (retry == 1 && config) {
     495             :                 /* TODO: could prevent the current password from being used
     496             :                  * again at least for some period of time */
     497           0 :                 if (!config->mschapv2_retry)
     498           0 :                         eap_sm_request_identity(sm);
     499           0 :                 eap_sm_request_password(sm);
     500           0 :                 config->mschapv2_retry = 1;
     501           3 :         } else if (config) {
     502             :                 /* TODO: prevent retries using same username/password */
     503           3 :                 config->mschapv2_retry = 0;
     504             :         }
     505             : 
     506           3 :         return retry == 1;
     507             : }
     508             : 
     509             : 
     510           0 : static struct wpabuf * eap_mschapv2_change_password(
     511             :         struct eap_sm *sm, struct eap_mschapv2_data *data,
     512             :         struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
     513             : {
     514             : #ifdef CONFIG_NO_RC4
     515             :         wpa_printf(MSG_ERROR,
     516             :                 "EAP-MSCHAPV2: RC4 not support in the build - cannot change password");
     517             :         return NULL;
     518             : #else /* CONFIG_NO_RC4 */
     519             :         struct wpabuf *resp;
     520             :         int ms_len;
     521             :         const u8 *username, *password, *new_password;
     522             :         size_t username_len, password_len, new_password_len;
     523             :         struct eap_mschapv2_hdr *ms;
     524             :         struct ms_change_password *cp;
     525             :         u8 password_hash[16], password_hash_hash[16];
     526             :         int pwhash;
     527             : 
     528           0 :         username = eap_get_config_identity(sm, &username_len);
     529           0 :         password = eap_get_config_password2(sm, &password_len, &pwhash);
     530           0 :         new_password = eap_get_config_new_password(sm, &new_password_len);
     531           0 :         if (username == NULL || password == NULL || new_password == NULL)
     532           0 :                 return NULL;
     533             : 
     534           0 :         username = mschapv2_remove_domain(username, &username_len);
     535             : 
     536           0 :         ret->ignore = FALSE;
     537           0 :         ret->methodState = METHOD_MAY_CONT;
     538           0 :         ret->decision = DECISION_COND_SUCC;
     539           0 :         ret->allowNotifications = TRUE;
     540             : 
     541           0 :         ms_len = sizeof(*ms) + sizeof(*cp);
     542           0 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
     543             :                              EAP_CODE_RESPONSE, id);
     544           0 :         if (resp == NULL)
     545           0 :                 return NULL;
     546             : 
     547           0 :         ms = wpabuf_put(resp, sizeof(*ms));
     548           0 :         ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
     549           0 :         ms->mschapv2_id = req->mschapv2_id + 1;
     550           0 :         WPA_PUT_BE16(ms->ms_length, ms_len);
     551           0 :         cp = wpabuf_put(resp, sizeof(*cp));
     552             : 
     553             :         /* Encrypted-Password */
     554           0 :         if (pwhash) {
     555           0 :                 if (encrypt_pw_block_with_password_hash(
     556             :                             new_password, new_password_len,
     557           0 :                             password, cp->encr_password))
     558           0 :                         goto fail;
     559             :         } else {
     560           0 :                 if (new_password_encrypted_with_old_nt_password_hash(
     561             :                             new_password, new_password_len,
     562           0 :                             password, password_len, cp->encr_password))
     563           0 :                         goto fail;
     564             :         }
     565             : 
     566             :         /* Encrypted-Hash */
     567           0 :         if (pwhash) {
     568             :                 u8 new_password_hash[16];
     569           0 :                 if (nt_password_hash(new_password, new_password_len,
     570             :                                      new_password_hash))
     571           0 :                         goto fail;
     572           0 :                 nt_password_hash_encrypted_with_block(password,
     573             :                                                       new_password_hash,
     574           0 :                                                       cp->encr_hash);
     575             :         } else {
     576           0 :                 if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
     577             :                             new_password, new_password_len,
     578           0 :                             password, password_len, cp->encr_hash))
     579           0 :                         goto fail;
     580             :         }
     581             : 
     582             :         /* Peer-Challenge */
     583           0 :         if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
     584           0 :                 goto fail;
     585             : 
     586             :         /* Reserved, must be zero */
     587           0 :         os_memset(cp->reserved, 0, 8);
     588             : 
     589             :         /* NT-Response */
     590           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
     591           0 :                     data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
     592           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
     593           0 :                     cp->peer_challenge, MSCHAPV2_CHAL_LEN);
     594           0 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
     595             :                           username, username_len);
     596           0 :         wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
     597             :                               new_password, new_password_len);
     598           0 :         generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
     599             :                              username, username_len,
     600             :                              new_password, new_password_len,
     601           0 :                              cp->nt_response);
     602           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
     603           0 :                     cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
     604             : 
     605             :         /* Authenticator response is not really needed yet, but calculate it
     606             :          * here so that challenges need not be saved. */
     607           0 :         generate_authenticator_response(new_password, new_password_len,
     608           0 :                                         cp->peer_challenge,
     609           0 :                                         data->passwd_change_challenge,
     610             :                                         username, username_len,
     611           0 :                                         cp->nt_response, data->auth_response);
     612           0 :         data->auth_response_valid = 1;
     613             : 
     614             :         /* Likewise, generate master_key here since we have the needed data
     615             :          * available. */
     616           0 :         if (nt_password_hash(new_password, new_password_len, password_hash) ||
     617           0 :             hash_nt_password_hash(password_hash, password_hash_hash) ||
     618           0 :             get_master_key(password_hash_hash, cp->nt_response,
     619           0 :                            data->master_key)) {
     620           0 :                 data->auth_response_valid = 0;
     621           0 :                 goto fail;
     622             :         }
     623           0 :         data->master_key_valid = 1;
     624             : 
     625             :         /* Flags */
     626           0 :         os_memset(cp->flags, 0, 2);
     627             : 
     628           0 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
     629           0 :                    "(change pw)", id, ms->mschapv2_id);
     630             : 
     631           0 :         return resp;
     632             : 
     633             : fail:
     634           0 :         wpabuf_free(resp);
     635           0 :         return NULL;
     636             : #endif /* CONFIG_NO_RC4 */
     637             : }
     638             : 
     639             : 
     640             : /**
     641             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
     642             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     643             :  * @data: Pointer to private EAP method data from eap_mschapv2_init()
     644             :  * @ret: Return values from EAP request validation and processing
     645             :  * @req: Pointer to EAP-MSCHAPv2 header from the request
     646             :  * @req_len: Length of the EAP-MSCHAPv2 data
     647             :  * @id: EAP identifier used in th erequest
     648             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     649             :  * no reply available
     650             :  */
     651           3 : static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
     652             :                                             struct eap_mschapv2_data *data,
     653             :                                             struct eap_method_ret *ret,
     654             :                                             const struct eap_mschapv2_hdr *req,
     655             :                                             size_t req_len, u8 id)
     656             : {
     657             :         struct wpabuf *resp;
     658           3 :         const u8 *msdata = (const u8 *) (req + 1);
     659             :         char *buf;
     660           3 :         size_t len = req_len - sizeof(*req);
     661           3 :         int retry = 0;
     662             : 
     663           3 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
     664           3 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
     665             :                           msdata, len);
     666             :         /*
     667             :          * eap_mschapv2_failure_txt() expects a nul terminated string, so we
     668             :          * must allocate a large enough temporary buffer to create that since
     669             :          * the received message does not include nul termination.
     670             :          */
     671           3 :         buf = dup_binstr(msdata, len);
     672           3 :         if (buf) {
     673           3 :                 retry = eap_mschapv2_failure_txt(sm, data, buf);
     674           3 :                 os_free(buf);
     675             :         }
     676             : 
     677           3 :         ret->ignore = FALSE;
     678           3 :         ret->methodState = METHOD_DONE;
     679           3 :         ret->decision = DECISION_FAIL;
     680           3 :         ret->allowNotifications = FALSE;
     681             : 
     682           3 :         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
     683           0 :             data->passwd_change_version == 3) {
     684           0 :                 struct eap_peer_config *config = eap_get_config(sm);
     685           0 :                 if (config && config->new_password)
     686           0 :                         return eap_mschapv2_change_password(sm, data, ret, req,
     687             :                                                             id);
     688           0 :                 if (config && config->pending_req_new_password)
     689           0 :                         return NULL;
     690           3 :         } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
     691             :                 /* TODO: could try to retry authentication, e.g, after having
     692             :                  * changed the username/password. In this case, EAP MS-CHAP-v2
     693             :                  * Failure Response would not be sent here. */
     694           0 :                 return NULL;
     695             :         }
     696             : 
     697             :         /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
     698             :          * message. */
     699           3 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
     700             :                              EAP_CODE_RESPONSE, id);
     701           3 :         if (resp == NULL)
     702           0 :                 return NULL;
     703             : 
     704           3 :         wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
     705             : 
     706           3 :         return resp;
     707             : }
     708             : 
     709             : 
     710         125 : static int eap_mschapv2_check_config(struct eap_sm *sm)
     711             : {
     712             :         size_t len;
     713             : 
     714         125 :         if (eap_get_config_identity(sm, &len) == NULL) {
     715           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
     716           0 :                 eap_sm_request_identity(sm);
     717           0 :                 return -1;
     718             :         }
     719             : 
     720         125 :         if (eap_get_config_password(sm, &len) == NULL) {
     721           2 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
     722           2 :                 eap_sm_request_password(sm);
     723           2 :                 return -1;
     724             :         }
     725             : 
     726         123 :         return 0;
     727             : }
     728             : 
     729             : 
     730         123 : static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
     731             :                                     const struct eap_mschapv2_hdr *ms)
     732             : {
     733         123 :         size_t ms_len = WPA_GET_BE16(ms->ms_length);
     734             : 
     735         123 :         if (ms_len == len)
     736         123 :                 return 0;
     737             : 
     738           0 :         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
     739             :                    "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
     740           0 :         if (sm->workaround) {
     741             :                 /* Some authentication servers use invalid ms_len,
     742             :                  * ignore it for interoperability. */
     743           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
     744             :                            " invalid ms_len %lu (len %lu)",
     745             :                            (unsigned long) ms_len,
     746             :                            (unsigned long) len);
     747           0 :                 return 0;
     748             :         }
     749             : 
     750           0 :         return -1;
     751             : }
     752             : 
     753             : 
     754          63 : static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
     755             :                                         const struct wpabuf *reqData)
     756             : {
     757             :         /*
     758             :          * Store a copy of the challenge message, so that it can be processed
     759             :          * again in case retry is allowed after a possible failure.
     760             :          */
     761          63 :         wpabuf_free(data->prev_challenge);
     762          63 :         data->prev_challenge = wpabuf_dup(reqData);
     763          63 : }
     764             : 
     765             : 
     766             : /**
     767             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
     768             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     769             :  * @priv: Pointer to private EAP method data from eap_mschapv2_init()
     770             :  * @ret: Return values from EAP request validation and processing
     771             :  * @reqData: EAP request to be processed (eapReqData)
     772             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     773             :  * no reply available
     774             :  */
     775         125 : static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
     776             :                                             struct eap_method_ret *ret,
     777             :                                             const struct wpabuf *reqData)
     778             : {
     779         125 :         struct eap_mschapv2_data *data = priv;
     780         125 :         struct eap_peer_config *config = eap_get_config(sm);
     781             :         const struct eap_mschapv2_hdr *ms;
     782         125 :         int using_prev_challenge = 0;
     783             :         const u8 *pos;
     784             :         size_t len;
     785             :         u8 id;
     786             : 
     787         125 :         if (eap_mschapv2_check_config(sm)) {
     788           2 :                 ret->ignore = TRUE;
     789           2 :                 return NULL;
     790             :         }
     791             : 
     792         123 :         if (config->mschapv2_retry && data->prev_challenge &&
     793           0 :             data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
     794           0 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
     795             :                            "with the previous challenge");
     796             : 
     797           0 :                 reqData = data->prev_challenge;
     798           0 :                 using_prev_challenge = 1;
     799           0 :                 config->mschapv2_retry = 0;
     800             :         }
     801             : 
     802         123 :         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
     803             :                                &len);
     804         123 :         if (pos == NULL || len < sizeof(*ms) + 1) {
     805           0 :                 ret->ignore = TRUE;
     806           0 :                 return NULL;
     807             :         }
     808             : 
     809         123 :         ms = (const struct eap_mschapv2_hdr *) pos;
     810         123 :         if (eap_mschapv2_check_mslen(sm, len, ms)) {
     811           0 :                 ret->ignore = TRUE;
     812           0 :                 return NULL;
     813             :         }
     814             : 
     815         123 :         id = eap_get_id(reqData);
     816         123 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
     817         123 :                    id, ms->mschapv2_id);
     818             : 
     819         123 :         switch (ms->op_code) {
     820             :         case MSCHAPV2_OP_CHALLENGE:
     821          63 :                 if (!using_prev_challenge)
     822          63 :                         eap_mschapv2_copy_challenge(data, reqData);
     823          63 :                 return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
     824             :         case MSCHAPV2_OP_SUCCESS:
     825          57 :                 return eap_mschapv2_success(sm, data, ret, ms, len, id);
     826             :         case MSCHAPV2_OP_FAILURE:
     827           3 :                 return eap_mschapv2_failure(sm, data, ret, ms, len, id);
     828             :         default:
     829           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
     830           0 :                            ms->op_code);
     831           0 :                 ret->ignore = TRUE;
     832           0 :                 return NULL;
     833             :         }
     834             : }
     835             : 
     836             : 
     837          21 : static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
     838             : {
     839          21 :         struct eap_mschapv2_data *data = priv;
     840          21 :         return data->success && data->master_key_valid;
     841             : }
     842             : 
     843             : 
     844          21 : static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
     845             : {
     846          21 :         struct eap_mschapv2_data *data = priv;
     847             :         u8 *key;
     848             :         int key_len;
     849             : 
     850          21 :         if (!data->master_key_valid || !data->success)
     851           0 :                 return NULL;
     852             : 
     853          21 :         key_len = 2 * MSCHAPV2_KEY_LEN;
     854             : 
     855          21 :         key = os_malloc(key_len);
     856          21 :         if (key == NULL)
     857           0 :                 return NULL;
     858             : 
     859             :         /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
     860             :          *      peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
     861          21 :         get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
     862          21 :         get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
     863             :                                 MSCHAPV2_KEY_LEN, 0, 0);
     864             : 
     865          21 :         wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
     866             :                         key, key_len);
     867             : 
     868          21 :         *len = key_len;
     869          21 :         return key;
     870             : }
     871             : 
     872             : 
     873             : /**
     874             :  * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
     875             :  * Returns: 0 on success, -1 on failure
     876             :  *
     877             :  * This function is used to register EAP-MSCHAPv2 peer method into the EAP
     878             :  * method list.
     879             :  */
     880          49 : int eap_peer_mschapv2_register(void)
     881             : {
     882             :         struct eap_method *eap;
     883             :         int ret;
     884             : 
     885          49 :         eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
     886             :                                     EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
     887             :                                     "MSCHAPV2");
     888          49 :         if (eap == NULL)
     889           0 :                 return -1;
     890             : 
     891          49 :         eap->init = eap_mschapv2_init;
     892          49 :         eap->deinit = eap_mschapv2_deinit;
     893          49 :         eap->process = eap_mschapv2_process;
     894          49 :         eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
     895          49 :         eap->getKey = eap_mschapv2_getKey;
     896             : 
     897          49 :         ret = eap_peer_method_register(eap);
     898          49 :         if (ret)
     899           0 :                 eap_peer_method_free(eap);
     900          49 :         return ret;
     901             : }

Generated by: LCOV version 1.10