IScsiCHAP.c 14 KB

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