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