public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* SecurityPkg: untrusted OptionRom is loaded despite DENY_EXECUTE_ON_SECURITY_VIOLATION set.
       [not found] <CY1PR0701MB185234B24E2441C917CB087EEF4F0@CY1PR0701MB1852.namprd07.prod.outlook.com>
@ 2017-10-16 16:57 ` Zmuda, Wojciech
  2017-10-16 18:11   ` Laszlo Ersek
  0 siblings, 1 reply; 4+ messages in thread
From: Zmuda, Wojciech @ 2017-10-16 16:57 UTC (permalink / raw)
  To: edk2-devel@lists.01.org

Hello,

I'd like to ask you for help with understanding Secure Boot policy mechanism, specifically DENY_EXECUTE_ON_SECURITY_VIOLATION for PcdOptionRomImageVerificationPolicy. The OptionROM in my setup is loaded while, in my opinion, it should be rejected.

I'm testing the following scenario: Secure Boot is enabled with my own PK and KEK enrolled, but with no db, to make sure nothing unsigned or signed by somebody else but me can be executed. A PCIe card with OptionROM (some EBC code) is installed. gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy  is set to 0x04 in my platform's package. What I expect, is the OptionROM execution being denied, as it is not signed by my certificate. What I observe, on the other hand, is a message on the console, that no EBC interpreter is found, which suggest, that the  OptionROM is loaded, just fails at the execution of EBC. The same message is printed when Secure Boot is disabled.

I tried to understand the code by stepping through it in the DS-5. The following part of SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c seems suspicious to me:

    SecurityStatus = gSecurity->FileAuthenticationState (
                                  gSecurity,
                                  AuthenticationStatus,
                                  OriginalFilePath
                                  );
  }

  //
  // Check Security Status.
  //
  if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) {
    if (SecurityStatus == EFI_ACCESS_DENIED) {
      //
      // Image was not loaded because the platform policy prohibits the image from being loaded.
      // It's the only place we could meet EFI_ACCESS_DENIED.
      //
      *ImageHandle = NULL;
    }
    Status = SecurityStatus;
    Image = NULL;
    goto Done;
}

The return code of gSecurity->FileAuthenticationState () (which is implemented in MdeModulePkg/Core/Dxe/Image/Image.c:DxeImageVerificationHandler ()), is EFI_SECURITY_VIOLATION. Such return code skips this if-statement, that prevents the image to be loaded.  According to the comment in the if-statement: for the policy to be respected, gSecurity->FileAuthenticationState () should return EFI_ACCESS_DENIED.

That being said, I stepped through DxeImageVerificationHandler (). The PCD with OptionROM policy is checked correctly. The function handles ALWAYS_EXECUTE and NEVER_EXECUTE policies properly and hangs on QUERY_USER_ and ALLOW_EXECUTE_ON_SECURITY_VIOLATION.  This seems fine, however there is no code handling the DENY_EXECUTE_ON_SECURITY_VIOLATION (0x04) case. Stepping through this function shows that the image to be loaded cannot be found in the db (correct, as there's no db). Then, the function jumps to its very  end and returns EFI_SECURITY_VIOLATION, which skips the aforementioned if-statement:

Done:
  if (Status != EFI_SUCCESS) {
    //
    // Policy decides to defer or reject the image; add its information in image executable information table.
    //
    NameStr = ConvertDevicePathToText (File, FALSE, TRUE);
    AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);
    if (NameStr != NULL) {
      DEBUG((EFI_D_INFO, "The image doesn't pass verification: %s\n", NameStr));
      FreePool(NameStr);
    }
    Status = EFI_SECURITY_VIOLATION;
  }

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

  return Status;
}

Is there anything I'm doing wrong trying to prevent untrusted OptionROM execution? If my understanding is correct, my case should make DxeImageVerificationHandler () return EFI_ACCESS_DENIED here. For example, in the snippet above, Status should be set to EFI_ACCESS_DENIED  if Policy == DENY_EXECUTE_ON_SECURITY_VIOLATION.

