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 1422976643 Lines: 225 393 57.3 %
Date: 2015-02-03 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          49 : static void * eap_mschapv2_init(struct eap_sm *sm)
     105             : {
     106             :         struct eap_mschapv2_data *data;
     107          49 :         data = os_zalloc(sizeof(*data));
     108          49 :         if (data == NULL)
     109           0 :                 return NULL;
     110             : 
     111          49 :         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          49 :         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          49 :         data->phase2 = sm->init_phase2;
     132             : 
     133          49 :         return data;
     134             : }
     135             : 
     136             : 
     137          49 : static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
     138             : {
     139          49 :         struct eap_mschapv2_data *data = priv;
     140          49 :         os_free(data->peer_challenge);
     141          49 :         os_free(data->auth_challenge);
     142          49 :         wpabuf_free(data->prev_challenge);
     143          49 :         bin_clear_free(data, sizeof(*data));
     144          49 : }
     145             : 
     146             : 
     147          53 : 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          53 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
     161             : 
     162          53 :         identity = eap_get_config_identity(sm, &identity_len);
     163          53 :         password = eap_get_config_password2(sm, &password_len, &pwhash);
     164          53 :         if (identity == NULL || password == NULL)
     165           0 :                 return NULL;
     166             : 
     167          53 :         ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
     168          53 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
     169             :                              EAP_CODE_RESPONSE, id);
     170          53 :         if (resp == NULL)
     171           0 :                 return NULL;
     172             : 
     173          53 :         ms = wpabuf_put(resp, sizeof(*ms));
     174          53 :         ms->op_code = MSCHAPV2_OP_RESPONSE;
     175          53 :         ms->mschapv2_id = mschapv2_id;
     176          53 :         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          53 :         WPA_PUT_BE16(ms->ms_length, ms_len);
     186             : 
     187          53 :         wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
     188             : 
     189             :         /* Response */
     190          53 :         r = wpabuf_put(resp, sizeof(*r));
     191          53 :         peer_challenge = r->peer_challenge;
     192          53 :         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          49 :         } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
     198           0 :                 wpabuf_free(resp);
     199           0 :                 return NULL;
     200             :         }
     201          53 :         os_memset(r->reserved, 0, 8);
     202          53 :         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          53 :         if (mschapv2_derive_response(identity, identity_len, password,
     208             :                                      password_len, pwhash, auth_challenge,
     209          53 :                                      peer_challenge, r->nt_response,
     210          53 :                                      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          53 :         data->auth_response_valid = 1;
     217          53 :         data->master_key_valid = 1;
     218             : 
     219          53 :         r->flags = 0; /* reserved, must be zero */
     220             : 
     221          53 :         wpabuf_put_data(resp, identity, identity_len);
     222          53 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
     223          53 :                    "(response)", id, ms->mschapv2_id);
     224          53 :         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          53 : 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         106 :         if (eap_get_config_identity(sm, &len) == NULL ||
     248          53 :             eap_get_config_password(sm, &len) == NULL)
     249           0 :                 return NULL;
     250             : 
     251          53 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
     252          53 :         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          53 :         pos = (const u8 *) (req + 1);
     259          53 :         challenge_len = *pos++;
     260          53 :         len = req_len - sizeof(*req) - 1;
     261          53 :         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          53 :         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          53 :         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          53 :                 challenge = pos;
     282          53 :         pos += challenge_len;
     283          53 :         len -= challenge_len;
     284          53 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
     285             :                     pos, len);
     286             : 
     287          53 :         ret->ignore = FALSE;
     288          53 :         ret->methodState = METHOD_MAY_CONT;
     289          53 :         ret->decision = DECISION_FAIL;
     290          53 :         ret->allowNotifications = TRUE;
     291             : 
     292          53 :         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          47 : 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          47 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
     355          47 :         len = req_len - sizeof(*req);
     356          47 :         pos = (const u8 *) (req + 1);
     357          94 :         if (!data->auth_response_valid ||
     358          47 :             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          47 :         pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
     366          47 :         len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
     367         141 :         while (len > 0 && *pos == ' ') {
     368          47 :                 pos++;
     369          47 :                 len--;
     370             :         }
     371          47 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
     372             :                           pos, len);
     373          47 :         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          47 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
     378             :                              EAP_CODE_RESPONSE, id);
     379          47 :         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          47 :         wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
     387             : 
     388          47 :         ret->methodState = METHOD_DONE;
     389          47 :         ret->decision = DECISION_UNCOND_SUCC;
     390          47 :         ret->allowNotifications = FALSE;
     391          47 :         data->success = 1;
     392             : 
     393          47 :         if (data->prev_error == ERROR_PASSWD_EXPIRED)
     394           0 :                 eap_mschapv2_password_changed(sm, data);
     395             : 
     396          47 :         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             :         struct wpabuf *resp;
     515             :         int ms_len;
     516             :         const u8 *username, *password, *new_password;
     517             :         size_t username_len, password_len, new_password_len;
     518             :         struct eap_mschapv2_hdr *ms;
     519             :         struct ms_change_password *cp;
     520             :         u8 password_hash[16], password_hash_hash[16];
     521             :         int pwhash;
     522             : 
     523           0 :         username = eap_get_config_identity(sm, &username_len);
     524           0 :         password = eap_get_config_password2(sm, &password_len, &pwhash);
     525           0 :         new_password = eap_get_config_new_password(sm, &new_password_len);
     526           0 :         if (username == NULL || password == NULL || new_password == NULL)
     527           0 :                 return NULL;
     528             : 
     529           0 :         username = mschapv2_remove_domain(username, &username_len);
     530             : 
     531           0 :         ret->ignore = FALSE;
     532           0 :         ret->methodState = METHOD_MAY_CONT;
     533           0 :         ret->decision = DECISION_COND_SUCC;
     534           0 :         ret->allowNotifications = TRUE;
     535             : 
     536           0 :         ms_len = sizeof(*ms) + sizeof(*cp);
     537           0 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
     538             :                              EAP_CODE_RESPONSE, id);
     539           0 :         if (resp == NULL)
     540           0 :                 return NULL;
     541             : 
     542           0 :         ms = wpabuf_put(resp, sizeof(*ms));
     543           0 :         ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
     544           0 :         ms->mschapv2_id = req->mschapv2_id + 1;
     545           0 :         WPA_PUT_BE16(ms->ms_length, ms_len);
     546           0 :         cp = wpabuf_put(resp, sizeof(*cp));
     547             : 
     548             :         /* Encrypted-Password */
     549           0 :         if (pwhash) {
     550           0 :                 if (encrypt_pw_block_with_password_hash(
     551             :                             new_password, new_password_len,
     552           0 :                             password, cp->encr_password))
     553           0 :                         goto fail;
     554             :         } else {
     555           0 :                 if (new_password_encrypted_with_old_nt_password_hash(
     556             :                             new_password, new_password_len,
     557           0 :                             password, password_len, cp->encr_password))
     558           0 :                         goto fail;
     559             :         }
     560             : 
     561             :         /* Encrypted-Hash */
     562           0 :         if (pwhash) {
     563             :                 u8 new_password_hash[16];
     564           0 :                 if (nt_password_hash(new_password, new_password_len,
     565             :                                      new_password_hash))
     566           0 :                         goto fail;
     567           0 :                 nt_password_hash_encrypted_with_block(password,
     568             :                                                       new_password_hash,
     569           0 :                                                       cp->encr_hash);
     570             :         } else {
     571           0 :                 if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
     572             :                             new_password, new_password_len,
     573           0 :                             password, password_len, cp->encr_hash))
     574           0 :                         goto fail;
     575             :         }
     576             : 
     577             :         /* Peer-Challenge */
     578           0 :         if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
     579           0 :                 goto fail;
     580             : 
     581             :         /* Reserved, must be zero */
     582           0 :         os_memset(cp->reserved, 0, 8);
     583             : 
     584             :         /* NT-Response */
     585           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
     586           0 :                     data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
     587           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
     588           0 :                     cp->peer_challenge, MSCHAPV2_CHAL_LEN);
     589           0 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
     590             :                           username, username_len);
     591           0 :         wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
     592             :                               new_password, new_password_len);
     593           0 :         generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
     594             :                              username, username_len,
     595             :                              new_password, new_password_len,
     596           0 :                              cp->nt_response);
     597           0 :         wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
     598           0 :                     cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
     599             : 
     600             :         /* Authenticator response is not really needed yet, but calculate it
     601             :          * here so that challenges need not be saved. */
     602           0 :         generate_authenticator_response(new_password, new_password_len,
     603           0 :                                         cp->peer_challenge,
     604           0 :                                         data->passwd_change_challenge,
     605             :                                         username, username_len,
     606           0 :                                         cp->nt_response, data->auth_response);
     607           0 :         data->auth_response_valid = 1;
     608             : 
     609             :         /* Likewise, generate master_key here since we have the needed data
     610             :          * available. */
     611           0 :         if (nt_password_hash(new_password, new_password_len, password_hash) ||
     612           0 :             hash_nt_password_hash(password_hash, password_hash_hash) ||
     613           0 :             get_master_key(password_hash_hash, cp->nt_response,
     614           0 :                            data->master_key)) {
     615           0 :                 data->auth_response_valid = 0;
     616           0 :                 goto fail;
     617             :         }
     618           0 :         data->master_key_valid = 1;
     619             : 
     620             :         /* Flags */
     621           0 :         os_memset(cp->flags, 0, 2);
     622             : 
     623           0 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
     624           0 :                    "(change pw)", id, ms->mschapv2_id);
     625             : 
     626           0 :         return resp;
     627             : 
     628             : fail:
     629           0 :         wpabuf_free(resp);
     630           0 :         return NULL;
     631             : }
     632             : 
     633             : 
     634             : /**
     635             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
     636             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     637             :  * @data: Pointer to private EAP method data from eap_mschapv2_init()
     638             :  * @ret: Return values from EAP request validation and processing
     639             :  * @req: Pointer to EAP-MSCHAPv2 header from the request
     640             :  * @req_len: Length of the EAP-MSCHAPv2 data
     641             :  * @id: EAP identifier used in th erequest
     642             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     643             :  * no reply available
     644             :  */
     645           3 : static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
     646             :                                             struct eap_mschapv2_data *data,
     647             :                                             struct eap_method_ret *ret,
     648             :                                             const struct eap_mschapv2_hdr *req,
     649             :                                             size_t req_len, u8 id)
     650             : {
     651             :         struct wpabuf *resp;
     652           3 :         const u8 *msdata = (const u8 *) (req + 1);
     653             :         char *buf;
     654           3 :         size_t len = req_len - sizeof(*req);
     655           3 :         int retry = 0;
     656             : 
     657           3 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
     658           3 :         wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
     659             :                           msdata, len);
     660             :         /*
     661             :          * eap_mschapv2_failure_txt() expects a nul terminated string, so we
     662             :          * must allocate a large enough temporary buffer to create that since
     663             :          * the received message does not include nul termination.
     664             :          */
     665           3 :         buf = dup_binstr(msdata, len);
     666           3 :         if (buf) {
     667           3 :                 retry = eap_mschapv2_failure_txt(sm, data, buf);
     668           3 :                 os_free(buf);
     669             :         }
     670             : 
     671           3 :         ret->ignore = FALSE;
     672           3 :         ret->methodState = METHOD_DONE;
     673           3 :         ret->decision = DECISION_FAIL;
     674           3 :         ret->allowNotifications = FALSE;
     675             : 
     676           3 :         if (data->prev_error == ERROR_PASSWD_EXPIRED &&
     677           0 :             data->passwd_change_version == 3) {
     678           0 :                 struct eap_peer_config *config = eap_get_config(sm);
     679           0 :                 if (config && config->new_password)
     680           0 :                         return eap_mschapv2_change_password(sm, data, ret, req,
     681             :                                                             id);
     682           0 :                 if (config && config->pending_req_new_password)
     683           0 :                         return NULL;
     684           3 :         } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
     685             :                 /* TODO: could try to retry authentication, e.g, after having
     686             :                  * changed the username/password. In this case, EAP MS-CHAP-v2
     687             :                  * Failure Response would not be sent here. */
     688           0 :                 return NULL;
     689             :         }
     690             : 
     691             :         /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
     692             :          * message. */
     693           3 :         resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
     694             :                              EAP_CODE_RESPONSE, id);
     695           3 :         if (resp == NULL)
     696           0 :                 return NULL;
     697             : 
     698           3 :         wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
     699             : 
     700           3 :         return resp;
     701             : }
     702             : 
     703             : 
     704         105 : static int eap_mschapv2_check_config(struct eap_sm *sm)
     705             : {
     706             :         size_t len;
     707             : 
     708         105 :         if (eap_get_config_identity(sm, &len) == NULL) {
     709           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
     710           0 :                 eap_sm_request_identity(sm);
     711           0 :                 return -1;
     712             :         }
     713             : 
     714         105 :         if (eap_get_config_password(sm, &len) == NULL) {
     715           2 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
     716           2 :                 eap_sm_request_password(sm);
     717           2 :                 return -1;
     718             :         }
     719             : 
     720         103 :         return 0;
     721             : }
     722             : 
     723             : 
     724         103 : static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
     725             :                                     const struct eap_mschapv2_hdr *ms)
     726             : {
     727         103 :         size_t ms_len = WPA_GET_BE16(ms->ms_length);
     728             : 
     729         103 :         if (ms_len == len)
     730         103 :                 return 0;
     731             : 
     732           0 :         wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
     733             :                    "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
     734           0 :         if (sm->workaround) {
     735             :                 /* Some authentication servers use invalid ms_len,
     736             :                  * ignore it for interoperability. */
     737           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
     738             :                            " invalid ms_len %lu (len %lu)",
     739             :                            (unsigned long) ms_len,
     740             :                            (unsigned long) len);
     741           0 :                 return 0;
     742             :         }
     743             : 
     744           0 :         return -1;
     745             : }
     746             : 
     747             : 
     748          53 : static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
     749             :                                         const struct wpabuf *reqData)
     750             : {
     751             :         /*
     752             :          * Store a copy of the challenge message, so that it can be processed
     753             :          * again in case retry is allowed after a possible failure.
     754             :          */
     755          53 :         wpabuf_free(data->prev_challenge);
     756          53 :         data->prev_challenge = wpabuf_dup(reqData);
     757          53 : }
     758             : 
     759             : 
     760             : /**
     761             :  * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
     762             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     763             :  * @priv: Pointer to private EAP method data from eap_mschapv2_init()
     764             :  * @ret: Return values from EAP request validation and processing
     765             :  * @reqData: EAP request to be processed (eapReqData)
     766             :  * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
     767             :  * no reply available
     768             :  */
     769         105 : static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
     770             :                                             struct eap_method_ret *ret,
     771             :                                             const struct wpabuf *reqData)
     772             : {
     773         105 :         struct eap_mschapv2_data *data = priv;
     774         105 :         struct eap_peer_config *config = eap_get_config(sm);
     775             :         const struct eap_mschapv2_hdr *ms;
     776         105 :         int using_prev_challenge = 0;
     777             :         const u8 *pos;
     778             :         size_t len;
     779             :         u8 id;
     780             : 
     781         105 :         if (eap_mschapv2_check_config(sm)) {
     782           2 :                 ret->ignore = TRUE;
     783           2 :                 return NULL;
     784             :         }
     785             : 
     786         103 :         if (config->mschapv2_retry && data->prev_challenge &&
     787           0 :             data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
     788           0 :                 wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
     789             :                            "with the previous challenge");
     790             : 
     791           0 :                 reqData = data->prev_challenge;
     792           0 :                 using_prev_challenge = 1;
     793           0 :                 config->mschapv2_retry = 0;
     794             :         }
     795             : 
     796         103 :         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
     797             :                                &len);
     798         103 :         if (pos == NULL || len < sizeof(*ms) + 1) {
     799           0 :                 ret->ignore = TRUE;
     800           0 :                 return NULL;
     801             :         }
     802             : 
     803         103 :         ms = (const struct eap_mschapv2_hdr *) pos;
     804         103 :         if (eap_mschapv2_check_mslen(sm, len, ms)) {
     805           0 :                 ret->ignore = TRUE;
     806           0 :                 return NULL;
     807             :         }
     808             : 
     809         103 :         id = eap_get_id(reqData);
     810         103 :         wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
     811         103 :                    id, ms->mschapv2_id);
     812             : 
     813         103 :         switch (ms->op_code) {
     814             :         case MSCHAPV2_OP_CHALLENGE:
     815          53 :                 if (!using_prev_challenge)
     816          53 :                         eap_mschapv2_copy_challenge(data, reqData);
     817          53 :                 return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
     818             :         case MSCHAPV2_OP_SUCCESS:
     819          47 :                 return eap_mschapv2_success(sm, data, ret, ms, len, id);
     820             :         case MSCHAPV2_OP_FAILURE:
     821           3 :                 return eap_mschapv2_failure(sm, data, ret, ms, len, id);
     822             :         default:
     823           0 :                 wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
     824           0 :                            ms->op_code);
     825           0 :                 ret->ignore = TRUE;
     826           0 :                 return NULL;
     827             :         }
     828             : }
     829             : 
     830             : 
     831          20 : static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
     832             : {
     833          20 :         struct eap_mschapv2_data *data = priv;
     834          20 :         return data->success && data->master_key_valid;
     835             : }
     836             : 
     837             : 
     838          20 : static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
     839             : {
     840          20 :         struct eap_mschapv2_data *data = priv;
     841             :         u8 *key;
     842             :         int key_len;
     843             : 
     844          20 :         if (!data->master_key_valid || !data->success)
     845           0 :                 return NULL;
     846             : 
     847          20 :         key_len = 2 * MSCHAPV2_KEY_LEN;
     848             : 
     849          20 :         key = os_malloc(key_len);
     850          20 :         if (key == NULL)
     851           0 :                 return NULL;
     852             : 
     853             :         /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
     854             :          *      peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
     855          20 :         get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
     856          20 :         get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
     857             :                                 MSCHAPV2_KEY_LEN, 0, 0);
     858             : 
     859          20 :         wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
     860             :                         key, key_len);
     861             : 
     862          20 :         *len = key_len;
     863          20 :         return key;
     864             : }
     865             : 
     866             : 
     867             : /**
     868             :  * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
     869             :  * Returns: 0 on success, -1 on failure
     870             :  *
     871             :  * This function is used to register EAP-MSCHAPv2 peer method into the EAP
     872             :  * method list.
     873             :  */
     874          30 : int eap_peer_mschapv2_register(void)
     875             : {
     876             :         struct eap_method *eap;
     877             :         int ret;
     878             : 
     879          30 :         eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
     880             :                                     EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
     881             :                                     "MSCHAPV2");
     882          30 :         if (eap == NULL)
     883           0 :                 return -1;
     884             : 
     885          30 :         eap->init = eap_mschapv2_init;
     886          30 :         eap->deinit = eap_mschapv2_deinit;
     887          30 :         eap->process = eap_mschapv2_process;
     888          30 :         eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
     889          30 :         eap->getKey = eap_mschapv2_getKey;
     890             : 
     891          30 :         ret = eap_peer_method_register(eap);
     892          30 :         if (ret)
     893           0 :                 eap_peer_method_free(eap);
     894          30 :         return ret;
     895             : }

Generated by: LCOV version 1.10