📜 ⬆️ ⬇️

Intel Software Guard Extensions Extensions Tutorial. Part 6, two branches of code



In the sixth part of the Intel Software Guard Extensions (Intel SGX) series of tutorials, we temporarily set the enclave aside to tackle the other requirement that we set out in the second part (draft application): we will dedicate this series to supporting the two code branches . Our Tutorial Password Manager application needs to work on PCs with and without Intel SGX support. Most of the content of this material is taken from the article Proper detection of Intel Software Guard Extensions extensions in applications .

Source code is provided with this part of the series.

All applications using the Intel Software Guard Extensions must contain two code branches.


First of all, it is important to emphasize that all applications using Intel SGX must contain two code branches. Even if the application is written in such a way that it should be executed only if the Intel SGX extensions are available and enabled, the application should have a spare code branch that displays a clear error message to the user and the application closes correctly.
')
In short, the application should not crash and refuse only because the application does not support Intel SGX .

Identify the problem


In the fifth part of this series, we created the first version of the enclave of the application and tested it, rigidly including support for the enclave in the code. To do this, we set the _supports_sgx flag in the PasswordCoreNative.cpp file.

PasswordManagerCoreNative::PasswordManagerCoreNative(void) { _supports_sgx= 1; adsize= 0; accountdata= NULL; timer = NULL; } 

Of course, this flag should not be enabled by default. The ideology of the detection of components is as follows: by default, all components are disabled, and upon detection, they are enabled. Therefore, the first thing to do is to return the value 0 to this flag and thereby disable the Intel SGX code branch.

 PasswordManagerCoreNative::PasswordManagerCoreNative(void) { _supports_sgx= 0; adsize= 0; accountdata= NULL; timer = NULL; } 

However, before proceeding with the detection of components, we will arrange for a console application that runs our test suite, the CLI Test App, a brief functional test: we will run it on a system that does not support Intel SGX. If you set this flag to zero, the application will not use the branch of the Intel SGX code and should work fine.

Here is the result obtained on a laptop with a fourth-generation Intel Core i7 processor running the 64-bit version of Microsoft Windows * 8.1. This system does not support Intel SGX.



What happened?


There is a problem, although the code branch with Intel SGX is explicitly disabled in the program. This application, in the form in which it is written, does not work on a system that does not support Intel SGX. It did not even start to run. What is the matter?

The necessary hint gives us an error message in the console window.

System.IO.FileNotFoundException: Could not load file or assembly 'PasswordManagerCore.dll' or one of its dependencies. The specified file could not be found.

Consider the PasswordManagerCore.dll library and its dependencies.



In addition to the main OS libraries, dependencies include bcrypt.lib and EnclaveBridge.lib, which will require the bcrypt.dll and EnclaveBridge.dll libraries at runtime. Since the bcrypt.dll library is supplied by Microsoft and is included with the OS, we can assume that its dependencies, if any, have already been installed. Remains EnclaveBridge.dll.

Consider the dependencies of this library. Here is what we see.



This is the problem. Although we have explicitly disabled the Intel SGX code branch, EnclaveBridge.dll still refers to the Intel SGX runtime libraries. All characters in the object module must be resolved immediately after it is loaded. Disabling the Intel SGX code branch does not matter: there are still undefined characters in the DLL.

When loading PasswordManagerCore.dll, this library resolves undefined characters by loading bcrypt.dll and EnclaveBridge.dll, the latter of these two libraries, in turn, tries to resolve its undefined files by downloading sgx_urts.dll and sgx_uae_service.dll. In the system where we tried to start the test application, these libraries are missing, and since the OS cannot resolve all these characters, it throws an exception, and the program crashes before it starts.

These two DLLs are part of the Intel SGX Platform Software (PSW) package. Without them, it is impossible to run Intel SGX applications written using the Intel SGX Software Development Kit (SDK). Our application should work even in the absence of these libraries.

Platform software package


As mentioned above, runtime libraries are part of the PSW package. The PSW package, in addition to these support libraries, includes the following.


The PSW package must be installed by the application installer when deploying the Intel SGX application, since the PSW package is not available for direct download to end users. Software developers should not rely on this package being installed on a target system. Moreover, the Intel SGX License Agreement specifically states that licensees themselves must distribute PSW along with their applications.

We will discuss the PSW installer in more detail in one of the next releases in this series on packaging and deployment.

Detection of support for Intel Software Guard Extensions extensions


So far, we have dealt only with the problem of launching our application on systems that do not support Intel SGX, and more specifically on systems without the PSW package. The next step is to determine after the launch of the application whether Intel SGX extensions are supported and enabled.

