]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/IScsiDxe/IScsiCHAP.c
Use Mde library and definition instead of some native definitions in NetLib, to simpl...
[mirror_edk2.git] / MdeModulePkg / Universal / Network / IScsiDxe / IScsiCHAP.c
1 /*++
2
3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
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 (CompareMem (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_CHAP_AUTH_DATA *AuthData;
123 CHAR8 *Value;
124 UINT8 *Data;
125 UINT32 Len;
126 LIST_ENTRY *KeyValueList;
127 UINTN Algorithm;
128 CHAR8 *Identifier;
129 CHAR8 *Challenge;
130 CHAR8 *Name;
131 CHAR8 *Response;
132 UINT8 TargetRsp[ISCSI_CHAP_RSP_LEN];
133 UINT32 RspLen;
134
135 ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
136 ASSERT (Conn->RspQue.BufNum != 0);
137
138 Session = Conn->Session;
139 AuthData = &Session->AuthData;
140
141 Len = Conn->RspQue.BufSize;
142 Data = AllocatePool (Len);
143 if (Data == NULL) {
144 return EFI_OUT_OF_RESOURCES;
145 }
146 //
147 // Copy the data in case the data spans over multiple PDUs.
148 //
149 NetbufQueCopy (&Conn->RspQue, 0, Len, Data);
150
151 //
152 // Build the key-value list from the data segment of the Login Response.
153 //
154 KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len);
155 if (KeyValueList == NULL) {
156 Status = EFI_OUT_OF_RESOURCES;
157 goto ON_EXIT;
158 }
159
160 Status = EFI_PROTOCOL_ERROR;
161
162 switch (Conn->CHAPStep) {
163 case ISCSI_CHAP_INITIAL:
164 //
165 // The first Login Response.
166 //
167 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
168 if (Value == NULL) {
169 goto ON_EXIT;
170 }
171
172 Session->TargetPortalGroupTag = (UINT16) AsciiStrDecimalToUintn (Value);
173
174 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);
175 if (Value == NULL) {
176 goto ON_EXIT;
177 }
178 //
179 // Initiator mandates CHAP authentication but target replies without "CHAP" or
180 // initiator suggets "None" but target replies with some kind of auth method.
181 //
182 if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) == 0) {
183 if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_NONE) {
184 goto ON_EXIT;
185 }
186 } else {
187 if (AuthData->AuthConfig.CHAPType != ISCSI_CHAP_NONE) {
188 goto ON_EXIT;
189 }
190 }
191 //
192 // Transit to CHAP step one.
193 //
194 Conn->CHAPStep = ISCSI_CHAP_STEP_ONE;
195 Status = EFI_SUCCESS;
196 break;
197
198 case ISCSI_CHAP_STEP_TWO:
199 //
200 // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
201 //
202 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM);
203 if (Value == NULL) {
204 goto ON_EXIT;
205 }
206
207 Algorithm = AsciiStrDecimalToUintn (Value);
208 if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {
209 //
210 // Unsupported algorithm is chosen by target.
211 //
212 goto ON_EXIT;
213 }
214
215 Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);
216 if (Identifier == NULL) {
217 goto ON_EXIT;
218 }
219
220 Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);
221 if (Challenge == NULL) {
222 goto ON_EXIT;
223 }
224 //
225 // Process the CHAP identifier and CHAP Challenge from Target
226 // Calculate Response value
227 //
228 AuthData->InIdentifier = (UINT32) AsciiStrDecimalToUintn (Identifier);
229 AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;
230 IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);
231 Status = IScsiCHAPCalculateResponse (
232 AuthData->InIdentifier,
233 AuthData->AuthConfig.CHAPSecret,
234 (UINT32) AsciiStrLen (AuthData->AuthConfig.CHAPSecret),
235 AuthData->InChallenge,
236 AuthData->InChallengeLength,
237 AuthData->CHAPResponse
238 );
239
240 //
241 // Transit to next step.
242 //
243 Conn->CHAPStep = ISCSI_CHAP_STEP_THREE;
244 break;
245
246 case ISCSI_CHAP_STEP_THREE:
247 //
248 // one way CHAP authentication and the target would like to
249 // authenticate us.
250 //
251 Status = EFI_SUCCESS;
252 break;
253
254 case ISCSI_CHAP_STEP_FOUR:
255 ASSERT (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_MUTUAL);
256 //
257 // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
258 //
259 Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
260 if (Name == NULL) {
261 goto ON_EXIT;
262 }
263
264 Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);
265 if (Response == NULL) {
266 goto ON_EXIT;
267 }
268
269 RspLen = ISCSI_CHAP_RSP_LEN;
270 IScsiHexToBin (TargetRsp, &RspLen, Response);
271
272 //
273 // Check the CHAP Name and Response replied by Target.
274 //
275 Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
276 break;
277
278 default:
279 break;
280 }
281
282 ON_EXIT:
283
284 IScsiFreeKeyValueList (KeyValueList);
285
286 gBS->FreePool (Data);
287
288 return Status;
289 }
290
291 EFI_STATUS
292 IScsiCHAPToSendReq (
293 IN ISCSI_CONNECTION *Conn,
294 IN NET_BUF *Pdu
295 )
296 /*++
297
298 Routine Description:
299
300 This function fills the CHAP authentication information into the login PDU
301 during the security negotiation stage in the iSCSI connection login.
302
303 Arguments:
304
305 Conn - The iSCSI connection.
306 Pdu - The PDU to send out.
307
308 Returns:
309
310 EFI_SUCCESS - All check passed and the phase-related CHAP authentication
311 info is filled into the iSCSI PDU.
312 EFI_OUT_OF_RESOURCES - Failed to allocate memory.
313 EFI_PROTOCOL_ERROR - Some kind of protocol error happend.
314
315 --*/
316 {
317 EFI_STATUS Status;
318 ISCSI_SESSION *Session;
319 ISCSI_LOGIN_REQUEST *LoginReq;
320 ISCSI_CHAP_AUTH_DATA *AuthData;
321 CHAR8 *Value;
322 CHAR8 ValueStr[256];
323 CHAR8 *Response;
324 UINT32 RspLen;
325 CHAR8 *Challenge;
326 UINT32 ChallengeLen;
327
328 ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
329
330 Session = Conn->Session;
331 AuthData = &Session->AuthData;
332 LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0);
333 Status = EFI_SUCCESS;
334
335 RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
336 Response = AllocatePool (RspLen);
337 if (Response == NULL) {
338 return EFI_OUT_OF_RESOURCES;
339 }
340
341 ChallengeLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
342 Challenge = AllocatePool (ChallengeLen);
343 if (Challenge == NULL) {
344 return EFI_OUT_OF_RESOURCES;
345 }
346
347 switch (Conn->CHAPStep) {
348 case ISCSI_CHAP_INITIAL:
349 //
350 // It's the initial Login Request. Fill in the key=value pairs mandatory
351 // for the initial Login Request.
352 //
353 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, Session->InitiatorName);
354 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
355 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_TARGET_NAME, Session->ConfigData.NvData.TargetName);
356
357 if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_NONE) {
358 Value = ISCSI_KEY_VALUE_NONE;
359 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
360 } else {
361 Value = ISCSI_AUTH_METHOD_CHAP;
362 }
363
364 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
365
366 break;
367
368 case ISCSI_CHAP_STEP_ONE:
369 //
370 // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.
371 //
372 AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);
373 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);
374
375 Conn->CHAPStep = ISCSI_CHAP_STEP_TWO;
376 break;
377
378 case ISCSI_CHAP_STEP_THREE:
379 //
380 // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
381 // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target ahtentication is
382 // required too.
383 //
384 // CHAP_N=<N>
385 //
386 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig.CHAPName);
387 //
388 // CHAP_R=<R>
389 //
390 IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);
391 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
392
393 if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_MUTUAL) {
394 //
395 // CHAP_I=<I>
396 //
397 IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);
398 AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
399 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
400 //
401 // CHAP_C=<C>
402 //
403 IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);
404 AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;
405 IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);
406 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
407
408 Conn->CHAPStep = ISCSI_CHAP_STEP_FOUR;
409 }
410 //
411 // set the stage transition flag.
412 //
413 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
414 break;
415
416 default:
417 Status = EFI_PROTOCOL_ERROR;
418 break;
419 }
420
421 gBS->FreePool (Response);
422 gBS->FreePool (Challenge);
423
424 return Status;
425 }