IScsiCHAP.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. /** @file
  2. This file is for Challenge-Handshake Authentication Protocol (CHAP)
  3. Configuration.
  4. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. **/
  7. #include "IScsiImpl.h"
  8. //
  9. // Supported CHAP hash algorithms, mapped to sets of BaseCryptLib APIs and
  10. // macros. CHAP_HASH structures at lower subscripts in the array are preferred
  11. // by the initiator.
  12. //
  13. STATIC CONST CHAP_HASH mChapHash[] = {
  14. {
  15. ISCSI_CHAP_ALGORITHM_SHA256,
  16. SHA256_DIGEST_SIZE,
  17. Sha256GetContextSize,
  18. Sha256Init,
  19. Sha256Update,
  20. Sha256Final
  21. },
  22. #ifdef ENABLE_MD5_DEPRECATED_INTERFACES
  23. //
  24. // Keep the deprecated MD5 entry at the end of the array (making MD5 the
  25. // least preferred choice of the initiator).
  26. //
  27. {
  28. ISCSI_CHAP_ALGORITHM_MD5,
  29. MD5_DIGEST_SIZE,
  30. Md5GetContextSize,
  31. Md5Init,
  32. Md5Update,
  33. Md5Final
  34. },
  35. #endif // ENABLE_MD5_DEPRECATED_INTERFACES
  36. };
  37. //
  38. // Ordered list of mChapHash[*].Algorithm values. It is formatted for the
  39. // CHAP_A=<A1,A2...> value string, by the IScsiCHAPInitHashList() function. It
  40. // is sent by the initiator in ISCSI_CHAP_STEP_ONE.
  41. //
  42. STATIC CHAR8 mChapHashListString[
  43. 3 + // UINT8 identifier in
  44. // decimal
  45. (1 + 3) * (ARRAY_SIZE (mChapHash) - 1) + // comma prepended for
  46. // entries after the
  47. // first
  48. 1 + // extra character for
  49. // AsciiSPrint()
  50. // truncation check
  51. 1 // terminating NUL
  52. ];
  53. /**
  54. Initiator calculates its own expected hash value.
  55. @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.
  56. @param[in] ChapSecret iSCSI CHAP secret of the authenticator.
  57. @param[in] SecretLength The length of iSCSI CHAP secret.
  58. @param[in] ChapChallenge The challenge message sent by authenticator.
  59. @param[in] ChallengeLength The length of iSCSI CHAP challenge message.
  60. @param[in] Hash Pointer to the CHAP_HASH structure that
  61. determines the hashing algorithm to use. The
  62. caller is responsible for making Hash point
  63. to an "mChapHash" element.
  64. @param[out] ChapResponse The calculation of the expected hash value.
  65. @retval EFI_SUCCESS The expected hash value was calculatedly
  66. successfully.
  67. @retval EFI_PROTOCOL_ERROR The length of the secret should be at least
  68. the length of the hash value for the hashing
  69. algorithm chosen.
  70. @retval EFI_PROTOCOL_ERROR Hash operation fails.
  71. @retval EFI_OUT_OF_RESOURCES Failure to allocate resource to complete
  72. hashing.
  73. **/
  74. EFI_STATUS
  75. IScsiCHAPCalculateResponse (
  76. IN UINT32 ChapIdentifier,
  77. IN CHAR8 *ChapSecret,
  78. IN UINT32 SecretLength,
  79. IN UINT8 *ChapChallenge,
  80. IN UINT32 ChallengeLength,
  81. IN CONST CHAP_HASH *Hash,
  82. OUT UINT8 *ChapResponse
  83. )
  84. {
  85. UINTN ContextSize;
  86. VOID *Ctx;
  87. CHAR8 IdByte[1];
  88. EFI_STATUS Status;
  89. if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {
  90. return EFI_PROTOCOL_ERROR;
  91. }
  92. ASSERT (Hash != NULL);
  93. ContextSize = Hash->GetContextSize ();
  94. Ctx = AllocatePool (ContextSize);
  95. if (Ctx == NULL) {
  96. return EFI_OUT_OF_RESOURCES;
  97. }
  98. Status = EFI_PROTOCOL_ERROR;
  99. if (!Hash->Init (Ctx)) {
  100. goto Exit;
  101. }
  102. //
  103. // Hash Identifier - Only calculate 1 byte data (RFC1994)
  104. //
  105. IdByte[0] = (CHAR8)ChapIdentifier;
  106. if (!Hash->Update (Ctx, IdByte, 1)) {
  107. goto Exit;
  108. }
  109. //
  110. // Hash Secret
  111. //
  112. if (!Hash->Update (Ctx, ChapSecret, SecretLength)) {
  113. goto Exit;
  114. }
  115. //
  116. // Hash Challenge received from Target
  117. //
  118. if (!Hash->Update (Ctx, ChapChallenge, ChallengeLength)) {
  119. goto Exit;
  120. }
  121. if (Hash->Final (Ctx, ChapResponse)) {
  122. Status = EFI_SUCCESS;
  123. }
  124. Exit:
  125. FreePool (Ctx);
  126. return Status;
  127. }
  128. /**
  129. The initiator checks the CHAP response replied by target against its own
  130. calculation of the expected hash value.
  131. @param[in] AuthData iSCSI CHAP authentication data.
  132. @param[in] TargetResponse The response from target.
  133. @retval EFI_SUCCESS The response from target passed
  134. authentication.
  135. @retval EFI_SECURITY_VIOLATION The response from target was not expected
  136. value.
  137. @retval Others Other errors as indicated.
  138. **/
  139. EFI_STATUS
  140. IScsiCHAPAuthTarget (
  141. IN ISCSI_CHAP_AUTH_DATA *AuthData,
  142. IN UINT8 *TargetResponse
  143. )
  144. {
  145. EFI_STATUS Status;
  146. UINT32 SecretSize;
  147. UINT8 VerifyRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];
  148. INTN Mismatch;
  149. Status = EFI_SUCCESS;
  150. SecretSize = (UINT32)AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);
  151. ASSERT (AuthData->Hash != NULL);
  152. Status = IScsiCHAPCalculateResponse (
  153. AuthData->OutIdentifier,
  154. AuthData->AuthConfig->ReverseCHAPSecret,
  155. SecretSize,
  156. AuthData->OutChallenge,
  157. AuthData->Hash->DigestSize, // ChallengeLength
  158. AuthData->Hash,
  159. VerifyRsp
  160. );
  161. Mismatch = CompareMem (
  162. VerifyRsp,
  163. TargetResponse,
  164. AuthData->Hash->DigestSize
  165. );
  166. if (Mismatch != 0) {
  167. Status = EFI_SECURITY_VIOLATION;
  168. }
  169. return Status;
  170. }
  171. /**
  172. This function checks the received iSCSI Login Response during the security
  173. negotiation stage.
  174. @param[in] Conn The iSCSI connection.
  175. @retval EFI_SUCCESS The Login Response passed the CHAP validation.
  176. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
  177. @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
  178. @retval Others Other errors as indicated.
  179. **/
  180. EFI_STATUS
  181. IScsiCHAPOnRspReceived (
  182. IN ISCSI_CONNECTION *Conn
  183. )
  184. {
  185. EFI_STATUS Status;
  186. ISCSI_SESSION *Session;
  187. ISCSI_CHAP_AUTH_DATA *AuthData;
  188. CHAR8 *Value;
  189. UINT8 *Data;
  190. UINT32 Len;
  191. LIST_ENTRY *KeyValueList;
  192. UINTN Algorithm;
  193. CHAR8 *Identifier;
  194. CHAR8 *Challenge;
  195. CHAR8 *Name;
  196. CHAR8 *Response;
  197. UINT8 TargetRsp[ISCSI_CHAP_MAX_DIGEST_SIZE];
  198. UINT32 RspLen;
  199. UINTN Result;
  200. UINTN HashIndex;
  201. ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
  202. ASSERT (Conn->RspQue.BufNum != 0);
  203. Session = Conn->Session;
  204. AuthData = &Session->AuthData.CHAP;
  205. Len = Conn->RspQue.BufSize;
  206. Data = AllocateZeroPool (Len);
  207. if (Data == NULL) {
  208. return EFI_OUT_OF_RESOURCES;
  209. }
  210. //
  211. // Copy the data in case the data spans over multiple PDUs.
  212. //
  213. NetbufQueCopy (&Conn->RspQue, 0, Len, Data);
  214. //
  215. // Build the key-value list from the data segment of the Login Response.
  216. //
  217. KeyValueList = IScsiBuildKeyValueList ((CHAR8 *)Data, Len);
  218. if (KeyValueList == NULL) {
  219. Status = EFI_OUT_OF_RESOURCES;
  220. goto ON_EXIT;
  221. }
  222. Status = EFI_PROTOCOL_ERROR;
  223. switch (Conn->AuthStep) {
  224. case ISCSI_AUTH_INITIAL:
  225. //
  226. // The first Login Response.
  227. //
  228. Value = IScsiGetValueByKeyFromList (
  229. KeyValueList,
  230. ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
  231. );
  232. if (Value == NULL) {
  233. goto ON_EXIT;
  234. }
  235. Result = IScsiNetNtoi (Value);
  236. if (Result > 0xFFFF) {
  237. goto ON_EXIT;
  238. }
  239. Session->TargetPortalGroupTag = (UINT16)Result;
  240. Value = IScsiGetValueByKeyFromList (
  241. KeyValueList,
  242. ISCSI_KEY_AUTH_METHOD
  243. );
  244. if (Value == NULL) {
  245. goto ON_EXIT;
  246. }
  247. //
  248. // Initiator mandates CHAP authentication but target replies without
  249. // "CHAP", or initiator suggets "None" but target replies with some kind of
  250. // auth method.
  251. //
  252. if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
  253. if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {
  254. goto ON_EXIT;
  255. }
  256. } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
  257. if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {
  258. goto ON_EXIT;
  259. }
  260. } else {
  261. goto ON_EXIT;
  262. }
  263. //
  264. // Transit to CHAP step one.
  265. //
  266. Conn->AuthStep = ISCSI_CHAP_STEP_ONE;
  267. Status = EFI_SUCCESS;
  268. break;
  269. case ISCSI_CHAP_STEP_TWO:
  270. //
  271. // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
  272. //
  273. Value = IScsiGetValueByKeyFromList (
  274. KeyValueList,
  275. ISCSI_KEY_CHAP_ALGORITHM
  276. );
  277. if (Value == NULL) {
  278. goto ON_EXIT;
  279. }
  280. Algorithm = IScsiNetNtoi (Value);
  281. for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) {
  282. if (Algorithm == mChapHash[HashIndex].Algorithm) {
  283. break;
  284. }
  285. }
  286. if (HashIndex == ARRAY_SIZE (mChapHash)) {
  287. //
  288. // Unsupported algorithm is chosen by target.
  289. //
  290. goto ON_EXIT;
  291. }
  292. //
  293. // Remember the target's chosen hash algorithm.
  294. //
  295. ASSERT (AuthData->Hash == NULL);
  296. AuthData->Hash = &mChapHash[HashIndex];
  297. Identifier = IScsiGetValueByKeyFromList (
  298. KeyValueList,
  299. ISCSI_KEY_CHAP_IDENTIFIER
  300. );
  301. if (Identifier == NULL) {
  302. goto ON_EXIT;
  303. }
  304. Challenge = IScsiGetValueByKeyFromList (
  305. KeyValueList,
  306. ISCSI_KEY_CHAP_CHALLENGE
  307. );
  308. if (Challenge == NULL) {
  309. goto ON_EXIT;
  310. }
  311. //
  312. // Process the CHAP identifier and CHAP Challenge from Target.
  313. // Calculate Response value.
  314. //
  315. Result = IScsiNetNtoi (Identifier);
  316. if (Result > 0xFF) {
  317. goto ON_EXIT;
  318. }
  319. AuthData->InIdentifier = (UINT32)Result;
  320. AuthData->InChallengeLength = (UINT32)sizeof (AuthData->InChallenge);
  321. Status = IScsiHexToBin (
  322. (UINT8 *)AuthData->InChallenge,
  323. &AuthData->InChallengeLength,
  324. Challenge
  325. );
  326. if (EFI_ERROR (Status)) {
  327. Status = EFI_PROTOCOL_ERROR;
  328. goto ON_EXIT;
  329. }
  330. Status = IScsiCHAPCalculateResponse (
  331. AuthData->InIdentifier,
  332. AuthData->AuthConfig->CHAPSecret,
  333. (UINT32)AsciiStrLen (AuthData->AuthConfig->CHAPSecret),
  334. AuthData->InChallenge,
  335. AuthData->InChallengeLength,
  336. AuthData->Hash,
  337. AuthData->CHAPResponse
  338. );
  339. //
  340. // Transit to next step.
  341. //
  342. Conn->AuthStep = ISCSI_CHAP_STEP_THREE;
  343. break;
  344. case ISCSI_CHAP_STEP_THREE:
  345. //
  346. // One way CHAP authentication and the target would like to
  347. // authenticate us.
  348. //
  349. Status = EFI_SUCCESS;
  350. break;
  351. case ISCSI_CHAP_STEP_FOUR:
  352. ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);
  353. //
  354. // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
  355. //
  356. Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
  357. if (Name == NULL) {
  358. goto ON_EXIT;
  359. }
  360. Response = IScsiGetValueByKeyFromList (
  361. KeyValueList,
  362. ISCSI_KEY_CHAP_RESPONSE
  363. );
  364. if (Response == NULL) {
  365. goto ON_EXIT;
  366. }
  367. ASSERT (AuthData->Hash != NULL);
  368. RspLen = AuthData->Hash->DigestSize;
  369. Status = IScsiHexToBin (TargetRsp, &RspLen, Response);
  370. if (EFI_ERROR (Status) || (RspLen != AuthData->Hash->DigestSize)) {
  371. Status = EFI_PROTOCOL_ERROR;
  372. goto ON_EXIT;
  373. }
  374. //
  375. // Check the CHAP Name and Response replied by Target.
  376. //
  377. Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
  378. break;
  379. default:
  380. break;
  381. }
  382. ON_EXIT:
  383. if (KeyValueList != NULL) {
  384. IScsiFreeKeyValueList (KeyValueList);
  385. }
  386. FreePool (Data);
  387. return Status;
  388. }
  389. /**
  390. This function fills the CHAP authentication information into the login PDU
  391. during the security negotiation stage in the iSCSI connection login.
  392. @param[in] Conn The iSCSI connection.
  393. @param[in, out] Pdu The PDU to send out.
  394. @retval EFI_SUCCESS All check passed and the phase-related CHAP
  395. authentication info is filled into the iSCSI
  396. PDU.
  397. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
  398. @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
  399. **/
  400. EFI_STATUS
  401. IScsiCHAPToSendReq (
  402. IN ISCSI_CONNECTION *Conn,
  403. IN OUT NET_BUF *Pdu
  404. )
  405. {
  406. EFI_STATUS Status;
  407. ISCSI_SESSION *Session;
  408. ISCSI_LOGIN_REQUEST *LoginReq;
  409. ISCSI_CHAP_AUTH_DATA *AuthData;
  410. CHAR8 *Value;
  411. CHAR8 ValueStr[256];
  412. CHAR8 *Response;
  413. UINT32 RspLen;
  414. CHAR8 *Challenge;
  415. UINT32 ChallengeLen;
  416. EFI_STATUS BinToHexStatus;
  417. ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
  418. Session = Conn->Session;
  419. AuthData = &Session->AuthData.CHAP;
  420. LoginReq = (ISCSI_LOGIN_REQUEST *)NetbufGetByte (Pdu, 0, 0);
  421. if (LoginReq == NULL) {
  422. return EFI_PROTOCOL_ERROR;
  423. }
  424. Status = EFI_SUCCESS;
  425. RspLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;
  426. Response = AllocateZeroPool (RspLen);
  427. if (Response == NULL) {
  428. return EFI_OUT_OF_RESOURCES;
  429. }
  430. ChallengeLen = 2 * ISCSI_CHAP_MAX_DIGEST_SIZE + 3;
  431. Challenge = AllocateZeroPool (ChallengeLen);
  432. if (Challenge == NULL) {
  433. FreePool (Response);
  434. return EFI_OUT_OF_RESOURCES;
  435. }
  436. switch (Conn->AuthStep) {
  437. case ISCSI_AUTH_INITIAL:
  438. //
  439. // It's the initial Login Request. Fill in the key=value pairs mandatory
  440. // for the initial Login Request.
  441. //
  442. IScsiAddKeyValuePair (
  443. Pdu,
  444. ISCSI_KEY_INITIATOR_NAME,
  445. mPrivate->InitiatorName
  446. );
  447. IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
  448. IScsiAddKeyValuePair (
  449. Pdu,
  450. ISCSI_KEY_TARGET_NAME,
  451. Session->ConfigData->SessionConfigData.TargetName
  452. );
  453. if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
  454. Value = ISCSI_KEY_VALUE_NONE;
  455. ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
  456. } else {
  457. Value = ISCSI_AUTH_METHOD_CHAP;
  458. }
  459. IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
  460. break;
  461. case ISCSI_CHAP_STEP_ONE:
  462. //
  463. // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
  464. // pair.
  465. //
  466. IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, mChapHashListString);
  467. Conn->AuthStep = ISCSI_CHAP_STEP_TWO;
  468. break;
  469. case ISCSI_CHAP_STEP_THREE:
  470. //
  471. // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
  472. // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
  473. // required too.
  474. //
  475. // CHAP_N=<N>
  476. //
  477. IScsiAddKeyValuePair (
  478. Pdu,
  479. ISCSI_KEY_CHAP_NAME,
  480. (CHAR8 *)&AuthData->AuthConfig->CHAPName
  481. );
  482. //
  483. // CHAP_R=<R>
  484. //
  485. ASSERT (AuthData->Hash != NULL);
  486. BinToHexStatus = IScsiBinToHex (
  487. (UINT8 *)AuthData->CHAPResponse,
  488. AuthData->Hash->DigestSize,
  489. Response,
  490. &RspLen
  491. );
  492. ASSERT_EFI_ERROR (BinToHexStatus);
  493. IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
  494. if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
  495. //
  496. // CHAP_I=<I>
  497. //
  498. IScsiGenRandom ((UINT8 *)&AuthData->OutIdentifier, 1);
  499. AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
  500. IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
  501. //
  502. // CHAP_C=<C>
  503. //
  504. IScsiGenRandom (
  505. (UINT8 *)AuthData->OutChallenge,
  506. AuthData->Hash->DigestSize
  507. );
  508. BinToHexStatus = IScsiBinToHex (
  509. (UINT8 *)AuthData->OutChallenge,
  510. AuthData->Hash->DigestSize,
  511. Challenge,
  512. &ChallengeLen
  513. );
  514. ASSERT_EFI_ERROR (BinToHexStatus);
  515. IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
  516. Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;
  517. }
  518. //
  519. // Set the stage transition flag.
  520. //
  521. ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
  522. break;
  523. default:
  524. Status = EFI_PROTOCOL_ERROR;
  525. break;
  526. }
  527. FreePool (Response);
  528. FreePool (Challenge);
  529. return Status;
  530. }
  531. /**
  532. Initialize the CHAP_A=<A1,A2...> *value* string for the entire driver, to be
  533. sent by the initiator in ISCSI_CHAP_STEP_ONE.
  534. This function sanity-checks the internal table of supported CHAP hashing
  535. algorithms, as well.
  536. **/
  537. VOID
  538. IScsiCHAPInitHashList (
  539. VOID
  540. )
  541. {
  542. CHAR8 *Position;
  543. UINTN Left;
  544. UINTN HashIndex;
  545. CONST CHAP_HASH *Hash;
  546. UINTN Printed;
  547. Position = mChapHashListString;
  548. Left = sizeof (mChapHashListString);
  549. for (HashIndex = 0; HashIndex < ARRAY_SIZE (mChapHash); HashIndex++) {
  550. Hash = &mChapHash[HashIndex];
  551. //
  552. // Format the next hash identifier.
  553. //
  554. // Assert that we can format at least one non-NUL character, i.e. that we
  555. // can progress. Truncation is checked after printing.
  556. //
  557. ASSERT (Left >= 2);
  558. Printed = AsciiSPrint (
  559. Position,
  560. Left,
  561. "%a%d",
  562. (HashIndex == 0) ? "" : ",",
  563. Hash->Algorithm
  564. );
  565. //
  566. // There's no way to differentiate between the "buffer filled to the brim,
  567. // but not truncated" result and the "truncated" result of AsciiSPrint().
  568. // This is why "mChapHashListString" has an extra byte allocated, and the
  569. // reason why we use the less-than (rather than the less-than-or-equal-to)
  570. // relational operator in the assertion below -- we enforce "no truncation"
  571. // by excluding the "completely used up" case too.
  572. //
  573. ASSERT (Printed + 1 < Left);
  574. Position += Printed;
  575. Left -= Printed;
  576. //
  577. // Sanity-check the digest size for Hash.
  578. //
  579. ASSERT (Hash->DigestSize <= ISCSI_CHAP_MAX_DIGEST_SIZE);
  580. }
  581. }