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