Home | History | Annotate | Download | only in IScsiDxe
      1 /** @file
      2   This file is for Challenge-Handshake Authentication Protocol (CHAP) Configuration.
      3 
      4 Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "IScsiImpl.h"
     16 
     17 /**
     18   Initator calculates its own expected hash value.
     19 
     20   @param[in]   ChapIdentifier     iSCSI CHAP identifier sent by authenticator.
     21   @param[in]   ChapSecret         iSCSI CHAP secret of the authenticator.
     22   @param[in]   SecretLength       The length of iSCSI CHAP secret.
     23   @param[in]   ChapChallenge      The challenge message sent by authenticator.
     24   @param[in]   ChallengeLength    The length of iSCSI CHAP challenge message.
     25   @param[out]  ChapResponse       The calculation of the expected hash value.
     26 
     27   @retval EFI_SUCCESS             The expected hash value was calculatedly successfully.
     28   @retval EFI_PROTOCOL_ERROR      The length of the secret should be at least the
     29                                   length of the hash value for the hashing algorithm chosen.
     30   @retval EFI_PROTOCOL_ERROR      MD5 hash operation fail.
     31   @retval EFI_OUT_OF_RESOURCES    Fail to allocate resource to complete MD5.
     32 
     33 **/
     34 EFI_STATUS
     35 IScsiCHAPCalculateResponse (
     36   IN  UINT32  ChapIdentifier,
     37   IN  CHAR8   *ChapSecret,
     38   IN  UINT32  SecretLength,
     39   IN  UINT8   *ChapChallenge,
     40   IN  UINT32  ChallengeLength,
     41   OUT UINT8   *ChapResponse
     42   )
     43 {
     44   UINTN       Md5ContextSize;
     45   VOID        *Md5Ctx;
     46   CHAR8       IdByte[1];
     47   EFI_STATUS  Status;
     48 
     49   if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {
     50     return EFI_PROTOCOL_ERROR;
     51   }
     52 
     53   Md5ContextSize = Md5GetContextSize ();
     54   Md5Ctx = AllocatePool (Md5ContextSize);
     55   if (Md5Ctx == NULL) {
     56     return EFI_OUT_OF_RESOURCES;
     57   }
     58 
     59   Status = EFI_PROTOCOL_ERROR;
     60 
     61   if (!Md5Init (Md5Ctx)) {
     62     goto Exit;
     63   }
     64 
     65   //
     66   // Hash Identifier - Only calculate 1 byte data (RFC1994)
     67   //
     68   IdByte[0] = (CHAR8) ChapIdentifier;
     69   if (!Md5Update (Md5Ctx, IdByte, 1)) {
     70     goto Exit;
     71   }
     72 
     73   //
     74   // Hash Secret
     75   //
     76   if (!Md5Update (Md5Ctx, ChapSecret, SecretLength)) {
     77     goto Exit;
     78   }
     79 
     80   //
     81   // Hash Challenge received from Target
     82   //
     83   if (!Md5Update (Md5Ctx, ChapChallenge, ChallengeLength)) {
     84     goto Exit;
     85   }
     86 
     87   if (Md5Final (Md5Ctx, ChapResponse)) {
     88     Status = EFI_SUCCESS;
     89   }
     90 
     91 Exit:
     92   FreePool (Md5Ctx);
     93   return Status;
     94 }
     95 
     96 /**
     97   The initator checks the CHAP response replied by target against its own
     98   calculation of the expected hash value.
     99 
    100   @param[in]   AuthData             iSCSI CHAP authentication data.
    101   @param[in]   TargetResponse       The response from target.
    102 
    103   @retval EFI_SUCCESS               The response from target passed authentication.
    104   @retval EFI_SECURITY_VIOLATION    The response from target was not expected value.
    105   @retval Others                    Other errors as indicated.
    106 
    107 **/
    108 EFI_STATUS
    109 IScsiCHAPAuthTarget (
    110   IN  ISCSI_CHAP_AUTH_DATA  *AuthData,
    111   IN  UINT8                 *TargetResponse
    112   )
    113 {
    114   EFI_STATUS  Status;
    115   UINT32      SecretSize;
    116   UINT8       VerifyRsp[ISCSI_CHAP_RSP_LEN];
    117 
    118   Status      = EFI_SUCCESS;
    119 
    120   SecretSize  = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);
    121   Status = IScsiCHAPCalculateResponse (
    122              AuthData->OutIdentifier,
    123              AuthData->AuthConfig->ReverseCHAPSecret,
    124              SecretSize,
    125              AuthData->OutChallenge,
    126              AuthData->OutChallengeLength,
    127              VerifyRsp
    128              );
    129 
    130   if (CompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN) != 0) {
    131     Status = EFI_SECURITY_VIOLATION;
    132   }
    133 
    134   return Status;
    135 }
    136 
    137 
    138 /**
    139   This function checks the received iSCSI Login Response during the security
    140   negotiation stage.
    141 
    142   @param[in] Conn             The iSCSI connection.
    143 
    144   @retval EFI_SUCCESS          The Login Response passed the CHAP validation.
    145   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
    146   @retval EFI_PROTOCOL_ERROR   Some kind of protocol error occurred.
    147   @retval Others               Other errors as indicated.
    148 
    149 **/
    150 EFI_STATUS
    151 IScsiCHAPOnRspReceived (
    152   IN ISCSI_CONNECTION  *Conn
    153   )
    154 {
    155   EFI_STATUS                  Status;
    156   ISCSI_SESSION               *Session;
    157   ISCSI_CHAP_AUTH_DATA        *AuthData;
    158   CHAR8                       *Value;
    159   UINT8                       *Data;
    160   UINT32                      Len;
    161   LIST_ENTRY                  *KeyValueList;
    162   UINTN                       Algorithm;
    163   CHAR8                       *Identifier;
    164   CHAR8                       *Challenge;
    165   CHAR8                       *Name;
    166   CHAR8                       *Response;
    167   UINT8                       TargetRsp[ISCSI_CHAP_RSP_LEN];
    168   UINT32                      RspLen;
    169   UINTN                       Result;
    170 
    171   ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
    172   ASSERT (Conn->RspQue.BufNum != 0);
    173 
    174   Session     = Conn->Session;
    175   AuthData    = &Session->AuthData.CHAP;
    176   Len         = Conn->RspQue.BufSize;
    177   Data        = AllocateZeroPool (Len);
    178   if (Data == NULL) {
    179     return EFI_OUT_OF_RESOURCES;
    180   }
    181   //
    182   // Copy the data in case the data spans over multiple PDUs.
    183   //
    184   NetbufQueCopy (&Conn->RspQue, 0, Len, Data);
    185 
    186   //
    187   // Build the key-value list from the data segment of the Login Response.
    188   //
    189   KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len);
    190   if (KeyValueList == NULL) {
    191     Status = EFI_OUT_OF_RESOURCES;
    192     goto ON_EXIT;
    193   }
    194 
    195   Status = EFI_PROTOCOL_ERROR;
    196 
    197   switch (Conn->AuthStep) {
    198   case ISCSI_AUTH_INITIAL:
    199     //
    200     // The first Login Response.
    201     //
    202     Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
    203     if (Value == NULL) {
    204       goto ON_EXIT;
    205     }
    206 
    207     Result = IScsiNetNtoi (Value);
    208     if (Result > 0xFFFF) {
    209       goto ON_EXIT;
    210     }
    211 
    212     Session->TargetPortalGroupTag = (UINT16) Result;
    213 
    214     Value                         = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);
    215     if (Value == NULL) {
    216       goto ON_EXIT;
    217     }
    218     //
    219     // Initiator mandates CHAP authentication but target replies without "CHAP", or
    220     // initiator suggets "None" but target replies with some kind of auth method.
    221     //
    222     if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
    223       if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {
    224         goto ON_EXIT;
    225       }
    226     } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
    227       if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {
    228         goto ON_EXIT;
    229       }
    230     } else {
    231       goto ON_EXIT;
    232     }
    233 
    234     //
    235     // Transit to CHAP step one.
    236     //
    237     Conn->AuthStep  = ISCSI_CHAP_STEP_ONE;
    238     Status          = EFI_SUCCESS;
    239     break;
    240 
    241   case ISCSI_CHAP_STEP_TWO:
    242     //
    243     // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
    244     //
    245     Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM);
    246     if (Value == NULL) {
    247       goto ON_EXIT;
    248     }
    249 
    250     Algorithm = IScsiNetNtoi (Value);
    251     if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {
    252       //
    253       // Unsupported algorithm is chosen by target.
    254       //
    255       goto ON_EXIT;
    256     }
    257 
    258     Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);
    259     if (Identifier == NULL) {
    260       goto ON_EXIT;
    261     }
    262 
    263     Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);
    264     if (Challenge == NULL) {
    265       goto ON_EXIT;
    266     }
    267     //
    268     // Process the CHAP identifier and CHAP Challenge from Target.
    269     // Calculate Response value.
    270     //
    271     Result = IScsiNetNtoi (Identifier);
    272     if (Result > 0xFF) {
    273       goto ON_EXIT;
    274     }
    275 
    276     AuthData->InIdentifier      = (UINT32) Result;
    277     AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;
    278     IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);
    279     Status = IScsiCHAPCalculateResponse (
    280                AuthData->InIdentifier,
    281                AuthData->AuthConfig->CHAPSecret,
    282                (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret),
    283                AuthData->InChallenge,
    284                AuthData->InChallengeLength,
    285                AuthData->CHAPResponse
    286                );
    287 
    288     //
    289     // Transit to next step.
    290     //
    291     Conn->AuthStep = ISCSI_CHAP_STEP_THREE;
    292     break;
    293 
    294   case ISCSI_CHAP_STEP_THREE:
    295     //
    296     // One way CHAP authentication and the target would like to
    297     // authenticate us.
    298     //
    299     Status = EFI_SUCCESS;
    300     break;
    301 
    302   case ISCSI_CHAP_STEP_FOUR:
    303     ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);
    304     //
    305     // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
    306     //
    307     Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
    308     if (Name == NULL) {
    309       goto ON_EXIT;
    310     }
    311 
    312     Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);
    313     if (Response == NULL) {
    314       goto ON_EXIT;
    315     }
    316 
    317     RspLen = ISCSI_CHAP_RSP_LEN;
    318     IScsiHexToBin (TargetRsp, &RspLen, Response);
    319 
    320     //
    321     // Check the CHAP Name and Response replied by Target.
    322     //
    323     Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
    324     break;
    325 
    326   default:
    327     break;
    328   }
    329 
    330 ON_EXIT:
    331 
    332   if (KeyValueList != NULL) {
    333     IScsiFreeKeyValueList (KeyValueList);
    334   }
    335 
    336   FreePool (Data);
    337 
    338   return Status;
    339 }
    340 
    341 
    342 /**
    343   This function fills the CHAP authentication information into the login PDU
    344   during the security negotiation stage in the iSCSI connection login.
    345 
    346   @param[in]       Conn        The iSCSI connection.
    347   @param[in, out]  Pdu         The PDU to send out.
    348 
    349   @retval EFI_SUCCESS           All check passed and the phase-related CHAP
    350                                 authentication info is filled into the iSCSI PDU.
    351   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
    352   @retval EFI_PROTOCOL_ERROR    Some kind of protocol error occurred.
    353 
    354 **/
    355 EFI_STATUS
    356 IScsiCHAPToSendReq (
    357   IN      ISCSI_CONNECTION  *Conn,
    358   IN OUT  NET_BUF           *Pdu
    359   )
    360 {
    361   EFI_STATUS                  Status;
    362   ISCSI_SESSION               *Session;
    363   ISCSI_LOGIN_REQUEST         *LoginReq;
    364   ISCSI_CHAP_AUTH_DATA        *AuthData;
    365   CHAR8                       *Value;
    366   CHAR8                       ValueStr[256];
    367   CHAR8                       *Response;
    368   UINT32                      RspLen;
    369   CHAR8                       *Challenge;
    370   UINT32                      ChallengeLen;
    371 
    372   ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
    373 
    374   Session     = Conn->Session;
    375   AuthData    = &Session->AuthData.CHAP;
    376   LoginReq    = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0);
    377   if (LoginReq == NULL) {
    378     return EFI_PROTOCOL_ERROR;
    379   }
    380   Status      = EFI_SUCCESS;
    381 
    382   RspLen      = 2 * ISCSI_CHAP_RSP_LEN + 3;
    383   Response    = AllocateZeroPool (RspLen);
    384   if (Response == NULL) {
    385     return EFI_OUT_OF_RESOURCES;
    386   }
    387 
    388   ChallengeLen  = 2 * ISCSI_CHAP_RSP_LEN + 3;
    389   Challenge     = AllocateZeroPool (ChallengeLen);
    390   if (Challenge == NULL) {
    391     FreePool (Response);
    392     return EFI_OUT_OF_RESOURCES;
    393   }
    394 
    395   switch (Conn->AuthStep) {
    396   case ISCSI_AUTH_INITIAL:
    397     //
    398     // It's the initial Login Request. Fill in the key=value pairs mandatory
    399     // for the initial Login Request.
    400     //
    401     IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, mPrivate->InitiatorName);
    402     IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
    403     IScsiAddKeyValuePair (
    404       Pdu,
    405       ISCSI_KEY_TARGET_NAME,
    406       Session->ConfigData->SessionConfigData.TargetName
    407       );
    408 
    409     if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
    410       Value = ISCSI_KEY_VALUE_NONE;
    411       ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
    412     } else {
    413       Value = ISCSI_AUTH_METHOD_CHAP;
    414     }
    415 
    416     IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
    417 
    418     break;
    419 
    420   case ISCSI_CHAP_STEP_ONE:
    421     //
    422     // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.
    423     //
    424     AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);
    425     IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);
    426 
    427     Conn->AuthStep = ISCSI_CHAP_STEP_TWO;
    428     break;
    429 
    430   case ISCSI_CHAP_STEP_THREE:
    431     //
    432     // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
    433     // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
    434     // required too.
    435     //
    436     // CHAP_N=<N>
    437     //
    438     IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName);
    439     //
    440     // CHAP_R=<R>
    441     //
    442     IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);
    443     IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
    444 
    445     if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
    446       //
    447       // CHAP_I=<I>
    448       //
    449       IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);
    450       AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
    451       IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
    452       //
    453       // CHAP_C=<C>
    454       //
    455       IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);
    456       AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;
    457       IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);
    458       IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
    459 
    460       Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;
    461     }
    462     //
    463     // Set the stage transition flag.
    464     //
    465     ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
    466     break;
    467 
    468   default:
    469     Status = EFI_PROTOCOL_ERROR;
    470     break;
    471   }
    472 
    473   FreePool (Response);
    474   FreePool (Challenge);
    475 
    476   return Status;
    477 }
    478