From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id AECD381FA8 for ; Thu, 26 Jan 2017 03:50:33 -0800 (PST) Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga101.fm.intel.com with ESMTP; 26 Jan 2017 03:50:33 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.33,289,1477983600"; d="scan'208";a="57767629" Received: from haifengy-mobl2.ccr.corp.intel.com (HELO jyao1-MOBL.ccr.corp.intel.com) ([10.254.213.43]) by fmsmga005.fm.intel.com with ESMTP; 26 Jan 2017 03:50:30 -0800 From: Jiewen Yao To: edk2-devel@lists.01.org Cc: Qin Long , Chao Zhang Date: Thu, 26 Jan 2017 19:50:17 +0800 Message-Id: <1485431418-16540-6-git-send-email-jiewen.yao@intel.com> X-Mailer: git-send-email 2.7.4.windows.1 In-Reply-To: <1485431418-16540-1-git-send-email-jiewen.yao@intel.com> References: <1485431418-16540-1-git-send-email-jiewen.yao@intel.com> Subject: [PATCH 5/6] SecurityPkg/Password: Add Password based UserAuthentication modules. X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 26 Jan 2017 11:50:33 -0000 This password based user authentication is to verify user when a user wants to enter BIOS setup page. The DXE driver registers report status code listener. When it gets (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_PC_USER_SETUP) progress code, it will let the user input password. If and only if the user inputs the right password, the status code handler will return and let the setup driver continue running. If the user inputs the wrong password, the status code handler will let user try again. If the user inputs the wrong password 3 times, the status code handler will reset the system. The DXE driver also register a setup page in setup browser, so that user may update the password. The DXE driver uses SMI to let SMM driver do the password verification and management. The SMM driver registers SMI handler to perform the request from DXE. The password must meet below criteria: 1) Length >= 8 char 2) It must include lower case, upper case, number and symbol. 3) It must not duplicate with 5 previous password. If above criteria is met, the password will be saved to a read only UEFI variable. The format is password hash+salt, which is generated by Pkcs5HashPassword algorithm (SHA256+1000 iteration). If the SMM driver gets wrong password 3 times, the interface is locked and does not accept more request. If the SMM driver will detect IsPasswordCleared() at the entry point and clear the password if IsPasswordCleared() is TRUE. This can be used when the user forgets the password. Cc: Qin Long Cc: Chao Zhang Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jiewen Yao --- SecurityPkg/Password/UserAuthentication/KeyLib.c | 209 ++++++ SecurityPkg/Password/UserAuthentication/KeyLib.h | 121 ++++ SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.c | 718 ++++++++++++++++++++ SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.h | 115 ++++ SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.inf | 76 +++ SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.uni | 20 + SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeExtra.uni | 20 + SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeFormset.h | 30 + SecurityPkg/Password/UserAuthentication/UserAuthenticationDxePassword.c | 300 ++++++++ SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeStrings.uni | 29 + SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeVfr.vfr | 38 ++ SecurityPkg/Password/UserAuthentication/UserAuthenticationGuid.h | 65 ++ SecurityPkg/Password/UserAuthentication/UserAuthenticationSmm.c | 672 ++++++++++++++++++ SecurityPkg/Password/UserAuthentication/UserAuthenticationSmm.inf | 74 ++ SecurityPkg/Password/UserAuthentication/UserAuthenticationSmmExtra.uni | 20 + 15 files changed, 2507 insertions(+) diff --git a/SecurityPkg/Password/UserAuthentication/KeyLib.c b/SecurityPkg/Password/UserAuthentication/KeyLib.c new file mode 100644 index 0000000..295c73d --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/KeyLib.c @@ -0,0 +1,209 @@ +/** @file + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include "KeyLib.h" + +#define DEFAULT_AES_KEY_BIT_SIZE 256 +#define DEFAULT_PBKDF2_ITERATION_COUNT 1000 + +/** + Compares the contents of two buffers with slow algorithm + + This function compares Length bytes of SourceBuffer to Length bytes of DestinationBuffer. + If all Length bytes of the two buffers are identical, then 0 is returned. Otherwise, the + value returned is the first mismatched byte in SourceBuffer subtracted from the first + mismatched byte in DestinationBuffer. + + If Length > 0 and DestinationBuffer is NULL, then ASSERT(). + If Length > 0 and SourceBuffer is NULL, then ASSERT(). + If Length is greater than (MAX_ADDRESS - DestinationBuffer + 1), then ASSERT(). + If Length is greater than (MAX_ADDRESS - SourceBuffer + 1), then ASSERT(). + + @param DestinationBuffer The pointer to the destination buffer to compare. + @param SourceBuffer The pointer to the source buffer to compare. + @param Length The number of bytes to compare. + + @return 0 All Length bytes of the two buffers are identical. + @retval -1 The SourceBuffer is not identical to DestinationBuffer. + +**/ +INTN +EFIAPI +KeyLibSlowCompareMem ( + IN CONST VOID *DestinationBuffer, + IN CONST VOID *SourceBuffer, + IN UINTN Length + ) +{ + UINT8 Delta; + UINTN Index; + UINT8 *Destination; + UINT8 *Source; + + Destination = (UINT8 *)DestinationBuffer; + Source = (UINT8 *)SourceBuffer; + Delta = 0; + for (Index = 0; Index < Length; Index++) { + Delta |= Destination[Index] ^ Source[Index]; + } + if (Delta == 0) { + return 0; + } else { + return -1; + } +} + +/** + Generate Salt value. + + @param[in, out] SaltValue Points to the salt buffer + @param[in] SaltSize Size of the salt buffer + + @retval TRUE Salt is generated. + @retval FALSE Salt is not generated. +**/ +BOOLEAN +EFIAPI +KeyLibGenerateSalt ( + IN OUT UINT8 *SaltValue, + IN UINTN SaltSize + ) +{ + if (SaltValue == NULL) { + return FALSE; + } + RandomSeed(NULL, 0); + RandomBytes(SaltValue, SaltSize); + return TRUE; +} + +/** + Hash the data. + + @param[in] HashType Hash type + @param[in] Key Points to the key buffer + @param[in] KeySize Key buffer size + @param[in] SaltValue Points to the salt buffer + @param[in] SaltSize Size of the salt buffer + @param[out] KeyHash Points to the hashed result + @param[in] KeyHashSize Size of the hash buffer + + @retval TRUE Hash the data successfully. + @retval FALSE Failed to hash the data. + +**/ +BOOLEAN +EFIAPI +KeyLibGenerateHash( + IN UINT32 HashType, + IN VOID *Key, + IN UINTN KeySize, + IN UINT8 *SaltValue, + IN UINTN SaltSize, + OUT UINT8 *KeyHash, + IN UINTN KeyHashSize + ) +{ + BOOLEAN Status; + UINTN HashSize; + VOID *HashContext; + + if (HashType != HASH_TYPE_SHA256) { + return FALSE; + } + if (KeyHashSize != SHA256_DIGEST_SIZE) { + return FALSE; + } + + if ((Key == NULL) || (SaltValue == NULL) || (KeyHash == NULL)) { + return FALSE; + } + + HashSize = Sha256GetContextSize (); + HashContext = AllocateZeroPool (HashSize); + if (HashContext == NULL) { + return FALSE; + } + + Status = Sha256Init(HashContext); + if (!Status) { + goto Done; + } + + Status = Sha256Update(HashContext, SaltValue, SaltSize); + if (!Status) { + goto Done; + } + Status = Sha256Update(HashContext, Key, KeySize); + if (!Status) { + goto Done; + } + + Status = Sha256Final(HashContext, KeyHash); +Done: + FreePool (HashContext); + return Status; +} + +/** + Hash the password with PBKDF2. + + @param[in] HashType Hash type + @param[in] Key Points to the key buffer + @param[in] KeySize Key buffer size + @param[in] SaltValue Points to the salt buffer + @param[in] SaltSize Size of the salt buffer + @param[out] KeyHash Points to the hashed result + @param[in] KeyHashSize Size of the hash buffer + + @retval TRUE Hash the data successfully. + @retval FALSE Failed to hash the data. + +**/ +BOOLEAN +EFIAPI +KeyLibGeneratePBKDF2Hash ( + IN UINT32 HashType, + IN VOID *Key, + IN UINTN KeySize, + IN UINT8 *SaltValue, + IN UINTN SaltSize, + OUT UINT8 *KeyHash, + IN UINTN KeyHashSize + ) +{ + BOOLEAN Result; + + if (HashType != HASH_TYPE_SHA256) { + return FALSE; + } + if (KeyHashSize != SHA256_DIGEST_SIZE) { + return FALSE; + } + + Result = Pkcs5HashPassword ( + KeySize, + Key, + SaltSize, + SaltValue, + DEFAULT_PBKDF2_ITERATION_COUNT, + SHA256_DIGEST_SIZE, + KeyHashSize, + KeyHash + ); + return Result; +} diff --git a/SecurityPkg/Password/UserAuthentication/KeyLib.h b/SecurityPkg/Password/UserAuthentication/KeyLib.h new file mode 100644 index 0000000..82886f0 --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/KeyLib.h @@ -0,0 +1,121 @@ +/** @file + + Copyright (c) 2016, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __KEY_LIB_H__ +#define __KEY_LIB_H__ + +/** + Compares the contents of two buffers with slow algorithm + + This function compares Length bytes of SourceBuffer to Length bytes of DestinationBuffer. + If all Length bytes of the two buffers are identical, then 0 is returned. Otherwise, the + value returned is the first mismatched byte in SourceBuffer subtracted from the first + mismatched byte in DestinationBuffer. + + If Length > 0 and DestinationBuffer is NULL, then ASSERT(). + If Length > 0 and SourceBuffer is NULL, then ASSERT(). + If Length is greater than (MAX_ADDRESS - DestinationBuffer + 1), then ASSERT(). + If Length is greater than (MAX_ADDRESS - SourceBuffer + 1), then ASSERT(). + + @param DestinationBuffer The pointer to the destination buffer to compare. + @param SourceBuffer The pointer to the source buffer to compare. + @param Length The number of bytes to compare. + + @return 0 All Length bytes of the two buffers are identical. + @retval -1 The SourceBuffer is not identical to DestinationBuffer. + +**/ +INTN +EFIAPI +KeyLibSlowCompareMem ( + IN CONST VOID *DestinationBuffer, + IN CONST VOID *SourceBuffer, + IN UINTN Length + ); + +/** + Generate Salt value. + + @param[in, out] SaltValue Points to the salt buffer + @param[in] SaltSize Size of the salt buffer + + @retval TRUE Salt is generated. + @retval FALSE Salt is not generated. +**/ +BOOLEAN +EFIAPI +KeyLibGenerateSalt( + IN OUT UINT8 *SaltValue, + IN UINTN SaltSize + ); + +#define HASH_TYPE_SHA256 0x000B + +#define SHA256_DIGEST_SIZE 32 + +/** + Hash the data. + + @param[in] HashType Hash type + @param[in] Key Points to the key buffer + @param[in] KeySize Key buffer size + @param[in] SaltValue Points to the salt buffer + @param[in] SaltSize Size of the salt buffer + @param[out] KeyHash Points to the hashed result + @param[in] KeyHashSize Size of the hash buffer + + @retval TRUE Hash the data successfully. + @retval FALSE Failed to hash the data. + +**/ +BOOLEAN +EFIAPI +KeyLibGenerateHash( + IN UINT32 HashType, + IN VOID *Key, + IN UINTN KeySize, + IN UINT8 *SaltValue, + IN UINTN SaltSize, + OUT UINT8 *KeyHash, + IN UINTN KeyHashSize + ); + +/** + Hash the password with PBKDF2. + + @param[in] HashType Hash type + @param[in] Key Points to the key buffer + @param[in] KeySize Key buffer size + @param[in] SaltValue Points to the salt buffer + @param[in] SaltSize Size of the salt buffer + @param[out] KeyHash Points to the hashed result + @param[in] KeyHashSize Size of the hash buffer + + @retval TRUE Hash the data successfully. + @retval FALSE Failed to hash the data. + +**/ +BOOLEAN +EFIAPI +KeyLibGeneratePBKDF2Hash ( + IN UINT32 HashType, + IN VOID *Key, + IN UINTN KeySize, + IN UINT8 *SaltValue, + IN UINTN SaltSize, + OUT UINT8 *KeyHash, + IN UINTN KeyHashSize + ); + +#endif + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.c b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.c new file mode 100644 index 0000000..90e2236 --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.c @@ -0,0 +1,718 @@ +/** @file + This Driver mainly do user authentication before entering Setup. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UserAuthenticationDxe.h" + +EFI_EVENT mExitBootServicesEvent = NULL; +EFI_RSC_HANDLER_PROTOCOL *mRscHandlerProtocol = NULL; +USER_AUTHENTICATION_PRIVATE_DATA *mUserAuthenticationData = NULL; + +UINTN mAdminPasswordTryCount; + +EFI_GUID gAdminAuthenticationGuid = ADMIN_AUTHENTICATION_GUID; + +HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + USER_AUTHENTICATION_FORMSET_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +/** + 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) { + 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_TAB) || + (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 { + // + // add Next key entry + // + UserInput[InputLength] = InputKey.UnicodeChar; + Mask[InputLength] = L'*'; + InputLength++; + if (InputLength == UserInputMaxLen) { + // + // Add the null terminator. + // + UserInput[InputLength] = 0; + Mask[InputLength] = 0; + break; + } + } + } + } + } + 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 +PrintPasswordStatus ( + IN EFI_STATUS ReturnStatus + ) +{ + CHAR16 *DisplayString; + + if (ReturnStatus == EFI_SUCCESS) { + DisplayString = L"New password is updated successfully!"; + } else if (ReturnStatus == EFI_UNSUPPORTED) { + DisplayString = L"New password is not strong enough!"; + } else if (ReturnStatus == EFI_ALREADY_STARTED) { + DisplayString = L"New password is found in history!"; + } else { + DisplayString = L"New password update fails!"; + } + + MessageBox (DisplayString); +} + +/** + Require user input password. + + @param[in] UserGuid The user GUID of the password. + + @retval TRUE User input correct password successfully. + @retval FALSE The password is not set. +**/ +BOOLEAN +RequireUserPassword ( + IN EFI_GUID *UserGuid + ) +{ + EFI_STATUS Status; + CHAR16 UserInputPw[MAX_PASSWORD_LEN + 1]; + CHAR16 TmpPassword[MAX_PASSWORD_LEN + 1]; + CHAR16 *PopUpString; + UINTN *PasswordTryCount; + + Status = EFI_SUCCESS; + ZeroMem(UserInputPw, sizeof(UserInputPw)); + ZeroMem(TmpPassword, sizeof(TmpPassword)); + + if (!IsPasswordSet(UserGuid)) { + return FALSE; + } + + if (CompareGuid (UserGuid, &gAdminAuthenticationGuid)) { + PopUpString = L"Please input admin password"; + PasswordTryCount = &mAdminPasswordTryCount; + } else { + DEBUG ((DEBUG_ERROR, "Invalid User Guid - %g\n", UserGuid)); + return FALSE; + } + + while (TRUE) { + gST->ConOut->ClearScreen(gST->ConOut); + GetUserInput (PopUpString, UserInputPw, MAX_PASSWORD_LEN); + + CopyGuid (UserGuid, &gAdminAuthenticationGuid); + Status = ValidatePassword (UserGuid, UserInputPw, StrSize(UserInputPw)); + if (!EFI_ERROR(Status)) { + break; + } + *PasswordTryCount = *PasswordTryCount + 1; + if (*PasswordTryCount >= PASSWORD_MAX_TRY_COUNT) { + ForceSystemReset (); + } + MessageBox (L"Incorrect password!"); + } + *PasswordTryCount = 0; + + ZeroMem(UserInputPw, sizeof(UserInputPw)); + ZeroMem(TmpPassword, sizeof(TmpPassword)); + + gST->ConOut->ClearScreen(gST->ConOut); + + return TRUE; +} + +/** + Set user password. + + @param[in] UserGuid The user GUID of the password. + + @retval TRUE The password is set. + @retval FALSE The password is not set. +**/ +BOOLEAN +SetUserPassword ( + IN EFI_GUID *UserGuid + ) +{ + EFI_STATUS Status; + CHAR16 UserInputPw[MAX_PASSWORD_LEN + 1]; + CHAR16 TmpPassword[MAX_PASSWORD_LEN + 1]; + CHAR16 *PopUpString; + CHAR16 *PopUpString2; + + Status = EFI_SUCCESS; + ZeroMem(UserInputPw, sizeof(UserInputPw)); + ZeroMem(TmpPassword, sizeof(TmpPassword)); + + if (CompareGuid (UserGuid, &gAdminAuthenticationGuid)) { + PopUpString = L"Please set admin password"; + } else { + DEBUG ((DEBUG_ERROR, "Invalid User Guid - %g\n", UserGuid)); + return FALSE; + } + + while (TRUE) { + gST->ConOut->ClearScreen(gST->ConOut); + GetUserInput (PopUpString, UserInputPw, MAX_PASSWORD_LEN); + + PopUpString2 = L"Please confirm your new password"; + gST->ConOut->ClearScreen(gST->ConOut); + GetUserInput (PopUpString2, TmpPassword, MAX_PASSWORD_LEN); + if (StrCmp (TmpPassword, UserInputPw) != 0) { + MessageBox (L"Password are not the same!"); + continue; + } + + Status = SetPassword (UserGuid, UserInputPw, StrSize(UserInputPw), NULL, 0); + PrintPasswordStatus (Status); + if (!EFI_ERROR(Status)) { + break; + } + } + + ZeroMem(UserInputPw, sizeof(UserInputPw)); + ZeroMem(TmpPassword, sizeof(TmpPassword)); + + return EFI_SUCCESS; +} + +/** + Check password before entering into setup. + + @param CodeType Indicates the type of status code being reported. Type EFI_STATUS_CODE_TYPE is defined in "Related Definitions" below. + + @param Value Describes the current status of a hardware or software entity. + This included information about the class and subclass that is used to classify the entity + as well as an operation. For progress codes, the operation is the current activity. + For error codes, it is the exception. For debug codes, it is not defined at this time. + Type EFI_STATUS_CODE_VALUE is defined in "Related Definitions" below. + Specific values are discussed in the Intel? Platform Innovation Framework for EFI Status Code Specification. + + @param Instance The enumeration of a hardware or software entity within the system. + A system may contain multiple entities that match a class/subclass pairing. + The instance differentiates between them. An instance of 0 indicates that instance information is unavailable, + not meaningful, or not relevant. Valid instance numbers start with 1. + + + @param CallerId This optional parameter may be used to identify the caller. + This parameter allows the status code driver to apply different rules to different callers. + Type EFI_GUID is defined in InstallProtocolInterface() in the UEFI 2.0 Specification. + + + @param Data This optional parameter may be used to pass additional data + + @retval EFI_SUCCESS Status code is what we expected. + @retval EFI_UNSUPPORTED Status code not supported. + +**/ +EFI_STATUS +EFIAPI +CheckForPassword ( + IN EFI_STATUS_CODE_TYPE CodeType, + IN EFI_STATUS_CODE_VALUE Value, + IN UINT32 Instance, + IN EFI_GUID *CallerId, OPTIONAL + IN EFI_STATUS_CODE_DATA *Data OPTIONAL + ) +{ + BOOLEAN PasswordSet; + + if (((CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) && + (Value == (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_PC_USER_SETUP))) { + // + // Check whether enter setup page. + // + PasswordSet = RequireUserPassword (&gAdminAuthenticationGuid); + if (PasswordSet) { + DEBUG ((DEBUG_INFO, "Welcome Admin!\n")); + } else { + DEBUG ((DEBUG_INFO, "Admin password is not set!\n")); + if (NeedEnrollPassword()) { + SetUserPassword (&gAdminAuthenticationGuid); + } + } + + return EFI_SUCCESS; + } else{ + return EFI_UNSUPPORTED; + } +} + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Request A null-terminated Unicode string in + format. + @param Progress On return, points to a character in the Request + string. Points to the string's null terminator if + request was successful. Points to the most recent + '&' before the first failing name/value pair (or + the beginning of the string if the failure is in + the first name/value pair) if the request was not + successful. + @param Results A null-terminated Unicode string in + format which has all values filled + in for the names in the Request string. String to + be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this + driver. + +**/ +EFI_STATUS +EFIAPI +ExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + if (Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + *Progress = Request; + return EFI_NOT_FOUND; +} + + +/** + This function processes the results of changes in configuration. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Configuration A null-terminated Unicode string in + format. + @param Progress A pointer to a string filled in with the offset of + the most recent '&' before the first failing + name/value pair (or the beginning of the string if + the failure is in the first name/value pair) or + the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this + driver. + +**/ +EFI_STATUS +EFIAPI +RouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + if (Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Configuration; + + return EFI_NOT_FOUND; +} + +/** + This function processes the results of changes in configuration. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original + exporting driver. + @param ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. + +**/ +EFI_STATUS +EFIAPI +UserAuthenticationCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + EFI_STATUS Status; + CHAR16 *UserInputPassword; + EFI_GUID *UserGuid; + UINTN *PasswordTryCount; + + Status = EFI_SUCCESS; + + if (((Value == NULL) && (Action != EFI_BROWSER_ACTION_FORM_OPEN) && (Action != EFI_BROWSER_ACTION_FORM_CLOSE))|| + (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + switch (Action) { + case EFI_BROWSER_ACTION_CHANGING: + { + switch (QuestionId) { + case ADMIN_PASSWORD_KEY_ID: + UserGuid = &gAdminAuthenticationGuid; + PasswordTryCount = &mAdminPasswordTryCount; + if ((Type == EFI_IFR_TYPE_STRING) && (Value->string == 0) && + (mUserAuthenticationData->PasswordState == BROWSER_STATE_SET_PASSWORD)) { + mUserAuthenticationData->PasswordState = BROWSER_STATE_VALIDATE_PASSWORD; + ZeroMem (mUserAuthenticationData->OldPassword, sizeof(mUserAuthenticationData->OldPassword)); + return EFI_INVALID_PARAMETER; + } + // + // The Callback is responsible for validating old password input by user, + // If Callback return EFI_SUCCESS, it indicates validation pass. + // + switch (mUserAuthenticationData->PasswordState) { + case BROWSER_STATE_VALIDATE_PASSWORD: + UserInputPassword = HiiGetString (mUserAuthenticationData->HiiHandle, Value->string, NULL); + if ((StrLen (UserInputPassword) >= PASSWORD_MAX_SIZE) || (UserInputPassword[0] == 0)) { + Status = EFI_NOT_READY; + break; + } + Status = ValidatePassword (UserGuid, UserInputPassword, StrSize (UserInputPassword)); + if (Status == EFI_SUCCESS) { + mUserAuthenticationData->PasswordState = BROWSER_STATE_SET_PASSWORD; + StrCpyS ( + mUserAuthenticationData->OldPassword, + sizeof(mUserAuthenticationData->OldPassword)/sizeof(CHAR16), + UserInputPassword + ); + *PasswordTryCount = 0; + } else { + // + // Old password mismatch, return EFI_NOT_READY to prompt for error message. + // + Status = EFI_NOT_READY; + *PasswordTryCount = *PasswordTryCount + 1; + if (*PasswordTryCount >= PASSWORD_MAX_TRY_COUNT) { + ForceSystemReset (); + } + } + break; + + case BROWSER_STATE_SET_PASSWORD: + UserInputPassword = HiiGetString (mUserAuthenticationData->HiiHandle, Value->string, NULL); + if ((StrLen (UserInputPassword) >= PASSWORD_MAX_SIZE) || (UserInputPassword[0] == 0)) { + Status = EFI_NOT_READY; + break; + } + Status = SetPassword (UserGuid, UserInputPassword, StrSize (UserInputPassword), mUserAuthenticationData->OldPassword, StrSize(mUserAuthenticationData->OldPassword)); + PrintPasswordStatus (Status); + ZeroMem (mUserAuthenticationData->OldPassword, sizeof(mUserAuthenticationData->OldPassword)); + mUserAuthenticationData->PasswordState = BROWSER_STATE_VALIDATE_PASSWORD; + break; + + default: + break; + } + default: + break; + } + } + default: + break; + } + return Status; +} + +/** + Unregister status code callback functions. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context, which is + always zero in current implementation. + +**/ +VOID +EFIAPI +UnregisterBootTimeHandlers ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + mRscHandlerProtocol->Unregister (CheckForPassword); +} + +/** + User Authentication entry point. + + @param ImageHandle The image handle. + @param SystemTable The system table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @return other Contain some other errors. + +**/ +EFI_STATUS +EFIAPI +UserAuthenticationEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE DriverHandle; + EFI_HII_HANDLE HiiHandle; + + DriverHandle = NULL; + + mUserAuthenticationData = AllocateZeroPool (sizeof (USER_AUTHENTICATION_PRIVATE_DATA)); + if (mUserAuthenticationData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + mUserAuthenticationData->ConfigAccess.ExtractConfig = ExtractConfig; + mUserAuthenticationData->ConfigAccess.RouteConfig = RouteConfig; + mUserAuthenticationData->ConfigAccess.Callback = UserAuthenticationCallback; + mUserAuthenticationData->PasswordState = BROWSER_STATE_VALIDATE_PASSWORD; + + // + // Install Config Access protocol to driver handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &DriverHandle, + &gEfiDevicePathProtocolGuid, + &mHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &mUserAuthenticationData->ConfigAccess, + NULL + ); + ASSERT_EFI_ERROR (Status); + mUserAuthenticationData->DriverHandle = DriverHandle; + + // + // Add HII data to database. + // + HiiHandle = HiiAddPackages ( + &gAdminAuthenticationGuid, + DriverHandle, + UserAuthenticationDxeStrings, + UserAuthenticationDxeVfrBin, + NULL + ); + if (HiiHandle == NULL) { + return EFI_OUT_OF_RESOURCES; + } + mUserAuthenticationData->HiiHandle = HiiHandle; + + // + // Locate report status code protocol. + // + Status = gBS->LocateProtocol ( + &gEfiRscHandlerProtocolGuid, + NULL, + (VOID **) &mRscHandlerProtocol + ); + ASSERT_EFI_ERROR (Status); + + // + //Register the callback function for ReportStatusCode() notification. + // + mRscHandlerProtocol->Register (CheckForPassword, TPL_HIGH_LEVEL); + + // + // Unregister boot time report status code listener at ExitBootService Event. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UnregisterBootTimeHandlers, + NULL, + &gEfiEventExitBootServicesGuid, + &mExitBootServicesEvent + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + +/** + Unloads the application and its installed protocol. + + @param[in] ImageHandle Handle that identifies the image to be unloaded. + + @retval EFI_SUCCESS The image has been unloaded. +**/ +EFI_STATUS +EFIAPI +UserAuthenticationUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + ASSERT (mUserAuthenticationData != NULL); + + // + // Uninstall Config Access Protocol. + // + if (mUserAuthenticationData->DriverHandle != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + mUserAuthenticationData->DriverHandle, + &gEfiDevicePathProtocolGuid, + &mHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &mUserAuthenticationData->ConfigAccess, + NULL + ); + mUserAuthenticationData->DriverHandle = NULL; + } + + // + // Remove Hii Data. + // + if (mUserAuthenticationData->HiiHandle != NULL) { + HiiRemovePackages (mUserAuthenticationData->HiiHandle); + } + + FreePool (mUserAuthenticationData); + mUserAuthenticationData = NULL; + + return EFI_SUCCESS; +} + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.h b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.h new file mode 100644 index 0000000..30c64fc --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.h @@ -0,0 +1,115 @@ +/** @file + Header file for UserAuthenticationDxe. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#ifndef _USER_AUTHENTICATION_DXE_H_ +#define _USER_AUTHENTICATION_DXE_H_ + + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "UserAuthenticationGuid.h" +#include "UserAuthenticationDxeFormset.h" + +extern UINT8 UserAuthenticationDxeVfrBin[]; +extern UINT8 UserAuthenticationDxeStrings[]; + +typedef struct { + EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess; + EFI_HANDLE DriverHandle; + EFI_HII_HANDLE HiiHandle; + UINT8 PasswordState; + CHAR16 OldPassword[PASSWORD_MAX_SIZE]; +} USER_AUTHENTICATION_PRIVATE_DATA; + +#pragma pack(1) +/// +/// HII specific Vendor Device Path definition. +/// +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; +#pragma pack() + +/** + Validata if the password is correct. + + @param[in] UserGuid The user GUID of the password. + @param[in] Password The user input password. + @param[in] PasswordSize The size of Password in byte. + + @retval EFI_SUCCESS The password is correct. + @retval EFI_SECURITY_VIOLATION The password is incorrect. +**/ +EFI_STATUS +ValidatePassword ( + IN EFI_GUID *UserGuid, + IN CHAR16 *Password, + IN UINTN PasswordSize + ); + +/** + Set a new password. + + @param[in] UserGuid The user GUID of the password. + @param[in] NewPassword The user input new password. + NULL means clear password. + @param[in] NewPasswordSize The size of NewPassword in byte. + @param[in] OldPassword The user input old password. + NULL means no old password. + @param[in] OldPasswordSize The size of OldPassword in byte. + + @retval EFI_SUCCESS The password is correct. + @retval EFI_SECURITY_VIOLATION The password is incorrect. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to set the password. +**/ +EFI_STATUS +SetPassword ( + IN EFI_GUID *UserGuid, + IN CHAR16 *NewPassword, OPTIONAL + IN UINTN NewPasswordSize, + IN CHAR16 *OldPassword, OPTIONAL + IN UINTN OldPasswordSize + ); + +/** + Return if the password is set. + + @param[in] UserGuid The user GUID of the password. + + @retval TRUE The password is set. + @retval FALSE The password is not set. +**/ +BOOLEAN +IsPasswordSet ( + IN EFI_GUID *UserGuid + ); + +#endif diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.inf b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.inf new file mode 100644 index 0000000..8cb4c1a --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.inf @@ -0,0 +1,76 @@ +## @file +# This Driver mainly do user authentication before entering Setup. +# +# Copyright (c) 2016, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UserAuthenticationDxe + MODULE_UNI_FILE = UserAuthenticationDxe.uni + FILE_GUID = 0683FB88-664C-4BA6-9ED4-1C0916EE43A4 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 2.0 + ENTRY_POINT = UserAuthenticationEntry + ENTRY_POINT = PasswordDxeInit + UNLOAD_IMAGE = UserAuthenticationUnload + + +[Sources] + UserAuthenticationDxe.c + UserAuthenticationDxe.h + UserAuthenticationDxePassword.c + UserAuthenticationDxeFormset.h + UserAuthenticationGuid.h + UserAuthenticationDxeVfr.vfr + UserAuthenticationDxeStrings.uni + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + UefiLib + HiiLib + DevicePathLib + MemoryAllocationLib + PlatformPasswordLib + +[Guids] + gEfiEventExitBootServicesGuid ## CONSUMES ## Event + gEdkiiPiSmmCommunicationRegionTableGuid ## CONSUMES ## SystemTable + +[Protocols] + gEfiRscHandlerProtocolGuid ## CONSUMES + gEfiDevicePathProtocolGuid ## PRODUCES + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + gEfiSmmCommunicationProtocolGuid ## CONSUMES + +[Depex] + gEfiSimpleTextOutProtocolGuid AND + gEfiHiiDatabaseProtocolGuid AND + gEfiVariableArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + UserAuthenticationDxeExtra.uni + + + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.uni b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.uni new file mode 100644 index 0000000..a92788f --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxe.uni @@ -0,0 +1,20 @@ +// /** @file +// This driver mainly do the user authentication before entering Setup. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "This driver mainly do user authentication before entering Setup." + +#string STR_MODULE_DESCRIPTION #language en-US "This driver mainly do user authentication before entering Setup." + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeExtra.uni b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeExtra.uni new file mode 100644 index 0000000..021a50a --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeExtra.uni @@ -0,0 +1,20 @@ +// /** @file +// User Authentication Dxe Driver. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"User Authentication DXE Driver" + + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeFormset.h b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeFormset.h new file mode 100644 index 0000000..9b971bd --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeFormset.h @@ -0,0 +1,30 @@ +/** @file + Header file for UserAuthenticationDxe. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#ifndef _USER_AUTHENTICATION_DXE_FORMSET_H_ +#define _USER_AUTHENTICATION_DXE_FORMSET_H_ + +// +// Vendor GUID of the formset +// +#define USER_AUTHENTICATION_FORMSET_GUID \ + { 0x760e3022, 0xf149, 0x4560, {0x9c, 0x6f, 0x33, 0xaa, 0x7d, 0x48, 0x75, 0xfa} } + +#define ADMIN_PASSWORD_KEY_ID 0x2001 + +#define MAX_PASSWORD_LEN 20 +#define MIN_PASSWORD_LEN 8 + +#endif diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxePassword.c b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxePassword.c new file mode 100644 index 0000000..d30818b --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxePassword.c @@ -0,0 +1,300 @@ +/** @file + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UserAuthenticationDxe.h" +#include +#include + +EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication = NULL; + +/** + 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. + @param[in] UserGuid The user GUID of the password. + + @retval EFI_INVALID_PARAMETER The data size is too big. + @retval EFI_SUCCESS Find the specified variable. +**/ +VOID* +InitCommunicateBuffer ( + OUT VOID **DataPtr OPTIONAL, + IN UINTN DataSize, + IN UINTN Function, + IN EFI_GUID *UserGuid + ) +{ + 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, UserGuid); + 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; + CopyGuid (&SmmPasswordFunctionHeader->UserGuid, UserGuid); + 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 This size of the function header and the data. + + @retval EFI_SUCCESS Success is returned from the functin in SMM. + @retval Others Failure is returned from the function in SMM. + +**/ +EFI_STATUS +SendCommunicateBuffer ( + IN VOID *Buffer, + IN UINTN DataSize + ) +{ + EFI_STATUS Status; + UINTN CommSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_PASSWORD_COMMUNICATE_HEADER *SmmPasswordFunctionHeader; + + CommSize = DataSize + OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + sizeof (SMM_PASSWORD_COMMUNICATE_HEADER); + + Status = mSmmCommunication->Communicate (mSmmCommunication, Buffer, &CommSize); + ASSERT_EFI_ERROR (Status); + + SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) Buffer; + SmmPasswordFunctionHeader = (SMM_PASSWORD_COMMUNICATE_HEADER *)SmmCommunicateHeader->Data; + return SmmPasswordFunctionHeader->ReturnStatus; +} + +/** + Validata if the password is correct. + + @param[in] UserGuid The user GUID of the password. + @param[in] Password The user input password. + @param[in] PasswordSize The size of Password in byte. + + @retval EFI_SUCCESS The password is correct. + @retval EFI_SECURITY_VIOLATION The password is incorrect. +**/ +EFI_STATUS +ValidatePassword ( + IN EFI_GUID *UserGuid, + IN CHAR16 *Password, + IN UINTN PasswordSize + ) +{ + EFI_STATUS Status; + VOID *Buffer; + SMM_PASSWORD_COMMUNICATE_VERIFY_PASSWORD *VerifyPassword; + + ASSERT (Password != NULL); + + if (PasswordSize > sizeof(VerifyPassword->Password) * sizeof(CHAR16)) { + return EFI_INVALID_PARAMETER; + } + + Buffer = InitCommunicateBuffer ( + (VOID**)&VerifyPassword, + sizeof(*VerifyPassword), + SMM_PASSWORD_FUNCTION_VERIFY_PASSWORD, + UserGuid + ); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = UnicodeStrToAsciiStrS (Password, VerifyPassword->Password, sizeof(VerifyPassword->Password)); + if (EFI_ERROR(Status)) { + return Status; + } + + Status = SendCommunicateBuffer (Buffer, sizeof(*VerifyPassword)); + if (EFI_ERROR (Status)) { + goto EXIT; + } + +EXIT: + ZeroMem (VerifyPassword, sizeof(*VerifyPassword)); + return Status; +} + +/** + Set a new password. + + @param[in] UserGuid The user GUID of the password. + @param[in] NewPassword The user input new password. + NULL means clear password. + @param[in] NewPasswordSize The size of NewPassword in byte. + @param[in] OldPassword The user input old password. + NULL means no old password. + @param[in] OldPasswordSize The size of OldPassword in byte. + + @retval EFI_SUCCESS The password is correct. + @retval EFI_SECURITY_VIOLATION The password is incorrect. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to set the password. +**/ +EFI_STATUS +SetPassword ( + IN EFI_GUID *UserGuid, + IN CHAR16 *NewPassword, OPTIONAL + IN UINTN NewPasswordSize, + IN CHAR16 *OldPassword, OPTIONAL + IN UINTN OldPasswordSize + ) +{ + EFI_STATUS Status; + VOID *Buffer; + SMM_PASSWORD_COMMUNICATE_SET_PASSWORD *SetPassword; + + if (NewPasswordSize > sizeof(SetPassword->NewPassword) * sizeof(CHAR16)) { + return EFI_INVALID_PARAMETER; + } + if (OldPasswordSize > sizeof(SetPassword->OldPassword) * sizeof(CHAR16)) { + return EFI_INVALID_PARAMETER; + } + + Buffer = InitCommunicateBuffer ( + (VOID**)&SetPassword, + sizeof(*SetPassword), + SMM_PASSWORD_FUNCTION_SET_PASSWORD, + UserGuid + ); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (NewPassword != NULL) { + Status = UnicodeStrToAsciiStrS (NewPassword, SetPassword->NewPassword, sizeof(SetPassword->NewPassword)); + if (EFI_ERROR(Status)) { + return Status; + } + } else { + SetPassword->NewPassword[0] = 0; + } + + if (OldPassword != NULL) { + Status = UnicodeStrToAsciiStrS (OldPassword, SetPassword->OldPassword, sizeof(SetPassword->OldPassword)); + if (EFI_ERROR(Status)) { + return Status; + } + } else { + SetPassword->OldPassword[0] = 0; + } + + Status = SendCommunicateBuffer (Buffer, sizeof(*SetPassword)); + if (EFI_ERROR (Status)) { + goto EXIT; + } + +EXIT: + ZeroMem (SetPassword, sizeof(*SetPassword)); + return Status; +} + +/** + Return if the password is set. + + @param[in] UserGuid The user GUID of the password. + + @retval TRUE The password is set. + @retval FALSE The password is not set. +**/ +BOOLEAN +IsPasswordSet ( + IN EFI_GUID *UserGuid + ) +{ + EFI_STATUS Status; + VOID *Buffer; + + Buffer = InitCommunicateBuffer ( + NULL, + 0, + SMM_PASSWORD_FUNCTION_IS_PASSWORD_SET, + UserGuid + ); + if (Buffer == NULL) { + return FALSE; + } + + Status = SendCommunicateBuffer (Buffer, 0); + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Main entry for this driver. + + @param ImageHandle Image handle this driver. + @param SystemTable Pointer to SystemTable. + + @retval EFI_SUCESS This function always complete successfully. + +**/ +EFI_STATUS +EFIAPI +PasswordDxeInit ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeStrings.uni b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeStrings.uni new file mode 100644 index 0000000..91b46bc --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeStrings.uni @@ -0,0 +1,29 @@ +/** @file +// String definitions for User Authentication formset. + +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php + +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#langdef en-US "English" +#langdef fr-FR "Francais" + + +#string STR_FORM_SET_TITLE #language en-US "User Password Management" + #language fr-FR "User Password Management" +#string STR_FORM_SET_TITLE_HELP #language en-US "This Driver mainly handle user's password" + #language fr-FR "This Driver mainly handle user's password" +#string STR_FORM_TITLE #language en-US "Password Management Form" + #language fr-FR "Password Management Form" +#string STR_ADMIN_PASSWORD_PROMPT #language en-US "Change Admin Password" + #language fr-FR "Change Admin Password" +#string STR_ADMIN_PASSWORD_HELP #language en-US "Input old admin password, then you can change the password to a new one. After the change action, you need input the new password when you enter UI. The new password must be at least 8 char and include lowercase, uppercase alphabetic, numbers, and symbols." + #language fr-FR "Input old admin password, then you can change the password to a new one. After the change action, you need input the new password when you enter UI. The new password must be at least 8 char and include lowercase, uppercase alphabetic, numbers, and symbols." + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeVfr.vfr b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeVfr.vfr new file mode 100644 index 0000000..41ee8c2 --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationDxeVfr.vfr @@ -0,0 +1,38 @@ +///** @file +// UserAuthentication formset. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +//**/ + +#include +#include "UserAuthenticationDxeFormset.h" + +formset + guid = USER_AUTHENTICATION_FORMSET_GUID, + title = STRING_TOKEN(STR_FORM_SET_TITLE), + help = STRING_TOKEN(STR_FORM_SET_TITLE_HELP), + classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID, + + form formid = 1, + title = STRING_TOKEN(STR_FORM_TITLE); + + password + prompt = STRING_TOKEN(STR_ADMIN_PASSWORD_PROMPT), + help = STRING_TOKEN(STR_ADMIN_PASSWORD_HELP), + flags = INTERACTIVE, + key = ADMIN_PASSWORD_KEY_ID, + minsize = MIN_PASSWORD_LEN, + maxsize = MAX_PASSWORD_LEN, + endpassword; + + endform; + +endformset; diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationGuid.h b/SecurityPkg/Password/UserAuthentication/UserAuthenticationGuid.h new file mode 100644 index 0000000..228a0fe --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationGuid.h @@ -0,0 +1,65 @@ +/** @file + GUID is for UserPassword variable. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __USER_AUTHENTICATION_GUID_H__ +#define __USER_AUTHENTICATION_GUID_H__ + +#define PASSWORD_MIN_SIZE 9 // MIN number of char of password, including NULL +#define PASSWORD_MAX_SIZE 32 // MAX number of char of password, including NULL +#define PASSWORD_SALT_SIZE 32 +#define PASSWORD_HASH_SIZE 32 // SHA256_DIGEST_SIZE + +#define PASSWORD_MAX_TRY_COUNT 3 +#define PASSWORD_HISTORY_CHECK_COUNT 5 + +// +// Vendor GUID of the variable +// +#define ADMIN_AUTHENTICATION_GUID \ + { 0xee24a7f7, 0x606b, 0x4724, { 0xb3, 0xc9, 0xf5, 0xae, 0x4a, 0x3b, 0x81, 0x65} } + +// +// Name of the variable +// +#define USER_AUTHENTICATION_VAR_NAME L"Password" +#define USER_AUTHENTICATION_HISTORY_LAST_VAR_NAME L"PasswordLast" + +// +// Variable storage +// +typedef struct { + UINT8 PasswordHash[PASSWORD_HASH_SIZE]; + UINT8 PasswordSalt[PASSWORD_SALT_SIZE]; +} USER_PASSWORD_VAR_STRUCT; + +typedef struct { + UINTN Function; + EFI_STATUS ReturnStatus; + EFI_GUID UserGuid; +} SMM_PASSWORD_COMMUNICATE_HEADER; + +#define SMM_PASSWORD_FUNCTION_IS_PASSWORD_SET 1 +#define SMM_PASSWORD_FUNCTION_SET_PASSWORD 2 +#define SMM_PASSWORD_FUNCTION_VERIFY_PASSWORD 3 + +typedef struct { + CHAR8 NewPassword[PASSWORD_MAX_SIZE]; + CHAR8 OldPassword[PASSWORD_MAX_SIZE]; +} SMM_PASSWORD_COMMUNICATE_SET_PASSWORD; + +typedef struct { + CHAR8 Password[PASSWORD_MAX_SIZE]; +} SMM_PASSWORD_COMMUNICATE_VERIFY_PASSWORD; + +#endif diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmm.c b/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmm.c new file mode 100644 index 0000000..59f4bdc --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmm.c @@ -0,0 +1,672 @@ +/** @file + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "KeyLib.h" +#include "UserAuthenticationGuid.h" + +EFI_GUID gAdminAuthenticationGuid = ADMIN_AUTHENTICATION_GUID; + +EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable; + +UINTN mAdminPasswordTryCount; + +/** + Verify if the password is correct. + + @param[in] Password The user input password. + @param[in] PasswordSize The size of Password in byte. + @param[in] UserPasswordVarStruct The storage of password in variable. + + @retval EFI_SUCCESS The password is correct. + @retval EFI_SECURITY_VIOLATION The password is incorrect. +**/ +EFI_STATUS +VerifyPassword ( + IN CHAR8 *Password, + IN UINTN PasswordSize, + IN USER_PASSWORD_VAR_STRUCT *UserPasswordVarStruct + ) +{ + BOOLEAN HashOk; + UINT8 HashData[PASSWORD_HASH_SIZE]; + + HashOk = KeyLibGeneratePBKDF2Hash ( + HASH_TYPE_SHA256, + (UINT8 *)Password, + PasswordSize, + UserPasswordVarStruct->PasswordSalt, + sizeof(UserPasswordVarStruct->PasswordSalt), + HashData, + sizeof(HashData) + ); + if (!HashOk) { + return EFI_DEVICE_ERROR; + } + if (KeyLibSlowCompareMem (UserPasswordVarStruct->PasswordHash, HashData, PASSWORD_HASH_SIZE) == 0) { + return EFI_SUCCESS; + } else { + return EFI_SECURITY_VIOLATION; + } +} + +/** + Get hash data of password from non-volatile variable region. + + @param[in] UserGuid The user GUID of the password variable. + @param[in] Index The index of the password. + 0 means current password. + Non-0 means the password history. + @param[out] UserPasswordVarStruct The storage of password in variable. + + @retval EFI_SUCCESS The password hash is returned successfully. + @retval EFI_NOT_FOUND The password hash is not found. +**/ +EFI_STATUS +GetPasswordHashFromVariable ( + IN EFI_GUID *UserGuid, + IN UINTN Index, + OUT USER_PASSWORD_VAR_STRUCT *UserPasswordVarStruct + ) +{ + EFI_STATUS Status; + UINTN DataSize; + CHAR16 PasswordName[sizeof(USER_AUTHENTICATION_VAR_NAME)/sizeof(CHAR16) + 4]; + + if (Index != 0) { + UnicodeSPrint (PasswordName, sizeof (PasswordName), L"%s%04x", USER_AUTHENTICATION_VAR_NAME, Index); + } else { + UnicodeSPrint (PasswordName, sizeof (PasswordName), L"%s", USER_AUTHENTICATION_VAR_NAME); + } + + DataSize = sizeof(*UserPasswordVarStruct); + Status = mSmmVariable->SmmGetVariable ( + PasswordName, + UserGuid, + NULL, + &DataSize, + UserPasswordVarStruct + ); + if (EFI_ERROR(Status)) { + return Status; + } + + return Status; +} + +/** + Save password hash data to non-volatile variable region. + + @param[in] UserGuid The user GUID of the password variable. + @param[in] UserPasswordVarStruct The storage of password in variable. + + @retval EFI_SUCCESS The password hash is saved successfully. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to save the password hash. +**/ +EFI_STATUS +SavePasswordHashToVariable ( + IN EFI_GUID *UserGuid, + IN USER_PASSWORD_VAR_STRUCT *UserPasswordVarStruct + ) +{ + EFI_STATUS Status; + + if (UserPasswordVarStruct == NULL) { + Status = mSmmVariable->SmmSetVariable ( + USER_AUTHENTICATION_VAR_NAME, + UserGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + 0, + NULL + ); + } else { + Status = mSmmVariable->SmmSetVariable ( + USER_AUTHENTICATION_VAR_NAME, + UserGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof(*UserPasswordVarStruct), + UserPasswordVarStruct + ); + } + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "SavePasswordHashToVariable fails with %r\n", Status)); + } + + return Status; +} + +/** + Save old password hash data to non-volatile variable region as history. + + The number of password history variable is limited. + If all the password history variables are used, the new password history + will override the oldest one. + + @param[in] UserGuid The user GUID of the password variable. + @param[in] UserPasswordVarStruct The storage of password in variable. + + @retval EFI_SUCCESS The password hash is saved successfully. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to save the password hash. +**/ +EFI_STATUS +SaveOldPasswordToHistory ( + IN EFI_GUID *UserGuid, + IN USER_PASSWORD_VAR_STRUCT *UserPasswordVarStruct + ) +{ + EFI_STATUS Status; + UINTN DataSize; + UINT32 LastIndex; + CHAR16 PasswordName[sizeof(USER_AUTHENTICATION_VAR_NAME)/sizeof(CHAR16) + 4]; + + DEBUG ((DEBUG_INFO, "SaveOldPasswordToHistory\n")); + + DataSize = sizeof(LastIndex); + Status = mSmmVariable->SmmGetVariable ( + USER_AUTHENTICATION_HISTORY_LAST_VAR_NAME, + UserGuid, + NULL, + &DataSize, + &LastIndex + ); + if (EFI_ERROR(Status)) { + LastIndex = 0; + } + if (LastIndex >= PASSWORD_HISTORY_CHECK_COUNT) { + LastIndex = 0; + } + + LastIndex ++; + UnicodeSPrint (PasswordName, sizeof (PasswordName), L"%s%04x", USER_AUTHENTICATION_VAR_NAME, LastIndex); + + + Status = mSmmVariable->SmmSetVariable ( + PasswordName, + UserGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof(*UserPasswordVarStruct), + UserPasswordVarStruct + ); + DEBUG ((DEBUG_INFO, " -- to %s, %r\n", PasswordName, Status)); + if (!EFI_ERROR(Status)) { + Status = mSmmVariable->SmmSetVariable ( + USER_AUTHENTICATION_HISTORY_LAST_VAR_NAME, + UserGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof(LastIndex), + &LastIndex + ); + DEBUG ((DEBUG_INFO, " LastIndex - 0x%04x, %r\n", LastIndex, Status)); + } + + return Status; +} + +/** + Calculate password hash data and save it to non-volatile variable region. + + @param[in] UserGuid The user GUID of the password variable. + @param[in] Password The user input password. + NULL means delete the password variable. + @param[in] PasswordSize The size of Password in byte. + + @retval EFI_SUCCESS The password hash is calculated and saved. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to save the password hash. +**/ +EFI_STATUS +SavePasswordToVariable ( + IN EFI_GUID *UserGuid, + IN CHAR8 *Password, OPTIONAL + IN UINTN PasswordSize + ) +{ + EFI_STATUS Status; + USER_PASSWORD_VAR_STRUCT UserPasswordVarStruct; + BOOLEAN HashOk; + + // + // If password is NULL, it means we want to clean password field saved in variable region. + // + if (Password != NULL) { + KeyLibGenerateSalt (UserPasswordVarStruct.PasswordSalt, sizeof(UserPasswordVarStruct.PasswordSalt)); + HashOk = KeyLibGeneratePBKDF2Hash ( + HASH_TYPE_SHA256, + (UINT8 *)Password, + PasswordSize, + UserPasswordVarStruct.PasswordSalt, + sizeof(UserPasswordVarStruct.PasswordSalt), + UserPasswordVarStruct.PasswordHash, + sizeof(UserPasswordVarStruct.PasswordHash) + ); + if (!HashOk) { + return EFI_DEVICE_ERROR; + } + Status = SavePasswordHashToVariable (UserGuid, &UserPasswordVarStruct); + // + // Save Password data to history variable + // + if (!EFI_ERROR(Status)) { + SaveOldPasswordToHistory (UserGuid, &UserPasswordVarStruct); + } + } else { + Status = SavePasswordHashToVariable (UserGuid, NULL); + } + + return Status; +} + +/** + Verify the password. + If the password variable does not exist, it passes the verification. + If the password variable exists, it does verification based upon password variable. + + @param[in] UserGuid The user GUID of the password variable. + @param[in] Password The user input password. + @param[in] PasswordSize The size of Password in byte. + + @retval TRUE The verification passes. + @retval FALSE The verification fails. +**/ +BOOLEAN +IsPasswordVerified ( + IN EFI_GUID *UserGuid, + IN CHAR8 *Password, + IN UINTN PasswordSize + ) +{ + USER_PASSWORD_VAR_STRUCT UserPasswordVarStruct; + EFI_STATUS Status; + + Status = GetPasswordHashFromVariable (UserGuid, 0, &UserPasswordVarStruct); + if (EFI_ERROR(Status)) { + return TRUE; + } + + // + // Old password exists + // + Status = VerifyPassword (Password, PasswordSize, &UserPasswordVarStruct); + if (EFI_ERROR(Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Return if the password is set. + + @param[in] UserGuid The user GUID of the password variable. + + @retval TRUE The password is set. + @retval FALSE The password is not set. +**/ +BOOLEAN +IsPasswordSet ( + IN EFI_GUID *UserGuid + ) +{ + USER_PASSWORD_VAR_STRUCT UserPasswordVarStruct; + EFI_STATUS Status; + + Status = GetPasswordHashFromVariable(UserGuid, 0, &UserPasswordVarStruct); + if (EFI_ERROR(Status)) { + return FALSE; + } + return TRUE; +} + +/** + Return if the password is strong. + Criteria: + 1) length >= PASSWORD_MIN_SIZE + 2) include lower case, upper case, number, symbol. + + @param[in] Password The user input password. + @param[in] PasswordSize The size of Password in byte. + + @retval TRUE The password is strong. + @retval FALSE The password is weak. +**/ +BOOLEAN +IsPasswordStrong ( + IN CHAR8 *Password, + IN UINTN PasswordSize + ) +{ + UINTN Index; + BOOLEAN HasLowerCase; + BOOLEAN HasUpperCase; + BOOLEAN HasNumber; + BOOLEAN HasSymbol; + + if (PasswordSize < PASSWORD_MIN_SIZE) { + return FALSE; + } + + HasLowerCase = FALSE; + HasUpperCase = FALSE; + HasNumber = FALSE; + HasSymbol = FALSE; + for (Index = 0; Index < PasswordSize - 1; Index++) { + if (Password[Index] >= 'a' && Password[Index] <= 'z') { + HasLowerCase = TRUE; + } else if (Password[Index] >= 'A' && Password[Index] <= 'Z') { + HasUpperCase = TRUE; + } else if (Password[Index] >= '0' && Password[Index] <= '9') { + HasNumber = TRUE; + } else { + HasSymbol = TRUE; + } + } + if ((!HasLowerCase) || (!HasUpperCase) || (!HasNumber) || (!HasSymbol)) { + return FALSE; + } + return TRUE; +} + +/** + Return if the password is set before in PASSWORD_HISTORY_CHECK_COUNT. + + @param[in] UserGuid The user GUID of the password variable. + @param[in] Password The user input password. + @param[in] PasswordSize The size of Password in byte. + + @retval TRUE The password is set before. + @retval FALSE The password is not set before. +**/ +BOOLEAN +IsPasswordInHistory ( + IN EFI_GUID *UserGuid, + IN CHAR8 *Password, + IN UINTN PasswordSize + ) +{ + EFI_STATUS Status; + USER_PASSWORD_VAR_STRUCT UserPasswordVarStruct; + UINTN Index; + + for (Index = 1; Index <= PASSWORD_HISTORY_CHECK_COUNT; Index++) { + Status = GetPasswordHashFromVariable (UserGuid, Index, &UserPasswordVarStruct); + if (!EFI_ERROR(Status)) { + Status = VerifyPassword (Password, PasswordSize, &UserPasswordVarStruct); + if (!EFI_ERROR(Status)) { + return TRUE; + } + } + } + + return FALSE; +} + +/** + Communication service SMI Handler entry. + + This SMI handler provides services for password management. + + @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param[in] RegisterContext Points to an optional handler context which was specified when the + handler was registered. + @param[in, out] CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param[in, out] CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. +**/ +EFI_STATUS +EFIAPI +SmmPasswordHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + SMM_PASSWORD_COMMUNICATE_HEADER *SmmFunctionHeader; + UINTN CommBufferPayloadSize; + UINTN TempCommBufferSize; + SMM_PASSWORD_COMMUNICATE_SET_PASSWORD SmmCommunicateSetPassword; + SMM_PASSWORD_COMMUNICATE_VERIFY_PASSWORD SmmCommunicateVerifyPassword; + UINTN PasswordLen; + EFI_GUID UserGuid; + UINTN *PasswordTryCount; + + // + // If input is invalid, stop processing this SMI + // + if (CommBuffer == NULL || CommBufferSize == NULL) { + return EFI_SUCCESS; + } + + TempCommBufferSize = *CommBufferSize; + + if (TempCommBufferSize < sizeof (SMM_PASSWORD_COMMUNICATE_HEADER)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + + CommBufferPayloadSize = TempCommBufferSize - sizeof (SMM_PASSWORD_COMMUNICATE_HEADER); + + Status = EFI_SUCCESS; + SmmFunctionHeader = (SMM_PASSWORD_COMMUNICATE_HEADER *)CommBuffer; + CopyGuid (&UserGuid, &SmmFunctionHeader->UserGuid); + + if (CompareGuid (&UserGuid, &gAdminAuthenticationGuid)) { + PasswordTryCount = &mAdminPasswordTryCount; + } else { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: Invalid UserGuid\n")); + PasswordTryCount = NULL; + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + + switch (SmmFunctionHeader->Function) { + case SMM_PASSWORD_FUNCTION_IS_PASSWORD_SET: + PasswordTryCount = NULL; + if (CommBufferPayloadSize != 0) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: IS_PASSWORD_SET payload buffer invalid!\n")); + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + if (IsPasswordSet(&UserGuid)) { + Status = EFI_SUCCESS; + } else { + Status = EFI_NOT_FOUND; + } + break; + case SMM_PASSWORD_FUNCTION_SET_PASSWORD: + if (*PasswordTryCount >= PASSWORD_MAX_TRY_COUNT) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: SET_PASSWORD try count reach!\n")); + PasswordTryCount = NULL; + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + if (CommBufferPayloadSize != sizeof(SMM_PASSWORD_COMMUNICATE_SET_PASSWORD)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: SET_PASSWORD payload buffer invalid!\n")); + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + CopyMem (&SmmCommunicateSetPassword, SmmFunctionHeader + 1, sizeof(SmmCommunicateSetPassword)); + + PasswordLen = AsciiStrnLenS(SmmCommunicateSetPassword.OldPassword, sizeof(SmmCommunicateSetPassword.OldPassword)); + if (PasswordLen == sizeof(SmmCommunicateSetPassword.OldPassword)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: OldPassword invalid!\n")); + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + + if (!IsPasswordVerified (&UserGuid, SmmCommunicateSetPassword.OldPassword, PasswordLen + 1)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: PasswordVerify - FAIL\n")); + Status = EFI_SECURITY_VIOLATION; + goto EXIT; + } + + PasswordLen = AsciiStrnLenS(SmmCommunicateSetPassword.NewPassword, sizeof(SmmCommunicateSetPassword.NewPassword)); + if (PasswordLen == sizeof(SmmCommunicateSetPassword.NewPassword)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: NewPassword invalid!\n")); + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + if (!IsPasswordStrong (SmmCommunicateSetPassword.NewPassword, PasswordLen + 1)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: NewPassword too weak!\n")); + Status = EFI_UNSUPPORTED; + goto EXIT; + } + if (IsPasswordInHistory (&UserGuid, SmmCommunicateSetPassword.NewPassword, PasswordLen + 1)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: NewPassword in history!\n")); + Status = EFI_ALREADY_STARTED; + goto EXIT; + } + + if (PasswordLen == 0) { + Status = SavePasswordToVariable (&UserGuid, NULL, 0); + } else { + Status = SavePasswordToVariable (&UserGuid, SmmCommunicateSetPassword.NewPassword, PasswordLen + 1); + } + break; + + case SMM_PASSWORD_FUNCTION_VERIFY_PASSWORD: + if (*PasswordTryCount >= PASSWORD_MAX_TRY_COUNT) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: VERIFY_PASSWORD try count reach!\n")); + PasswordTryCount = NULL; + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + if (CommBufferPayloadSize != sizeof(SMM_PASSWORD_COMMUNICATE_VERIFY_PASSWORD)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: VERIFY_PASSWORD payload buffer invalid!\n")); + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + CopyMem (&SmmCommunicateVerifyPassword, SmmFunctionHeader + 1, sizeof(SmmCommunicateVerifyPassword)); + + PasswordLen = AsciiStrnLenS(SmmCommunicateVerifyPassword.Password, sizeof(SmmCommunicateVerifyPassword.Password)); + if (PasswordLen == sizeof(SmmCommunicateVerifyPassword.Password)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: Password invalid!\n")); + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + if (!IsPasswordVerified (&UserGuid, SmmCommunicateVerifyPassword.Password, PasswordLen + 1)) { + DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: PasswordVerify - FAIL\n")); + Status = EFI_SECURITY_VIOLATION; + goto EXIT; + } + Status = EFI_SUCCESS; + break; + + default: + PasswordTryCount = NULL; + Status = EFI_UNSUPPORTED; + break; + } + +EXIT: + if (PasswordTryCount != NULL) { + if (Status == EFI_SUCCESS) { + *PasswordTryCount = 0; + } else { + *PasswordTryCount = *PasswordTryCount + 1; + } + } + SmmFunctionHeader->ReturnStatus = Status; + + return EFI_SUCCESS; +} + +/** + Main entry for this driver. + + @param ImageHandle Image handle this driver. + @param SystemTable Pointer to SystemTable. + + @retval EFI_SUCESS This function always complete successfully. + +**/ +EFI_STATUS +EFIAPI +PasswordSmmInit ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE SmmHandle; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; + CHAR16 PasswordHistoryName[sizeof(USER_AUTHENTICATION_VAR_NAME)/sizeof(CHAR16) + 4]; + UINTN Index; + + ASSERT (PASSWORD_HASH_SIZE == SHA256_DIGEST_SIZE); + ASSERT (PASSWORD_HISTORY_CHECK_COUNT < 0xFFFF); + + Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID**)&mSmmVariable); + ASSERT_EFI_ERROR (Status); + + SmmHandle = NULL; + + // + // Make "HddPassword" varible read-only for DXE driver for security concern. + // + Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock); + if (!EFI_ERROR (Status)) { + Status = VariableLock->RequestToLock (VariableLock, USER_AUTHENTICATION_VAR_NAME, &gAdminAuthenticationGuid); + ASSERT_EFI_ERROR (Status); + + for (Index = 1; Index <= PASSWORD_HISTORY_CHECK_COUNT; Index++) { + UnicodeSPrint (PasswordHistoryName, sizeof (PasswordHistoryName), L"%s%04x", USER_AUTHENTICATION_VAR_NAME, Index); + Status = VariableLock->RequestToLock (VariableLock, PasswordHistoryName, &gAdminAuthenticationGuid); + ASSERT_EFI_ERROR (Status); + } + Status = VariableLock->RequestToLock (VariableLock, USER_AUTHENTICATION_HISTORY_LAST_VAR_NAME, &gAdminAuthenticationGuid); + ASSERT_EFI_ERROR (Status); + } + + SmmHandle = NULL; + Status = gSmst->SmiHandlerRegister (SmmPasswordHandler, &gAdminAuthenticationGuid, &SmmHandle); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + if (IsPasswordCleared()) { + DEBUG ((DEBUG_INFO, "IsPasswordCleared\n")); + SavePasswordToVariable (&gAdminAuthenticationGuid, NULL, 0); + } + + return EFI_SUCCESS; + +EXIT: + if (SmmHandle != NULL) { + gSmst->SmiHandlerUnRegister (SmmHandle); + } + + return Status; +} + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmm.inf b/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmm.inf new file mode 100644 index 0000000..e0a3b12 --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmm.inf @@ -0,0 +1,74 @@ +## +# This file contains a 'Sample Driver' and is licensed as such +# under the terms of your license agreement with Intel or your +# vendor. This file may be modified by the user, subject to +# the additional terms of the license agreement +# +## +## @file +# HddPassword Smm module which is used to provide HDD unlock interface for S3 resume path. +# +# Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+# +# This software and associated documentation (if any) is furnished +# under a license and may only be used or copied in accordance +# with the terms of the license. Except as permitted by such +# license, no part of this software or documentation may be +# reproduced, stored in a retrieval system, or transmitted in any +# form or by any means without the express written consent of +# Intel Corporation. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UserAuthenticationSmm + FILE_GUID = 458B03ED-6E53-414f-9F07-3A829C990641 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = PasswordSmmInit + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + UserAuthenticationSmm.c + UserAuthenticationGuid.h + KeyLib.c + KeyLib.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + BaseLib + BaseMemoryLib + PrintLib + SmmServicesTableLib + MemoryAllocationLib + UefiLib + BaseCryptLib + PlatformPasswordLib + +[Protocols] + gEdkiiVariableLockProtocolGuid ## CONSUMES + gEfiSmmVariableProtocolGuid ## CONSUMES + +[Depex] + gEfiSmmVariableProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + UserAuthenticationDxeExtra.uni + + diff --git a/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmmExtra.uni b/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmmExtra.uni new file mode 100644 index 0000000..6011481 --- /dev/null +++ b/SecurityPkg/Password/UserAuthentication/UserAuthenticationSmmExtra.uni @@ -0,0 +1,20 @@ +// /** @file +// User Authentication Smm Driver. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"User Authentication SMM Driver" + + -- 2.7.4.windows.1