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