/** @file UserPasswordUiLib instance provides services to do password authentication. Copyright (c) 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include /** Initialize the communicate buffer using DataSize and Function. @param[out] DataPtr Points to the data in the communicate buffer. @param[in] DataSize The data size to send to SMM. @param[in] Function The function number to initialize the communicate header. @return Communicate buffer. **/ VOID* UserPasswordUiLibInitCommunicateBuffer ( OUT VOID **DataPtr OPTIONAL, IN UINTN DataSize, IN UINTN Function ) { EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_PASSWORD_COMMUNICATE_HEADER *SmmPasswordFunctionHeader; VOID *Buffer; EDKII_PI_SMM_COMMUNICATION_REGION_TABLE *SmmCommRegionTable; EFI_MEMORY_DESCRIPTOR *SmmCommMemRegion; UINTN Index; UINTN Size; EFI_STATUS Status; Buffer = NULL; Status = EfiGetSystemConfigurationTable ( &gEdkiiPiSmmCommunicationRegionTableGuid, (VOID **) &SmmCommRegionTable ); if (EFI_ERROR (Status)) { return NULL; } ASSERT (SmmCommRegionTable != NULL); SmmCommMemRegion = (EFI_MEMORY_DESCRIPTOR *) (SmmCommRegionTable + 1); Size = 0; for (Index = 0; Index < SmmCommRegionTable->NumberOfEntries; Index++) { if (SmmCommMemRegion->Type == EfiConventionalMemory) { Size = EFI_PAGES_TO_SIZE ((UINTN) SmmCommMemRegion->NumberOfPages); if (Size >= (DataSize + OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + sizeof (SMM_PASSWORD_COMMUNICATE_HEADER))) { break; } } SmmCommMemRegion = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) SmmCommMemRegion + SmmCommRegionTable->DescriptorSize); } ASSERT (Index < SmmCommRegionTable->NumberOfEntries); Buffer = (VOID*)(UINTN)SmmCommMemRegion->PhysicalStart; ASSERT (Buffer != NULL); SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) Buffer; CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gUserAuthenticationGuid); SmmCommunicateHeader->MessageLength = DataSize + sizeof (SMM_PASSWORD_COMMUNICATE_HEADER); SmmPasswordFunctionHeader = (SMM_PASSWORD_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; ZeroMem (SmmPasswordFunctionHeader, DataSize + sizeof (SMM_PASSWORD_COMMUNICATE_HEADER)); SmmPasswordFunctionHeader->Function = Function; if (DataPtr != NULL) { *DataPtr = SmmPasswordFunctionHeader + 1; } return Buffer; } /** Send the data in communicate buffer to SMM. @param[in] Buffer Points to the data in the communicate buffer. @param[in] DataSize The data size to send to SMM. @retval EFI_SUCCESS Success is returned from the function in SMM. @retval Others Failure is returned from the function in SMM. **/ EFI_STATUS UserPasswordUiLibSendCommunicateBuffer ( IN VOID *Buffer, IN UINTN DataSize ) { EFI_STATUS Status; UINTN CommSize; EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_PASSWORD_COMMUNICATE_HEADER *SmmPasswordFunctionHeader; EFI_SMM_COMMUNICATION_PROTOCOL *SmmCommunication; // // Locates SMM Communication protocol. // Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &SmmCommunication); ASSERT_EFI_ERROR (Status); CommSize = DataSize + OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + sizeof (SMM_PASSWORD_COMMUNICATE_HEADER); Status = SmmCommunication->Communicate (SmmCommunication, Buffer, &CommSize); ASSERT_EFI_ERROR (Status); SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) Buffer; SmmPasswordFunctionHeader = (SMM_PASSWORD_COMMUNICATE_HEADER *)SmmCommunicateHeader->Data; return SmmPasswordFunctionHeader->ReturnStatus; } /** Set password verification policy. @param[in] NeedReVerify Need re-verify or not. @retval EFI_SUCCESS Set verification policy successfully. @retval EFI_OUT_OF_RESOURCES Insufficient resources to set verification policy. **/ EFI_STATUS EFIAPI UiSetPasswordVerificationPolicy ( IN BOOLEAN NeedReVerify ) { VOID *Buffer; SMM_PASSWORD_COMMUNICATE_VERIFY_POLICY *SetVerifyPolicy; Buffer = UserPasswordUiLibInitCommunicateBuffer ( (VOID**)&SetVerifyPolicy, sizeof(*SetVerifyPolicy), SMM_PASSWORD_FUNCTION_SET_VERIFY_POLICY ); if (Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } SetVerifyPolicy->NeedReVerify = NeedReVerify; return UserPasswordUiLibSendCommunicateBuffer (Buffer, sizeof(*SetVerifyPolicy)); } /** Get a user input string. @param[in] PopUpString A popup string to inform user. @param[in, out] UserInput The user input string @param[in] UserInputMaxLen The max unicode count of the UserInput without NULL terminator. **/ EFI_STATUS GetUserInput ( IN CHAR16 *PopUpString, IN OUT CHAR16 *UserInput, IN UINTN UserInputMaxLen ) { EFI_INPUT_KEY InputKey; UINTN InputLength; CHAR16 *Mask; UserInput[0] = 0; Mask = AllocateZeroPool ((UserInputMaxLen + 1) * sizeof(CHAR16)); if (Mask == NULL) { return EFI_OUT_OF_RESOURCES; } InputLength = 0; while (TRUE) { if (InputLength < UserInputMaxLen) { Mask[InputLength] = L'_'; } CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &InputKey, PopUpString, L"--------------------------------", Mask, NULL ); if (InputKey.ScanCode == SCAN_NULL) { // // Check whether finish inputing password. // if (InputKey.UnicodeChar == CHAR_CARRIAGE_RETURN && InputLength > 0) { // // Add the null terminator. // UserInput[InputLength] = 0; break; } else if ((InputKey.UnicodeChar == CHAR_NULL) || (InputKey.UnicodeChar == CHAR_LINEFEED) || (InputKey.UnicodeChar == CHAR_CARRIAGE_RETURN) ) { continue; } else { // // delete last key entered // if (InputKey.UnicodeChar == CHAR_BACKSPACE) { if (InputLength > 0) { UserInput[InputLength] = 0; Mask[InputLength] = 0; InputLength--; } } else { if (InputLength == UserInputMaxLen) { Mask[InputLength] = 0; continue; } // // add Next key entry // UserInput[InputLength] = InputKey.UnicodeChar; Mask[InputLength] = L'*'; InputLength++; } } } } FreePool (Mask); return EFI_SUCCESS; } /** Display a message box to end user. @param[in] DisplayString The string in message box. **/ VOID MessageBox ( IN CHAR16 *DisplayString ) { EFI_INPUT_KEY Key; do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", DisplayString, L"Press ENTER to continue ...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); } /** Force system reset. **/ VOID ForceSystemReset ( VOID ) { MessageBox (L"Password retry count reach, reset system!"); gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); CpuDeadLoop(); } /** Display message for set password. @param[in] ReturnStatus The return status for set password. **/ VOID PrintSetPasswordStatus ( IN EFI_STATUS ReturnStatus ) { CHAR16 *DisplayString; CHAR16 *DisplayString2; EFI_INPUT_KEY Key; if (ReturnStatus == EFI_UNSUPPORTED) { DisplayString = L"New password is not strong enough!"; DisplayString2 = L"Password must at least 8 chars and include lowercase, uppercase alphabetic, number and symbol"; do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", DisplayString, DisplayString2, L"Press ENTER to continue ...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); } else { if (ReturnStatus == EFI_SUCCESS) { DisplayString = L"New password is updated successfully!"; } else if (ReturnStatus == EFI_ALREADY_STARTED) { DisplayString = L"New password is found in the history passwords!"; } else { DisplayString = L"New password update fails!"; } do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", DisplayString, L"Press ENTER to continue ...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); } } /** Get password verification policy. @param[out] VerifyPolicy Verification policy. @retval EFI_SUCCESS Get verification policy successfully. @retval EFI_OUT_OF_RESOURCES Insufficient resources to get verification policy. **/ EFI_STATUS GetPasswordVerificationPolicy ( OUT SMM_PASSWORD_COMMUNICATE_VERIFY_POLICY *VerifyPolicy ) { EFI_STATUS Status; VOID *Buffer; SMM_PASSWORD_COMMUNICATE_VERIFY_POLICY *TempVerifyPolicy; Buffer = UserPasswordUiLibInitCommunicateBuffer ( (VOID**)&TempVerifyPolicy, sizeof(*TempVerifyPolicy), SMM_PASSWORD_FUNCTION_GET_VERIFY_POLICY ); if (Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } Status = UserPasswordUiLibSendCommunicateBuffer (Buffer, sizeof(*TempVerifyPolicy)); if (!EFI_ERROR (Status)) { CopyMem (VerifyPolicy, TempVerifyPolicy, sizeof (SMM_PASSWORD_COMMUNICATE_VERIFY_POLICY)); } return Status; } /** Return if the password was verified. @retval TRUE The password was verified. @retval FALSE The password was not verified. **/ BOOLEAN WasPasswordVerified ( VOID ) { EFI_STATUS Status; VOID *Buffer; Buffer = UserPasswordUiLibInitCommunicateBuffer ( NULL, 0, SMM_PASSWORD_FUNCTION_WAS_PASSWORD_VERIFIED ); if (Buffer == NULL) { return FALSE; } Status = UserPasswordUiLibSendCommunicateBuffer (Buffer, 0); if (EFI_ERROR (Status)) { return FALSE; } return TRUE; } /** Require user input password. @retval TRUE User input correct password successfully. @retval FALSE The password is not set. **/ BOOLEAN RequireUserPassword ( VOID ) { EFI_STATUS Status; CHAR16 UserInputPw[PASSWORD_MAX_SIZE]; CHAR16 *PopUpString; SMM_PASSWORD_COMMUNICATE_VERIFY_POLICY VerifyPolicy; Status = EFI_SUCCESS; ZeroMem(UserInputPw, sizeof(UserInputPw)); if (!IsPasswordInstalled ()) { return FALSE; } Status = GetPasswordVerificationPolicy (&VerifyPolicy); if (!EFI_ERROR (Status)) { if (WasPasswordVerified() && (!VerifyPolicy.NeedReVerify)) { DEBUG ((DEBUG_INFO, "Password was verified and Re-verify is not needed\n")); return TRUE; } } PopUpString = L"Please input admin password"; while (TRUE) { gST->ConOut->ClearScreen(gST->ConOut); GetUserInput (PopUpString, UserInputPw, PASSWORD_MAX_SIZE - 1); Status = VerifyPassword (UserInputPw, StrSize(UserInputPw)); if (!EFI_ERROR(Status)) { break; } if (Status == EFI_ACCESS_DENIED) { // // Password retry count reach. // ForceSystemReset (); } MessageBox (L"Incorrect password!"); } ZeroMem(UserInputPw, sizeof(UserInputPw)); gST->ConOut->ClearScreen(gST->ConOut); return TRUE; } /** Set user password. **/ VOID SetUserPassword ( VOID ) { EFI_STATUS Status; CHAR16 UserInputPw[PASSWORD_MAX_SIZE]; CHAR16 TmpPassword[PASSWORD_MAX_SIZE]; CHAR16 *PopUpString; CHAR16 *PopUpString2; ZeroMem(UserInputPw, sizeof(UserInputPw)); ZeroMem(TmpPassword, sizeof(TmpPassword)); PopUpString = L"Please set admin password"; while (TRUE) { gST->ConOut->ClearScreen(gST->ConOut); GetUserInput (PopUpString, UserInputPw, PASSWORD_MAX_SIZE - 1); PopUpString2 = L"Please confirm your new password"; gST->ConOut->ClearScreen(gST->ConOut); GetUserInput (PopUpString2, TmpPassword, PASSWORD_MAX_SIZE - 1); if (StrCmp (TmpPassword, UserInputPw) != 0) { MessageBox (L"Password are not the same!"); continue; } Status = SetPassword (UserInputPw, StrSize(UserInputPw), NULL, 0); PrintSetPasswordStatus (Status); if (!EFI_ERROR(Status)) { break; } } ZeroMem(UserInputPw, sizeof(UserInputPw)); ZeroMem(TmpPassword, sizeof(TmpPassword)); gST->ConOut->ClearScreen(gST->ConOut); } /** Do password authentication. @retval EFI_SUCCESS Password authentication pass. **/ EFI_STATUS EFIAPI UiDoPasswordAuthentication ( VOID ) { BOOLEAN PasswordSet; PasswordSet = RequireUserPassword (); if (PasswordSet) { DEBUG ((DEBUG_INFO, "Welcome Admin!\n")); } else { DEBUG ((DEBUG_INFO, "Admin password is not set!\n")); if (NeedEnrollPassword()) { SetUserPassword (); } } return EFI_SUCCESS; }