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