/** @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;
}