/** Get variable header that has consecutive content. @param StoreInfo Pointer to variable store info structure. @param Variable Pointer to the Variable Header. @param VariableHeader Pointer to Pointer to the Variable Header that has consecutive content. @retval TRUE Variable header is valid. @retval FALSE Variable header is not valid. **/ BOOLEAN GetVariableHeader ( IN VARIABLE_STORE_INFO *StoreInfo, IN VARIABLE_HEADER *Variable, OUT VARIABLE_HEADER **VariableHeader ) { EFI_PHYSICAL_ADDRESS TargetAddress; EFI_PHYSICAL_ADDRESS SpareAddress; EFI_HOB_GUID_TYPE *GuidHob; UINTN PartialHeaderSize; // // First assume variable header pointed by Variable is consecutive. // *VariableHeader = Variable; if ((Variable != NULL) && (StoreInfo->FtwLastWriteData != NULL)) { TargetAddress = StoreInfo->FtwLastWriteData->TargetAddress; SpareAddress = StoreInfo->FtwLastWriteData->SpareAddress; if (((UINTN) Variable < (UINTN) TargetAddress) && (((UINTN) Variable + sizeof (VARIABLE_HEADER)) > (UINTN) TargetAddress)) { // // Variable header pointed by Variable is inconsecutive, // create a guid hob to combine the two partial variable header content together. // GuidHob = GetFirstGuidHob (&gEfiCallerIdGuid); if (GuidHob != NULL) { *VariableHeader = (VARIABLE_HEADER *) GET_GUID_HOB_DATA (GuidHob); } else { *VariableHeader = (VARIABLE_HEADER *) BuildGuidHob (&gEfiCallerIdGuid, sizeof (VARIABLE_HEADER)); PartialHeaderSize = (UINTN) TargetAddress - (UINTN) Variable; // // Partial content is in NV storage. // CopyMem ((UINT8 *) *VariableHeader, (UINT8 *) Variable, PartialHeaderSize); // // Another partial content is in spare block. // CopyMem ((UINT8 *) *VariableHeader + PartialHeaderSize, (UINT8 *) (UINTN) SpareAddress, sizeof (VARIABLE_HEADER) - PartialHeaderSize); } } } return IsValidVariableHeader (*VariableHeader); }
/** This code gets the pointer to the next variable header. @param Variable Pointer to the Variable Header. @return A VARIABLE_HEADER* pointer to next variable header. **/ VARIABLE_HEADER * GetNextVariablePtr ( IN VARIABLE_HEADER *Variable ) { UINTN Value; if (!IsValidVariableHeader (Variable)) { return NULL; } Value = (UINTN) GetVariableDataPtr (Variable); Value += DataSizeOfVariable (Variable); Value += GET_PAD_SIZE (DataSizeOfVariable (Variable)); // // Be careful about pad size for alignment // return (VARIABLE_HEADER *) HEADER_ALIGN (Value); }
/** Process variable with key exchange key for verification. @param[in] VariableName The name of Variable to be found. @param[in] VendorGuid The variable vendor GUID. @param[in] Data The data pointer. @param[in] DataSize The size of Data found. If size is less than the data, this value contains the required size. @param[in] VirtualMode The current calling mode for this function. @param[in] Global The context of this Extended SAL Variable Services Class call. @param[in] Variable The variable information which is used to keep track of variable usage. @param[in] Attributes The attribute value of the variable. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_SECURITY_VIOLATION The variable did NOT pass the validation check carried out by the firmware. @retval EFI_SUCCESS The variable passed validation successfully. **/ EFI_STATUS ProcessVarWithKek ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN VOID *Data, IN UINTN DataSize, IN BOOLEAN VirtualMode, IN ESAL_VARIABLE_GLOBAL *Global, IN VARIABLE_POINTER_TRACK *Variable, IN UINT32 Attributes OPTIONAL ) { EFI_STATUS Status; VARIABLE_POINTER_TRACK KekVariable; EFI_SIGNATURE_LIST *KekList; EFI_SIGNATURE_DATA *KekItem; UINT32 KekCount; EFI_VARIABLE_AUTHENTICATION *CertData; EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock; BOOLEAN IsFound; UINT32 Index; VARIABLE_HEADER VariableHeader; BOOLEAN Valid; KekList = NULL; ZeroMem (&VariableHeader, sizeof (VARIABLE_HEADER)); if (mPlatformMode == USER_MODE) { if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) { // // In user mode, should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute. // return EFI_INVALID_PARAMETER; } CertData = (EFI_VARIABLE_AUTHENTICATION *) Data; CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData); if (Variable->CurrPtr != 0x0) { Valid = IsValidVariableHeader ( Variable->CurrPtr, Variable->Volatile, &Global->VariableGlobal[VirtualMode], Global->FvbInstance, &VariableHeader ); ASSERT (Valid); if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) { // // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION. // return EFI_SECURITY_VIOLATION; } } // // Get KEK database from variable. // Status = FindVariable ( Global->VariableName[VirtualMode][VAR_KEY_EXCHANGE_KEY], Global->GlobalVariableGuid[VirtualMode], &KekVariable, &Global->VariableGlobal[VirtualMode], Global->FvbInstance ); ASSERT_EFI_ERROR (Status); ZeroMem (Global->KeyList, MAX_KEYDB_SIZE); GetVariableDataPtr ( KekVariable.CurrPtr, KekVariable.Volatile, &Global->VariableGlobal[VirtualMode], Global->FvbInstance, (CHAR16 *) Global->KeyList ); // // Enumerate all Kek items in this list to verify the variable certificate data. // If anyone is authenticated successfully, it means the variable is correct! // KekList = (EFI_SIGNATURE_LIST *) Global->KeyList; IsFound = FALSE; KekCount = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize; KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize); for (Index = 0; Index < KekCount; Index++) { if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) { IsFound = TRUE; break; } KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize); } if (!IsFound) { return EFI_SECURITY_VIOLATION; } Status = VerifyDataPayload (VirtualMode, Global, Data, DataSize, CertBlock->PublicKey); if (!EFI_ERROR (Status)) { Status = UpdateVariable ( VariableName, VendorGuid, (UINT8*)Data + AUTHINFO_SIZE, DataSize - AUTHINFO_SIZE, Attributes, 0, CertData->MonotonicCount, VirtualMode, Global, Variable ); } } else { // // If in setup mode, no authentication needed. // Status = UpdateVariable ( VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, VirtualMode, Global, Variable ); } return Status; }
/** Process variable with platform key for verification. @param[in] VariableName The name of Variable to be found. @param[in] VendorGuid The variable vendor GUID. @param[in] Data The data pointer. @param[in] DataSize The size of Data found. If size is less than the data, this value contains the required size. @param[in] VirtualMode The current calling mode for this function. @param[in] Global The context of this Extended SAL Variable Services Class call. @param[in] Variable The variable information which is used to keep track of variable usage. @param[in] Attributes The attribute value of the variable. @param[in] IsPk Indicates whether to process pk. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation check carried out by the firmware. @retval EFI_SUCCESS The variable passed validation successfully. **/ EFI_STATUS ProcessVarWithPk ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN VOID *Data, IN UINTN DataSize, IN BOOLEAN VirtualMode, IN ESAL_VARIABLE_GLOBAL *Global, IN VARIABLE_POINTER_TRACK *Variable, IN UINT32 Attributes OPTIONAL, IN BOOLEAN IsPk ) { EFI_STATUS Status; VARIABLE_POINTER_TRACK PkVariable; EFI_SIGNATURE_LIST *OldPkList; EFI_SIGNATURE_DATA *OldPkData; EFI_VARIABLE_AUTHENTICATION *CertData; VARIABLE_HEADER VariableHeader; BOOLEAN Valid; OldPkList = NULL; ZeroMem (&VariableHeader, sizeof (VARIABLE_HEADER)); if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { // // PK and KEK should set EFI_VARIABLE_NON_VOLATILE attribute. // return EFI_INVALID_PARAMETER; } if (mPlatformMode == USER_MODE) { if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) { // // In user mode, PK and KEK should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute. // return EFI_INVALID_PARAMETER; } CertData = (EFI_VARIABLE_AUTHENTICATION *) Data; if (Variable->CurrPtr != 0x0) { Valid = IsValidVariableHeader ( Variable->CurrPtr, Variable->Volatile, &Global->VariableGlobal[VirtualMode], Global->FvbInstance, &VariableHeader ); ASSERT (Valid); if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) { // // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION. // return EFI_SECURITY_VIOLATION; } } // // Get platform key from variable. // Status = FindVariable ( Global->VariableName[VirtualMode][VAR_PLATFORM_KEY], Global->GlobalVariableGuid[VirtualMode], &PkVariable, &Global->VariableGlobal[VirtualMode], Global->FvbInstance ); ASSERT_EFI_ERROR (Status); ZeroMem (Global->KeyList, MAX_KEYDB_SIZE); GetVariableDataPtr ( PkVariable.CurrPtr, PkVariable.Volatile, &Global->VariableGlobal[VirtualMode], Global->FvbInstance, (CHAR16 *) Global->KeyList ); OldPkList = (EFI_SIGNATURE_LIST *) Global->KeyList; OldPkData = (EFI_SIGNATURE_DATA *) ((UINT8 *) OldPkList + sizeof (EFI_SIGNATURE_LIST) + OldPkList->SignatureHeaderSize); Status = VerifyDataPayload (VirtualMode, Global, Data, DataSize, OldPkData->SignatureData); if (!EFI_ERROR (Status)) { Status = UpdateVariable ( VariableName, VendorGuid, (UINT8*)Data + AUTHINFO_SIZE, DataSize - AUTHINFO_SIZE, Attributes, 0, CertData->MonotonicCount, VirtualMode, Global, Variable ); if (!EFI_ERROR (Status)) { // // If delete PK in user mode, need change to setup mode. // if ((DataSize == AUTHINFO_SIZE) && IsPk) { UpdatePlatformMode (VirtualMode, Global, SETUP_MODE); } } } } else { Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, VirtualMode, Global, Variable); // // If enroll PK in setup mode, need change to user mode. // if ((DataSize != 0) && IsPk) { UpdatePlatformMode (VirtualMode, Global, USER_MODE); } } return Status; }
/** Initializes for authenticated varibale service. @retval EFI_SUCCESS The function successfully executed. @retval EFI_OUT_OF_RESOURCES Failed to allocate enough memory resources. **/ EFI_STATUS AutenticatedVariableServiceInitialize ( VOID ) { EFI_STATUS Status; VARIABLE_POINTER_TRACK Variable; UINT8 VarValue; UINT32 VarAttr; UINTN DataSize; UINTN CtxSize; VARIABLE_HEADER VariableHeader; BOOLEAN Valid; ZeroMem (&VariableHeader, sizeof (VARIABLE_HEADER)); mVariableModuleGlobal->AuthenticatedVariableGuid[Physical] = &gEfiAuthenticatedVariableGuid; mVariableModuleGlobal->CertRsa2048Sha256Guid[Physical] = &gEfiCertRsa2048Sha256Guid; mVariableModuleGlobal->ImageSecurityDatabaseGuid[Physical] = &gEfiImageSecurityDatabaseGuid; // // Initialize hash context. // CtxSize = Sha256GetContextSize (); mVariableModuleGlobal->HashContext[Physical] = AllocateRuntimePool (CtxSize); ASSERT (mVariableModuleGlobal->HashContext[Physical] != NULL); // // Check "AuthVarKeyDatabase" variable's existence. // If it doesn't exist, create a new one with initial value of 0 and EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. // Status = FindVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_AUTH_KEY_DB], &gEfiAuthenticatedVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance ); if (Variable.CurrPtr == 0x0) { VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; VarValue = 0; mPubKeyNumber = 0; Status = UpdateVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_AUTH_KEY_DB], &gEfiAuthenticatedVariableGuid, &VarValue, sizeof(UINT8), VarAttr, 0, 0, FALSE, mVariableModuleGlobal, &Variable ); if (EFI_ERROR (Status)) { return Status; } } else { // // Load database in global variable for cache. // Valid = IsValidVariableHeader ( Variable.CurrPtr, Variable.Volatile, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance, &VariableHeader ); ASSERT (Valid); DataSize = DataSizeOfVariable (&VariableHeader); ASSERT (DataSize <= MAX_KEYDB_SIZE); GetVariableDataPtr ( Variable.CurrPtr, Variable.Volatile, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance, (CHAR16 *) mVariableModuleGlobal->PubKeyStore ); mPubKeyNumber = (UINT32) (DataSize / EFI_CERT_TYPE_RSA2048_SIZE); } // // Check "SetupMode" variable's existence. // If it doesn't exist, check PK database's existence to determine the value. // Then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. // Status = FindVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_SETUP_MODE], &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance ); if (Variable.CurrPtr == 0x0) { Status = FindVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_PLATFORM_KEY], &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance ); if (Variable.CurrPtr == 0x0) { mPlatformMode = SETUP_MODE; } else { mPlatformMode = USER_MODE; } VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; Status = UpdateVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_SETUP_MODE], &gEfiGlobalVariableGuid, &mPlatformMode, sizeof(UINT8), VarAttr, 0, 0, FALSE, mVariableModuleGlobal, &Variable ); if (EFI_ERROR (Status)) { return Status; } } else { GetVariableDataPtr ( Variable.CurrPtr, Variable.Volatile, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance, (CHAR16 *) &mPlatformMode ); } // // Check "SignatureSupport" variable's existence. // If it doesn't exist, then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. // Status = FindVariable ( EFI_SIGNATURE_SUPPORT_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance ); if (Variable.CurrPtr == 0x0) { VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; Status = UpdateVariable ( EFI_SIGNATURE_SUPPORT_NAME, &gEfiGlobalVariableGuid, mSignatureSupport, SIGSUPPORT_NUM * sizeof(EFI_GUID), VarAttr, 0, 0, FALSE, mVariableModuleGlobal, &Variable ); } return Status; }
int main(int argc, char *argv[]) { char *progname = argv[0], *buf, *vardata, *timestampstr = NULL; uint32_t attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; int flashfile, varfile, i, offset, varlen, varfilesize, listvars = 0; const int chunk = 8; wchar_t var[128]; struct stat st; EFI_GUID *owner = NULL, guid; EFI_TIME timestamp; while (argc > 1 && argv[1][0] == '-') { if (strcmp("--version", argv[1]) == 0) { version(progname); exit(0); } else if (strcmp("--help", argv[1]) == 0) { help(progname); exit(0); } else if (strcmp(argv[1], "-g") == 0) { if (str_to_guid(argv[2], &guid)) { fprintf(stderr, "Invalid GUID %s\n", argv[2]); exit(1); } owner = &guid; argv += 2; argc -= 2; } else if (strcmp("-t", argv[1]) == 0) { timestampstr = argv[2]; argv += 2; argc -= 2; } else if (strcmp("-l", argv[1]) == 0) { listvars = 1; argv += 1; argc -= 1; } else { /* unrecognised option */ break; } } if ((argc != 4 && !listvars) || (argc != 2 && listvars)) { usage(progname); exit(1); } /* copy to wchar16_t including trailing zero */ for (i = 0; i < strlen(argv[2]) + 1; i++) var[i] = argv[2][i]; varlen = i*2; /* size of storage including zero */ if (!owner) owner = get_owner_guid(argv[2]); if (!owner) { fprintf(stderr, "variable %s has no defined guid, one must be specified\n", argv[2]); exit(1); } memset(×tamp, 0, sizeof(timestamp)); time_t t; struct tm *tm, tms; memset(&tms, 0, sizeof(tms)); if (timestampstr) { strptime(timestampstr, "%Y-%m-%d %H:%M:%S", &tms); tm = &tms; } else { time(&t); tm = localtime(&t); } /* timestamp.Year is from 0 not 1900 as tm year is */ timestamp.Year = tm->tm_year + 1900; /* timestamp Month is 1-12 not 0-11 as tm_mon is */ timestamp.Month = tm->tm_mon + 1; timestamp.Day = tm->tm_mday; timestamp.Hour = tm->tm_hour; timestamp.Minute = tm->tm_min; timestamp.Second = tm->tm_sec; printf("Timestamp is %d-%d-%d %02d:%02d:%02d\n", timestamp.Year, timestamp.Month, timestamp.Day, timestamp.Hour, timestamp.Minute, timestamp.Second); flashfile = open(argv[1], O_RDWR); if (flashfile < 0) { fprintf(stderr, "Failed to read file %s:", argv[1]); perror(""); } varfile = open(argv[3], O_RDONLY); if (varfile < 0) { fprintf(stderr, "Failed to read file %s:", argv[1]); perror(""); } fstat(varfile, &st); varfilesize = st.st_size; vardata = malloc(varfilesize); if (read(varfile, vardata, varfilesize) != varfilesize) { perror("Failed to read variable file"); exit(1); } close(varfile); buf = malloc(sizeof(EFI_GUID)); for (i = 0; ; i += chunk) { lseek(flashfile, i, SEEK_SET); if (read(flashfile, buf, sizeof(EFI_GUID)) != sizeof(EFI_GUID)) goto eof; if (memcmp(buf, &SECURE_VARIABLE_GUID, sizeof(EFI_GUID)) == 0) break; } offset = i; printf("Variable header found at offset 0x%x\n", offset); lseek(flashfile, offset, SEEK_SET); free(buf); buf = malloc(sizeof(VARIABLE_STORE_HEADER)); read(flashfile, buf, sizeof(VARIABLE_STORE_HEADER)); VARIABLE_STORE_HEADER *vsh = (VARIABLE_STORE_HEADER *)buf; if (vsh->Format != VARIABLE_STORE_FORMATTED && vsh->State != VARIABLE_STORE_HEALTHY) { fprintf(stderr, "Variable store header is corrupt\n"); exit(1); } UINT32 size = vsh->Size; free(buf); buf = malloc(size); lseek(flashfile, offset, SEEK_SET); read(flashfile, buf, size); vsh = (VARIABLE_STORE_HEADER *)buf; printf("Variable Store Size = 0x%x\n", vsh->Size); VARIABLE_HEADER *vh = (void *)HEADER_ALIGN(vsh + 1); printf("variables begin at 0x%x\n", (int)((char *)vh - (char *)vsh)); for (i = 0; IsValidVariableHeader(vh); i++) { vh = (void *)HEADER_ALIGN((char *)(vh + 1) + vh->NameSize + vh->DataSize); } printf("Found %d variables, now at offset %ld\n", i, (long)((char *)vh - (char *)vsh)); memset(vh, 0, sizeof(*vh)); vh->StartId = VARIABLE_DATA; vh->State = VAR_ADDED; vh->Attributes = attributes; vh->NameSize = varlen; vh->DataSize = varfilesize; vh->TimeStamp = timestamp; vh->VendorGuid = *owner; buf = (void *)(vh + 1); memcpy (buf, var, varlen); buf += varlen; memcpy (buf, vardata, varfilesize); lseek(flashfile, offset, SEEK_SET); write(flashfile, vsh, vsh->Size); close(flashfile); exit(0); eof: printf("No variables found in file at offset 0x%x\n", i); exit(2); }