]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.c
Clean up the private GUID definition in module Level.
[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 - 2011, 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 caculates 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 caculatedly 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
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
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
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
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 Status = EFI_SUCCESS;
336
337 RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
338 Response = AllocatePool (RspLen);
339 if (Response == NULL) {
340 return EFI_OUT_OF_RESOURCES;
341 }
342
343 ChallengeLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
344 Challenge = AllocatePool (ChallengeLen);
345 if (Challenge == NULL) {
346 return EFI_OUT_OF_RESOURCES;
347 }
348
349 switch (Conn->CHAPStep) {
350 case ISCSI_CHAP_INITIAL:
351 //
352 // It's the initial Login Request. Fill in the key=value pairs mandatory
353 // for the initial Login Request.
354 //
355 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, Session->InitiatorName);
356 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
357 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_TARGET_NAME, Session->ConfigData.NvData.TargetName);
358
359 if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_NONE) {
360 Value = ISCSI_KEY_VALUE_NONE;
361 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
362 } else {
363 Value = ISCSI_AUTH_METHOD_CHAP;
364 }
365
366 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
367
368 break;
369
370 case ISCSI_CHAP_STEP_ONE:
371 //
372 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.
373 //
374 AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);
375 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);
376
377 Conn->CHAPStep = ISCSI_CHAP_STEP_TWO;
378 break;
379
380 case ISCSI_CHAP_STEP_THREE:
381 //
382 // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
383 // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target ahtentication is
384 // required too.
385 //
386 // CHAP_N=<N>
387 //
388 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig.CHAPName);
389 //
390 // CHAP_R=<R>
391 //
392 IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);
393 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
394
395 if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_MUTUAL) {
396 //
397 // CHAP_I=<I>
398 //
399 IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);
400 AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
401 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
402 //
403 // CHAP_C=<C>
404 //
405 IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);
406 AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;
407 IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);
408 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
409
410 Conn->CHAPStep = ISCSI_CHAP_STEP_FOUR;
411 }
412 //
413 // set the stage transition flag.
414 //
415 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
416 break;
417
418 default:
419 Status = EFI_PROTOCOL_ERROR;
420 break;
421 }
422
423 FreePool (Response);
424 FreePool (Challenge);
425
426 return Status;
427 }