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