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