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