Finding an Intel SGX component, unfortunately, is not a simple task. The system supports Intel SGX if the following four conditions are met.

  1. CPU must support Intel SGX.
  2. The BIOS must support Intel SGX.
  3. In the BIOS, the Intel SGX extensions must either be explicitly enabled or set to the "software control" state.
  4. The platform must have the PSW package installed.

Note that the CPUID instruction alone is not enough to determine whether the platform supports Intel SGX. This instruction can only determine if the CPU supports the Intel SGX extensions, but cannot determine the BIOS configuration and software installed on the system. If you rely solely on the results of the CPUID instruction when making decisions about supporting Intel SGX, then the program may fail.

The definition of components is further complicated by the fact that analyzing the state of the BIOS is a nontrivial task, which, as a rule, cannot be accomplished from the user process. Fortunately, the Intel SGX SDK provides a simple solution: the sgx_enable_device function checks for the presence of the Intel SGX extensions and attempts to enable them if software controls these extensions are selected in the BIOS (software control aims to allow applications to enable Intel SGX without restarting the computer and run BIOS setup: not the safest and most frightening procedure, if users are not too technically savvy).

There is only one problem with the sgx_enable_device function: this function is part of the Intel SGX runtime, therefore, the PSW package must be installed on the system to use it. Therefore, before calling the sgx_enable_device function, you need to determine the presence of the PSW package.

Implementation


We decided on the area of ​​problems that need to be solved, so now we can make a list of actions necessary for our application with two branches of code to work properly. This is what our application should do.

  1. Download and start execution even without the Intel SGX runtime libraries.
  2. Determine whether the PSW package is installed.
  3. Determine whether Intel SGX extensions are enabled (and try to enable them).

Download and run without Intel Software Guard Extensions runtime


Our main application depends on the PasswordManagerCore.dll library, which depends on the EnclaveBridge.dll library, which, in turn, depends on the Intel SGX runtime. Since you need to allow all characters when loading an application, you need to somehow make sure that the bootloader does not try to resolve characters coming from the Intel SGX runtime libraries. Two options are available.

Option 1. Dynamic loading


With dynamic loading, there is no explicit library layout in the project. Instead, system calls are used to load the library at run time, then the names of each function are planned to be used to obtain the addresses at which they were placed in memory. After that, the functions contained in the library are invoked indirectly using pointers.

The method of dynamic loading can not be called simple and convenient. Even if only a few functions are needed, it may take a lot of effort to create prototypes of pointers for each desired function and get their download addresses one by one. It also loses some of the benefits provided by the integrated development environment (for example, assistance in creating prototypes), since you no longer call functions explicitly by name.

Dynamic loading is commonly used in applications with an extensible architecture (for example, plug-ins).

Option 2. Delayed loading of DLL libraries


In this case, all the libraries are dynamically linked in the project, but Windows receives the command to postpone the loading of the problematic DLL libraries. When deferred loading of Windows DLLs does not attempt to resolve the characters defined by the library, when you run the application. Instead, the system waits for the first program call to the function defined in the library. It is at this point that the library is loaded, and the symbol is resolved (along with all its dependencies). This essentially means that the library does not load until the application needs it. The advantage of this approach is that applications can refer to libraries that are not installed in the system, if the functions contained in these libraries are not called.

We find ourselves in precisely such a situation when the flag of the Intel SGX component is disabled, so we use option number 2.

The delayed loading of the DLL library is specified in the project configuration for the dependent application or library. For Tutorial Password Manager, it is best to use deferred download for EnclaveBridge.dll, since we only call this library when the Intel SGX code branch is enabled. If this library does not load, the two Intel SGX runtime libraries will not be loaded either.

The corresponding parameter is configured on the Linker → Input page in the configuration window of the PasswordManagerCore.dll project.



After re-assembling and installing the library on a system with a fourth-generation Intel Core processor, the console test application starts working as desired.



Platform Software Package Discovery


Before calling the sgx_enable_device function to verify that platform-level support for Intel SGX is available, you must ensure that the PSW package is installed on the system, since the sgx_enable_device function is part of the Intel SGX runtime. For this, it is best to try to load runtime libraries.

From the previous step, we know that we cannot simply dynamically assemble them: this will lead to an error when trying to start the program if the system does not support Intel SGX (or if the PSW package is not installed). But you cannot use the deferred loading of libraries: with this method of loading it is impossible to determine whether the library is installed, because, if it is absent, the application will fail. This means that you need to use dynamic loading to check for runtime libraries.

PSW runtime libraries must be installed in the Windows system folder, so we will use GetSystemDirectory to get this path, and to limit the scope of the library search, use the SetDllDirectory call. Finally, to load these libraries, we use the LoadLibrary function. If any of these calls fail, we will know that the PSW package is not installed and that the main application should not attempt to run a branch of the Intel SGX code.

Detect and enable Intel Software Guard Extensions extensions.

