diff --git a/MosbyPkg.inf b/MosbyPkg.inf index b74b398..72ee5f2 100644 --- a/MosbyPkg.inf +++ b/MosbyPkg.inf @@ -54,9 +54,7 @@ [Guids] gEfiCertPkcs7Guid - gEfiCertX509Sha256Guid - gEfiCertX509Sha384Guid - gEfiCertX509Sha512Guid + gEfiCertX509Guid gEfiGlobalVariableGuid gEfiImageSecurityDatabaseGuid gEfiFileInfoGuid diff --git a/build.sh b/build.sh index 6ac2dd1..13306ef 100755 --- a/build.sh +++ b/build.sh @@ -19,7 +19,7 @@ else fi # DISABLED = SecureBoot=0, SetupMode=0 # SETUP = SecureBoot=0, SetupMode=1 -SB_MODE=DISABLED +SB_MODE=SETUP cp OVMF_VARS_4M.secboot.$SB_MODE.fd OVMF_VARS_4M.secboot.fd export QEMU_CMD="qemu-system-x86_64 $CPU_OPT -m 1024 -M q35 -L . \ -drive if=pflash,format=raw,unit=0,file=OVMF_CODE_4M.secboot.fd,readonly=on \ diff --git a/src/mosby.c b/src/mosby.c index 2314c60..de6c688 100644 --- a/src/mosby.c +++ b/src/mosby.c @@ -36,39 +36,39 @@ STATIC EFI_GUID gEfiShimLockGuid = /* Attributes for the "key" types we support */ STATIC struct { - CHAR8 *Name; - CHAR16 *VarName; - EFI_GUID *VarGuid; + CHAR8 *DisplayName; + CHAR16 *VariableName; + EFI_GUID *VariableGuid; } KeyInfo[MAX_TYPES] = { [PK] = { - .Name = "PK", - .VarName = EFI_PLATFORM_KEY_NAME, - .VarGuid = &gEfiGlobalVariableGuid, + .DisplayName = "PK", + .VariableName = EFI_PLATFORM_KEY_NAME, + .VariableGuid = &gEfiGlobalVariableGuid, }, [KEK] = { - .Name = "KEK", - .VarName = EFI_KEY_EXCHANGE_KEY_NAME, - .VarGuid = &gEfiGlobalVariableGuid, + .DisplayName = "KEK", + .VariableName = EFI_KEY_EXCHANGE_KEY_NAME, + .VariableGuid = &gEfiGlobalVariableGuid, }, [DB] = { - .Name = "DB", - .VarName = EFI_IMAGE_SECURITY_DATABASE, - .VarGuid = &gEfiImageSecurityDatabaseGuid, + .DisplayName = "DB", + .VariableName = EFI_IMAGE_SECURITY_DATABASE, + .VariableGuid = &gEfiImageSecurityDatabaseGuid, }, [DBX] = { - .Name = "DBX", - .VarName = EFI_IMAGE_SECURITY_DATABASE1, - .VarGuid = &gEfiImageSecurityDatabaseGuid, + .DisplayName = "DBX", + .VariableName = EFI_IMAGE_SECURITY_DATABASE1, + .VariableGuid = &gEfiImageSecurityDatabaseGuid, }, [DBT] = { - .Name = "DBT", - .VarName = EFI_IMAGE_SECURITY_DATABASE2, - .VarGuid = &gEfiImageSecurityDatabaseGuid, + .DisplayName = "DBT", + .VariableName = EFI_IMAGE_SECURITY_DATABASE2, + .VariableGuid = &gEfiImageSecurityDatabaseGuid, }, [MOK] = { - .Name = "MOK", - .VarName = L"MokList", - .VarGuid = &gEfiShimLockGuid, + .DisplayName = "MOK", + .VariableName = L"MokList", + .VariableGuid = &gEfiShimLockGuid, } }; @@ -181,13 +181,13 @@ EFI_STATUS ParseList( }; } else { for (Type = 0; Type < MAX_TYPES; Type++) { - if (i + AsciiStrLen(KeyInfo[Type].Name) >= Installable->ListDataSize) + if (i + AsciiStrLen(KeyInfo[Type].DisplayName) >= Installable->ListDataSize) continue; - if (AsciiStrnCmp(KeyInfo[Type].Name, &Installable->ListData[i], AsciiStrLen(KeyInfo[Type].Name)) != 0) + if (AsciiStrnCmp(KeyInfo[Type].DisplayName, &Installable->ListData[i], AsciiStrLen(KeyInfo[Type].DisplayName)) != 0) continue; - if (!IsWhiteSpace(Installable->ListData[i + AsciiStrLen(KeyInfo[Type].Name)])) + if (!IsWhiteSpace(Installable->ListData[i + AsciiStrLen(KeyInfo[Type].DisplayName)])) continue; - i += AsciiStrLen(KeyInfo[Type].Name); + i += AsciiStrLen(KeyInfo[Type].DisplayName); while (IsWhiteSpace(Installable->ListData[i]) && i < Installable->ListDataSize) i++; if (Installable->List[Type].NumEntries < MOSBY_MAX_ENTRIES) { @@ -223,6 +223,9 @@ EFI_STATUS EFIAPI efi_main( IN EFI_SYSTEM_TABLE* SystemTable ) { + // TODO: MOK may need different options + CONST UINT32 Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; BOOLEAN TestMode = FALSE; EFI_STATUS Status, SetStatus; INTN Argc, Type, Entry, Sel; @@ -355,42 +358,44 @@ EFI_STATUS EFIAPI efi_main( EFI_HANDLE Handle = NULL; SafeFree(Installable.List[Type].Path[Entry]); UnicodeSPrint(Title, ARRAY_SIZE(Title), L"Please select %a %s", - KeyInfo[Type].Name, (Type == DBX) ? L"binary" : L"certificate"); + KeyInfo[Type].DisplayName, (Type == DBX) ? L"binary" : L"certificate"); Status = SimpleFileSelector(&Handle, (CONST CHAR16 *[]){ L"", Title, NULL - }, L"\\", L".cer|.crt|.esl|.bin", &Installable.List[Type].Path[Entry]); + }, L"\\", L".cer|.crt|.esl|.bin|.auth", &Installable.List[Type].Path[Entry]); RecallPrintRestore(); if (EFI_ERROR(Status)) continue; if (!SimpleFileExistsByPath(gBaseImageHandle, Installable.List[Type].Path[Entry])) { - RecallPrint(L"No valid file selected for %a[%d] - Ignoring\n", KeyInfo[Type].Name, Entry); + RecallPrint(L"No valid file selected for %a[%d] - Ignoring\n", KeyInfo[Type].DisplayName, Entry); continue; } } - Installable.List[Type].Esl[Entry] = LoadToEsl(Installable.List[Type].Path[Entry]); - - if (Installable.List[Type].Esl[Entry] == NULL) { - RecallPrint(L"Failed to load %a[%d] - Aborting\n", KeyInfo[Type].Name, Entry); + Status = LoadToAuthVar(Installable.List[Type].Path[Entry], &Installable.List[Type].Variable[Entry]); + if (EFI_ERROR(Status)) { + RecallPrint(L"Failed to load %a[%d] - Aborting\n", KeyInfo[Type].DisplayName, Entry); goto exit; } } } /* 6. Generate a keyless PK cert if none was specified */ - if (Installable.List[PK].Esl[0] == NULL) { + if (Installable.List[PK].Variable[0].Data == NULL) { RecallPrint(L"Generating PK certificate...\n"); SafeFree(Installable.List[PK].Path[0]); Installable.List[PK].Path[0] = StrDup(L"AutoGenerated"); - Cert = GenerateCredentials("Mosby Generated PK", NULL); - Installable.List[PK].Esl[0] = CertToEsl(Cert); - if (Installable.List[PK].Esl[0] == NULL) { + Cert = GenerateCredentials("Mosby Generated PK", &Key); + Status = CertToAuthVar(Cert, &Installable.List[PK].Variable[0]); + if (EFI_ERROR(Status)) { SafeFree(Cert); goto exit; } + Status = SignToAuthVar(KeyInfo[PK].VariableName, KeyInfo[PK].VariableGuid, + Attributes, &Installable.List[PK].Variable[0], Cert, Key); + FreeCredentials(Cert, Key); } /* 7. Generate DB credentials if requested */ @@ -400,9 +405,9 @@ EFI_STATUS EFIAPI efi_main( RecallPrint(L"Generating Secure Boot signing credentials...\n"); SafeFree(Installable.List[DB].Path[Entry]); Installable.List[DB].Path[Entry] = StrDup(L"AutoGenerated"); - Cert = GenerateCredentials("Secure Boot signing", &Key);; - Installable.List[DB].Esl[Entry] = CertToEsl(Cert); - if (Installable.List[DB].Esl[Entry] == NULL) { + Cert = GenerateCredentials("Secure Boot signing", &Key); + Status = CertToAuthVar(Cert, &Installable.List[DB].Variable[Entry]); + if (EFI_ERROR(Status)) { SafeFree(Cert); goto exit; } @@ -410,6 +415,7 @@ EFI_STATUS EFIAPI efi_main( if (EFI_ERROR(Status)) goto exit; RecallPrint(L"Saved Secure Boot signing credentials as '%s'\n", MOSBY_CRED_NAME); + FreeCredentials(Cert, Key); } /* 8. Install the cert and DBX variables, making sure that we finish with the PK. */ @@ -418,12 +424,15 @@ EFI_STATUS EFIAPI efi_main( Status = EFI_NOT_FOUND; for (Type = MAX_TYPES - 1; Type >= 0; Type--) { for (Entry = 0; Entry < Installable.List[Type].NumEntries; Entry++) { - if (Installable.List[Type].Esl[Entry] != NULL) { - RecallPrint(L"Installing %a[%d] (from %s)\n", KeyInfo[Type].Name, Entry, Installable.List[Type].Path[Entry]); - SetStatus = SetSecureBootVariable(KeyInfo[Type].VarName, KeyInfo[Type].VarGuid, - Installable.List[Type].Esl[Entry], (Entry != 0)); - if (EFI_ERROR(SetStatus)) + if (Installable.List[Type].Variable[Entry].Data != NULL) { + RecallPrint(L"Installing %a[%d] (from %s)\n", KeyInfo[Type].DisplayName, Entry, Installable.List[Type].Path[Entry]); + SetStatus = gRT->SetVariable(KeyInfo[Type].VariableName, KeyInfo[Type].VariableGuid, + Attributes | ((Entry != 0) ? EFI_VARIABLE_APPEND_WRITE : 0), + Installable.List[Type].Variable[Entry].Size, Installable.List[Type].Variable[Entry].Data); + if (EFI_ERROR(SetStatus)) { + Print(L"Failed to set Secure Boot variable: %r\n", SetStatus); Status = SetStatus; + } } } } @@ -432,7 +441,7 @@ EFI_STATUS EFIAPI efi_main( for (Type = 0; Type < MAX_TYPES; Type++) for (Entry = 0; Entry < MOSBY_MAX_ENTRIES; Entry++) { FreePool(Installable.List[Type].Path[Entry]); - FreePool(Installable.List[Type].Esl[Entry]); + FreePool(Installable.List[Type].Variable[Entry].Data); } FreePool(Installable.ListData); FreePool(Argv); diff --git a/src/mosby.h b/src/mosby.h index 3af4853..1f24e29 100644 --- a/src/mosby.h +++ b/src/mosby.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -56,11 +57,16 @@ enum { MAX_TYPES }; +typedef struct { + UINTN Size; + EFI_VARIABLE_AUTHENTICATION_2 *Data; +} AUTHENTICATED_VARIABLE; + /* Structure containing the list of "keys" for a specific type */ typedef struct { UINTN NumEntries; CHAR16 *Path[MOSBY_MAX_ENTRIES]; - EFI_SIGNATURE_LIST *Esl[MOSBY_MAX_ENTRIES]; + AUTHENTICATED_VARIABLE Variable[MOSBY_MAX_ENTRIES]; } INSTALLABLE_LIST; /* Structure containing the collection of all the lists */ diff --git a/src/pki.c b/src/pki.c index d84c95a..f812aaf 100644 --- a/src/pki.c +++ b/src/pki.c @@ -48,6 +48,8 @@ ERR_print_errors_cb(OpenSSLErrorCallback, _ErrMsg); goto exit; \ } while(0) +STATIC EFI_TIME mTime = { 0 }; + /* For OpenSSL error reporting */ STATIC int OpenSSLErrorCallback( CONST CHAR8 *AsciiString, @@ -66,6 +68,17 @@ EFI_STATUS InitializePki( CONST CHAR8 DefaultSeed[] = "Mosby crypto default seed"; EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; CHAR16 *Seed = NULL; + EFI_STATUS Status; + + Status = gRT->GetTime(&mTime, NULL); + if (EFI_ERROR(Status)) + ReportErrorAndExit(L"Failed to get current time: %r\n", Status); + + // SetVariable() *will* fail with "Security Violation" unless you + // explicitly zero these before calling CreateTimeBasedPayload() + mTime.Nanosecond = 0; + mTime.TimeZone = 0; + mTime.Daylight = 0; // Try to use the loaded image's DevicePath (of the DeviceHandle) as our seed since // it is both unique enough and *not* time-based (therefore harder to guess). @@ -81,7 +94,10 @@ EFI_STATUS InitializePki( RAND_seed(DefaultSeed, sizeof(DefaultSeed)); } FreePool(Seed); - return (RAND_status() != 1 && !TestMode) ? EFI_UNSUPPORTED : EFI_SUCCESS; + Status = (RAND_status() != 1 && !TestMode) ? EFI_UNSUPPORTED : EFI_SUCCESS; + +exit: + return Status; } /* Helper function to add X509 extensions */ @@ -268,13 +284,21 @@ EFI_STATUS SaveCredentials( #if 0 // Save certificate as DER encoded .cer + Size = (INTN)i2d_X509(Cert, NULL); + if (Size <= 0) + ReportOpenSSLErrorAndExit(EFI_PROTOCOL_ERROR); + Buffer = AllocateZeroPool(Size); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + ReportErrorAndExit(L"Failed to allocate DER buffer\n"); + } Ptr = Buffer; // i2d_###() modifies the pointer - Size = (INTN)i2d_X509((X509*)Cert, &Ptr); + Size = (INTN)i2d_X509(Cert, &Ptr); if (Size < 0) ReportOpenSSLErrorAndExit(EFI_PROTOCOL_ERROR); UnicodeSPrint(Path, ARRAY_SIZE(Path), L"%s.cer", BaseName); Status = SimpleFileWriteAllByPath(gBaseImageHandle, Path, (UINTN)Size, Buffer); - OPENSSL_free(Buffer); + SafeFree(Buffer); if (EFI_ERROR(Status)) goto exit; #endif @@ -300,50 +324,41 @@ EFI_STATUS SaveCredentials( return Status; } -EFI_SIGNATURE_LIST* CertToEsl( - CONST IN VOID *Cert +VOID FreeCredentials( + IN CONST VOID *Cert, + IN CONST VOID *Key ) { + X509_free((X509*)Cert); + EVP_PKEY_free((EVP_PKEY*)Key); +} + +EFI_STATUS CertToAuthVar( + CONST IN VOID *Cert, + OUT AUTHENTICATED_VARIABLE *Variable +) +{ + EFI_STATUS Status = EFI_INVALID_PARAMETER; EFI_SIGNATURE_LIST *Esl = NULL; EFI_SIGNATURE_DATA *Data = NULL; INTN Size; UINT8 *Ptr; - EFI_GUID OwnerGuid, *TypeGuid = NULL; + EFI_GUID OwnerGuid; UINT8 Sha1[SHA_DIGEST_LENGTH] = { 0 }; - if (Cert == NULL) - return NULL; + if (Cert == NULL || Variable == NULL) + return EFI_INVALID_PARAMETER; Size = (INTN)i2d_X509((X509*)Cert, NULL); if (Size <= 0) - return NULL; + goto exit; Esl = AllocateZeroPool(sizeof(EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + Size); - if (Esl == NULL) + if (Esl == NULL) { + Status = EFI_OUT_OF_RESOURCES; ReportErrorAndExit(L"Failed to allocate ESL\n"); - - switch (X509_get_signature_nid((X509*)Cert)) { - case NID_sha256: - case NID_sha256WithRSAEncryption: - TypeGuid = &gEfiCertX509Sha256Guid; - break; - case NID_sha384: - case NID_sha384WithRSAEncryption: - TypeGuid = &gEfiCertX509Sha384Guid; - break; - case NID_sha512: - case NID_sha512WithRSAEncryption: - TypeGuid = &gEfiCertX509Sha512Guid; - break; - default: - break; - } - - if (TypeGuid == NULL) { - FreePool(Esl); - ReportErrorAndExit(L"Unsupported signature algorithm\n"); } - CopyGuid(&Esl->SignatureType, TypeGuid); + CopyGuid(&Esl->SignatureType, &gEfiCertX509Guid); Esl->SignatureListSize = sizeof(EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + Size; Esl->SignatureSize = sizeof(EFI_SIGNATURE_DATA) - 1 + Size; @@ -361,22 +376,39 @@ EFI_SIGNATURE_LIST* CertToEsl( CopyMem(&OwnerGuid.Data4, &Sha1[8], 8); CopyGuid(&Data->SignatureOwner, &OwnerGuid); + Variable->Size = Esl->SignatureListSize; + Variable->Data = (EFI_VARIABLE_AUTHENTICATION_2*)Esl; + // NB: CreateTimeBasedPayload() frees the input buffer before replacing it + Status = CreateTimeBasedPayload(&Variable->Size, (UINT8**)&Variable->Data, &mTime); + if (EFI_ERROR(Status)) { + FreePool(Esl); + ReportErrorAndExit(L"Failed to create time-based data payload: %r\n", Status); + } + exit: - return Esl; + if (EFI_ERROR(Status)) { + Variable->Size = 0; + Variable->Data = NULL; + } + return Status; } -EFI_SIGNATURE_LIST* LoadToEsl( - IN CONST CHAR16 *Path +EFI_STATUS LoadToAuthVar( + IN CONST CHAR16 *Path, + OUT AUTHENTICATED_VARIABLE* Variable ) { EFI_STATUS Status; UINTN Size, HeaderSize; UINT8 *Buffer = NULL; CONST UINT8 *Ptr; - EFI_SIGNATURE_LIST* Esl = NULL; - EFI_VARIABLE_AUTHENTICATION_2* SignedEsl = NULL; + EFI_SIGNATURE_LIST *Esl = NULL; + EFI_VARIABLE_AUTHENTICATION_2 *SignedEsl = NULL; BIO *bio = NULL; + if (Path == NULL || Variable == NULL) + return EFI_INVALID_PARAMETER; + if (!SimpleFileExistsByPath(gBaseImageHandle, Path)) ReportErrorAndExit(L"File '%s' does not exist\n", Path); @@ -384,10 +416,11 @@ EFI_SIGNATURE_LIST* LoadToEsl( if (EFI_ERROR(Status)) goto exit; + Status = EFI_INVALID_PARAMETER; if (Size < sizeof(EFI_SIGNATURE_LIST)) ReportErrorAndExit(L"'%s' is too small to be a valid certificate or signature list\n", Path); - // Check for signed ESL (PKCS#7 only, as it's what DBX updates use) + // Check for signed ESL (PKCS#7 only) SignedEsl = (EFI_VARIABLE_AUTHENTICATION_2*)Buffer; if (Size > sizeof(EFI_VARIABLE_AUTHENTICATION_2) && SignedEsl->AuthInfo.Hdr.dwLength < Size && @@ -402,44 +435,145 @@ EFI_SIGNATURE_LIST* LoadToEsl( HeaderSize += 4; // For the 4 extra bytes above if (HeaderSize + sizeof(EFI_SIGNATURE_LIST) > Size) ReportErrorAndExit(L"Invalid signed ESL '%s'\n", Path); - Esl = AllocateZeroPool(Size - HeaderSize); - if (Esl == NULL) - ReportErrorAndExit(L"Failed to allocate unsigned ESL for '%s'\n", Path); - CopyMem(Esl, &Buffer[HeaderSize], Size - HeaderSize); - SafeFree(Buffer); - if (Esl->SignatureListSize != Size - HeaderSize) { - SafeFree(Esl); + Esl = (EFI_SIGNATURE_LIST*)&Buffer[HeaderSize]; + if (Esl->SignatureListSize != Size - HeaderSize) ReportErrorAndExit(L"Invalid signed ESL '%s'\n", Path); - } + Buffer = NULL; // Don't free our data + Variable->Size = Size; + Variable->Data = SignedEsl; + Status = EFI_SUCCESS; goto exit; } // Check for a DER encoded X509 certificate Ptr = Buffer; // d2i_###() modifies the pointer - Esl = CertToEsl(d2i_X509(NULL, &Ptr, Size)); - if (Esl != NULL) + Status = CertToAuthVar(d2i_X509(NULL, &Ptr, Size), Variable); + if (Status == EFI_SUCCESS) goto exit; // Check for a PEM encoded X509 certificate bio = BIO_new_mem_buf(Buffer, Size); if (bio == NULL) ReportErrorAndExit(L"Failed to allocate X509 buffer\n"); - Esl = CertToEsl(PEM_read_bio_X509(bio, NULL, NULL, NULL)); - if (Esl != NULL) + Status = CertToAuthVar(PEM_read_bio_X509(bio, NULL, NULL, NULL), Variable); + if (Status == EFI_SUCCESS) goto exit; // Check for an unsigned ESL Esl = (EFI_SIGNATURE_LIST*)Buffer; if (Esl->SignatureListSize == Size) { - Buffer = NULL; // Don't free the ESL - goto exit; + Variable->Size = Esl->SignatureListSize; + Variable->Data = (EFI_VARIABLE_AUTHENTICATION_2*)Esl; + // NB: CreateTimeBasedPayload() frees the input buffer before replacing it + Status = CreateTimeBasedPayload(&Variable->Size, (UINT8**)&Variable->Data, &mTime); + if (EFI_ERROR(Status)) + ReportErrorAndExit(L"Failed to create time-based data payload: %r\n", Status); + Buffer = NULL; // CreateTimeBasedPayload freed Esl = Buffer already } - Esl = NULL; ReportErrorAndExit(L"Failed to process '%s'\n", Path); exit: BIO_free(bio); FreePool(Buffer); - return Esl; + if (EFI_ERROR(Status)) { + Variable->Size = 0; + Variable->Data = NULL; + } + return Status; +} + +EFI_STATUS SignToAuthVar( + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VendorGuid, + IN CONST UINT32 Attributes, + IN OUT AUTHENTICATED_VARIABLE *Variable, + IN CONST VOID *Cert, + IN CONST VOID *Key +) +{ + CONST INTN PAYLOAD = 4; + CONST int flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOATTR; + CONST struct { + UINT8 *Ptr; + UINTN Size; + } SignableElement[5] = { + { (UINT8*)VariableName, StrLen(VariableName) * sizeof(CHAR16) }, + { (UINT8*)VendorGuid, sizeof(EFI_GUID) }, + { (UINT8*)&Attributes, sizeof(Attributes) }, + { (UINT8*)&Variable->Data->TimeStamp, sizeof(EFI_TIME) }, + { &(((UINT8*)Variable->Data)[OFFSET_OF_AUTHINFO2_CERT_DATA]), Variable->Size - OFFSET_OF_AUTHINFO2_CERT_DATA } + }; + EFI_STATUS Status = EFI_INVALID_PARAMETER; + UINT8 *SignData = NULL; + EFI_VARIABLE_AUTHENTICATION_2 *SignedVariable = NULL; + UINT8 *Payload, *Ptr; + UINTN i, SignDataSize, SignatureSize, Offset; + BIO *bio; + PKCS7 *p7; + + if (Variable->Size < OFFSET_OF_AUTHINFO2_CERT_DATA) + ReportErrorAndExit(L"Variable to sign (%d) is too small (%d)\n", Variable->Size, OFFSET_OF_AUTHINFO2_CERT_DATA); + + // We can only sign for PKCS#7 + if (!CompareGuid(&Variable->Data->AuthInfo.CertType, &gEfiCertPkcs7Guid)) + ReportErrorAndExit(L"Variable to sign is not PKCS#7\n"); + + // Make sure we are dealing with a variable that does NOT already contain a signature + if (Variable->Data->AuthInfo.Hdr.dwLength != OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) + ReportErrorAndExit(L"Variable is already signed\n"); + + // Construct the data buffer to sign + SignDataSize = 0; + for (i = 0; i < ARRAY_SIZE(SignableElement); i++) + SignDataSize += SignableElement[i].Size; + SignData = AllocateZeroPool(SignDataSize); + if (SignData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + ReportErrorAndExit(L"Failed to allocate buffer to sign\n"); + } + Offset = 0; + for (i = 0; i < ARRAY_SIZE(SignableElement); i++) { + CopyMem(&SignData[Offset], SignableElement[i].Ptr, SignableElement[i].Size); + Offset += SignableElement[i].Size; + } + + // Sign the constructed data buffer + bio = BIO_new_mem_buf(SignData, SignDataSize); + if (bio == NULL) + ReportOpenSSLErrorAndExit(EFI_PROTOCOL_ERROR); + + p7 = PKCS7_sign(NULL, NULL, NULL, bio, flags | PKCS7_PARTIAL); + if (p7 == NULL) + ReportOpenSSLErrorAndExit(EFI_PROTOCOL_ERROR); + if (PKCS7_sign_add_signer(p7, (X509*)Cert, (EVP_PKEY*)Key, EVP_get_digestbyname("SHA256"), flags) == NULL) + ReportOpenSSLErrorAndExit(EFI_PROTOCOL_ERROR); + if (!PKCS7_final(p7, bio, flags)) + ReportOpenSSLErrorAndExit(EFI_PROTOCOL_ERROR); + SignatureSize = i2d_PKCS7(p7, NULL); + + // Create the signed variable + SignedVariable = AllocateZeroPool(Variable->Size + SignatureSize); + if (SignedVariable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + ReportErrorAndExit(L"Failed to allocate buffer for signed variable\n"); + } + CopyMem(SignedVariable, Variable->Data, OFFSET_OF_AUTHINFO2_CERT_DATA); + SignedVariable->AuthInfo.Hdr.dwLength = OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData) + SignatureSize; + Ptr = SignedVariable->AuthInfo.CertData; + SignatureSize = i2d_PKCS7(p7, &Ptr); + Payload = (UINT8*)SignedVariable; + Payload = &Payload[OFFSET_OF_AUTHINFO2_CERT_DATA + SignatureSize]; + CopyMem(Payload, SignableElement[PAYLOAD].Ptr, SignableElement[PAYLOAD].Size); + + // Update the variable passed as parameter with the signed one + FreePool(Variable->Data); + Variable->Data = SignedVariable; + Variable->Size = Variable->Size + SignatureSize; + + Status = EFI_SUCCESS; + +exit: + FreePool(SignData); + return Status; } diff --git a/src/pki.h b/src/pki.h index 85e0b59..c24945c 100644 --- a/src/pki.h +++ b/src/pki.h @@ -36,10 +36,26 @@ EFI_STATUS SaveCredentials( IN CONST CHAR16 *BaseName ); -EFI_SIGNATURE_LIST* CertToEsl( - CONST IN VOID *Cert +VOID FreeCredentials( + IN CONST VOID *Cert, + IN CONST VOID *Key +); + +EFI_STATUS CertToAuthVar( + CONST IN VOID *Cert, + OUT AUTHENTICATED_VARIABLE *Variable ); -EFI_SIGNATURE_LIST* LoadToEsl( - IN CONST CHAR16 *Path +EFI_STATUS LoadToAuthVar( + IN CONST CHAR16 *Path, + OUT AUTHENTICATED_VARIABLE* Variable +); + +EFI_STATUS SignToAuthVar( + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VendorGuid, + IN CONST UINT32 Attributes, + IN OUT AUTHENTICATED_VARIABLE *Variable, + IN CONST VOID *Cert, + IN CONST VOID *Key ); diff --git a/src/variables.c b/src/variables.c index c833cf7..304538b 100644 --- a/src/variables.c +++ b/src/variables.c @@ -198,53 +198,3 @@ EFI_STATUS CheckSetupMode( exit: return Status; } - -// We would just use EDK2's EnrollFromInput() if we could, but it doesn't support appending... -EFI_STATUS SetSecureBootVariable( - IN CONST CHAR16 *VariableName, - IN CONST EFI_GUID *VendorGuid, - IN CONST EFI_SIGNATURE_LIST *Esl, - IN CONST BOOLEAN Append -) -{ - // TODO: MOK may need different options - CONST UINT32 Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | - EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; - EFI_STATUS Status; - EFI_TIME Time = { 0 }; - UINT8 *Data = NULL; - UINTN Size; - - // Work on a copy of the ESL - Size = Esl->SignatureListSize; - Data = AllocateZeroPool(Size); - if (Data == NULL) { - Status = EFI_OUT_OF_RESOURCES; - ReportErrorAndExit(L"Failed to allocate time-based data payload: %r\n", Status); - } - CopyMem(Data, Esl, Size); - - Status = gRT->GetTime(&Time, NULL); - if (EFI_ERROR(Status)) - ReportErrorAndExit(L"Failed to get current time: %r\n", Status); - - // SetVariable() *will* fail with "Security Violation" unless you - // explicitly zero these before calling CreateTimeBasedPayload() - Time.Nanosecond = 0; - Time.TimeZone = 0; - Time.Daylight = 0; - - // NB: CreateTimeBasedPayload() frees the input buffer before replacing it - Status = CreateTimeBasedPayload(&Size, &Data, &Time); - if (EFI_ERROR(Status)) - ReportErrorAndExit(L"Failed to create time-based data payload: %r\n", Status); - - Status = gRT->SetVariable((CHAR16*)VariableName, (EFI_GUID*)VendorGuid, - Attributes | (Append ? EFI_VARIABLE_APPEND_WRITE : 0), Size, Data); - if (EFI_ERROR(Status)) - ReportErrorAndExit(L"Failed to set Secure Boot variable: %r\n", Status); - -exit: - FreePool(Data); - return Status; -} diff --git a/src/variables.h b/src/variables.h index 8795cd3..1dcead6 100644 --- a/src/variables.h +++ b/src/variables.h @@ -19,10 +19,3 @@ EFI_STATUS CheckSetupMode( IN CONST BOOLEAN TestMode ); - -EFI_STATUS SetSecureBootVariable( - IN CONST CHAR16 *VariableName, - IN CONST EFI_GUID *VendorGuid, - IN CONST EFI_SIGNATURE_LIST *Esl, - IN CONST BOOLEAN Append -);