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