]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiCHAP.c
NetworkPkg/IScsiDxe: check IScsiHexToBin() return values
[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 Status = IScsiHexToBin (
294 (UINT8 *) AuthData->InChallenge,
295 &AuthData->InChallengeLength,
296 Challenge
297 );
298 if (EFI_ERROR (Status)) {
299 Status = EFI_PROTOCOL_ERROR;
300 goto ON_EXIT;
301 }
302 Status = IScsiCHAPCalculateResponse (
303 AuthData->InIdentifier,
304 AuthData->AuthConfig->CHAPSecret,
305 (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret),
306 AuthData->InChallenge,
307 AuthData->InChallengeLength,
308 AuthData->CHAPResponse
309 );
310
311 //
312 // Transit to next step.
313 //
314 Conn->AuthStep = ISCSI_CHAP_STEP_THREE;
315 break;
316
317 case ISCSI_CHAP_STEP_THREE:
318 //
319 // One way CHAP authentication and the target would like to
320 // authenticate us.
321 //
322 Status = EFI_SUCCESS;
323 break;
324
325 case ISCSI_CHAP_STEP_FOUR:
326 ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);
327 //
328 // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
329 //
330 Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
331 if (Name == NULL) {
332 goto ON_EXIT;
333 }
334
335 Response = IScsiGetValueByKeyFromList (
336 KeyValueList,
337 ISCSI_KEY_CHAP_RESPONSE
338 );
339 if (Response == NULL) {
340 goto ON_EXIT;
341 }
342
343 RspLen = ISCSI_CHAP_RSP_LEN;
344 Status = IScsiHexToBin (TargetRsp, &RspLen, Response);
345 if (EFI_ERROR (Status) || RspLen != ISCSI_CHAP_RSP_LEN) {
346 Status = EFI_PROTOCOL_ERROR;
347 goto ON_EXIT;
348 }
349
350 //
351 // Check the CHAP Name and Response replied by Target.
352 //
353 Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
354 break;
355
356 default:
357 break;
358 }
359
360 ON_EXIT:
361
362 if (KeyValueList != NULL) {
363 IScsiFreeKeyValueList (KeyValueList);
364 }
365
366 FreePool (Data);
367
368 return Status;
369 }
370
371
372 /**
373 This function fills the CHAP authentication information into the login PDU
374 during the security negotiation stage in the iSCSI connection login.
375
376 @param[in] Conn The iSCSI connection.
377 @param[in, out] Pdu The PDU to send out.
378
379 @retval EFI_SUCCESS All check passed and the phase-related CHAP
380 authentication info is filled into the iSCSI
381 PDU.
382 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
383 @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
384
385 **/
386 EFI_STATUS
387 IScsiCHAPToSendReq (
388 IN ISCSI_CONNECTION *Conn,
389 IN OUT NET_BUF *Pdu
390 )
391 {
392 EFI_STATUS Status;
393 ISCSI_SESSION *Session;
394 ISCSI_LOGIN_REQUEST *LoginReq;
395 ISCSI_CHAP_AUTH_DATA *AuthData;
396 CHAR8 *Value;
397 CHAR8 ValueStr[256];
398 CHAR8 *Response;
399 UINT32 RspLen;
400 CHAR8 *Challenge;
401 UINT32 ChallengeLen;
402 EFI_STATUS BinToHexStatus;
403
404 ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
405
406 Session = Conn->Session;
407 AuthData = &Session->AuthData.CHAP;
408 LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0);
409 if (LoginReq == NULL) {
410 return EFI_PROTOCOL_ERROR;
411 }
412 Status = EFI_SUCCESS;
413
414 RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
415 Response = AllocateZeroPool (RspLen);
416 if (Response == NULL) {
417 return EFI_OUT_OF_RESOURCES;
418 }
419
420 ChallengeLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
421 Challenge = AllocateZeroPool (ChallengeLen);
422 if (Challenge == NULL) {
423 FreePool (Response);
424 return EFI_OUT_OF_RESOURCES;
425 }
426
427 switch (Conn->AuthStep) {
428 case ISCSI_AUTH_INITIAL:
429 //
430 // It's the initial Login Request. Fill in the key=value pairs mandatory
431 // for the initial Login Request.
432 //
433 IScsiAddKeyValuePair (
434 Pdu,
435 ISCSI_KEY_INITIATOR_NAME,
436 mPrivate->InitiatorName
437 );
438 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
439 IScsiAddKeyValuePair (
440 Pdu,
441 ISCSI_KEY_TARGET_NAME,
442 Session->ConfigData->SessionConfigData.TargetName
443 );
444
445 if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
446 Value = ISCSI_KEY_VALUE_NONE;
447 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
448 } else {
449 Value = ISCSI_AUTH_METHOD_CHAP;
450 }
451
452 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
453
454 break;
455
456 case ISCSI_CHAP_STEP_ONE:
457 //
458 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
459 // pair.
460 //
461 AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);
462 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);
463
464 Conn->AuthStep = ISCSI_CHAP_STEP_TWO;
465 break;
466
467 case ISCSI_CHAP_STEP_THREE:
468 //
469 // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
470 // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
471 // required too.
472 //
473 // CHAP_N=<N>
474 //
475 IScsiAddKeyValuePair (
476 Pdu,
477 ISCSI_KEY_CHAP_NAME,
478 (CHAR8 *) &AuthData->AuthConfig->CHAPName
479 );
480 //
481 // CHAP_R=<R>
482 //
483 BinToHexStatus = IScsiBinToHex (
484 (UINT8 *) AuthData->CHAPResponse,
485 ISCSI_CHAP_RSP_LEN,
486 Response,
487 &RspLen
488 );
489 ASSERT_EFI_ERROR (BinToHexStatus);
490 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
491
492 if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
493 //
494 // CHAP_I=<I>
495 //
496 IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);
497 AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
498 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
499 //
500 // CHAP_C=<C>
501 //
502 IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);
503 BinToHexStatus = IScsiBinToHex (
504 (UINT8 *) AuthData->OutChallenge,
505 ISCSI_CHAP_RSP_LEN,
506 Challenge,
507 &ChallengeLen
508 );
509 ASSERT_EFI_ERROR (BinToHexStatus);
510 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
511
512 Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;
513 }
514 //
515 // Set the stage transition flag.
516 //
517 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
518 break;
519
520 default:
521 Status = EFI_PROTOCOL_ERROR;
522 break;
523 }
524
525 FreePool (Response);
526 FreePool (Challenge);
527
528 return Status;
529 }