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