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