Thank you all for your time,
Wojciech Zmuda

    

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: SecurityPkg: untrusted OptionRom is loaded despite DENY_EXECUTE_ON_SECURITY_VIOLATION set.
  2017-10-16 16:57 ` SecurityPkg: untrusted OptionRom is loaded despite DENY_EXECUTE_ON_SECURITY_VIOLATION set Zmuda, Wojciech
@ 2017-10-16 18:11   ` Laszlo Ersek
  2017-10-17 19:07     ` Zmuda, Wojciech
  0 siblings, 1 reply; 4+ messages in thread
From: Laszlo Ersek @ 2017-10-16 18:11 UTC (permalink / raw)
  To: Zmuda, Wojciech; +Cc: edk2-devel@lists.01.org

On 10/16/17 18:57, Zmuda, Wojciech wrote:
> Hello,
>
> I'd like to ask you for help with understanding Secure Boot policy
> mechanism, specifically DENY_EXECUTE_ON_SECURITY_VIOLATION for
> PcdOptionRomImageVerificationPolicy. The OptionROM in my setup is
> loaded while, in my opinion, it should be rejected.
>
> I'm testing the following scenario: Secure Boot is enabled with my own
> PK and KEK enrolled, but with no db, to make sure nothing unsigned or
> signed by somebody else but me can be executed. A PCIe card with
> OptionROM (some EBC code) is installed.
> gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy  is
> set to 0x04 in my platform's package. What I expect, is the OptionROM
> execution being denied, as it is not signed by my certificate. What I
> observe, on the other hand, is a message on the console, that no EBC
> interpreter is found, which suggest, that the  OptionROM is loaded,
> just fails at the execution of EBC. The same message is printed when
> Secure Boot is disabled.
>
> I tried to understand the code by stepping through it in the DS-5. The
> following part of
> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> seems suspicious to me:
>
>     SecurityStatus = gSecurity->FileAuthenticationState (
>                                   gSecurity,
>                                   AuthenticationStatus,
>                                   OriginalFilePath
>                                   );
>   }

This code is not from "DxeImageVerificationLib.c"; it is from
CoreLoadImageCommon() in "MdeModulePkg/Core/Dxe/Image/Image.c". With
more context:

  } else if ((gSecurity != NULL) && (OriginalFilePath != NULL)) {
    //
    // Verify the Authentication Status through the Security Architectural Protocol
    //
    SecurityStatus = gSecurity->FileAuthenticationState (
                                  gSecurity,
                                  AuthenticationStatus,
                                  OriginalFilePath
                                  );
  }

>
>   //
>   // Check Security Status.
>   //
>   if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) {
>     if (SecurityStatus == EFI_ACCESS_DENIED) {
>       //
>       // Image was not loaded because the platform policy prohibits the image from being loaded.
>       // It's the only place we could meet EFI_ACCESS_DENIED.
>       //
>       *ImageHandle = NULL;
>     }
>     Status = SecurityStatus;
>     Image = NULL;
>     goto Done;
> }
>
> The return code of gSecurity->FileAuthenticationState () (which is
> implemented in
> MdeModulePkg/Core/Dxe/Image/Image.c:DxeImageVerificationHandler ()),
> is EFI_SECURITY_VIOLATION. Such return code skips this if-statement,
> that prevents the image to be loaded.  According to the comment in the
> if-statement: for the policy to be respected,
> gSecurity->FileAuthenticationState () should return EFI_ACCESS_DENIED.

No, that's a different kind of "platform policy".

The PCD that you mention is indeed platform policy, but only for
DxeImageVerificationLib. The platform policy being referred-to in this
comment is on the level of the gSecurity->FileAuthenticationState()
function call; and for that, we have to review the type definition of
the

  EFI_SECURITY_ARCH_PROTOCOL.FileAuthenticationState

member function. The type name for this member function is
EFI_SECURITY_FILE_AUTHENTICATION_STATE, and it is defined in
"MdePkg/Include/Protocol/Security.h".

I will not quote the entire description from said header file, just the
part that explains the difference:

>   @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and
>                                 the platform policy dictates that File should be placed
>                                 in the untrusted state. A file may be promoted from
>                                 the untrusted to the trusted state at a future time
>                                 with a call to the Trust() DXE Service.
>   @retval EFI_ACCESS_DENIED     The file specified by File did not authenticate, and
>                                 the platform policy dictates that File should not be
>                                 used for any purpose.

Back to your email:

On 10/16/17 18:57, Zmuda, Wojciech wrote:
> That being said, I stepped through DxeImageVerificationHandler (). The
> PCD with OptionROM policy is checked correctly. The function handles
> ALWAYS_EXECUTE and NEVER_EXECUTE policies properly and hangs on
> QUERY_USER_ and ALLOW_EXECUTE_ON_SECURITY_VIOLATION.  This seems fine,
> however there is no code handling the
> DENY_EXECUTE_ON_SECURITY_VIOLATION (0x04) case. Stepping through this
> function shows that the image to be loaded cannot be found in the db
> (correct, as there's no db). Then, the function jumps to its very  end
> and returns EFI_SECURITY_VIOLATION, which skips the aforementioned
> if-statement:
>
> Done:
>   if (Status != EFI_SUCCESS) {
>     //
>     // Policy decides to defer or reject the image; add its information in image executable information table.
>     //
>     NameStr = ConvertDevicePathToText (File, FALSE, TRUE);
>     AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);
>     if (NameStr != NULL) {
>       DEBUG((EFI_D_INFO, "The image doesn't pass verification: %s\n", NameStr));
>       FreePool(NameStr);
>     }
>     Status = EFI_SECURITY_VIOLATION;
>   }
>
>   if (SignatureList != NULL) {
>     FreePool (SignatureList);
>   }
>
>   return Status;
> }

This quote is indeed from
"SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c",
and indeed the EFI_SECURITY_VIOLATION value returned by it causes the
"if" statement you quoted from "MdeModulePkg/Core/Dxe/Image/Image.c" to
be skipped.

However, that's not the whole story. In
"MdeModulePkg/Core/Dxe/Image/Image.c", in the CoreLoadImageCommon()
function, track the "SecurityStatus" variable from the point where the
EFI_ACCESS_DENIED branch is *not* taken, to the end of the function.
Near the end of the function, "Status" is EFI_SUCCESS, and thus
"SecurityStatus" will be assigned to it (and the function will
ultimately return EFI_SECURITY_VIOLATION):

Done:
  //
  // All done accessing the source file
  // If we allocated the Source buffer, free it
  //
  if (FHand.FreeBuffer) {
    CoreFreePool (FHand.Source);
  }
  if (OriginalFilePath != InputFilePath) {
    CoreFreePool (OriginalFilePath);
  }

  //
  // There was an error.  If there's an Image structure, free it
  //
  if (EFI_ERROR (Status)) {
    if (Image != NULL) {
      CoreUnloadAndCloseImage (Image, (BOOLEAN)(DstBuffer == 0));
      Image = NULL;
    }
  } else if (EFI_ERROR (SecurityStatus)) {
    Status = SecurityStatus;
  }

  //
  // Track the return status from LoadImage.
  //
  if (Image != NULL) {
    Image->LoadImageStatus = Status;
  }

  return Status;
}

Back to your email:

On 10/16/17 18:57, Zmuda, Wojciech wrote:
> Is there anything I'm doing wrong trying to prevent untrusted
> OptionROM execution? If my understanding is correct, my case should
> make DxeImageVerificationHandler () return EFI_ACCESS_DENIED here. For
> example, in the snippet above, Status should be set to
> EFI_ACCESS_DENIED  if Policy == DENY_EXECUTE_ON_SECURITY_VIOLATION.

To me it seems like everything is working by design. The image is loaded
and the security violation is recorded. Please see the documentation of
the EFI_SECURITY_VIOLATION return code in the UEFI-2.7 spec, under
EFI_BOOT_SERVICES.LoadImage():

  Image was loaded and an ImageHandle was created with a valid
  EFI_LOADED_IMAGE_PROTOCOL.. However, the current platform policy
  specifies that the image should not be started.

(This section also has a pointer to  "35.1.5 Deferred Execution".)


I believe the misunderstanding comes from the facts that

(a) the message

  CoreLoadPeImage: There is no EBC interpreter for an EBC image.

is printed by the CoreLoadPeImage() function, and

(b) that CoreLoadImageCommon() calls CoreLoadPeImage() *between* the
EFI_ACCESS_DENIED check that you thought should have been taken -- which
is not the case -- and the final setting of Status to
EFI_SECURITY_VIOLATION (from "SecurityStatus"), which actually *would*
occur, if you had an EBC driver included in your platform firmware.


In other words, CoreLoadImageCommon() identifies (and prepares to
return) the EFI_SECURITY_VIOLATION status code, based on
PcdOptionRomImageVerificationPolicy. However, before the function could
finish processing (and return this error code), it comes across a more
severe error -- no EBC interpreter -- which takes priority both with and
without the Secure Boot operational mode.

If you want to be completely sure, I suggest including the EBC
interpreter in your platform, and retesting.

(Based on commit 81d9f86f8a71 ("ArmVirtPkg: enable EBC interpreter for
AArch64", 2016-07-29), adding the EBC interpreter should be easy.)

Again, to me it looks like everything is working by design.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: SecurityPkg: untrusted OptionRom is loaded despite DENY_EXECUTE_ON_SECURITY_VIOLATION set.
  2017-10-16 18:11   ` Laszlo Ersek
