mirror of https://github.com/acidanthera/audk.git
674 lines
20 KiB
C
674 lines
20 KiB
C
/** @file
|
|
This file is for Challenge-Handshake Authentication Protocol (CHAP)
|
|
Configuration.
|
|
|
|
Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "IScsiImpl.h"
|
|
|
|
//
|
|
// Supported CHAP hash algorithms, mapped to sets of BaseCryptLib APIs and
|
|
// macros. CHAP_HASH structures at lower subscripts in the array are preferred
|
|
// by the initiator.
|
|
//
|
|
STATIC CONST CHAP_HASH mChapHash[] = {
|
|
{
|
|
ISCSI_CHAP_ALGORITHM_SHA256,
|
|
SHA256_DIGEST_SIZE,
|
|
Sha256GetContextSize,
|
|
Sha256Init,
|
|
Sha256Update,
|
|
Sha256Final
|
|
},
|
|
#ifdef ENABLE_MD5_DEPRECATED_INTERFACES
|
|
//
|
|
// Keep the deprecated MD5 entry at the end of the array (making MD5 the
|
|
// least preferred choice of the initiator).
|
|
//
|
|
{
|
|
ISCSI_CHAP_ALGORITHM_MD5,
|
|
MD5_DIGEST_SIZE,
|
|
Md5GetContextSize,
|
|
Md5Init,
|
|
Md5Update,
|
|
Md5Final
|
|
},
|
|
#endif // ENABLE_MD5_DEPRECATED_INTERFACES
|
|
};
|
|
|
|
//
|
|
// Ordered list of mChapHash[*].Algorithm values. It is formatted for the
|
|
// CHAP_A=<A1,A2...> value string, by the IScsiCHAPInitHashList() function. It
|
|
// is sent by the initiator in ISCSI_CHAP_STEP_ONE.
|
|
//
|
|
STATIC CHAR8 mChapHashListString[
|
|
3 + // UINT8 identifier in
|
|
// decimal
|
|
(1 + 3) * (ARRAY_SIZE (mChapHash) - 1) + // comma prepended for
|
|
// entries after the
|
|
// first
|
|
1 + // extra character for
|
|
// AsciiSPrint()
|
|
// truncation check
|
|
1 // terminating NUL
|
|
];
|
|
|
|
/**
|
|
Initiator calculates its own expected hash value.
|
|
|
|
@param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.
|
|
@param[in] ChapSecret iSCSI CHAP secret of the authenticator.
|
|
@param[in] SecretLength The length of iSCSI CHAP secret.
|
|
@param[in] ChapChallenge The challenge message sent by authenticator.
|
|
@param[in] ChallengeLength The length of iSCSI CHAP challenge message.
|
|
@param[in] Hash Pointer to the CHAP_HASH structure that
|
|
determines the hashing algorithm to use. The
|
|
caller is responsible for making Hash point
|
|
to an "mChapHash" element.
|
|
@param[out] ChapResponse The calculation of the expected hash value.
|
|
|
|
@retval EFI_SUCCESS The expected hash value was calculatedly
|
|
successfully.
|
|
@retval EFI_PROTOCOL_ERROR The length of the secret should be at least
|
|
the length of the hash value for the hashing
|
|
algorithm chosen.
|
|
@retval EFI_PROTOCOL_ERROR Hash operation fails.
|
|
@retval EFI_OUT_OF_RESOURCES Failure to allocate resource to complete
|
|
hashing.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
IScsiCHAPCalculateResponse (
|
|
IN UINT32 ChapIdentifier,
|
|
IN CHAR8 *ChapSecret,
|
|
IN UINT32 SecretLength,
|
|
IN UINT8 *ChapChallenge,
|
|
IN UINT32 ChallengeLength,
|
|
IN CONST CHAP_HASH *Hash,
|
|
OUT UINT8 *ChapResponse
|
|
)
|
|
{
|
|
UINTN ContextSize;
|
|
VOID *Ctx;
|
|
CHAR8 IdByte[1];
|
|
EFI_STATUS Status;
|
|
|
|
if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
ASSERT (Hash != NULL);
|
|
|
|
ContextSize = Hash->GetContextSize ();
|
|
Ctx = AllocatePool (ContextSize);
|
|
if (Ctx == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = EFI_PROTOCOL_ERROR;
|
|
|
|
if (!Hash->Init (Ctx)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Hash Identifier - Only calculate 1 byte data (RFC1994)
|
|
//
|
|
IdByte[0] = (CHAR8)ChapIdentifier;
|
|
if (!Hash->Update (Ctx, IdByte, 1)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Hash Secret
|
|
//
|
|
if (!Hash->Update (Ctx, ChapSecret, SecretLength)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Hash Challenge received from Target
|
|
//
|
|
if (!Hash->Update (Ctx, ChapChallenge, ChallengeLength)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (Hash->Final (Ctx, ChapResponse)) {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
Exit:
|
|
FreePool (Ctx);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
The initiator checks the CHAP response replied by target against its own
|
|
calculation of the expected hash value.
|
|
|
|
@param[in] AuthData iSCSI CHAP authentication data.
|
|
@param[in] TargetResponse The response from target.
|
|
|
|
@retval EFI_SUCCESS The response from target passed
|
|
authentication.
|
|
@retval EFI_SECURITY_VIOLATION The response from target was not expected
|
|
value.
|
|
@retval Others Other errors as indicated.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
IScsiCHAPAuthTarget (
|
|
IN ISCSI_CHAP_AUTH_DATA *AuthData,
|
|
IN UINT8 *TargetResponse
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 SecretSize;
|
|
UINT8 VerifyRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];
|
|
INTN Mismatch;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
SecretSize = (UINT32)AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);
|
|
|
|
ASSERT (AuthData->Hash != NULL);
|
|
|
|
Status = IScsiCHAPCalculateResponse (
|
|
AuthData->OutIdentifier,
|
|
AuthData->AuthConfig->ReverseCHAPSecret,
|
|
SecretSize,
|
|
AuthData->OutChallenge,
|
|
AuthData->Hash->DigestSize, // ChallengeLength
|
|
AuthData->Hash,
|
|
VerifyRsp
|
|
);
|
|
|
|
Mismatch = CompareMem (
|
|
VerifyRsp,
|
|
TargetResponse,
|
|
AuthData->Hash->DigestSize
|
|
);
|
|
if (Mismatch != 0) {
|
|
Status = EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This function checks the received iSCSI Login Response during the security
|
|
negotiation stage.
|
|
|
|
@param[in] Conn The iSCSI connection.
|
|
|
|
@retval EFI_SUCCESS The Login Response passed the CHAP validation.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
|
|
@retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
|
|
@retval Others Other errors as indicated.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
IScsiCHAPOnRspReceived (
|
|
IN ISCSI_CONNECTION *Conn
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
ISCSI_SESSION *Session;
|
|
ISCSI_CHAP_AUTH_DATA *AuthData;
|
|
CHAR8 *Value;
|
|
UINT8 *Data;
|
|
UINT32 Len;
|
|
LIST_ENTRY *KeyValueList;
|
|
UINTN Algorithm;
|
|
CHAR8 *Identifier;
|
|
CHAR8 *Challenge;
|
|
CHAR8 *Name;
|
|
CHAR8 *Response;
|
|
UINT8 TargetRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];
|
|
UINT32 RspLen;
|
|
UINTN Result;
|
|
UINTN HashIndex;
|
|
|
|
ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
|
|
ASSERT (Conn->RspQue.BufNum != 0);
|
|
|
|
Session = Conn->Session;
|
|
AuthData = &Session->AuthData.CHAP;
|
|
Len = Conn->RspQue.BufSize;
|
|
Data = AllocateZeroPool (Len);
|
|
if (Data == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Copy the data in case the data spans over multiple PDUs.
|
|
//
|
|
NetbufQueCopy (&Conn->RspQue, 0, Len, Data);
|
|
|
|
//
|
|
// Build the key-value list from the data segment of the Login Response.
|
|
//
|
|
KeyValueList = IScsiBuildKeyValueList ((CHAR8 *)Data, Len);
|
|
if (KeyValueList == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Status = EFI_PROTOCOL_ERROR;
|
|
|
|
switch (Conn->AuthStep) {
|
|
case ISCSI_AUTH_INITIAL:
|
|
//
|
|
// The first Login Response.
|
|
//
|
|
Value = IScsiGetValueByKeyFromList (
|
|
KeyValueList,
|
|
ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
|
|
);
|
|
if (Value == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Result = IScsiNetNtoi (Value);
|
|
if (Result > 0xFFFF) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Session->TargetPortalGroupTag = (UINT16)Result;
|
|
|
|
Value = IScsiGetValueByKeyFromList (
|
|
KeyValueList,
|
|
ISCSI_KEY_AUTH_METHOD
|
|
);
|
|
if (Value == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Initiator mandates CHAP authentication but target replies without
|
|
// "CHAP", or initiator suggets "None" but target replies with some kind of
|
|
// auth method.
|
|
//
|
|
if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
|
|
if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {
|
|
goto ON_EXIT;
|
|
}
|
|
} else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
|
|
if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {
|
|
goto ON_EXIT;
|
|
}
|
|
} else {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Transit to CHAP step one.
|
|
//
|
|
Conn->AuthStep = ISCSI_CHAP_STEP_ONE;
|
|
Status = EFI_SUCCESS;
|
|
break;
|
|
|
|
case ISCSI_CHAP_STEP_TWO:
|
|
//
|
|
// The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
|
|
//
|
|
Value = IScsiGetValueByKeyFromList (
|
|
KeyValueList,
|
|
ISCSI_KEY_CHAP_ALGORITHM
|
|
);
|
|
if (Value == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Algorithm = IScsiNetNtoi (Value);
|
|
for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) {
|
|
if (Algorithm == mChapHash[HashIndex].Algorithm) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (HashIndex == ARRAY_SIZE (mChapHash)) {
|
|
//
|
|
// Unsupported algorithm is chosen by target.
|
|
//
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Remember the target's chosen hash algorithm.
|
|
//
|
|
ASSERT (AuthData->Hash == NULL);
|
|
AuthData->Hash = &mChapHash[HashIndex];
|
|
|
|
Identifier = IScsiGetValueByKeyFromList (
|
|
KeyValueList,
|
|
ISCSI_KEY_CHAP_IDENTIFIER
|
|
);
|
|
if (Identifier == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Challenge = IScsiGetValueByKeyFromList (
|
|
KeyValueList,
|
|
ISCSI_KEY_CHAP_CHALLENGE
|
|
);
|
|
if (Challenge == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Process the CHAP identifier and CHAP Challenge from Target.
|
|
// Calculate Response value.
|
|
//
|
|
Result = IScsiNetNtoi (Identifier);
|
|
if (Result > 0xFF) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
AuthData->InIdentifier = (UINT32)Result;
|
|
AuthData->InChallengeLength = (UINT32)sizeof (AuthData->InChallenge);
|
|
Status = IScsiHexToBin (
|
|
(UINT8 *)AuthData->InChallenge,
|
|
&AuthData->InChallengeLength,
|
|
Challenge
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_PROTOCOL_ERROR;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Status = IScsiCHAPCalculateResponse (
|
|
AuthData->InIdentifier,
|
|
AuthData->AuthConfig->CHAPSecret,
|
|
(UINT32)AsciiStrLen (AuthData->AuthConfig->CHAPSecret),
|
|
AuthData->InChallenge,
|
|
AuthData->InChallengeLength,
|
|
AuthData->Hash,
|
|
AuthData->CHAPResponse
|
|
);
|
|
|
|
//
|
|
// Transit to next step.
|
|
//
|
|
Conn->AuthStep = ISCSI_CHAP_STEP_THREE;
|
|
break;
|
|
|
|
case ISCSI_CHAP_STEP_THREE:
|
|
//
|
|
// One way CHAP authentication and the target would like to
|
|
// authenticate us.
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
break;
|
|
|
|
case ISCSI_CHAP_STEP_FOUR:
|
|
ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);
|
|
//
|
|
// The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
|
|
//
|
|
Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
|
|
if (Name == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
Response = IScsiGetValueByKeyFromList (
|
|
KeyValueList,
|
|
ISCSI_KEY_CHAP_RESPONSE
|
|
);
|
|
if (Response == NULL) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
ASSERT (AuthData->Hash != NULL);
|
|
RspLen = AuthData->Hash->DigestSize;
|
|
Status = IScsiHexToBin (TargetRsp, &RspLen, Response);
|
|
if (EFI_ERROR (Status) || (RspLen != AuthData->Hash->DigestSize)) {
|
|
Status = EFI_PROTOCOL_ERROR;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Check the CHAP Name and Response replied by Target.
|
|
//
|
|
Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ON_EXIT:
|
|
|
|
if (KeyValueList != NULL) {
|
|
IScsiFreeKeyValueList (KeyValueList);
|
|
}
|
|
|
|
FreePool (Data);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This function fills the CHAP authentication information into the login PDU
|
|
during the security negotiation stage in the iSCSI connection login.
|
|
|
|
@param[in] Conn The iSCSI connection.
|
|
@param[in, out] Pdu The PDU to send out.
|
|
|
|
@retval EFI_SUCCESS All check passed and the phase-related CHAP
|
|
authentication info is filled into the iSCSI
|
|
PDU.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
|
|
@retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
IScsiCHAPToSendReq (
|
|
IN ISCSI_CONNECTION *Conn,
|
|
IN OUT NET_BUF *Pdu
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
ISCSI_SESSION *Session;
|
|
ISCSI_LOGIN_REQUEST *LoginReq;
|
|
ISCSI_CHAP_AUTH_DATA *AuthData;
|
|
CHAR8 *Value;
|
|
CHAR8 ValueStr[256];
|
|
CHAR8 *Response;
|
|
UINT32 RspLen;
|
|
CHAR8 *Challenge;
|
|
UINT32 ChallengeLen;
|
|
EFI_STATUS BinToHexStatus;
|
|
|
|
ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
|
|
|
|
Session = Conn->Session;
|
|
AuthData = &Session->AuthData.CHAP;
|
|
LoginReq = (ISCSI_LOGIN_REQUEST *)NetbufGetByte (Pdu, 0, 0);
|
|
if (LoginReq == NULL) {
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
RspLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;
|
|
Response = AllocateZeroPool (RspLen);
|
|
if (Response == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
ChallengeLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;
|
|
Challenge = AllocateZeroPool (ChallengeLen);
|
|
if (Challenge == NULL) {
|
|
FreePool (Response);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
switch (Conn->AuthStep) {
|
|
case ISCSI_AUTH_INITIAL:
|
|
//
|
|
// It's the initial Login Request. Fill in the key=value pairs mandatory
|
|
// for the initial Login Request.
|
|
//
|
|
IScsiAddKeyValuePair (
|
|
Pdu,
|
|
ISCSI_KEY_INITIATOR_NAME,
|
|
mPrivate->InitiatorName
|
|
);
|
|
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
|
|
IScsiAddKeyValuePair (
|
|
Pdu,
|
|
ISCSI_KEY_TARGET_NAME,
|
|
Session->ConfigData->SessionConfigData.TargetName
|
|
);
|
|
|
|
if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
|
|
Value = ISCSI_KEY_VALUE_NONE;
|
|
ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
|
|
} else {
|
|
Value = ISCSI_AUTH_METHOD_CHAP;
|
|
}
|
|
|
|
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
|
|
|
|
break;
|
|
|
|
case ISCSI_CHAP_STEP_ONE:
|
|
//
|
|
// First step, send the Login Request with CHAP_A=<A1,A2...> key-value
|
|
// pair.
|
|
//
|
|
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, mChapHashListString);
|
|
|
|
Conn->AuthStep = ISCSI_CHAP_STEP_TWO;
|
|
break;
|
|
|
|
case ISCSI_CHAP_STEP_THREE:
|
|
//
|
|
// Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
|
|
// CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
|
|
// required too.
|
|
//
|
|
// CHAP_N=<N>
|
|
//
|
|
IScsiAddKeyValuePair (
|
|
Pdu,
|
|
ISCSI_KEY_CHAP_NAME,
|
|
(CHAR8 *)&AuthData->AuthConfig->CHAPName
|
|
);
|
|
//
|
|
// CHAP_R=<R>
|
|
//
|
|
ASSERT (AuthData->Hash != NULL);
|
|
BinToHexStatus = IScsiBinToHex (
|
|
(UINT8 *)AuthData->CHAPResponse,
|
|
AuthData->Hash->DigestSize,
|
|
Response,
|
|
&RspLen
|
|
);
|
|
ASSERT_EFI_ERROR (BinToHexStatus);
|
|
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
|
|
|
|
if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
|
|
//
|
|
// CHAP_I=<I>
|
|
//
|
|
IScsiGenRandom ((UINT8 *)&AuthData->OutIdentifier, 1);
|
|
AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
|
|
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
|
|
//
|
|
// CHAP_C=<C>
|
|
//
|
|
IScsiGenRandom (
|
|
(UINT8 *)AuthData->OutChallenge,
|
|
AuthData->Hash->DigestSize
|
|
);
|
|
BinToHexStatus = IScsiBinToHex (
|
|
(UINT8 *)AuthData->OutChallenge,
|
|
AuthData->Hash->DigestSize,
|
|
Challenge,
|
|
&ChallengeLen
|
|
);
|
|
ASSERT_EFI_ERROR (BinToHexStatus);
|
|
IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
|
|
|
|
Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;
|
|
}
|
|
|
|
//
|
|
// Set the stage transition flag.
|
|
//
|
|
ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
|
|
break;
|
|
|
|
default:
|
|
Status = EFI_PROTOCOL_ERROR;
|
|
break;
|
|
}
|
|
|
|
FreePool (Response);
|
|
FreePool (Challenge);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Initialize the CHAP_A=<A1,A2...> *value* string for the entire driver, to be
|
|
sent by the initiator in ISCSI_CHAP_STEP_ONE.
|
|
|
|
This function sanity-checks the internal table of supported CHAP hashing
|
|
algorithms, as well.
|
|
**/
|
|
VOID
|
|
IScsiCHAPInitHashList (
|
|
VOID
|
|
)
|
|
{
|
|
CHAR8 *Position;
|
|
UINTN Left;
|
|
UINTN HashIndex;
|
|
CONST CHAP_HASH *Hash;
|
|
UINTN Printed;
|
|
|
|
Position = mChapHashListString;
|
|
Left = sizeof (mChapHashListString);
|
|
for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) {
|
|
Hash = &mChapHash[HashIndex];
|
|
|
|
//
|
|
// Format the next hash identifier.
|
|
//
|
|
// Assert that we can format at least one non-NUL character, i.e. that we
|
|
// can progress. Truncation is checked after printing.
|
|
//
|
|
ASSERT (Left >= 2);
|
|
Printed = AsciiSPrint (
|
|
Position,
|
|
Left,
|
|
"%a%d",
|
|
(HashIndex == 0) ? "" : ",",
|
|
Hash->Algorithm
|
|
);
|
|
//
|
|
// There's no way to differentiate between the "buffer filled to the brim,
|
|
// but not truncated" result and the "truncated" result of AsciiSPrint().
|
|
// This is why "mChapHashListString" has an extra byte allocated, and the
|
|
// reason why we use the less-than (rather than the less-than-or-equal-to)
|
|
// relational operator in the assertion below -- we enforce "no truncation"
|
|
// by excluding the "completely used up" case too.
|
|
//
|
|
ASSERT (Printed + 1 < Left);
|
|
|
|
Position += Printed;
|
|
Left -= Printed;
|
|
|
|
//
|
|
// Sanity-check the digest size for Hash.
|
|
//
|
|
ASSERT (Hash->DigestSize <= ISCSI_CHAP_MAX_DIGEST_SIZE);
|
|
}
|
|
}
|