• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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