@ 2017-10-17 19:07     ` Zmuda, Wojciech
  2017-10-17 19:22       ` Laszlo Ersek
  0 siblings, 1 reply; 4+ messages in thread
From: Zmuda, Wojciech @ 2017-10-17 19:07 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: edk2-devel@lists.01.org, Zhang, Jonathan, Dabros, Jan

Hi Laszlo,


Thanks for this detailed explanation. I repeated the experiment with EBC interpreter included, this time stepping much further in the code, and I actually can see that with Secure Boot enabled the EFI_SECURITY_VIOLATION status is propagated, which does not let the image to load. With Secure Boot disabled, I get early EFI_SUCCESS and the image is loaded, as far as I can see in the debugger. Everything seems to work as designed, then.


I have one more question in this matter, if I may ask: why doesn't this path terminate immediately after finding that the OptionROM is untrusted? It wouldn't be executed anyway, with or without EBC interpreter, so why bother with further checking?


Thank you very much,

Wojciech

________________________________
From: Laszlo Ersek <lersek@redhat.com>
Sent: Monday, October 16, 2017 11:11:01 AM
To: Zmuda, Wojciech
Cc: edk2-devel@lists.01.org
Subject: Re: [edk2] SecurityPkg: untrusted OptionRom is loaded despite DENY_EXECUTE_ON_SECURITY_VIOLATION set.

