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