Since the PSW runtime libraries were dynamically loaded in the previous step, it is now enough to manually find the sgx_enable_device symbol and call it using a function pointer. As a result, we will know if support for Intel SGX extensions is included.

Implementation


To implement these features in the Tutorial Password Manager application, we will create a new library, FeatureSupport.dll. This library can be dynamically safely linked with the main application, since it does not have explicit dependencies on other libraries.

Component detection will be implemented in a C ++ / CLI class called FeatureSupport. This class will also contain some high-level features for more information on the state of the Intel SGX. In rare cases, a program reboot may be required to enable Intel SGX programmatically, and in even more rare cases, a software enable may not work, so users will need to explicitly enable Intel SGX support in the BIOS.

The class declaration for FeatureSupport is shown below.

 typedef sgx_status_t(SGXAPI *fp_sgx_enable_device_t)(sgx_device_status_t *); public ref class FeatureSupport { private: UINT sgx_support; HINSTANCE h_urts, h_service; // Function pointers fp_sgx_enable_device_t fp_sgx_enable_device; int is_psw_installed(void); void check_sgx_support(void); void load_functions(void); public: FeatureSupport(); ~FeatureSupport(); UINT get_sgx_support(void); int is_enabled(void); int is_supported(void); int reboot_required(void); int bios_enable_required(void); // Wrappers around SGX functions sgx_status_t enable_device(sgx_device_status_t *device_status); }; 

Here are the low-level procedures that check for the presence of the PSW package and are trying to detect and enable Intel SGX.

 int FeatureSupport::is_psw_installed() { _TCHAR *systemdir; UINT rv, sz; // Get the system directory path. Start by finding out how much space we need // to hold it. sz = GetSystemDirectory(NULL, 0); if (sz == 0) return 0; systemdir = new _TCHAR[sz + 1]; rv = GetSystemDirectory(systemdir, sz); if (rv == 0 || rv > sz) return 0; // Set our DLL search path to just the System directory so we don't accidentally // load the DLLs from an untrusted path. if (SetDllDirectory(systemdir) == 0) { delete systemdir; return 0; } delete systemdir; // No longer need this // Need to be able to load both of these DLLs from the System directory. if ((h_service = LoadLibrary(_T("sgx_uae_service.dll"))) == NULL) { return 0; } if ((h_urts = LoadLibrary(_T("sgx_urts.dll"))) == NULL) { FreeLibrary(h_service); h_service = NULL; return 0; } load_functions(); return 1; } void FeatureSupport::check_sgx_support() { sgx_device_status_t sgx_device_status; if (sgx_support != SGX_SUPPORT_UNKNOWN) return; sgx_support = SGX_SUPPORT_NO; // Check for the PSW if (!is_psw_installed()) return; sgx_support = SGX_SUPPORT_YES; // Try to enable SGX if (this->enable_device(&sgx_device_status) != SGX_SUCCESS) return; // If SGX isn't enabled yet, perform the software opt-in/enable. if (sgx_device_status != SGX_ENABLED) { switch (sgx_device_status) { case SGX_DISABLED_REBOOT_REQUIRED: // A reboot is required. sgx_support |= SGX_SUPPORT_REBOOT_REQUIRED; break; case SGX_DISABLED_LEGACY_OS: // BIOS enabling is required sgx_support |= SGX_SUPPORT_ENABLE_REQUIRED; break; } return; } sgx_support |= SGX_SUPPORT_ENABLED; } void FeatureSupport::load_functions() { fp_sgx_enable_device = (fp_sgx_enable_device_t)GetProcAddress(h_service, "sgx_enable_device"); } // Wrappers around SDK functions so the user doesn't have to mess with dynamic loading by hand. sgx_status_t FeatureSupport::enable_device(sgx_device_status_t *device_status) { check_sgx_support(); if (fp_sgx_enable_device == NULL) { return SGX_ERROR_UNEXPECTED; } return fp_sgx_enable_device(device_status); } 


Summarizing


With the code changes described, we embedded the detection of Intel SGX components in our application. Now the application will work correctly both on systems with Intel SGX support and without Intel SGX, choosing the appropriate code branch.

As mentioned above, this section provides sample code for download.

The included archive includes the source code for the Tutorial Password Manager application kernel, including a new library for discovering components. In addition, we added a new GUI test program that automatically selects a branch of the Intel SGX code, but allows you to disable this branch if necessary (only available if the system supports Intel SGX).



The console test program has also been updated to detect Intel SGX, although it is not possible in this program to configure disabling the leg of the Intel SGX code without changing the source code.

In further releases


In the seventh part, we will return to working with the enclave for further refinement of the interface. Follow the news!

The downloads are available under the terms of the license agreement Intel Software Export Warning.

Source: https://habr.com/ru/post/323626/


All Articles