On 10/16/17 18:57, Zmuda, Wojciech wrote:
> Hello,
>
> I'd like to ask you for help with understanding Secure Boot policy
> mechanism, specifically DENY_EXECUTE_ON_SECURITY_VIOLATION for
> PcdOptionRomImageVerificationPolicy. The OptionROM in my setup is
> loaded while, in my opinion, it should be rejected.
>
> I'm testing the following scenario: Secure Boot is enabled with my own
> PK and KEK enrolled, but with no db, to make sure nothing unsigned or
> signed by somebody else but me can be executed. A PCIe card with
> OptionROM (some EBC code) is installed.
> gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy  is
> set to 0x04 in my platform's package. What I expect, is the OptionROM
> execution being denied, as it is not signed by my certificate. What I
> observe, on the other hand, is a message on the console, that no EBC
> interpreter is found, which suggest, that the  OptionROM is loaded,
> just fails at the execution of EBC. The same message is printed when
> Secure Boot is disabled.
>
> I tried to understand the code by stepping through it in the DS-5. The
> following part of
> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> seems suspicious to me:
>
>     SecurityStatus = gSecurity->FileAuthenticationState (
>                                   gSecurity,
>                                   AuthenticationStatus,
>                                   OriginalFilePath
>                                   );
>   }

This code is not from "DxeImageVerificationLib.c"; it is from
CoreLoadImageCommon() in "MdeModulePkg/Core/Dxe/Image/Image.c". With
more context:

  } else if ((gSecurity != NULL) && (OriginalFilePath != NULL)) {
    //
    // Verify the Authentication Status through the Security Architectural Protocol
    //
    SecurityStatus = gSecurity->FileAuthenticationState (
                                  gSecurity,
                                  AuthenticationStatus,
                                  OriginalFilePath
                                  );
  }

>
>   //
>   // Check Security Status.
>   //
>   if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) {
>     if (SecurityStatus == EFI_ACCESS_DENIED) {
>       //
>       // Image was not loaded because the platform policy prohibits the image from being loaded.
>       // It's the only place we could meet EFI_ACCESS_DENIED.
>       //
>       *ImageHandle = NULL;
>     }
>     Status = SecurityStatus;
>     Image = NULL;
>     goto Done;
> }
>
> The return code of gSecurity->FileAuthenticationState () (which is
> implemented in
> MdeModulePkg/Core/Dxe/Image/Image.c:DxeImageVerificationHandler ()),
> is EFI_SECURITY_VIOLATION. Such return code skips this if-statement,
> that prevents the image to be loaded.  According to the comment in the
> if-statement: for the policy to be respected,
> gSecurity->FileAuthenticationState () should return EFI_ACCESS_DENIED.

No, that's a different kind of "platform policy".

The PCD that you mention is indeed platform policy, but only for
DxeImageVerificationLib. The platform policy being referred-to in this
comment is on the level of the gSecurity->FileAuthenticationState()
function call; and for that, we have to review the type definition of
the

  EFI_SECURITY_ARCH_PROTOCOL.FileAuthenticationState

