/** @file
  This driver manages user information and produces user manager protocol.
  
Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
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 "UserIdentifyManager.h"

//
// Default user name.
//
CHAR16                      mUserName[]       = L"Administrator";

//
// Points to the user profile database.
//
USER_PROFILE_DB             *mUserProfileDb   = NULL;

//
// Points to the credential providers found in system.
//
CREDENTIAL_PROVIDER_INFO    *mProviderDb      = NULL;

//
// Current user shared in multi function.
//
EFI_USER_PROFILE_HANDLE     mCurrentUser      = NULL;

//
// Flag indicates a user is identified.
//
BOOLEAN                     mIdentified       = FALSE;
USER_MANAGER_CALLBACK_INFO  *mCallbackInfo    = NULL;
HII_VENDOR_DEVICE_PATH      mHiiVendorDevicePath = {
  {
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      {
        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
      }
    },
    USER_IDENTIFY_MANAGER_GUID
  },
  {
    END_DEVICE_PATH_TYPE,
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
    {
      (UINT8) (END_DEVICE_PATH_LENGTH),
      (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
    }
  }
};


EFI_USER_MANAGER_PROTOCOL gUserIdentifyManager = {
  UserProfileCreate,
  UserProfileDelete,
  UserProfileGetNext,
  UserProfileCurrent,
  UserProfileIdentify,
  UserProfileFind,
  UserProfileNotify,
  UserProfileGetInfo,
  UserProfileSetInfo,
  UserProfileDeleteInfo,
  UserProfileGetNextInfo,
};


/**
  Find the specified user in the user database.

  This function searches the specified user from the beginning of the user database. 
  And if NextUser is TRUE, return the next User in the user database.  
  
  @param[in, out] User         On entry, points to the user profile entry to search. 
                               On return, points to the user profile entry or NULL if not found.
  @param[in]      NextUser     If FALSE, find the user in user profile database specifyed by User
                               If TRUE, find the next user in user profile database specifyed 
                               by User. 
  @param[out]     ProfileIndex A pointer to the index of user profile database that matches the 
                               user specifyed by User.

  @retval EFI_NOT_FOUND        User was NULL, or User was not found, or the next user was not found.
  @retval EFI_SUCCESS          User or the next user are found in user profile database
  
**/
EFI_STATUS
FindUserProfile (
  IN OUT  USER_PROFILE_ENTRY                    **User,
  IN      BOOLEAN                               NextUser,
     OUT  UINTN                                 *ProfileIndex OPTIONAL
  )
{
  UINTN               Index;

  //
  // Check parameters
  //
  if ((mUserProfileDb == NULL) || (User == NULL)) {
    return EFI_NOT_FOUND;
  }
  
  //
  // Check whether the user profile is in the user profile database.
  //
  for (Index = 0; Index < mUserProfileDb->UserProfileNum; Index++) {
    if (mUserProfileDb->UserProfile[Index] == *User) {
      if (ProfileIndex != NULL) {
        *ProfileIndex = Index;
      }
      break;
    }
  }

  if (NextUser) {
    //
    // Find the next user profile.
    //
    Index++;
    if (Index < mUserProfileDb->UserProfileNum) {
      *User = mUserProfileDb->UserProfile[Index];
    } else if (Index == mUserProfileDb->UserProfileNum) {
      *User = NULL;
      return EFI_NOT_FOUND;
    } else {
      if ((mUserProfileDb->UserProfileNum > 0) && (*User == NULL)) {
        *User = mUserProfileDb->UserProfile[0];
      } else {
        *User = NULL;
        return EFI_NOT_FOUND;
      }
    }
  } else if (Index == mUserProfileDb->UserProfileNum) {
    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}

/**
  Find the specified user information record in the specified User profile.

  This function searches the specified user information record from the beginning of the user 
  profile. And if NextInfo is TRUE, return the next info in the user profile.  
  
  @param[in]      User     Points to the user profile entry.                             
  @param[in, out] Info     On entry, points to the user information record or NULL to start
                           searching with the first user information record.
                           On return, points to the user information record or NULL if not found.                           
  @param[in]      NextInfo If FALSE, find the user information record in profile specifyed by User.
                           If TRUE, find the next user information record in profile specifyed 
                           by User. 
  @param[out]     Offset   A pointer to the offset of the information record in the user profile.

  @retval EFI_INVALID_PARAMETER Info is NULL
  @retval EFI_NOT_FOUND         Info was not found, or the next Info was not found.
  @retval EFI_SUCCESS           Info or the next info are found in user profile.
  
**/
EFI_STATUS
FindUserInfo (
  IN     USER_PROFILE_ENTRY                    * User,
  IN OUT EFI_USER_INFO                         **Info,
  IN     BOOLEAN                               NextInfo,
     OUT UINTN                                 *Offset OPTIONAL
  )
{
  EFI_STATUS    Status;
  EFI_USER_INFO *UserInfo;
  UINTN         InfoLen;

  if (Info == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check user profile entry
  //
  Status = FindUserProfile (&User, FALSE, NULL);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Find user information in the specified user record.
  //
  InfoLen = 0;
  while (InfoLen < User->UserProfileSize) {
    UserInfo = (EFI_USER_INFO *) (User->ProfileInfo + InfoLen);
    if (UserInfo == *Info) {
      if (Offset != NULL) {
        *Offset = InfoLen;
      }
      break;
    }
    InfoLen += ALIGN_VARIABLE (UserInfo->InfoSize);
  }
  
  //
  // Check whether to find the next user information.
  //
  if (NextInfo) {
    if (InfoLen < User->UserProfileSize) {
      UserInfo = (EFI_USER_INFO *) (User->ProfileInfo + InfoLen);
      InfoLen += ALIGN_VARIABLE (UserInfo->InfoSize);
      if (InfoLen < User->UserProfileSize) {
        *Info = (EFI_USER_INFO *) (User->ProfileInfo + InfoLen);
        if (Offset != NULL) {
          *Offset = InfoLen;
        }
      } else if (InfoLen == User->UserProfileSize) {
        *Info = NULL;
        return EFI_NOT_FOUND;
      }
    } else {
      if (*Info == NULL) {
        *Info = (EFI_USER_INFO *) User->ProfileInfo;
        if (Offset != NULL) {
          *Offset = 0;
        }
      } else {
        *Info = NULL;
        return EFI_NOT_FOUND;
      }
    }
  } else if (InfoLen == User->UserProfileSize) {
    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}

/**
  Find a user infomation record by the information record type.

  This function searches all user information records of User. The search starts with the 
  user information record following Info and continues until either the information is found 
  or there are no more user infomation record.
  A match occurs when a Info.InfoType field matches the user information record type.

  @param[in]      User     Points to the user profile record to search.                          
  @param[in, out] Info     On entry, points to the user information record or NULL to start
                           searching with the first user information record.
                           On return, points to the user information record or NULL if not found.
  @param[in]      InfoType The infomation type to be searched.

  @retval EFI_SUCCESS           User information was found. Info points to the user information record.
  @retval EFI_NOT_FOUND         User information was not found.                           
  @retval EFI_INVALID_PARAMETER User is NULL or Info is NULL.
  
**/
EFI_STATUS
FindUserInfoByType (
  IN      USER_PROFILE_ENTRY                    *User,
  IN OUT  EFI_USER_INFO                         **Info,
  IN      UINT8                                 InfoType
  )
{
  EFI_STATUS    Status;
  EFI_USER_INFO *UserInfo;
  UINTN         InfoLen;

  if (Info == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check whether the user has the specified user information.
  //
  InfoLen = 0;
  if (*Info == NULL) {
    Status = FindUserProfile (&User, FALSE, NULL);
  } else {
    Status = FindUserInfo (User, Info, TRUE, &InfoLen);
  }

  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }
  
  while (InfoLen < User->UserProfileSize) {
    UserInfo = (EFI_USER_INFO *) (User->ProfileInfo + InfoLen);
    if (UserInfo->InfoType == InfoType) {
      if (UserInfo != *Info) {
        *Info = UserInfo;
        return EFI_SUCCESS;
      }
    }

    InfoLen += ALIGN_VARIABLE (UserInfo->InfoSize);
  }

  *Info = NULL;
  return EFI_NOT_FOUND;
}

/**
  Find a user using a user information record.

  This function searches all user profiles for the specified user information record. The 
  search starts with the user information record handle following UserInfo and continues 
  until either the information is found or there are no more user profiles.
  A match occurs when the Info.InfoType field matches the user information record type and the 
  user information record data matches the portion of Info passed the EFI_USER_INFO header.

  @param[in, out] User     On entry, points to the previously returned user profile record, 
                           or NULL to start searching with the first user profile. 
                           On return, points to the user profile entry, or NULL if not found.
  @param[in, out] UserInfo On entry, points to the previously returned user information record, 
                           or NULL to start searching with the first. 
                           On return, points to the user information record, or NULL if not found.
  @param[in]      Info     Points to the buffer containing the user information to be compared 
                           to the user information record.
  @param[in]      InfoSize The size of Info, in bytes. Same as Info->InfoSize.

  @retval EFI_SUCCESS           User information was found. User points to the user profile record, 
                                and UserInfo points to the user information record.
  @retval EFI_NOT_FOUND         User information was not found.                           
  @retval EFI_INVALID_PARAMETER User is NULL; Info is NULL; or, InfoSize is too small.
  
**/
EFI_STATUS
FindUserProfileByInfo (
  IN OUT  USER_PROFILE_ENTRY                    **User,
  IN OUT  EFI_USER_INFO                         **UserInfo, OPTIONAL
  IN      EFI_USER_INFO                         *Info,
  IN      UINTN                                 InfoSize
  )
{
  EFI_STATUS    Status;
  EFI_USER_INFO *InfoEntry;


  if ((User == NULL) || (Info == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (InfoSize < sizeof (EFI_USER_INFO)) {
    return EFI_INVALID_PARAMETER;
  }

  if (UserInfo != NULL) {
    InfoEntry = *UserInfo;
  } else {
    InfoEntry = NULL;
  }
  //
  // Find user profile according to information.
  //
  if (*User == NULL) {
    *User = mUserProfileDb->UserProfile[0];
  }
  
  //
  // Check user profile handle.
  //
  Status = FindUserProfile (User, FALSE, NULL);

  while (!EFI_ERROR (Status)) {
    //
    // Find the user information in a user profile.
    //
    while (TRUE) {
      Status = FindUserInfoByType (*User, &InfoEntry, Info->InfoType);
      if (EFI_ERROR (Status)) {
        break;
      }
      
      if (InfoSize == Info->InfoSize) {
        if (CompareMem ((UINT8 *) (InfoEntry + 1), (UINT8 *) (Info + 1), InfoSize - sizeof (EFI_USER_INFO)) == 0) {
          //
          // Found the infomation record.
          //
          if (UserInfo != NULL) {
            *UserInfo = InfoEntry;
          }
          return EFI_SUCCESS;
        }
      }      
    }
    
    //
    // Get next user profile.
    //
    InfoEntry = NULL;
    Status    = FindUserProfile (User, TRUE, NULL);
  }

  return EFI_NOT_FOUND;
}


/**
  Check whether the access policy is valid.

  @param[in]  PolicyInfo          Point to the access policy.
  @param[in]  InfoLen             The policy length.

  @retval TRUE     The policy is a valid access policy.
  @retval FALSE    The access policy is not a valid access policy.
  
**/
BOOLEAN
CheckAccessPolicy (
  IN  UINT8                                     *PolicyInfo,
  IN  UINTN                                     InfoLen
  )
{
  UINTN                         TotalLen;
  UINTN                         ValueLen;
  UINTN                         OffSet;
  EFI_USER_INFO_ACCESS_CONTROL  Access;
  EFI_DEVICE_PATH_PROTOCOL      *Path;
  UINTN                         PathSize;

  TotalLen = 0;
  while (TotalLen < InfoLen) {
    //
    // Check access policy according to type.
    //
    CopyMem (&Access, PolicyInfo + TotalLen, sizeof (Access));    
    ValueLen = Access.Size - sizeof (EFI_USER_INFO_ACCESS_CONTROL);
    switch (Access.Type) {
    case EFI_USER_INFO_ACCESS_FORBID_LOAD:
    case EFI_USER_INFO_ACCESS_PERMIT_LOAD:
    case EFI_USER_INFO_ACCESS_FORBID_CONNECT:
    case EFI_USER_INFO_ACCESS_PERMIT_CONNECT:
      OffSet = 0;
      while (OffSet < ValueLen) {
        Path      = (EFI_DEVICE_PATH_PROTOCOL *) (PolicyInfo + TotalLen + sizeof (Access) + OffSet);
        PathSize  = GetDevicePathSize (Path);
        OffSet += PathSize;
      }
      if (OffSet != ValueLen) {
        return FALSE;
      }
      break;

    case EFI_USER_INFO_ACCESS_SETUP:
      if (ValueLen % sizeof (EFI_GUID) != 0) {
        return FALSE;
      }
      break;

    case EFI_USER_INFO_ACCESS_BOOT_ORDER:
      if (ValueLen % sizeof (EFI_USER_INFO_ACCESS_BOOT_ORDER_HDR) != 0) {
        return FALSE;
      }
      break;

    case EFI_USER_INFO_ACCESS_ENROLL_SELF:
    case EFI_USER_INFO_ACCESS_ENROLL_OTHERS:
    case EFI_USER_INFO_ACCESS_MANAGE:
      if (ValueLen != 0) {
        return FALSE;
      }
      break;

    default:
      return FALSE;
      break;
    }

    TotalLen += Access.Size;
  }

  if (TotalLen != InfoLen) {
    return FALSE;
  }

  return TRUE;
}


/**
  Check whether the identity policy is valid.

  @param[in]  PolicyInfo          Point to the identity policy.
  @param[in]  InfoLen             The policy length.

  @retval TRUE     The policy is a valid identity policy.
  @retval FALSE    The access policy is not a valid identity policy.
  
**/
BOOLEAN
CheckIdentityPolicy (
  IN  UINT8                                     *PolicyInfo,
  IN  UINTN                                     InfoLen
  )
{
  UINTN                         TotalLen;
  UINTN                         ValueLen;
  EFI_USER_INFO_IDENTITY_POLICY *Identity;

  TotalLen  = 0;

  //
  // Check each part of policy expression.
  //
  while (TotalLen < InfoLen) {
    //
    // Check access polisy according to type.
    //
    Identity  = (EFI_USER_INFO_IDENTITY_POLICY *) (PolicyInfo + TotalLen);
    ValueLen  = Identity->Length - sizeof (EFI_USER_INFO_IDENTITY_POLICY);
    switch (Identity->Type) {
    //
    // Check False option.
    //
    case EFI_USER_INFO_IDENTITY_FALSE:
      if (ValueLen != 0) {
        return FALSE;
      }
      break;

    //
    // Check True option.
    //
    case EFI_USER_INFO_IDENTITY_TRUE:
      if (ValueLen != 0) {
        return FALSE;
      }
      break;

    //
    // Check negative operation.
    //
    case EFI_USER_INFO_IDENTITY_NOT:
      if (ValueLen != 0) {
        return FALSE;
      }
      break;

    //
    // Check and operation.
    //
    case EFI_USER_INFO_IDENTITY_AND:
      if (ValueLen != 0) {
        return FALSE;
      }
      break;

    //
    // Check or operation.
    //
    case EFI_USER_INFO_IDENTITY_OR:
      if (ValueLen != 0) {
        return FALSE;
      }
      break;

    //
    // Check credential provider by type.
    //
    case EFI_USER_INFO_IDENTITY_CREDENTIAL_TYPE:
      if (ValueLen != sizeof (EFI_GUID)) {
        return FALSE;
      }
      break;

    //
    // Check credential provider by ID.
    //
    case EFI_USER_INFO_IDENTITY_CREDENTIAL_PROVIDER:
      if (ValueLen != sizeof (EFI_GUID)) {
        return FALSE;
      }
      break;

    default:
      return FALSE;
      break;
    }

    TotalLen += Identity->Length;
  }

  if (TotalLen != InfoLen) {
    return FALSE;
  }

  return TRUE;
}


/**
  Check whether the user information is a valid user information record.

  @param[in]  Info points to the user information.

  @retval TRUE     The info is a valid user information record.
  @retval FALSE    The info is not a valid user information record.
  
**/
BOOLEAN
CheckUserInfo (
  IN CONST  EFI_USER_INFO                       *Info
  )
{
  UINTN       InfoLen;

  if (Info == NULL) {
    return FALSE;
  }
  //
  // Check user information according to information type.
  //
  InfoLen = Info->InfoSize - sizeof (EFI_USER_INFO);
  switch (Info->InfoType) {
  case EFI_USER_INFO_EMPTY_RECORD:
    if (InfoLen != 0) {
      return FALSE;
    }
    break;

  case EFI_USER_INFO_NAME_RECORD:
  case EFI_USER_INFO_CREDENTIAL_TYPE_NAME_RECORD:
  case EFI_USER_INFO_CREDENTIAL_PROVIDER_NAME_RECORD:
    break;

  case EFI_USER_INFO_CREATE_DATE_RECORD:
  case EFI_USER_INFO_USAGE_DATE_RECORD:
    if (InfoLen != sizeof (EFI_TIME)) {
      return FALSE;
    }
    break;

  case EFI_USER_INFO_USAGE_COUNT_RECORD:
    if (InfoLen != sizeof (UINT64)) {
      return FALSE;
    }
    break;

  case EFI_USER_INFO_IDENTIFIER_RECORD:
    if (InfoLen != 16) {
      return FALSE;
    }
    break;

  case EFI_USER_INFO_CREDENTIAL_TYPE_RECORD:
  case EFI_USER_INFO_CREDENTIAL_PROVIDER_RECORD:
  case EFI_USER_INFO_GUID_RECORD:
    if (InfoLen != sizeof (EFI_GUID)) {
      return FALSE;
    }
    break;

  case EFI_USER_INFO_PKCS11_RECORD:
  case EFI_USER_INFO_CBEFF_RECORD:
    break;

  case EFI_USER_INFO_FAR_RECORD:
  case EFI_USER_INFO_RETRY_RECORD:
    if (InfoLen != 1) {
      return FALSE;
    }
    break;

  case EFI_USER_INFO_ACCESS_POLICY_RECORD:
    if(!CheckAccessPolicy ((UINT8 *) (Info + 1), InfoLen)) {
      return FALSE;
    }
    break;

  case EFI_USER_INFO_IDENTITY_POLICY_RECORD:
    if (!CheckIdentityPolicy ((UINT8 *) (Info + 1), InfoLen)) {
      return FALSE;
    }
    break;

  default:
    return FALSE;
    break;
  }

  return TRUE;
}


/**
  Check the user profile data format to be added.

  @param[in]  UserProfileInfo     Points to the user profile data.
  @param[in]  UserProfileSize     The length of user profile data.

  @retval TRUE     It is a valid user profile.
  @retval FALSE    It is not a valid user profile.
  
**/
BOOLEAN
CheckProfileInfo (
  IN  UINT8                                     *UserProfileInfo,
  IN  UINTN                                     UserProfileSize
  )
{
  UINTN         ChkLen;
  EFI_USER_INFO *Info;

  if (UserProfileInfo == NULL) {
    return FALSE;
  }
  
  //
  // Check user profile information length.
  //
  ChkLen = 0;
  while (ChkLen < UserProfileSize) {
    Info = (EFI_USER_INFO *) (UserProfileInfo + ChkLen);
    //
    // Check user information format.
    //
    if (!CheckUserInfo (Info)) {
      return FALSE;
    }

    ChkLen += ALIGN_VARIABLE (Info->InfoSize);
  }

  if (ChkLen != UserProfileSize) {
    return FALSE;
  }

  return TRUE;
}


/**
  Find the specified RightType in current user profile.

  @param[in]  RightType      Could be EFI_USER_INFO_ACCESS_MANAGE,
                             EFI_USER_INFO_ACCESS_ENROLL_OTHERS or
                             EFI_USER_INFO_ACCESS_ENROLL_SELF.
  
  @retval TRUE     Find the specified RightType in current user profile.
  @retval FALSE    Can't find the right in the profile.
  
**/
BOOLEAN
CheckCurrentUserAccessRight (
  IN        UINT32                              RightType
  )
{
  EFI_STATUS                    Status;
  EFI_USER_INFO                 *Info;
  UINTN                         TotalLen;
  UINTN                         CheckLen;
  EFI_USER_INFO_ACCESS_CONTROL  Access;

  //
  // Get user access right information.
  //
  Info = NULL;
  Status = FindUserInfoByType (
            (USER_PROFILE_ENTRY *) mCurrentUser,
            &Info,
            EFI_USER_INFO_ACCESS_POLICY_RECORD
            );
  if (EFI_ERROR (Status)) {
    return FALSE;
  }

  ASSERT (Info != NULL);
  TotalLen  = Info->InfoSize - sizeof (EFI_USER_INFO);
  CheckLen  = 0;
  while (CheckLen < TotalLen) {
    //
    // Check right according to access type.
    //
    CopyMem (&Access, (UINT8 *) (Info + 1) + CheckLen, sizeof (Access));
    if (Access.Type == RightType) {
      return TRUE;;
    }

    CheckLen += Access.Size;
  }

  return FALSE;
}


/**
  Create a unique user identifier.

  @param[out]  Identifier     This points to the identifier.

**/
VOID
GenerateIdentifier (
   OUT    UINT8                               *Identifier
  )
{
  EFI_TIME  Time;
  UINT64    MonotonicCount;
  UINT32    *MonotonicPointer;
  UINTN     Index;

  //
  // Create a unique user identifier.
  //
  gRT->GetTime (&Time, NULL);
  CopyMem (Identifier, &Time, sizeof (EFI_TIME));
  //
  // Remove zeros.
  //
  for (Index = 0; Index < sizeof (EFI_TIME); Index++) {
    if (Identifier[Index] == 0) {
      Identifier[Index] = 0x5a;
    }
  }

  MonotonicPointer = (UINT32 *) Identifier;
  gBS->GetNextMonotonicCount (&MonotonicCount);
  MonotonicPointer[0] += (UINT32) MonotonicCount;
  MonotonicPointer[1] += (UINT32) MonotonicCount;
  MonotonicPointer[2] += (UINT32) MonotonicCount;
  MonotonicPointer[3] += (UINT32) MonotonicCount;
}


/**
  Generate unique user ID.

  @param[out]  UserId                 Points to the user identifer.

**/
VOID
GenerateUserId (
  OUT    UINT8                               *UserId
  )
{
  EFI_STATUS              Status;
  USER_PROFILE_ENTRY      *UserProfile;
  EFI_USER_INFO           *UserInfo;
  UINTN                   Index;

  //
  // Generate unique user ID
  //
  while (TRUE) {
    GenerateIdentifier (UserId);
    //
    // Check whether it's unique in user profile database.
    //
    if (mUserProfileDb == NULL) {
      return ;
    }

    for (Index = 0; Index < mUserProfileDb->UserProfileNum; Index++) {
      UserProfile = (USER_PROFILE_ENTRY *) (mUserProfileDb->UserProfile[Index]);
      UserInfo    = NULL;
      Status      = FindUserInfoByType (UserProfile, &UserInfo, EFI_USER_INFO_IDENTIFIER_RECORD);
      if (EFI_ERROR (Status)) {
        continue;
      }

      if (CompareMem ((UINT8 *) (UserInfo + 1), UserId, sizeof (EFI_USER_INFO_IDENTIFIER)) == 0) {
        break;
      }
    }

    if (Index == mUserProfileDb->UserProfileNum) {
      return ;
    }
  }
}


/**
  Expand user profile database.

  @retval TRUE     Success to expand user profile database.
  @retval FALSE    Fail to expand user profile database.
  
**/
BOOLEAN
ExpandUsermUserProfileDb (
  VOID
  )
{
  UINTN               MaxNum;
  USER_PROFILE_DB     *NewDataBase;

  //
  // Create new user profile database.
  //
  if (mUserProfileDb == NULL) {
    MaxNum = USER_NUMBER_INC;
  } else {
    MaxNum = mUserProfileDb->MaxProfileNum + USER_NUMBER_INC;
  }

  NewDataBase = AllocateZeroPool (
                  sizeof (USER_PROFILE_DB) - sizeof (EFI_USER_PROFILE_HANDLE) +
                  MaxNum * sizeof (EFI_USER_PROFILE_HANDLE)
                  );
  if (NewDataBase == NULL) {
    return FALSE;
  }

  NewDataBase->MaxProfileNum = MaxNum;

  //
  // Copy old user profile database value
  //
  if (mUserProfileDb == NULL) {
    NewDataBase->UserProfileNum = 0;
  } else {
    NewDataBase->UserProfileNum = mUserProfileDb->UserProfileNum;
    CopyMem (
      NewDataBase->UserProfile,
      mUserProfileDb->UserProfile,
      NewDataBase->UserProfileNum * sizeof (EFI_USER_PROFILE_HANDLE)
      );
    FreePool (mUserProfileDb);
  }

  mUserProfileDb = NewDataBase;
  return TRUE;
}


/**
  Expand user profile

  @param[in]  User                    Points to user profile.
  @param[in]  ExpandSize              The size of user profile. 

  @retval TRUE     Success to expand user profile size.
  @retval FALSE    Fail to expand user profile size.
  
**/
BOOLEAN
ExpandUserProfile (
  IN USER_PROFILE_ENTRY                         *User,
  IN UINTN                                      ExpandSize
  )
{
  UINT8 *Info;
  UINTN InfoSizeInc;

  //
  // Allocate new memory.
  //
  InfoSizeInc = 128;
  User->MaxProfileSize += ((ExpandSize + InfoSizeInc - 1) / InfoSizeInc) * InfoSizeInc;
  Info = AllocateZeroPool (User->MaxProfileSize);
  if (Info == NULL) {
    return FALSE;
  }
  
  //
  // Copy exist information.
  //
  if (User->UserProfileSize > 0) {
    CopyMem (Info, User->ProfileInfo, User->UserProfileSize);
    FreePool (User->ProfileInfo);
  }

  User->ProfileInfo = Info;
  return TRUE;
}


/**
  Save the user profile to non-volatile memory, or delete it from non-volatile memory.

  @param[in]  User         Point to the user profile
  @param[in]  Delete       If TRUE, delete the found user profile.
                           If FALSE, save the user profile.
  @retval EFI_SUCCESS      Save or delete user profile successfully.
  @retval Others           Fail to change the profile.
  
**/
EFI_STATUS
SaveNvUserProfile (
  IN  USER_PROFILE_ENTRY                        *User,
  IN  BOOLEAN                                   Delete
  )
{
  EFI_STATUS  Status;

  //
  // Check user profile entry.
  //
  Status = FindUserProfile (&User, FALSE, NULL);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // Save the user profile to non-volatile memory.
  //
  Status = gRT->SetVariable (
                  User->UserVarName,
                  &gUserIdentifyManagerGuid,
                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                  Delete ? 0 : User->UserProfileSize,
                  User->ProfileInfo
                  );
  return Status;
}

/**
  Add one new user info into the user's profile.

  @param[in]   User        point to the user profile
  @param[in]   Info        Points to the user information payload.
  @param[in]   InfoSize    The size of the user information payload, in bytes.
  @param[out]  UserInfo    Point to the new info in user profile
  @param[in]   Save        If TRUE, save the profile to NV flash.
                           If FALSE, don't need to save the profile to NV flash.

  @retval EFI_SUCCESS      Add user info to user profile successfully.
  @retval Others           Fail to add user info to user profile.

**/
EFI_STATUS
AddUserInfo (
  IN  USER_PROFILE_ENTRY                        *User,
  IN  UINT8                                     *Info,
  IN  UINTN                                     InfoSize,
  OUT EFI_USER_INFO                             **UserInfo, OPTIONAL
  IN  BOOLEAN                                   Save
  )
{
  EFI_STATUS  Status;

  if ((Info == NULL) || (User == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check user profile handle.
  //
  Status = FindUserProfile (&User, FALSE, NULL);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // Check user information memory size.
  //
  if (User->MaxProfileSize - User->UserProfileSize < ALIGN_VARIABLE (InfoSize)) {
    if (!ExpandUserProfile (User, ALIGN_VARIABLE (InfoSize))) {
      return EFI_OUT_OF_RESOURCES;
    }
  }
  
  //
  // Add new user information.
  //
  CopyMem (User->ProfileInfo + User->UserProfileSize, Info, InfoSize);
  if (UserInfo != NULL) {
    *UserInfo = (EFI_USER_INFO *) (User->ProfileInfo + User->UserProfileSize);
  }
  User->UserProfileSize += ALIGN_VARIABLE (InfoSize);

  //
  // Save user profile information.
  //
  if (Save) {
    Status = SaveNvUserProfile (User, FALSE);
  }

  return Status;
}


/**
  Get the user info from the specified user info handle.

  @param[in]      User            Point to the user profile.
  @param[in]      UserInfo        Point to the user information record to get.
  @param[out]     Info            On entry, points to a buffer of at least *InfoSize bytes. 
                                  On exit, holds the user information.
  @param[in, out] InfoSize        On entry, points to the size of Info. 
                                  On return, points to the size of the user information.
  @param[in]      ChkRight        If TRUE, check the user info attribute.
                                  If FALSE, don't check the user info attribute.


  @retval EFI_ACCESS_DENIED       The information cannot be accessed by the current user.
  @retval EFI_INVALID_PARAMETER   InfoSize is NULL or UserInfo is NULL.
  @retval EFI_BUFFER_TOO_SMALL    The number of bytes specified by *InfoSize is too small to hold the 
                                  returned data. The actual size required is returned in *InfoSize.
  @retval EFI_SUCCESS             Information returned successfully.

**/
EFI_STATUS
GetUserInfo (
  IN        USER_PROFILE_ENTRY                  *User,
  IN        EFI_USER_INFO                       *UserInfo,
  OUT       EFI_USER_INFO                       *Info,
  IN OUT    UINTN                               *InfoSize,
  IN        BOOLEAN                             ChkRight
  )
{
  EFI_STATUS  Status;

  if ((InfoSize == NULL) || (UserInfo == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((*InfoSize != 0) && (Info == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Find the user information to get.
  //
  Status = FindUserInfo (User, &UserInfo, FALSE, NULL);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // Check information attributes.
  //
  if (ChkRight) {
    switch (UserInfo->InfoAttribs & EFI_USER_INFO_ACCESS) {
    case EFI_USER_INFO_PRIVATE:
    case EFI_USER_INFO_PROTECTED:
      if (User != mCurrentUser) {
        return EFI_ACCESS_DENIED;
      }
      break;

    case EFI_USER_INFO_PUBLIC:
      break;

    default:
      return EFI_INVALID_PARAMETER;
      break;
    }
  }
  
  //
  // Get user information.
  //
  if (UserInfo->InfoSize > *InfoSize) {
    *InfoSize = UserInfo->InfoSize;
    return EFI_BUFFER_TOO_SMALL;
  }

  *InfoSize = UserInfo->InfoSize;
  if (Info != NULL) {
    CopyMem (Info, UserInfo, *InfoSize);
  }

  return EFI_SUCCESS;
}


/**
  Delete the specified user information from user profile.

  @param[in]  User        Point to the user profile.
  @param[in]  Info        Point to the user information record to delete.
  @param[in]  Save        If TRUE, save the profile to NV flash.
                          If FALSE, don't need to save the profile to NV flash.

  @retval EFI_SUCCESS     Delete user info from user profile successfully.
  @retval Others          Fail to delete user info from user profile.

**/
EFI_STATUS
DelUserInfo (
  IN  USER_PROFILE_ENTRY                        *User,
  IN  EFI_USER_INFO                             *Info,
  IN  BOOLEAN                                   Save
  )
{
  EFI_STATUS  Status;
  UINTN       Offset;
  UINTN       NextOffset;

  //
  // Check user information handle.
  //
  Status = FindUserInfo (User, &Info, FALSE, &Offset);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (Info->InfoType == EFI_USER_INFO_IDENTIFIER_RECORD) {
    return EFI_ACCESS_DENIED;
  }
  
  //
  // Delete the specified user information.
  //
  NextOffset = Offset + ALIGN_VARIABLE (Info->InfoSize);
  User->UserProfileSize -= ALIGN_VARIABLE (Info->InfoSize);
  if (Offset < User->UserProfileSize) {
    CopyMem (User->ProfileInfo + Offset, User->ProfileInfo + NextOffset, User->UserProfileSize - Offset);
  }

  if (Save) {
    Status = SaveNvUserProfile (User, FALSE);
  }

  return Status;
}


/**
  Add or update user information.

  @param[in]      User           Point to the user profile.
  @param[in, out] UserInfo       On entry, points to the user information to modify,
                                 or NULL to add a new UserInfo. 
                                 On return, points to the modified user information.
  @param[in]      Info           Points to the new user information.
  @param[in]      InfoSize       The size of Info,in bytes.

  @retval EFI_INVALID_PARAMETER  UserInfo is NULL or Info is NULL.
  @retval EFI_ACCESS_DENIED      The record is exclusive.
  @retval EFI_SUCCESS            User information was successfully changed/added.

**/
EFI_STATUS
ModifyUserInfo (
  IN        USER_PROFILE_ENTRY                  *User,
  IN OUT    EFI_USER_INFO                       **UserInfo,
  IN CONST  EFI_USER_INFO                       *Info,
  IN        UINTN                               InfoSize
  )
{
  EFI_STATUS    Status;
  UINTN         PayloadLen;
  EFI_USER_INFO *OldInfo;

  if ((UserInfo == NULL) || (Info == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (InfoSize < sizeof (EFI_USER_INFO) || InfoSize != Info->InfoSize) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check user information.
  //
  if (Info->InfoType == EFI_USER_INFO_IDENTIFIER_RECORD) {
    return EFI_ACCESS_DENIED;
  }
  
  if (!CheckUserInfo (Info)) {
    return EFI_INVALID_PARAMETER;
  }


  if (*UserInfo == NULL) {
    //
    // Add new user information.
    //
    OldInfo = NULL;
    do {
      Status = FindUserInfoByType (User, &OldInfo, Info->InfoType);
      if (EFI_ERROR (Status)) {
        break;
      }
      ASSERT (OldInfo != NULL);

      if (((OldInfo->InfoAttribs & EFI_USER_INFO_EXCLUSIVE) != 0) || 
           ((Info->InfoAttribs & EFI_USER_INFO_EXCLUSIVE) != 0)) {
        //
        //  Same type can not co-exist for exclusive information.
        //
        return EFI_ACCESS_DENIED;
      }

      //
      // Check whether it exists in DB.
      //
      if (Info->InfoSize != OldInfo->InfoSize) {
        continue;
      }

      if (!CompareGuid (&OldInfo->Credential, &Info->Credential)) {
        continue;
      }
  
      PayloadLen = Info->InfoSize - sizeof (EFI_USER_INFO);
      if (PayloadLen == 0) {
        continue;
      }

      if (CompareMem ((UINT8 *)(OldInfo + 1), (UINT8 *)(Info + 1), PayloadLen) != 0) {
        continue;
      }

      //
      // Yes. The new info is as same as the one in profile.
      //
      return EFI_SUCCESS;
    } while (!EFI_ERROR (Status));

    Status = AddUserInfo (User, (UINT8 *) Info, InfoSize, UserInfo, TRUE);
    return Status;
  }
  
  //
  // Modify existing user information.
  //
  OldInfo = *UserInfo;
  if (OldInfo->InfoType != Info->InfoType) {
    return EFI_INVALID_PARAMETER;
  }
  
  if (((Info->InfoAttribs & EFI_USER_INFO_EXCLUSIVE) != 0) && 
       (OldInfo->InfoAttribs & EFI_USER_INFO_EXCLUSIVE) == 0) {
    //
    // Try to add exclusive attrib in new info. 
    // Check whether there is another information with the same type in profile.
    //
    OldInfo = NULL;
    do {
      Status = FindUserInfoByType (User, &OldInfo, Info->InfoType);
      if (EFI_ERROR (Status)) {
        break;
      }
      if (OldInfo != *UserInfo) {
        //
        // There is another information with the same type in profile.
        // Therefore, can't modify existing user information to add exclusive attribute.
        //
        return EFI_ACCESS_DENIED;
      }
    } while (TRUE);    
  }

  Status = DelUserInfo (User, *UserInfo, FALSE);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  return AddUserInfo (User, (UINT8 *) Info, InfoSize, UserInfo, TRUE);
}


/**
  Delete the user profile from non-volatile memory and database.

  @param[in]  User              Points to the user profile.

  @retval EFI_SUCCESS      Delete user from the user profile successfully.
  @retval Others           Fail to delete user from user profile
  
**/
EFI_STATUS
DelUserProfile (
  IN  USER_PROFILE_ENTRY                        *User
  )
{
  EFI_STATUS          Status;
  UINTN               Index;

  //
  // Check whether it is in the user profile database.
  //
  Status = FindUserProfile (&User, FALSE, &Index);
  if (EFI_ERROR (Status)) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check whether it is the current user.
  //
  if (User == mCurrentUser) {
    return EFI_ACCESS_DENIED;
  }
  
  //
  // Delete user profile from the non-volatile memory.
  //
  Status    = SaveNvUserProfile (mUserProfileDb->UserProfile[mUserProfileDb->UserProfileNum - 1], TRUE);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  mUserProfileDb->UserProfileNum--;

  //
  // Modify user profile database.
  //
  if (Index != mUserProfileDb->UserProfileNum) {
    mUserProfileDb->UserProfile[Index] = mUserProfileDb->UserProfile[mUserProfileDb->UserProfileNum];
    CopyMem (
      ((USER_PROFILE_ENTRY *) mUserProfileDb->UserProfile[Index])->UserVarName,
      User->UserVarName,
      sizeof (User->UserVarName)
      );
    Status = SaveNvUserProfile (mUserProfileDb->UserProfile[Index], FALSE);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }
  //
  // Delete user profile information.
  //
  if (User->ProfileInfo != NULL) {
    FreePool (User->ProfileInfo);
  }

  FreePool (User);
  return EFI_SUCCESS;
}


/**
  Add user profile to user profile database.

  @param[out]   UserProfile   Point to the newly added user profile.
  @param[in]    ProfileSize   The size of the user profile.
  @param[in]    ProfileInfo   Point to the user profie data.
  @param[in]    Save          If TRUE, save the new added profile to NV flash.
                              If FALSE, don't save the profile to NV flash.

  @retval EFI_SUCCESS         Add user profile to user profile database successfully.
  @retval Others              Fail to add user profile to user profile database.

**/
EFI_STATUS
AddUserProfile (
     OUT  USER_PROFILE_ENTRY                    **UserProfile, OPTIONAL
  IN      UINTN                                 ProfileSize,
  IN      UINT8                                 *ProfileInfo,
  IN      BOOLEAN                               Save
  )
{
  EFI_STATUS              Status;
  USER_PROFILE_ENTRY      *User;

  //
  // Check the data format to be added.
  //
  if (!CheckProfileInfo (ProfileInfo, ProfileSize)) {
    return EFI_SECURITY_VIOLATION;
  }
  
  //
  // Create user profile entry.
  //
  User = AllocateZeroPool (sizeof (USER_PROFILE_ENTRY));
  if (User == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // Add the entry to the user profile database.
  //
  if (mUserProfileDb->UserProfileNum == mUserProfileDb->MaxProfileNum) {
    if (!ExpandUsermUserProfileDb ()) {
      FreePool (User);
      return EFI_OUT_OF_RESOURCES;
    }
  }

  UnicodeSPrint (
    User->UserVarName, 
    sizeof (User->UserVarName),
    L"User%04x", 
    mUserProfileDb->UserProfileNum
    );
  User->UserProfileSize = 0;
  User->MaxProfileSize  = 0;
  User->ProfileInfo     = NULL;
  mUserProfileDb->UserProfile[mUserProfileDb->UserProfileNum] = (EFI_USER_PROFILE_HANDLE) User;
  mUserProfileDb->UserProfileNum++;

  //
  // Add user profile information.
  //
  Status = AddUserInfo (User, ProfileInfo, ProfileSize, NULL, Save);
  if (EFI_ERROR (Status)) {
    DelUserProfile (User);
    return Status;
  }
  //
  // Set new user profile handle.
  //
  if (UserProfile != NULL) {
    *UserProfile = User;
  }

  return EFI_SUCCESS;
}


/**
  This function creates a new user profile with only a new user identifier
  attached and returns its handle. The user profile is non-volatile, but the
  handle User can change across reboots.

  @param[out]  User               Handle of a new user profile.

  @retval EFI_SUCCESS             User profile was successfully created.
  @retval Others                  Fail to create user profile

**/
EFI_STATUS
CreateUserProfile (
  OUT USER_PROFILE_ENTRY                        **User
  )
{
  EFI_STATUS    Status;
  EFI_USER_INFO *UserInfo;

  if (User == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Generate user id information.
  //
  UserInfo = AllocateZeroPool (sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_IDENTIFIER));
  if (UserInfo == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  UserInfo->InfoType    = EFI_USER_INFO_IDENTIFIER_RECORD;
  UserInfo->InfoSize    = sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_IDENTIFIER);
  UserInfo->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PUBLIC | EFI_USER_INFO_EXCLUSIVE;
  GenerateUserId ((UINT8 *) (UserInfo + 1));
  
  //
  // Add user profile to the user profile database.
  //
  Status = AddUserProfile (User, UserInfo->InfoSize, (UINT8 *) UserInfo, TRUE);
  FreePool (UserInfo);
  return Status;
}


/**
  Add a default user profile to user profile database.

  @retval EFI_SUCCESS             A default user profile is added successfully.
  @retval Others                  Fail to add a default user profile
  
**/
EFI_STATUS
AddDefaultUserProfile (
  VOID
  )
{
  EFI_STATUS                    Status;
  USER_PROFILE_ENTRY            *User;
  EFI_USER_INFO                 *Info;
  EFI_USER_INFO                 *NewInfo;
  EFI_USER_INFO_CREATE_DATE     CreateDate;
  EFI_USER_INFO_USAGE_COUNT     UsageCount;
  EFI_USER_INFO_ACCESS_CONTROL  *Access;
  EFI_USER_INFO_IDENTITY_POLICY *Policy;
  
  //
  // Create a user profile.
  //
  Status = CreateUserProfile (&User);
  if (EFI_ERROR (Status)) {
    return Status;
  }
 
  //
  // Allocate a buffer to add all default user information.
  //
  Info = AllocateZeroPool (sizeof (EFI_USER_INFO) + INFO_PAYLOAD_SIZE);
  if (Info == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Add user name.
  //
  Info->InfoType    = EFI_USER_INFO_NAME_RECORD;
  Info->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PUBLIC | EFI_USER_INFO_EXCLUSIVE;
  Info->InfoSize    = sizeof (EFI_USER_INFO) + sizeof (mUserName);
  CopyMem ((UINT8 *) (Info + 1), mUserName, sizeof (mUserName));
  NewInfo = NULL;
  Status  = ModifyUserInfo (User, &NewInfo, Info, Info->InfoSize);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  
  //
  // Add user profile create date record.
  //
  Info->InfoType    = EFI_USER_INFO_CREATE_DATE_RECORD;
  Info->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PUBLIC | EFI_USER_INFO_EXCLUSIVE;
  Info->InfoSize    = sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_CREATE_DATE);
  Status            = gRT->GetTime (&CreateDate, NULL);
  if (EFI_ERROR (Status)) {
    goto Done;
  }

  CopyMem ((UINT8 *) (Info + 1), &CreateDate, sizeof (EFI_USER_INFO_CREATE_DATE));
  NewInfo = NULL;
  Status  = ModifyUserInfo (User, &NewInfo, Info, Info->InfoSize);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  
  //
  // Add user profile usage count record.
  //
  Info->InfoType    = EFI_USER_INFO_USAGE_COUNT_RECORD;
  Info->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PUBLIC | EFI_USER_INFO_EXCLUSIVE;
  Info->InfoSize    = sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_USAGE_COUNT);
  UsageCount        = 0;
  CopyMem ((UINT8 *) (Info + 1), &UsageCount, sizeof (EFI_USER_INFO_USAGE_COUNT));
  NewInfo = NULL;
  Status  = ModifyUserInfo (User, &NewInfo, Info, Info->InfoSize);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
 
  //
  // Add user access right.
  //
  Info->InfoType    = EFI_USER_INFO_ACCESS_POLICY_RECORD;
  Info->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PUBLIC | EFI_USER_INFO_EXCLUSIVE;
  Access            = (EFI_USER_INFO_ACCESS_CONTROL *) (Info + 1);
  Access->Type      = EFI_USER_INFO_ACCESS_MANAGE;
  Access->Size      = sizeof (EFI_USER_INFO_ACCESS_CONTROL);
  Info->InfoSize    = sizeof (EFI_USER_INFO) + Access->Size;
  NewInfo = NULL;
  Status  = ModifyUserInfo (User, &NewInfo, Info, Info->InfoSize);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  
  //
  // Add user identity policy.
  //
  Info->InfoType    = EFI_USER_INFO_IDENTITY_POLICY_RECORD;
  Info->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PRIVATE | EFI_USER_INFO_EXCLUSIVE;
  Policy            = (EFI_USER_INFO_IDENTITY_POLICY *) (Info + 1);
  Policy->Type      = EFI_USER_INFO_IDENTITY_TRUE;
  Policy->Length    = sizeof (EFI_USER_INFO_IDENTITY_POLICY);  
  Info->InfoSize    = sizeof (EFI_USER_INFO) + Policy->Length;
  NewInfo = NULL;
  Status  = ModifyUserInfo (User, &NewInfo, Info, Info->InfoSize);

Done:
  FreePool (Info);
  return Status;
}


/**
  Publish current user information into EFI System Configuration Table.

  By UEFI spec, the User Identity Manager will publish the current user profile 
  into the EFI System Configuration Table. Currently, only the user identifier and user
  name are published.

  @retval EFI_SUCCESS      Current user information is published successfully.
  @retval Others           Fail to publish current user information

**/
EFI_STATUS
PublishUserTable (
  VOID
  )
{
  EFI_STATUS              Status;
  EFI_CONFIGURATION_TABLE *EfiConfigurationTable;
  EFI_USER_INFO_TABLE     *UserInfoTable;
  EFI_USER_INFO           *IdInfo;
  EFI_USER_INFO           *NameInfo;

  Status = EfiGetSystemConfigurationTable (
             &gEfiUserManagerProtocolGuid,
             (VOID **) &EfiConfigurationTable
             );
  if (!EFI_ERROR (Status)) {
    //
    // The table existed! 
    //
    return EFI_SUCCESS;
  }

  //
  // Get user ID information.
  //
  IdInfo  = NULL;
  Status  = FindUserInfoByType (mCurrentUser, &IdInfo, EFI_USER_INFO_IDENTIFIER_RECORD);
  if (EFI_ERROR (Status)) {
    return Status;

  }
  //
  // Get user name information.
  //
  NameInfo  = NULL;
  Status    = FindUserInfoByType (mCurrentUser, &NameInfo, EFI_USER_INFO_NAME_RECORD);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // Allocate a buffer for user information table.
  //
  UserInfoTable = (EFI_USER_INFO_TABLE *) AllocateRuntimePool (
                                            sizeof (EFI_USER_INFO_TABLE) + 
                                            IdInfo->InfoSize + 
                                            NameInfo->InfoSize
                                            );
  if (UserInfoTable == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    return Status;
  }

  UserInfoTable->Size = sizeof (EFI_USER_INFO_TABLE);  
  
  //
  // Append the user information to the user info table
  //
  CopyMem ((UINT8 *) UserInfoTable + UserInfoTable->Size, (UINT8 *) IdInfo, IdInfo->InfoSize);
  UserInfoTable->Size += IdInfo->InfoSize;

  CopyMem ((UINT8 *) UserInfoTable + UserInfoTable->Size, (UINT8 *) NameInfo, NameInfo->InfoSize);
  UserInfoTable->Size += NameInfo->InfoSize;

  Status = gBS->InstallConfigurationTable (&gEfiUserManagerProtocolGuid, (VOID *) UserInfoTable);
  return Status;
}


/**
  Get the user's identity type.

  The identify manager only supports the identity policy in which the credential 
  provider handles are connected by the operator 'AND' or 'OR'.


  @param[in]   User              Handle of a user profile.
  @param[out]  PolicyType        Point to the identity type.

  @retval EFI_SUCCESS            Get user's identity type successfully.
  @retval Others                 Fail to get user's identity type.

**/
EFI_STATUS
GetIdentifyType (
  IN      EFI_USER_PROFILE_HANDLE               User,
     OUT  UINT8                                 *PolicyType
  )
{
  EFI_STATUS                    Status;
  EFI_USER_INFO                 *IdentifyInfo;
  UINTN                         TotalLen;
  EFI_USER_INFO_IDENTITY_POLICY *Identity;

  //
  // Get user identify policy information.
  //
  IdentifyInfo  = NULL;
  Status        = FindUserInfoByType (User, &IdentifyInfo, EFI_USER_INFO_IDENTITY_POLICY_RECORD);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  ASSERT (IdentifyInfo != NULL);
  
  //
  // Search the user identify policy according to type.
  //
  TotalLen    = 0;
  *PolicyType = EFI_USER_INFO_IDENTITY_FALSE;
  while (TotalLen < IdentifyInfo->InfoSize - sizeof (EFI_USER_INFO)) {
    Identity = (EFI_USER_INFO_IDENTITY_POLICY *) ((UINT8 *) (IdentifyInfo + 1) + TotalLen);
    if (Identity->Type == EFI_USER_INFO_IDENTITY_AND) {
      *PolicyType = EFI_USER_INFO_IDENTITY_AND;
      break;
    }

    if (Identity->Type == EFI_USER_INFO_IDENTITY_OR) {
      *PolicyType = EFI_USER_INFO_IDENTITY_OR;
      break;
    }
    TotalLen += Identity->Length;
  }
  return EFI_SUCCESS;
}


/**
  Identify the User by the specfied provider.

  @param[in]  User                Handle of a user profile.
  @param[in]  Provider            Points to the identifier of credential provider.

  @retval EFI_INVALID_PARAMETER   Provider is NULL.
  @retval EFI_NOT_FOUND           Fail to identify the specified user.
  @retval EFI_SUCCESS             User is identified successfully.

**/
EFI_STATUS
IdentifyByProviderId (
  IN  EFI_USER_PROFILE_HANDLE                   User,
  IN  EFI_GUID                                  *Provider
  )
{
  EFI_STATUS                    Status;
  EFI_USER_INFO_IDENTIFIER      UserId;
  UINTN                         Index;
  EFI_CREDENTIAL_LOGON_FLAGS    AutoLogon;
  EFI_HII_HANDLE                HiiHandle;
  EFI_GUID                      FormSetId;
  EFI_FORM_ID                   FormId;
  EFI_USER_CREDENTIAL2_PROTOCOL *UserCredential;

  if (Provider == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check the user ID identified by the specified credential provider.
  //
  for (Index = 0; Index < mProviderDb->Count; Index++) {
    //
    // Check credential provider class.
    //
    UserCredential = mProviderDb->Provider[Index];
    if (CompareGuid (&UserCredential->Identifier, Provider)) {
      Status = UserCredential->Select (UserCredential, &AutoLogon);
      if (EFI_ERROR (Status)) {
        return Status;
      }

      if ((AutoLogon & EFI_CREDENTIAL_LOGON_FLAG_AUTO) == 0) {
        //
        // Get credential provider form.
        //
        Status = UserCredential->Form (
                                   UserCredential, 
                                   &HiiHandle, 
                                   &FormSetId, 
                                   &FormId
                                   );
        if (!EFI_ERROR (Status)) {        
          //
          // Send form to get user input.
          //
          Status = mCallbackInfo->FormBrowser2->SendForm (
                                                  mCallbackInfo->FormBrowser2,
                                                  &HiiHandle,
                                                  1,
                                                  &FormSetId,
                                                  FormId,
                                                  NULL,
                                                  NULL
                                                  );
          if (EFI_ERROR (Status)) {
            return Status;
          }                                            
        }        
      }

      Status = UserCredential->User (UserCredential, User, &UserId);
      if (EFI_ERROR (Status)) {
        return Status;
      }

      Status = UserCredential->Deselect (UserCredential);
      if (EFI_ERROR (Status)) {
        return Status;
      }
      
      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}


/**
  Update user information when user is logon on successfully.

  @param[in]  User         Points to user profile.

  @retval EFI_SUCCESS      Update user information successfully.
  @retval Others           Fail to update user information.

**/
EFI_STATUS
UpdateUserInfo (
  IN  USER_PROFILE_ENTRY                        *User
  )
{
  EFI_STATUS                Status;
  EFI_USER_INFO             *Info;
  EFI_USER_INFO             *NewInfo;
  EFI_USER_INFO_CREATE_DATE Date;
  EFI_USER_INFO_USAGE_COUNT UsageCount;
  UINTN                     InfoLen;

  //
  // Allocate a buffer to update user's date record and usage record.
  //
  InfoLen  = MAX (sizeof (EFI_USER_INFO_CREATE_DATE), sizeof (EFI_USER_INFO_USAGE_COUNT));
  Info     = AllocateZeroPool (sizeof (EFI_USER_INFO) + InfoLen);
  if (Info == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  
  //
  // Check create date record.
  //
  NewInfo = NULL;
  Status  = FindUserInfoByType (User, &NewInfo, EFI_USER_INFO_CREATE_DATE_RECORD);
  if (Status == EFI_NOT_FOUND) {
    Info->InfoType    = EFI_USER_INFO_CREATE_DATE_RECORD;
    Info->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PUBLIC | EFI_USER_INFO_EXCLUSIVE;
    Info->InfoSize    = sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_CREATE_DATE);
    Status            = gRT->GetTime (&Date, NULL);
    if (EFI_ERROR (Status)) {
      FreePool (Info);
      return Status;
    }

    CopyMem ((UINT8 *) (Info + 1), &Date, sizeof (EFI_USER_INFO_CREATE_DATE));
    NewInfo = NULL;
    Status  = ModifyUserInfo (User, &NewInfo, Info, Info->InfoSize);
    if (EFI_ERROR (Status)) {
      FreePool (Info);
      return Status;
    }
  }
  
  //
  // Update usage date record.
  //
  NewInfo = NULL;
  Status  = FindUserInfoByType (User, &NewInfo, EFI_USER_INFO_USAGE_DATE_RECORD);
  if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_FOUND)) {
    Info->InfoType    = EFI_USER_INFO_USAGE_DATE_RECORD;
    Info->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PUBLIC | EFI_USER_INFO_EXCLUSIVE;
    Info->InfoSize    = sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_USAGE_DATE);
    Status            = gRT->GetTime (&Date, NULL);
    if (EFI_ERROR (Status)) {
      FreePool (Info);
      return Status;
    }

    CopyMem ((UINT8 *) (Info + 1), &Date, sizeof (EFI_USER_INFO_USAGE_DATE));
    Status = ModifyUserInfo (User, &NewInfo, Info, Info->InfoSize);
    if (EFI_ERROR (Status)) {
      FreePool (Info);
      return Status;
    }
  }
  
  //
  // Update usage count record.
  //
  UsageCount  = 0;
  NewInfo     = NULL;
  Status      = FindUserInfoByType (User, &NewInfo, EFI_USER_INFO_USAGE_COUNT_RECORD);
  //
  // Get usage count.
  //
  if (Status == EFI_SUCCESS) {
    CopyMem (&UsageCount, (UINT8 *) (NewInfo + 1), sizeof (EFI_USER_INFO_USAGE_COUNT));
  }

  UsageCount++;
  if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_FOUND)) {
    Info->InfoType    = EFI_USER_INFO_USAGE_COUNT_RECORD;
    Info->InfoAttribs = EFI_USER_INFO_STORAGE_PLATFORM_NV | EFI_USER_INFO_PUBLIC | EFI_USER_INFO_EXCLUSIVE;
    Info->InfoSize    = sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_USAGE_COUNT);
    CopyMem ((UINT8 *) (Info + 1), &UsageCount, sizeof (EFI_USER_INFO_USAGE_COUNT));
    Status = ModifyUserInfo (User, &NewInfo, Info, Info->InfoSize);
    if (EFI_ERROR (Status)) {
      FreePool (Info);
      return Status;
    }
  }

  FreePool (Info);
  return EFI_SUCCESS;
}


/**
  Add a credenetial provider item in form.

  @param[in]  ProviderGuid        Points to the identifir of credential provider.
  @param[in]  OpCodeHandle        Points to container for dynamic created opcodes.

**/
VOID
AddProviderSelection (
  IN     EFI_GUID                               *ProviderGuid,
  IN     VOID                                   *OpCodeHandle
  )
{
  EFI_HII_HANDLE                HiiHandle;
  EFI_STRING_ID                 ProvID;
  CHAR16                        *ProvStr;
  UINTN                         Index;
  EFI_USER_CREDENTIAL2_PROTOCOL *UserCredential;

  for (Index = 0; Index < mProviderDb->Count; Index++) {
    UserCredential = mProviderDb->Provider[Index];
    if (CompareGuid (&UserCredential->Identifier, ProviderGuid)) {
      //
      // Add credential provider selection.
      //
      UserCredential->Title (UserCredential, &HiiHandle, &ProvID);
      ProvStr = HiiGetString (HiiHandle, ProvID, NULL);
      if (ProvStr == NULL) {
        continue ;
      }
      ProvID  = HiiSetString (mCallbackInfo->HiiHandle, 0, ProvStr, NULL);
      FreePool (ProvStr);
      HiiCreateActionOpCode (
        OpCodeHandle,                          // Container for dynamic created opcodes
        (EFI_QUESTION_ID)(LABEL_PROVIDER_NAME + Index),  // Question ID
        ProvID,                                // Prompt text
        STRING_TOKEN (STR_NULL_STRING),        // Help text
        EFI_IFR_FLAG_CALLBACK,                 // Question flag
        0                                      // Action String ID
        );
      break;
    }
  }
}


/**
  Add a username item in form.

  @param[in]  Index            The index of the user in the user name list.
  @param[in]  User             Points to the user profile whose username is added. 
  @param[in]  OpCodeHandle     Points to container for dynamic created opcodes.

  @retval EFI_SUCCESS          Add a username successfully.
  @retval Others               Fail to add a username.

**/
EFI_STATUS
AddUserSelection (
  IN     UINT16                                 Index,
  IN     USER_PROFILE_ENTRY                     *User,
  IN     VOID                                   *OpCodeHandle
  )
{
  EFI_STRING_ID UserName;
  EFI_STATUS    Status;
  EFI_USER_INFO *UserInfo;

  UserInfo  = NULL;
  Status    = FindUserInfoByType (User, &UserInfo, EFI_USER_INFO_NAME_RECORD);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // Add user name selection.
  //
  UserName = HiiSetString (mCallbackInfo->HiiHandle, 0, (EFI_STRING) (UserInfo + 1), NULL);
  if (UserName == 0) {
    return EFI_OUT_OF_RESOURCES;
  }

  HiiCreateGotoOpCode (
    OpCodeHandle,                   // Container for dynamic created opcodes
    FORMID_PROVIDER_FORM,           // Target Form ID
    UserName,                       // Prompt text
    STRING_TOKEN (STR_NULL_STRING), // Help text
    EFI_IFR_FLAG_CALLBACK,          // Question flag
    (UINT16) Index                  // Question ID
    );

  return EFI_SUCCESS;
}


/**
  Identify the user whose identity policy does not contain the operator 'OR'.
  
  @param[in]  User             Points to the user profile.

  @retval EFI_SUCCESS          The specified user is identified successfully.
  @retval Others               Fail to identify the user.
  
**/
EFI_STATUS
IdentifyAndTypeUser (
  IN  USER_PROFILE_ENTRY                        *User
  )
{
  EFI_STATUS                    Status;
  EFI_USER_INFO                 *IdentifyInfo;
  BOOLEAN                       Success;
  UINTN                         TotalLen;
  UINTN                         ValueLen;
  EFI_USER_INFO_IDENTITY_POLICY *Identity;

  //
  // Get user identify policy information.
  //
  IdentifyInfo  = NULL;
  Status        = FindUserInfoByType (User, &IdentifyInfo, EFI_USER_INFO_IDENTITY_POLICY_RECORD);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  ASSERT (IdentifyInfo != NULL);
  
  //
  // Check each part of identification policy expression.
  //
  Success   = FALSE;
  TotalLen  = 0;
  while (TotalLen < IdentifyInfo->InfoSize - sizeof (EFI_USER_INFO)) {
    Identity  = (EFI_USER_INFO_IDENTITY_POLICY *) ((UINT8 *) (IdentifyInfo + 1) + TotalLen);
    ValueLen  = Identity->Length - sizeof (EFI_USER_INFO_IDENTITY_POLICY);
    switch (Identity->Type) {

    case EFI_USER_INFO_IDENTITY_FALSE:
      //
      // Check False option.
      //
      Success = FALSE;
      break;

    case EFI_USER_INFO_IDENTITY_TRUE:
      //
      // Check True option.
      //
      Success = TRUE;
      break;

    case EFI_USER_INFO_IDENTITY_NOT:
      //
      // Check negative operation.
      //
      break;

    case EFI_USER_INFO_IDENTITY_AND:
      //
      // Check and operation.
      //
      if (!Success) {
        return EFI_NOT_READY;
      }

      Success = FALSE;
      break;

    case EFI_USER_INFO_IDENTITY_OR:
      //
      // Check or operation.
      //
      if (Success) {
        return EFI_SUCCESS;
      }
      break;

    case EFI_USER_INFO_IDENTITY_CREDENTIAL_TYPE:
      //
      // Check credential provider by type.
      //
      break;

    case EFI_USER_INFO_IDENTITY_CREDENTIAL_PROVIDER:
      //
      // Check credential provider by ID.
      //
      if (ValueLen != sizeof (EFI_GUID)) {
        return EFI_INVALID_PARAMETER;
      }

      Status = IdentifyByProviderId (User, (EFI_GUID *) (Identity + 1));
      if (EFI_ERROR (Status)) {
        return Status;
      }

      Success = TRUE;
      break;

    default:
      return EFI_INVALID_PARAMETER;
      break;
    }

    TotalLen += Identity->Length;
  }

  if (TotalLen != IdentifyInfo->InfoSize - sizeof (EFI_USER_INFO)) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Success) {
    return EFI_NOT_READY;
  }

  return EFI_SUCCESS;
}


/**
  Identify the user whose identity policy does not contain the operator 'AND'.
  
  @param[in]  User             Points to the user profile.

  @retval EFI_SUCCESS          The specified user is identified successfully.
  @retval Others               Fail to identify the user.
  
**/
EFI_STATUS
IdentifyOrTypeUser (
  IN  USER_PROFILE_ENTRY                        *User
  )
{
  EFI_STATUS                    Status;
  EFI_USER_INFO                 *IdentifyInfo;
  UINTN                         TotalLen;
  UINTN                         ValueLen;
  EFI_USER_INFO_IDENTITY_POLICY *Identity;
  VOID                          *StartOpCodeHandle;
  VOID                          *EndOpCodeHandle;
  EFI_IFR_GUID_LABEL            *StartLabel;
  EFI_IFR_GUID_LABEL            *EndLabel;

  //
  // Get user identify policy information.
  //
  IdentifyInfo  = NULL;
  Status        = FindUserInfoByType (User, &IdentifyInfo, EFI_USER_INFO_IDENTITY_POLICY_RECORD);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  ASSERT (IdentifyInfo != NULL);
  
  //
  // Initialize the container for dynamic opcodes.
  //
  StartOpCodeHandle = HiiAllocateOpCodeHandle ();
  ASSERT (StartOpCodeHandle != NULL);

  EndOpCodeHandle = HiiAllocateOpCodeHandle ();
  ASSERT (EndOpCodeHandle != NULL);

  //
  // Create Hii Extend Label OpCode.
  //
  StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
                                        StartOpCodeHandle,
                                        &gEfiIfrTianoGuid,
                                        NULL,
                                        sizeof (EFI_IFR_GUID_LABEL)
                                        );
  StartLabel->ExtendOpCode  = EFI_IFR_EXTEND_OP_LABEL;
  StartLabel->Number        = LABEL_PROVIDER_NAME;

  EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
                                      EndOpCodeHandle,
                                      &gEfiIfrTianoGuid,
                                      NULL,
                                      sizeof (EFI_IFR_GUID_LABEL)
                                      );
  EndLabel->ExtendOpCode  = EFI_IFR_EXTEND_OP_LABEL;
  EndLabel->Number        = LABEL_END;

  //
  // Add the providers that exists in the user's policy.
  //
  TotalLen = 0;
  while (TotalLen < IdentifyInfo->InfoSize - sizeof (EFI_USER_INFO)) {
    Identity  = (EFI_USER_INFO_IDENTITY_POLICY *) ((UINT8 *) (IdentifyInfo + 1) + TotalLen);
    ValueLen  = Identity->Length - sizeof (EFI_USER_INFO_IDENTITY_POLICY);
    if (Identity->Type == EFI_USER_INFO_IDENTITY_CREDENTIAL_PROVIDER) {
      AddProviderSelection ((EFI_GUID *) (Identity + 1), StartOpCodeHandle);
    }

    TotalLen += Identity->Length;
  }

  HiiUpdateForm (
    mCallbackInfo->HiiHandle, // HII handle
    &gUserIdentifyManagerGuid,// Formset GUID
    FORMID_PROVIDER_FORM,     // Form ID
    StartOpCodeHandle,        // Label for where to insert opcodes
    EndOpCodeHandle           // Replace data
    );

  HiiFreeOpCodeHandle (StartOpCodeHandle);
  HiiFreeOpCodeHandle (EndOpCodeHandle);

  return EFI_SUCCESS;
}


/**
  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 Others                 Fail to handle the action.

**/
EFI_STATUS
EFIAPI
UserIdentifyManagerCallback (
  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;
  USER_PROFILE_ENTRY      *User;
  UINT8                   PolicyType;
  UINT16                  Index;
  VOID                    *StartOpCodeHandle;
  VOID                    *EndOpCodeHandle;
  EFI_IFR_GUID_LABEL      *StartLabel;
  EFI_IFR_GUID_LABEL      *EndLabel;

  Status = EFI_SUCCESS;

  switch (Action) {
  case EFI_BROWSER_ACTION_FORM_OPEN:
    {
      //
      // Update user Form when user Form is opened.
      // This will be done only in FORM_OPEN CallBack of question with FORM_OPEN_QUESTION_ID from user Form.
      //
      if (QuestionId != FORM_OPEN_QUESTION_ID) {
        return EFI_SUCCESS;
      }
  
      //
      // Initialize the container for dynamic opcodes.
      //
      StartOpCodeHandle = HiiAllocateOpCodeHandle ();
      ASSERT (StartOpCodeHandle != NULL);
  
      EndOpCodeHandle = HiiAllocateOpCodeHandle ();
      ASSERT (EndOpCodeHandle != NULL);
  
      //
      // Create Hii Extend Label OpCode.
      //
      StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
                                            StartOpCodeHandle,
                                            &gEfiIfrTianoGuid,
                                            NULL,
                                            sizeof (EFI_IFR_GUID_LABEL)
                                            );
      StartLabel->ExtendOpCode  = EFI_IFR_EXTEND_OP_LABEL;
      StartLabel->Number        = LABEL_USER_NAME;
  
      EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
                                          EndOpCodeHandle,
                                          &gEfiIfrTianoGuid,
                                          NULL,
                                          sizeof (EFI_IFR_GUID_LABEL)
                                          );
      EndLabel->ExtendOpCode  = EFI_IFR_EXTEND_OP_LABEL;
      EndLabel->Number        = LABEL_END;
  
      //
      // Add all the user profile in the user profile database.
      //
      for (Index = 0; Index < mUserProfileDb->UserProfileNum; Index++) {
        User = (USER_PROFILE_ENTRY *) mUserProfileDb->UserProfile[Index];
        AddUserSelection ((UINT16)(LABEL_USER_NAME + Index), User, StartOpCodeHandle);
      }
  
      HiiUpdateForm (
        mCallbackInfo->HiiHandle, // HII handle
        &gUserIdentifyManagerGuid,// Formset GUID
        FORMID_USER_FORM,         // Form ID
        StartOpCodeHandle,        // Label for where to insert opcodes
        EndOpCodeHandle           // Replace data
        );
  
      HiiFreeOpCodeHandle (StartOpCodeHandle);
      HiiFreeOpCodeHandle (EndOpCodeHandle);
  
      return EFI_SUCCESS;
    }
    break;

  case EFI_BROWSER_ACTION_FORM_CLOSE:
    Status = EFI_SUCCESS;
    break;

  case EFI_BROWSER_ACTION_CHANGED:
    if (QuestionId >= LABEL_PROVIDER_NAME) {
      //
      // QuestionId comes from the second Form (Select a Credential Provider if identity  
      // policy is OR type). Identify the user by the selected provider.
      //
      Status = IdentifyByProviderId (mCurrentUser, &mProviderDb->Provider[QuestionId & 0xFFF]->Identifier);
      if (Status == EFI_SUCCESS) {
        mIdentified     = TRUE;
        *ActionRequest  = EFI_BROWSER_ACTION_REQUEST_EXIT;
      }
      return EFI_SUCCESS;
    }
    break;
    
  case EFI_BROWSER_ACTION_CHANGING:
    //
    // QuestionId comes from the first Form (Select a user to identify).
    //
    if (QuestionId >= LABEL_PROVIDER_NAME) {
      return EFI_SUCCESS;
    }

    User   = (USER_PROFILE_ENTRY *) mUserProfileDb->UserProfile[QuestionId & 0xFFF];
    Status = GetIdentifyType (User, &PolicyType);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    if (PolicyType == EFI_USER_INFO_IDENTITY_OR) {
      //
      // Identify the user by "OR" logical.
      //
      Status = IdentifyOrTypeUser (User);
      if (EFI_ERROR (Status)) {
        return Status;
      }

      mCurrentUser = (EFI_USER_PROFILE_HANDLE) User;
    } else {
      //
      // Identify the user by "AND" logical.
      //
      Status = IdentifyAndTypeUser (User);
      if (EFI_ERROR (Status)) {
        return Status;
      }

      mCurrentUser    = (EFI_USER_PROFILE_HANDLE) User;
      mIdentified     = TRUE;
      if (Type == EFI_IFR_TYPE_REF) {
        Value->ref.FormId = FORMID_INVALID_FORM;
      }
    }
  break;

  default:
    //
    // All other action return unsupported.
    //
    Status = EFI_UNSUPPORTED;
    break;
  }


  return Status;
}


/**
  This function construct user profile database from user data saved in the Flash.
  If no user is found in Flash, add one default user "administrator" in the user 
  profile database.

  @retval EFI_SUCCESS            Init user profile database successfully.
  @retval Others                 Fail to init user profile database.
  
**/
EFI_STATUS
InitUserProfileDb (
  VOID
  )
{
  EFI_STATUS  Status;
  UINT8       *VarData;
  UINTN       VarSize;
  UINTN       CurVarSize;
  CHAR16      VarName[10];
  UINTN       Index;
  UINT32      VarAttr;

  if (mUserProfileDb != NULL) {
    //
    // The user profiles had been already initialized.
    //
    return EFI_SUCCESS;
  }

  //
  // Init user profile database structure.
  //
  if (!ExpandUsermUserProfileDb ()) {
    return EFI_OUT_OF_RESOURCES;
  }

  CurVarSize = DEFAULT_PROFILE_SIZE;
  VarData    = AllocateZeroPool (CurVarSize);
  if (VarData == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  
  //
  // Get all user proifle entries.
  //
  Index = 0;
  while (TRUE) {
    //
    // Get variable name.
    //
    UnicodeSPrint (
      VarName, 
      sizeof (VarName),
      L"User%04x", 
      Index
      );
    Index++;

    //
    // Get variable value.
    //
    VarSize = CurVarSize;
    Status  = gRT->GetVariable (VarName, &gUserIdentifyManagerGuid, &VarAttr, &VarSize, VarData);
    if (Status == EFI_BUFFER_TOO_SMALL) {
      FreePool (VarData);
      VarData = AllocatePool (VarSize);
      if (VarData == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
        break;
      }

      CurVarSize  = VarSize;
      Status      = gRT->GetVariable (VarName, &gUserIdentifyManagerGuid, &VarAttr, &VarSize, VarData);
    }

    if (EFI_ERROR (Status)) {
      if (Status == EFI_NOT_FOUND) {
        Status = EFI_SUCCESS;
      }
      break;
    }
    
    //
    // Check variable attributes.
    //
    if (VarAttr != (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)) {
      Status = gRT->SetVariable (VarName, &gUserIdentifyManagerGuid, VarAttr, 0, NULL);
      continue;
    }
    
    //
    // Add user profile to the user profile database.
    //
    Status = AddUserProfile (NULL, VarSize, VarData, FALSE);
    if (EFI_ERROR (Status)) {
      if (Status == EFI_SECURITY_VIOLATION) {
        //
        // Delete invalid user profile
        //
        gRT->SetVariable (VarName, &gUserIdentifyManagerGuid, VarAttr, 0, NULL);
      } else if (Status == EFI_OUT_OF_RESOURCES) {
        break;
      }
    } else {
      //
      // Delete and save the profile again if some invalid profiles are deleted.
      //
      if (mUserProfileDb->UserProfileNum < Index) {
        gRT->SetVariable (VarName, &gUserIdentifyManagerGuid, VarAttr, 0, NULL);
        SaveNvUserProfile (mUserProfileDb->UserProfile[mUserProfileDb->UserProfileNum - 1], FALSE);
      }
    }
  }

  if (VarData != NULL) {
    FreePool (VarData);
  }

  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // Check whether the user profile database is empty.
  //
  if (mUserProfileDb->UserProfileNum == 0) {
    Status = AddDefaultUserProfile ();
  }

  return Status;
}


/**
  This function collects all the credential providers and saves to mProviderDb.

  @retval EFI_SUCCESS            Collect credential providers successfully.
  @retval Others                 Fail to collect credential providers.

**/
EFI_STATUS
InitProviderInfo (
  VOID
  )
{
  EFI_STATUS  Status;
  UINTN       HandleCount;
  EFI_HANDLE  *HandleBuf;
  UINTN       Index; 

  if (mProviderDb != NULL) {
    //
    // The credential providers had been collected before.
    //
    return EFI_SUCCESS;
  }

  //
  // Try to find all the user credential provider driver.
  //
  HandleCount = 0;
  HandleBuf   = NULL;
  Status = gBS->LocateHandleBuffer (
                  ByProtocol,
                  &gEfiUserCredential2ProtocolGuid,
                  NULL,
                  &HandleCount,
                  &HandleBuf
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Get provider infomation.
  //
  mProviderDb = AllocateZeroPool (
                  sizeof (CREDENTIAL_PROVIDER_INFO) - 
                  sizeof (EFI_USER_CREDENTIAL2_PROTOCOL *) + 
                  HandleCount * sizeof (EFI_USER_CREDENTIAL2_PROTOCOL *)
                  );
  if (mProviderDb == NULL) {
    FreePool (HandleBuf);
    return EFI_OUT_OF_RESOURCES;
  }

 mProviderDb->Count = HandleCount;
  for (Index = 0; Index < HandleCount; Index++) {
    Status = gBS->HandleProtocol (
                    HandleBuf[Index],
                    &gEfiUserCredential2ProtocolGuid,
                    (VOID **) &mProviderDb->Provider[Index]
                    );
    if (EFI_ERROR (Status)) {
      FreePool (HandleBuf);
      FreePool (mProviderDb);
      mProviderDb = NULL;
      return Status;
    }
  }

  FreePool (HandleBuf);
  return EFI_SUCCESS;
}


/**
  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 <ConfigRequest> 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 <ConfigAltResp> 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
FakeExtractConfig (
  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 <ConfigResp> 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
FakeRouteConfig (
  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;
  }

  return EFI_NOT_FOUND;
}


/**
  This function initialize the data mainly used in form browser.

  @retval EFI_SUCCESS          Initialize form data successfully.
  @retval Others               Fail to Initialize form data.

**/
EFI_STATUS
InitFormBrowser (
  VOID
  )
{
  EFI_STATUS                  Status;
  USER_MANAGER_CALLBACK_INFO  *CallbackInfo;
  EFI_HII_DATABASE_PROTOCOL   *HiiDatabase;
  EFI_HII_STRING_PROTOCOL     *HiiString;
  EFI_FORM_BROWSER2_PROTOCOL  *FormBrowser2;

  //
  // Initialize driver private data.
  //
  CallbackInfo = AllocateZeroPool (sizeof (USER_MANAGER_CALLBACK_INFO));
  if (CallbackInfo == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  CallbackInfo->Signature                   = USER_MANAGER_SIGNATURE;
  CallbackInfo->ConfigAccess.ExtractConfig  = FakeExtractConfig;
  CallbackInfo->ConfigAccess.RouteConfig    = FakeRouteConfig;
  CallbackInfo->ConfigAccess.Callback       = UserIdentifyManagerCallback;

  //
  // Locate Hii Database protocol.
  //
  Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **) &HiiDatabase);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  CallbackInfo->HiiDatabase = HiiDatabase;

  //
  // Locate HiiString protocol.
  //
  Status = gBS->LocateProtocol (&gEfiHiiStringProtocolGuid, NULL, (VOID **) &HiiString);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  CallbackInfo->HiiString = HiiString;

  //
  // Locate Formbrowser2 protocol.
  //
  Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  CallbackInfo->FormBrowser2  = FormBrowser2;
  CallbackInfo->DriverHandle  = NULL;
  
  //
  // Install Device Path Protocol and Config Access protocol to driver handle.
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &CallbackInfo->DriverHandle,
                  &gEfiDevicePathProtocolGuid,
                  &mHiiVendorDevicePath,
                  &gEfiHiiConfigAccessProtocolGuid,
                  &CallbackInfo->ConfigAccess,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);

  //
  // Publish HII data.
  //
  CallbackInfo->HiiHandle = HiiAddPackages (
                              &gUserIdentifyManagerGuid,
                              CallbackInfo->DriverHandle,
                              UserIdentifyManagerStrings,
                              UserIdentifyManagerVfrBin,
                              NULL
                              );
  if (CallbackInfo->HiiHandle == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  mCallbackInfo = CallbackInfo;

  return EFI_SUCCESS;
}


/**
  Identify the user whose identification policy supports auto logon.

  @param[in]   ProviderIndex   The provider index in the provider list.
  @param[out]  User            Points to user user profile if a user is identified successfully.

  @retval EFI_SUCCESS          Identify a user with the specified provider successfully.
  @retval Others               Fail to identify a user.

**/
EFI_STATUS
IdentifyAutoLogonUser (
  IN  UINTN                                     ProviderIndex,
  OUT USER_PROFILE_ENTRY                        **User
  )
{
  EFI_STATUS    Status;
  EFI_USER_INFO *Info;
  UINT8         PolicyType;

  Info = AllocateZeroPool (sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_IDENTIFIER));
  if (Info == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Info->InfoType  = EFI_USER_INFO_IDENTIFIER_RECORD;
  Info->InfoSize  = sizeof (EFI_USER_INFO) + sizeof (EFI_USER_INFO_IDENTIFIER);

  //
  // Identify the specified credential provider's auto logon user.
  //
  Status = mProviderDb->Provider[ProviderIndex]->User (
                                                   mProviderDb->Provider[ProviderIndex],
                                                   NULL,
                                                   (EFI_USER_INFO_IDENTIFIER *) (Info + 1)
                                                   );
  if (EFI_ERROR (Status)) {
    FreePool (Info);
    return Status;
  }
  
  //
  // Find user with the specified user ID.
  //
  *User   = NULL;
  Status  = FindUserProfileByInfo (User, NULL, Info, Info->InfoSize);
  FreePool (Info);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = GetIdentifyType ((EFI_USER_PROFILE_HANDLE) * User, &PolicyType);
  if (PolicyType == EFI_USER_INFO_IDENTITY_AND) {
    //
    // The identified user need also identified by other credential provider.
    // This can handle through select user.
    //
    return EFI_NOT_READY;
  }
  
  return Status;
}


/**
  Check whether the given console is ready.

  @param[in]   ProtocolGuid   Points to the protocol guid of sonsole .
  
  @retval TRUE     The given console is ready.
  @retval FALSE    The given console is not ready.
  
**/
BOOLEAN
CheckConsole (
  EFI_GUID                     *ProtocolGuid  
  )
{
  EFI_STATUS                   Status;
  UINTN                        HandleCount;
  EFI_HANDLE                   *HandleBuf;
  UINTN                        Index; 
  EFI_DEVICE_PATH_PROTOCOL     *DevicePath;
  
  //
  // Try to find all the handle driver.
  //
  HandleCount = 0;
  HandleBuf   = NULL;
  Status = gBS->LocateHandleBuffer (
                  ByProtocol,
                  ProtocolGuid,
                  NULL,
                  &HandleCount,
                  &HandleBuf
                  );
  if (EFI_ERROR (Status)) {
    return FALSE;
  }

  for (Index = 0; Index < HandleCount; Index++) {
    DevicePath = DevicePathFromHandle (HandleBuf[Index]);
    if (DevicePath != NULL) {
      FreePool (HandleBuf);
      return TRUE;
    }
  }
  FreePool (HandleBuf);  
  return FALSE;
}


/**
  Check whether the console is ready.

  @retval TRUE     The console is ready.
  @retval FALSE    The console is not ready.
  
**/
BOOLEAN
IsConsoleReady (
  VOID
  )
{
  if (!CheckConsole (&gEfiSimpleTextOutProtocolGuid)) {
    return FALSE;
  }

  if (!CheckConsole (&gEfiSimpleTextInProtocolGuid)) {
    if (!CheckConsole (&gEfiSimpleTextInputExProtocolGuid)) {
    return FALSE;
    }
  }
  
  return TRUE;
}


/**
  Identify a user to logon.

  @param[out]  User          Points to user user profile if a user is identified successfully.

  @retval EFI_SUCCESS        Identify a user successfully.

**/
EFI_STATUS
IdentifyUser (
  OUT USER_PROFILE_ENTRY                        **User
  )
{
  EFI_STATUS                    Status;
  UINTN                         Index;
  EFI_CREDENTIAL_LOGON_FLAGS    AutoLogon;
  EFI_USER_INFO                 *IdentifyInfo;
  EFI_USER_INFO_IDENTITY_POLICY *Identity;
  EFI_USER_CREDENTIAL2_PROTOCOL *UserCredential;
  USER_PROFILE_ENTRY            *UserEntry;

  //
  // Initialize credential providers.
  //
  InitProviderInfo ();

  //
  // Initialize user profile database.
  //
  InitUserProfileDb ();

  //
  // If only one user in system, and its identify policy is TRUE, then auto logon.
  //
  if (mUserProfileDb->UserProfileNum == 1) {
    UserEntry     = (USER_PROFILE_ENTRY *) mUserProfileDb->UserProfile[0];
    IdentifyInfo  = NULL;
    Status        = FindUserInfoByType (UserEntry, &IdentifyInfo, EFI_USER_INFO_IDENTITY_POLICY_RECORD);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    ASSERT (IdentifyInfo != NULL);

    Identity = (EFI_USER_INFO_IDENTITY_POLICY *) ((UINT8 *) (IdentifyInfo + 1));
    if (Identity->Type == EFI_USER_INFO_IDENTITY_TRUE) {
      mCurrentUser = (EFI_USER_PROFILE_HANDLE) UserEntry;
      UpdateUserInfo (UserEntry);
      *User = UserEntry;
      return EFI_SUCCESS;
    }
  }
  
  //
  // Find and login the default & AutoLogon user.
  //
  for (Index = 0; Index < mProviderDb->Count; Index++) {
    UserCredential = mProviderDb->Provider[Index];
    Status = UserCredential->Default (UserCredential, &AutoLogon);
    if (EFI_ERROR (Status)) {
      continue;
    }

    if ((AutoLogon & (EFI_CREDENTIAL_LOGON_FLAG_DEFAULT | EFI_CREDENTIAL_LOGON_FLAG_AUTO)) != 0) {
      Status = IdentifyAutoLogonUser (Index, &UserEntry);
      if (Status == EFI_SUCCESS) {
        mCurrentUser = (EFI_USER_PROFILE_HANDLE) UserEntry;
        UpdateUserInfo (UserEntry);
        *User = UserEntry;
        return EFI_SUCCESS;
      }
    }
  }
  
  if (!IsConsoleReady ()) {
    //
    // The console is still not ready for user selection.
    //
    return EFI_ACCESS_DENIED;
  }

  //
  // Select a user and identify it.
  //
  mCallbackInfo->FormBrowser2->SendForm (
                                 mCallbackInfo->FormBrowser2,
                                 &mCallbackInfo->HiiHandle,
                                 1,
                                 &gUserIdentifyManagerGuid,
                                 0,
                                 NULL,
                                 NULL
                                 );
  
  if (mIdentified) {
    *User = (USER_PROFILE_ENTRY *) mCurrentUser;
    UpdateUserInfo (*User);
    return EFI_SUCCESS;
  }
  
  return EFI_ACCESS_DENIED;
}


/**
  An empty function to pass error checking of CreateEventEx ().
 
  @param  Event         Event whose notification function is being invoked.
  @param  Context       Pointer to the notification function's context,
                        which is implementation-dependent.

**/
VOID
EFIAPI
InternalEmptyFuntion (
  IN EFI_EVENT                Event,
  IN VOID                     *Context
  )
{
}


/**
  Create, Signal, and Close the User Profile Changed event.

**/
VOID
SignalEventUserProfileChanged (
  VOID
  )
{
  EFI_STATUS    Status;
  EFI_EVENT     Event;

  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  InternalEmptyFuntion,
                  NULL,
                  &gEfiEventUserProfileChangedGuid,
                  &Event
                  );
  ASSERT_EFI_ERROR (Status);
  gBS->SignalEvent (Event);
  gBS->CloseEvent (Event);
}


/**
  Create a new user profile.

  This function creates a new user profile with only a new user identifier attached and returns 
  its handle. The user profile is non-volatile, but the handle User can change across reboots.

  @param[in]  This               Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[out] User               On return, points to the new user profile handle. 
                                 The user profile handle is unique only during this boot.
 
  @retval EFI_SUCCESS            User profile was successfully created.
  @retval EFI_ACCESS_DENIED      Current user does not have sufficient permissions to create a 
                                 user profile.
  @retval EFI_UNSUPPORTED        Creation of new user profiles is not supported.
  @retval EFI_INVALID_PARAMETER  The User parameter is NULL.
  
**/
EFI_STATUS
EFIAPI
UserProfileCreate (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  OUT       EFI_USER_PROFILE_HANDLE             *User
  )
{
  EFI_STATUS  Status;

  if ((This == NULL) || (User == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check the right of the current user.
  //
  if (!CheckCurrentUserAccessRight (EFI_USER_INFO_ACCESS_MANAGE)) {
    if (!CheckCurrentUserAccessRight (EFI_USER_INFO_ACCESS_ENROLL_OTHERS)) {
      return EFI_ACCESS_DENIED;
    }
  }
  
  //
  // Create new user profile
  //
  Status = CreateUserProfile ((USER_PROFILE_ENTRY **) User);
  if (EFI_ERROR (Status)) {
    return EFI_ACCESS_DENIED;
  }
  return EFI_SUCCESS;
}


/**
  Delete an existing user profile.

  @param[in] This                Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[in] User                User profile handle. 

  @retval EFI_SUCCESS            User profile was successfully deleted.
  @retval EFI_ACCESS_DENIED      Current user does not have sufficient permissions to delete a user
                                 profile or there is only one user profile.
  @retval EFI_UNSUPPORTED        Deletion of new user profiles is not supported.
  @retval EFI_INVALID_PARAMETER  User does not refer to a valid user profile.
  
**/
EFI_STATUS
EFIAPI
UserProfileDelete (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  IN        EFI_USER_PROFILE_HANDLE             User
  )
{
  EFI_STATUS  Status;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check the right of the current user.
  //
  if (!CheckCurrentUserAccessRight (EFI_USER_INFO_ACCESS_MANAGE)) {
    return EFI_ACCESS_DENIED;
  }
  
  //
  // Delete user profile.
  //
  Status = DelUserProfile (User);
  if (EFI_ERROR (Status)) {
    if (Status != EFI_INVALID_PARAMETER) {
      return EFI_ACCESS_DENIED;
    }
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}


/**
  Enumerate all of the enrolled users on the platform.

  This function returns the next enrolled user profile. To retrieve the first user profile handle,  
  point User at a NULL. Each subsequent call will retrieve another user profile handle until there 
  are no more, at which point User will point to NULL. 

  @param[in]      This           Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[in, out] User           On entry, points to the previous user profile handle or NULL to 
                                 start enumeration. On exit, points to the next user profile handle
                                 or NULL if there are no more user profiles.

  @retval EFI_SUCCESS            Next enrolled user profile successfully returned. 
  @retval EFI_ACCESS_DENIED      Next enrolled user profile was not successfully returned.
  @retval EFI_INVALID_PARAMETER  The User parameter is NULL.
**/
EFI_STATUS
EFIAPI
UserProfileGetNext (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  IN OUT    EFI_USER_PROFILE_HANDLE             *User
  )
{
  EFI_STATUS  Status;

  if ((This == NULL) || (User == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  
  Status = FindUserProfile ((USER_PROFILE_ENTRY **) User, TRUE, NULL);
  if (EFI_ERROR (Status)) {
    return EFI_ACCESS_DENIED;
  }
  return EFI_SUCCESS;
}


/**
  Return the current user profile handle.

  @param[in]  This               Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[out] CurrentUser        On return, points to the current user profile handle.

  @retval EFI_SUCCESS            Current user profile handle returned successfully. 
  @retval EFI_INVALID_PARAMETER  The CurrentUser parameter is NULL.
  
**/
EFI_STATUS
EFIAPI
UserProfileCurrent (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  OUT       EFI_USER_PROFILE_HANDLE             *CurrentUser
  )
{  
  //
  // Get current user profile.
  //
  if ((This == NULL) || (CurrentUser == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  *CurrentUser = mCurrentUser;
  return EFI_SUCCESS;
}


/**
  Identify a user.

  Identify the user and, if authenticated, returns the user handle and changes the current
  user profile. All user information marked as private in a previously selected profile
  is no longer available for inspection. 
  Whenever the current user profile is changed then the an event with the GUID 
  EFI_EVENT_GROUP_USER_PROFILE_CHANGED is signaled.

  @param[in]  This               Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[out] User               On return, points to the user profile handle for the current
                                 user profile.

  @retval EFI_SUCCESS            User was successfully identified.
  @retval EFI_ACCESS_DENIED      User was not successfully identified.
  @retval EFI_INVALID_PARAMETER  The User parameter is NULL.
  
**/
EFI_STATUS
EFIAPI
UserProfileIdentify (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  OUT       EFI_USER_PROFILE_HANDLE             *User
  )
{
  EFI_STATUS  Status;

  if ((This == NULL) || (User == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (mCurrentUser != NULL) {
    *User = mCurrentUser;
    return EFI_SUCCESS;
  }
  
  //
  // Identify user
  //
  Status = IdentifyUser ((USER_PROFILE_ENTRY **) User);
  if (EFI_ERROR (Status)) {
    return EFI_ACCESS_DENIED;
  }
  
  //
  // Publish the user info into the EFI system configuration table.
  //
  PublishUserTable ();

  //
  // Signal User Profile Changed event.
  //
  SignalEventUserProfileChanged ();
  return EFI_SUCCESS;
}

/**
  Find a user using a user information record.

  This function searches all user profiles for the specified user information record.
  The search starts with the user information record handle following UserInfo and 
  continues until either the information is found or there are no more user profiles.
  A match occurs when the Info.InfoType field matches the user information record
  type and the user information record data matches the portion of Info.

  @param[in]      This           Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[in, out] User           On entry, points to the previously returned user profile 
                                 handle, or NULL to start searching with the first user profile.
                                 On return, points to the user profile handle, or NULL if not
                                 found.
  @param[in, out] UserInfo       On entry, points to the previously returned user information
                                 handle, or NULL to start searching with the first. On return, 
                                 points to the user information handle of the user information
                                 record, or NULL if not found. Can be NULL, in which case only 
                                 one user information record per user can be returned. 
  @param[in]      Info           Points to the buffer containing the user information to be 
                                 compared to the user information record. If the user information 
                                 record data is empty, then only the user information record type 
                                 is compared. If InfoSize is 0, then the user information record 
                                 must be empty.

  @param[in]      InfoSize       The size of Info, in bytes. 

  @retval EFI_SUCCESS            User information was found. User points to the user profile
                                 handle, and UserInfo points to the user information handle.
  @retval EFI_NOT_FOUND          User information was not found. User points to NULL, and  
                                 UserInfo points to NULL.
  @retval EFI_INVALID_PARAMETER  User is NULL. Or Info is NULL.                           
  
**/
EFI_STATUS
EFIAPI
UserProfileFind (
  IN     CONST EFI_USER_MANAGER_PROTOCOL        *This,
  IN OUT EFI_USER_PROFILE_HANDLE                *User,
  IN OUT EFI_USER_INFO_HANDLE                   *UserInfo OPTIONAL,
  IN     CONST EFI_USER_INFO                    *Info,
  IN     UINTN                                  InfoSize
  )
{
  EFI_STATUS  Status;
  UINTN       Size;

  if ((This == NULL) || (User == NULL) || (Info == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (InfoSize == 0) {
    //
    // If InfoSize is 0, then the user information record must be empty.
    //
    if (Info->InfoSize != sizeof (EFI_USER_INFO)) {
      return EFI_INVALID_PARAMETER;
    }
  } else {
    if (InfoSize != Info->InfoSize) {
      return EFI_INVALID_PARAMETER;
    }
  }
  Size = Info->InfoSize;  
  
  //
  // Find user profile accdoring to user information.
  //
  Status = FindUserProfileByInfo (
            (USER_PROFILE_ENTRY **) User,
            (EFI_USER_INFO **) UserInfo,
            (EFI_USER_INFO *) Info,
            Size
            );
  if (EFI_ERROR (Status)) {
    *User = NULL;
    if (UserInfo != NULL) {
      *UserInfo = NULL;
    }
    return EFI_NOT_FOUND;
  }
  
  return EFI_SUCCESS;
}


/**
  Return information attached to the user.

  This function returns user information. The format of the information is described in User 
  Information. The function may return EFI_ACCESS_DENIED if the information is marked private 
  and the handle specified by User is not the current user profile. The function may return 
  EFI_ACCESS_DENIED if the information is marked protected and the information is associated 
  with a credential provider for which the user has not been authenticated.

  @param[in]      This           Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[in]      User           Handle of the user whose profile will be retrieved. 
  @param[in]      UserInfo       Handle of the user information data record.   
  @param[out]     Info           On entry, points to a buffer of at least *InfoSize bytes. On exit,  
                                 holds the user information. If the buffer is too small to hold the  
                                 information, then EFI_BUFFER_TOO_SMALL is returned and InfoSize is 
                                 updated to contain the number of bytes actually required. 
  @param[in, out] InfoSize       On entry, points to the size of Info. On return, points to the size  
                                 of the user information. 

  @retval EFI_SUCCESS            Information returned successfully.
  @retval EFI_ACCESS_DENIED      The information about the specified user cannot be accessed by the 
                                 current user.
  @retval EFI_BUFFER_TOO_SMALL   The number of bytes specified by *InfoSize is too small to hold the 
                                 returned data. The actual size required is returned in *InfoSize.
  @retval EFI_NOT_FOUND          User does not refer to a valid user profile or UserInfo does not refer 
                                 to a valid user info handle.
  @retval EFI_INVALID_PARAMETER  Info is NULL or InfoSize is NULL.
  
**/
EFI_STATUS
EFIAPI
UserProfileGetInfo (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  IN        EFI_USER_PROFILE_HANDLE             User,
  IN        EFI_USER_INFO_HANDLE                UserInfo,
  OUT       EFI_USER_INFO                       *Info,
  IN OUT    UINTN                               *InfoSize
  )
{
  EFI_STATUS  Status;

  if ((This == NULL) || (InfoSize == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((*InfoSize != 0) && (Info == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  
  if ((User == NULL) || (UserInfo == NULL)) {
    return EFI_NOT_FOUND;
  }
  
  Status = GetUserInfo (User, UserInfo, Info, InfoSize, TRUE);
  if (EFI_ERROR (Status)) {
    if (Status == EFI_BUFFER_TOO_SMALL) {
      return EFI_BUFFER_TOO_SMALL;
    }
    return EFI_ACCESS_DENIED;
  }
  return EFI_SUCCESS;
}


/**
  Add or update user information.

  This function changes user information.  If NULL is pointed to by UserInfo, then a new user 
  information record is created and its handle is returned in UserInfo. Otherwise, the existing  
  one is replaced.
  If EFI_USER_INFO_IDENITTY_POLICY_RECORD is changed, it is the caller's responsibility to keep 
  it to be synced with the information on credential providers.
  If EFI_USER_INFO_EXCLUSIVE is specified in Info and a user information record of the same 
  type already exists in the user profile, then EFI_ACCESS_DENIED will be returned and UserInfo
  will point to the handle of the existing record.

  @param[in]      This            Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[in]      User            Handle of the user whose profile will be retrieved. 
  @param[in, out] UserInfo        Handle of the user information data record.   
  @param[in]      Info            On entry, points to a buffer of at least *InfoSize bytes. On exit, 
                                  holds the user information. If the buffer is too small to hold the 
                                  information, then EFI_BUFFER_TOO_SMALL is returned and InfoSize is 
                                  updated to contain the number of bytes actually required. 
  @param[in]      InfoSize        On entry, points to the size of Info. On return, points to the size 
                                  of the user information. 

  @retval EFI_SUCCESS             Information returned successfully.
  @retval EFI_ACCESS_DENIED       The record is exclusive.
  @retval EFI_SECURITY_VIOLATION  The current user does not have permission to change the specified 
                                  user profile or user information record.
  @retval EFI_NOT_FOUND           User does not refer to a valid user profile or UserInfo does not  
                                  refer to a valid user info handle.
  @retval EFI_INVALID_PARAMETER   UserInfo is NULL or Info is NULL. 
**/
EFI_STATUS
EFIAPI
UserProfileSetInfo (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  IN        EFI_USER_PROFILE_HANDLE             User,
  IN OUT    EFI_USER_INFO_HANDLE                *UserInfo,
  IN CONST  EFI_USER_INFO                       *Info,
  IN        UINTN                               InfoSize
  )
{
  EFI_STATUS  Status;

  if ((This == NULL) || (User == NULL) || (UserInfo == NULL) || (Info == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check the right of the current user.
  //
  if (User != mCurrentUser) {
    if (!CheckCurrentUserAccessRight (EFI_USER_INFO_ACCESS_MANAGE)) {
      if (*UserInfo != NULL) {
        //
        // Can't update info in other profiles without MANAGE right.
        //
        return EFI_SECURITY_VIOLATION;
      }
      
      if (!CheckCurrentUserAccessRight (EFI_USER_INFO_ACCESS_ENROLL_OTHERS)) {
        //
        // Can't add info into other profiles.
        //
        return EFI_SECURITY_VIOLATION;
      }
    }
  }

  if (User == mCurrentUser) {
    if (CheckCurrentUserAccessRight (EFI_USER_INFO_ACCESS_ENROLL_SELF)) {
      //
      // Only identify policy can be added/updated.
      //
      if (Info->InfoType != EFI_USER_INFO_IDENTITY_POLICY_RECORD) {
        return EFI_SECURITY_VIOLATION;
      }
    }
  }
  
  //
  // Modify user information.
  //
  Status = ModifyUserInfo (User, (EFI_USER_INFO **) UserInfo, Info, InfoSize);
  if (EFI_ERROR (Status)) {
    if (Status == EFI_ACCESS_DENIED) {
      return EFI_ACCESS_DENIED;      
    }
    return EFI_SECURITY_VIOLATION;
  }
  return EFI_SUCCESS;
}


/**
  Called by credential provider to notify of information change.

  This function allows the credential provider to notify the User Identity Manager when user status  
  has changed.
  If the User Identity Manager doesn't support asynchronous changes in credentials, then this function 
  should return EFI_UNSUPPORTED. 
  If current user does not exist, and the credential provider can identify a user, then make the user 
  to be current user and signal the EFI_EVENT_GROUP_USER_PROFILE_CHANGED event.
  If current user already exists, and the credential provider can identify another user, then switch 
  current user to the newly identified user, and signal the EFI_EVENT_GROUP_USER_PROFILE_CHANGED event.
  If current user was identified by this credential provider and now the credential provider cannot identify 
  current user, then logout current user and signal the EFI_EVENT_GROUP_USER_PROFILE_CHANGED event.

  @param[in] This          Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[in] Changed       Handle on which is installed an instance of the EFI_USER_CREDENTIAL2_PROTOCOL 
                           where the user has changed.

  @retval EFI_SUCCESS      The User Identity Manager has handled the notification.
  @retval EFI_NOT_READY    The function was called while the specified credential provider was not selected.
  @retval EFI_UNSUPPORTED  The User Identity Manager doesn't support asynchronous notifications.
  
**/
EFI_STATUS
EFIAPI
UserProfileNotify (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  IN        EFI_HANDLE                          Changed
  )
{    
  return EFI_UNSUPPORTED;
}


/**
  Delete user information.

  Delete the user information attached to the user profile specified by the UserInfo.

  @param[in] This            Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[in] User            Handle of the user whose information will be deleted.
  @param[in] UserInfo        Handle of the user information to remove.

  @retval EFI_SUCCESS        User information deleted successfully.
  @retval EFI_NOT_FOUND      User information record UserInfo does not exist in the user profile.
  @retval EFI_ACCESS_DENIED  The current user does not have permission to delete this user information. 
  
**/
EFI_STATUS
EFIAPI
UserProfileDeleteInfo (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  IN        EFI_USER_PROFILE_HANDLE             User,
  IN        EFI_USER_INFO_HANDLE                UserInfo
  )
{
  EFI_STATUS  Status;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Check the right of the current user.
  //
  if (User != mCurrentUser) {
    if (!CheckCurrentUserAccessRight (EFI_USER_INFO_ACCESS_MANAGE)) {
      return EFI_ACCESS_DENIED;
    }
  }
  
  //
  // Delete user information.
  //
  Status = DelUserInfo (User, UserInfo, TRUE);
  if (EFI_ERROR (Status)) {
    if (Status == EFI_NOT_FOUND) {
      return EFI_NOT_FOUND;
    }
    return EFI_ACCESS_DENIED;
  } 
  return EFI_SUCCESS;
}


/**
  Enumerate user information of all the enrolled users on the platform.

  This function returns the next user information record. To retrieve the first user   
  information record handle, point UserInfo at a NULL. Each subsequent call will retrieve 
  another user information record handle until there are no more, at which point UserInfo 
  will point to NULL. 

  @param[in]      This           Points to this instance of the EFI_USER_MANAGER_PROTOCOL.
  @param[in]      User           Handle of the user whose information will be deleted.
  @param[in, out] UserInfo       Handle of the user information to remove.

  @retval EFI_SUCCESS            User information returned.
  @retval EFI_NOT_FOUND          No more user information found.
  @retval EFI_INVALID_PARAMETER  UserInfo is NULL.
  
**/
EFI_STATUS
EFIAPI
UserProfileGetNextInfo (
  IN CONST  EFI_USER_MANAGER_PROTOCOL           *This,
  IN        EFI_USER_PROFILE_HANDLE             User,
  IN OUT    EFI_USER_INFO_HANDLE                *UserInfo
  )
{
  if ((This == NULL) || (UserInfo == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Get next user information entry.
  //
  return FindUserInfo (User, (EFI_USER_INFO **) UserInfo, TRUE, NULL);
}


/**
  Main entry for this driver.

  @param[in] ImageHandle     Image handle this driver.
  @param[in] SystemTable     Pointer to SystemTable.

  @retval EFI_SUCESS         This function always complete successfully.

**/
EFI_STATUS
EFIAPI
UserIdentifyManagerInit (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

  EFI_STATUS  Status;

  //
  // It is NOT robust enough to be included in production.
  //
  #error "This implementation is just a sample, please comment this line if you really want to use this driver."

  //
  // Initiate form browser.
  //
  InitFormBrowser ();

  //
  // Install protocol interfaces for the User Identity Manager.
  //
  Status = gBS->InstallProtocolInterface (
                  &mCallbackInfo->DriverHandle,
                  &gEfiUserManagerProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &gUserIdentifyManager
                  );
  ASSERT_EFI_ERROR (Status);  

  LoadDeferredImageInit (ImageHandle);
  return EFI_SUCCESS;
}


