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