member function. The type name for this member function is
EFI_SECURITY_FILE_AUTHENTICATION_STATE, and it is defined in
"MdePkg/Include/Protocol/Security.h".

I will not quote the entire description from said header file, just the
part that explains the difference:

>   @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and
>                                 the platform policy dictates that File should be placed
>                                 in the untrusted state. A file may be promoted from
>                                 the untrusted to the trusted state at a future time
>                                 with a call to the Trust() DXE Service.
>   @retval EFI_ACCESS_DENIED     The file specified by File did not authenticate, and
>                                 the platform policy dictates that File should not be
>                                 used for any purpose.

Back to your email:

On 10/16/17 18:57, Zmuda, Wojciech wrote:
> That being said, I stepped through DxeImageVerificationHandler (). The
> PCD with OptionROM policy is checked correctly. The function handles
> ALWAYS_EXECUTE and NEVER_EXECUTE policies properly and hangs on
> QUERY_USER_ and ALLOW_EXECUTE_ON_SECURITY_VIOLATION.  This seems fine,
> however there is no code handling the
> DENY_EXECUTE_ON_SECURITY_VIOLATION (0x04) case. Stepping through this
> function shows that the image to be loaded cannot be found in the db
> (correct, as there's no db). Then, the function jumps to its very  end
> and returns EFI_SECURITY_VIOLATION, which skips the aforementioned
> if-statement:
>
> Done:
>   if (Status != EFI_SUCCESS) {
>     //
>     // Policy decides to defer or reject the image; add its information in image executable information table.
>     //
>     NameStr = ConvertDevicePathToText (File, FALSE, TRUE);
>     AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);
>     if (NameStr != NULL) {
>       DEBUG((EFI_D_INFO, "The image doesn't pass verification: %s\n", NameStr));
>       FreePool(NameStr);
>     }
>     Status = EFI_SECURITY_VIOLATION;
>   }
>
>   if (SignatureList != NULL) {
>     FreePool (SignatureList);
>   }
>
>   return Status;
> }

This quote is indeed from
"SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c",
and indeed the EFI_SECURITY_VIOLATION value returned by it causes the
"if" statement you quoted from "MdeModulePkg/Core/Dxe/Image/Image.c" to
be skipped.

However, that's not the whole story. In
"MdeModulePkg/Core/Dxe/Image/Image.c", in the CoreLoadImageCommon()
function, track the "SecurityStatus" variable from the point where the
EFI_ACCESS_DENIED branch is *not* taken, to the end of the function.
Near the end of the function, "Status" is EFI_SUCCESS, and thus
"SecurityStatus" will be assigned to it (and the function will
ultimately return EFI_SECURITY_VIOLATION):

Done:
  //
  // All done accessing the source file
  // If we allocated the Source buffer, free it
  //
  if (FHand.FreeBuffer) {
    CoreFreePool (FHand.Source);
  }
  if (OriginalFilePath != InputFilePath) {
    CoreFreePool (OriginalFilePath);
  }

  //
  // There was an error.  If there's an Image structure, free it
  //
  if (EFI_ERROR (Status)) {
    if (Image != NULL) {
      CoreUnloadAndCloseImage (Image, (BOOLEAN)(DstBuffer == 0));
      Image = NULL;
    }
  } else if (EFI_ERROR (SecurityStatus)) {
    Status = SecurityStatus;
  }

  //
  // Track the return status from LoadImage.
  //
  if (Image != NULL) {
    Image->LoadImageStatus = Status;
  }

  return Status;
}

Back to your email:

On 10/16/17 18:57, Zmuda, Wojciech wrote:
> Is there anything I'm doing wrong trying to prevent untrusted
> OptionROM execution? If my understanding is correct, my case should
> make DxeImageVerificationHandler () return EFI_ACCESS_DENIED here. For
> example, in the snippet above, Status should be set to
> EFI_ACCESS_DENIED  if Policy == DENY_EXECUTE_ON_SECURITY_VIOLATION.

To me it seems like everything is working by design. The image is loaded
and the security violation is recorded. Please see the documentation of
the EFI_SECURITY_VIOLATION return code in the UEFI-2.7 spec, under
EFI_BOOT_SERVICES.LoadImage():

  Image was loaded and an ImageHandle was created with a valid
  EFI_LOADED_IMAGE_PROTOCOL.. However, the current platform policy
  specifies that the image should not be started.

(This section also has a pointer to  "35.1.5 Deferred Execution".)


I believe the misunderstanding comes from the facts that

(a) the message

  CoreLoadPeImage: There is no EBC interpreter for an EBC image.

is printed by the CoreLoadPeImage() function, and

(b) that CoreLoadImageCommon() calls CoreLoadPeImage() *between* the
EFI_ACCESS_DENIED check that you thought should have been taken -- which
is not the case -- and the final setting of Status to
EFI_SECURITY_VIOLATION (from "SecurityStatus"), which actually *would*
occur, if you had an EBC driver included in your platform firmware.


In other words, CoreLoadImageCommon() identifies (and prepares to
return) the EFI_SECURITY_VIOLATION status code, based on
PcdOptionRomImageVerificationPolicy. However, before the function could
finish processing (and return this error code), it comes across a more
severe error -- no EBC interpreter -- which takes priority both with and
without the Secure Boot operational mode.

If you want to be completely sure, I suggest including the EBC
interpreter in your platform, and retesting.

(Based on commit 81d9f86f8a71 ("ArmVirtPkg: enable EBC interpreter for
AArch64", 2016-07-29), adding the EBC interpreter should be easy.)

Again, to me it looks like everything is working by design.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: SecurityPkg: untrusted OptionRom is loaded despite DENY_EXECUTE_ON_SECURITY_VIOLATION set.
  2017-10-17 19:07     ` Zmuda, Wojciech
@ 2017-10-17 19:22       ` Laszlo Ersek
  0 siblings, 0 replies; 4+ messages in thread
From: Laszlo Ersek @ 2017-10-17 19:22 UTC (permalink / raw)
  To: Zmuda, Wojciech; +Cc: edk2-devel@lists.01.org, Zhang, Jonathan, Dabros, Jan

On 10/17/17 21:07, Zmuda, Wojciech wrote:
> Hi Laszlo,
> 
> 
> Thanks for this detailed explanation. I repeated the experiment with EBC interpreter included, this time stepping much further in the code, and I actually can see that with Secure Boot enabled the EFI_SECURITY_VIOLATION status is propagated, which does not let the image to load. With Secure Boot disabled, I get early EFI_SUCCESS and the image is loaded, as far as I can see in the debugger. Everything seems to work as designed, then.
> 
> 
> I have one more question in this matter, if I may ask: why doesn't this path terminate immediately after finding that the OptionROM is untrusted? It wouldn't be executed anyway, with or without EBC interpreter, so why bother with further checking?

"It wouldn't be executed anyway" is not correct -- please search my
previous email (also visible below, in the context) for the following
string:

  Trust() DXE Service

You can read about that service in the PI spec, version 1.6, Volume 2,
7.3 Dispatcher Services.

(It might not apply to files loaded from option ROMs -- but in general,
considering the EFI_SECURITY_FILE_AUTHENTICATION_STATE member function's
contract, promotion of the security status seems to be the point, after
which gBS->StartImage() is expected to succeed. I guess!)

I can't answer your question any better, so please talk to SecurityPkg
maintainers if you'd like to see further details.

Thanks!
Laszlo

> 
> 
> Thank you very much,
> 
> Wojciech
> 
> ________________________________
> From: Laszlo Ersek <lersek@redhat.com>
> Sent: Monday, October 16, 2017 11:11:01 AM
> To: Zmuda, Wojciech
> Cc: edk2-devel@lists.01.org
> Subject: Re: [edk2] SecurityPkg: untrusted OptionRom is loaded despite DENY_EXECUTE_ON_SECURITY_VIOLATION set.
> 
> On 10/16/17 18:57, Zmuda, Wojciech wrote:
>> Hello,
>>
>> I'd like to ask you for help with understanding Secure Boot policy
>> mechanism, specifically DENY_EXECUTE_ON_SECURITY_VIOLATION for
>> PcdOptionRomImageVerificationPolicy. The OptionROM in my setup is
>> loaded while, in my opinion, it should be rejected.
>>
>> I'm testing the following scenario: Secure Boot is enabled with my own
>> PK and KEK enrolled, but with no db, to make sure nothing unsigned or
>> signed by somebody else but me can be executed. A PCIe card with
>> OptionROM (some EBC code) is installed.
>> gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy  is
>> set to 0x04 in my platform's package. What I expect, is the OptionROM
>> execution being denied, as it is not signed by my certificate. What I
>> observe, on the other hand, is a message on the console, that no EBC
>> interpreter is found, which suggest, that the  OptionROM is loaded,
>> just fails at the execution of EBC. The same message is printed when
>> Secure Boot is disabled.
>>
>> I tried to understand the code by stepping through it in the DS-5. The
>> following part of
>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> seems suspicious to me:
>>
>>     SecurityStatus = gSecurity->FileAuthenticationState (
>>                                   gSecurity,
>>                                   AuthenticationStatus,
>>                                   OriginalFilePath
>>                                   );
>>   }
> 
> This code is not from "DxeImageVerificationLib.c"; it is from
> CoreLoadImageCommon() in "MdeModulePkg/Core/Dxe/Image/Image.c". With
> more context:
> 
>   } else if ((gSecurity != NULL) && (OriginalFilePath != NULL)) {
>     //
>     // Verify the Authentication Status through the Security Architectural Protocol
>     //
>     SecurityStatus = gSecurity->FileAuthenticationState (
>                                   gSecurity,
>                                   AuthenticationStatus,
>                                   OriginalFilePath
>                                   );
>   }
> 
>>
>>   //
>>   // Check Security Status.
>>   //
>>   if (EFI_ERROR (SecurityStatus) && SecurityStatus != EFI_SECURITY_VIOLATION) {
>>     if (SecurityStatus == EFI_ACCESS_DENIED) {
>>       //
>>       // Image was not loaded because the platform policy prohibits the image from being loaded.
>>       // It's the only place we could meet EFI_ACCESS_DENIED.
>>       //
>>       *ImageHandle = NULL;
>>     }
>>     Status = SecurityStatus;
>>     Image = NULL;
>>     goto Done;
>> }
>>
>> The return code of gSecurity->FileAuthenticationState () (which is
>> implemented in
>> MdeModulePkg/Core/Dxe/Image/Image.c:DxeImageVerificationHandler ()),
>> is EFI_SECURITY_VIOLATION. Such return code skips this if-statement,
>> that prevents the image to be loaded.  According to the comment in the
>> if-statement: for the policy to be respected,
>> gSecurity->FileAuthenticationState () should return EFI_ACCESS_DENIED.
> 
> No, that's a different kind of "platform policy".
> 
> The PCD that you mention is indeed platform policy, but only for
> DxeImageVerificationLib. The platform policy being referred-to in this
> comment is on the level of the gSecurity->FileAuthenticationState()
> function call; and for that, we have to review the type definition of
> the
> 
>   EFI_SECURITY_ARCH_PROTOCOL.FileAuthenticationState
> 
> member function. The type name for this member function is
> EFI_SECURITY_FILE_AUTHENTICATION_STATE, and it is defined in
> "MdePkg/Include/Protocol/Security.h".
> 
> I will not quote the entire description from said header file, just the
> part that explains the difference:
> 
>>   @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and
>>                                 the platform policy dictates that File should be placed
>>                                 in the untrusted state. A file may be promoted from
>>                                 the untrusted to the trusted state at a future time
>>                                 with a call to the Trust() DXE Service.
>>   @retval EFI_ACCESS_DENIED     The file specified by File did not authenticate, and
>>                                 the platform policy dictates that File should not be
>>                                 used for any purpose.
> 
> Back to your email:
> 
> On 10/16/17 18:57, Zmuda, Wojciech wrote:
>> That being said, I stepped through DxeImageVerificationHandler (). The
>> PCD with OptionROM policy is checked correctly. The function handles
>> ALWAYS_EXECUTE and NEVER_EXECUTE policies properly and hangs on
>> QUERY_USER_ and ALLOW_EXECUTE_ON_SECURITY_VIOLATION.  This seems fine,
>> however there is no code handling the
>> DENY_EXECUTE_ON_SECURITY_VIOLATION (0x04) case. Stepping through this
>> function shows that the image to be loaded cannot be found in the db
>> (correct, as there's no db). Then, the function jumps to its very  end
>> and returns EFI_SECURITY_VIOLATION, which skips the aforementioned
>> if-statement:
>>
>> Done:
>>   if (Status != EFI_SUCCESS) {
>>     //
>>     // Policy decides to defer or reject the image; add its information in image executable information table.
>>     //
>>     NameStr = ConvertDevicePathToText (File, FALSE, TRUE);
>>     AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);
>>     if (NameStr != NULL) {
>>       DEBUG((EFI_D_INFO, "The image doesn't pass verification: %s\n", NameStr));
>>       FreePool(NameStr);
>>     }
>>     Status = EFI_SECURITY_VIOLATION;
>>   }
>>
>>   if (SignatureList != NULL) {
>>     FreePool (SignatureList);
>>   }
>>
>>   return Status;
>> }
> 
> This quote is indeed from
> "SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c",
> and indeed the EFI_SECURITY_VIOLATION value returned by it causes the
> "if" statement you quoted from "MdeModulePkg/Core/Dxe/Image/Image.c" to
> be skipped.
> 
> However, that's not the whole story. In
> "MdeModulePkg/Core/Dxe/Image/Image.c", in the CoreLoadImageCommon()
> function, track the "SecurityStatus" variable from the point where the
> EFI_ACCESS_DENIED branch is *not* taken, to the end of the function.
> Near the end of the function, "Status" is EFI_SUCCESS, and thus
> "SecurityStatus" will be assigned to it (and the function will
> ultimately return EFI_SECURITY_VIOLATION):
> 
> Done:
>   //
>   // All done accessing the source file
>   // If we allocated the Source buffer, free it
>   //
>   if (FHand.FreeBuffer) {
>     CoreFreePool (FHand.Source);
>   }
>   if (OriginalFilePath != InputFilePath) {
>     CoreFreePool (OriginalFilePath);
>   }
> 
>   //
>   // There was an error.  If there's an Image structure, free it
>   //
>   if (EFI_ERROR (Status)) {
>     if (Image != NULL) {
>       CoreUnloadAndCloseImage (Image, (BOOLEAN)(DstBuffer == 0));
>       Image = NULL;
>     }
>   } else if (EFI_ERROR (SecurityStatus)) {
>     Status = SecurityStatus;
>   }
> 
>   //
>   // Track the return status from LoadImage.
>   //
>   if (Image != NULL) {
>     Image->LoadImageStatus = Status;
>   }
> 
>   return Status;
> }
> 
> Back to your email:
> 
> On 10/16/17 18:57, Zmuda, Wojciech wrote:
>> Is there anything I'm doing wrong trying to prevent untrusted
>> OptionROM execution? If my understanding is correct, my case should
>> make DxeImageVerificationHandler () return EFI_ACCESS_DENIED here. For
>> example, in the snippet above, Status should be set to
>> EFI_ACCESS_DENIED  if Policy == DENY_EXECUTE_ON_SECURITY_VIOLATION.
> 
> To me it seems like everything is working by design. The image is loaded
> and the security violation is recorded. Please see the documentation of
> the EFI_SECURITY_VIOLATION return code in the UEFI-2.7 spec, under
> EFI_BOOT_SERVICES.LoadImage():
> 
>   Image was loaded and an ImageHandle was created with a valid
>   EFI_LOADED_IMAGE_PROTOCOL.. However, the current platform policy
>   specifies that the image should not be started.
> 
> (This section also has a pointer to  "35.1.5 Deferred Execution".)
> 
> 
> I believe the misunderstanding comes from the facts that
> 
> (a) the message
> 
>   CoreLoadPeImage: There is no EBC interpreter for an EBC image.
> 
> is printed by the CoreLoadPeImage() function, and
> 
> (b) that CoreLoadImageCommon() calls CoreLoadPeImage() *between* the
> EFI_ACCESS_DENIED check that you thought should have been taken -- which
> is not the case -- and the final setting of Status to
> EFI_SECURITY_VIOLATION (from "SecurityStatus"), which actually *would*
> occur, if you had an EBC driver included in your platform firmware.
> 
> 
> In other words, CoreLoadImageCommon() identifies (and prepares to
> return) the EFI_SECURITY_VIOLATION status code, based on
> PcdOptionRomImageVerificationPolicy. However, before the function could
> finish processing (and return this error code), it comes across a more
> severe error -- no EBC interpreter -- which takes priority both with and
> without the Secure Boot operational mode.
> 
> If you want to be completely sure, I suggest including the EBC
> interpreter in your platform, and retesting.
> 
> (Based on commit 81d9f86f8a71 ("ArmVirtPkg: enable EBC interpreter for
> AArch64", 2016-07-29), adding the EBC interpreter should be easy.)
> 
> Again, to me it looks like everything is working by design.
> 
> Thanks
> Laszlo
> 



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2017-10-17 19:19 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <CY1PR0701MB185234B24E2441C917CB087EEF4F0@CY1PR0701MB1852.namprd07.prod.outlook.com>
2017-10-16 16:57 ` SecurityPkg: untrusted OptionRom is loaded despite DENY_EXECUTE_ON_SECURITY_VIOLATION set Zmuda, Wojciech
2017-10-16 18:11   ` Laszlo Ersek
2017-10-17 19:07     ` Zmuda, Wojciech
2017-10-17 19:22       ` Laszlo Ersek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox