В шестой части серии учебных материалов, посвященных расширениям Intel Software Guard Extensions (Intel SGX), мы временно откладываем анклав в сторону, чтобы заняться выполнением другого требования, которое мы изложили во второй части (проект приложения): мы посвятим эту серию поддержке двух ветвей кода. Нужно, чтобы наше приложение Tutorial Password Manager работало на ПК как с поддержкой Intel SGX, так и без нее. Большая часть содержимого этого материала взята из статьи Правильное обнаружение расширений Intel Software Guard Extensions в приложениях.

Вместе с этой частью серии предоставляется исходный код.

Все приложения, использующие расширения Intel Software Guard Extensions, должны содержать две ветви кода


Прежде всего важно подчеркнуть, что все приложения, использующие Intel SGX, должны содержать две ветви кода. Даже если приложение написано таким образом, что оно должно выполняться только в случае, если расширения Intel SGX доступны и включены, в приложении должна быть запасная ветвь кода, отображающая пользователю понятное сообщение об ошибке и правильно завершающая работу приложения.

Коротко говоря, приложение не должно аварийно завершать работу и отказывать только из-за того, что приложение не поддерживает Intel SGX.

Выявление проблемы


В пятой части этой серии мы создали первую версию анклава приложения и протестировали его, жестко включив в коде поддержку анклава. Для этого мы установили флаг _supports_sgx в файле PasswordCoreNative.cpp.

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

Разумеется, этот флаг не должен быть включен по умолчанию. Идеология обнаружения компонентов такова: по умолчанию все компоненты отключены, а при обнаружении они включаются. Поэтому первое, что нужно сделать, — вернуть этому флагу значение 0 и тем самым отключить ветвь кода Intel SGX.

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

Впрочем, перед тем как приступить к процедуре обнаружения компонентов, мы устроим для консольного приложения, которое выполняет наш тестовый пакет, CLI Test App, краткий функциональный тест: мы запустим его в системе, не поддерживающей Intel SGX. Если установить для этого флага нулевое значение, приложение не будет использовать ветвь кода Intel SGX и должно нормально работать.

Вот результат, полученный на ноутбуке с процессором Intel Core i7 четвертого поколения под управлением 64-разрядной версии Microsoft Windows* 8.1. Эта система не поддерживает Intel SGX.



Что произошло?


Налицо проблема, хотя ветвь кода с Intel SGX явным образом отключена в программе. Это приложение, в том виде, в котором оно написано, не работает в системе, не поддерживающей Intel SGX. Оно даже не начало выполняться. В чем же дело?

Нужную подсказку нам дает сообщение об ошибке в окне консоли.

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

Рассмотрим библиотеку PasswordManagerCore.dll и ее зависимости.



Помимо основных библиотек ОС, в число зависимостей входят bcrypt.lib и EnclaveBridge.lib, для чего во время выполнения потребуются библиотеки bcrypt.dll и EnclaveBridge.dll. Поскольку библиотека bcrypt.dll поставляется корпорацией Майкрософт и входит в состав ОС, можно исходить из того, что ее зависимости, если таковые существуют, уже установлены. Остается EnclaveBridge.dll.

Рассмотрим зависимости этой библиотеки. Вот что мы видим.



В этом и заключается проблема. Несмотря на то что мы явным образом отключили ветвь кода Intel SGX, EnclaveBridge.dll по-прежнему ссылается на библиотеки времени выполнения Intel SGX. Все символы в модуле объекта должны быть разрешены сразу после его загрузки. Отключение ветви кода Intel SGX не имеет значения: в DLL-библиотеке по-прежнему есть неопределенные символы.

При загрузке PasswordManagerCore.dll эта библиотека разрешает неопределенные символы, загружая bcrypt.dll и EnclaveBridge.dll, причем последняя из этих двух библиотек, в свою очередь, пытается разрешить свои неопределенные файлы, загружая sgx_urts.dll и sgx_uae_service.dll. В системе, где мы попытались запустить тестовое приложение, эти библиотеки отсутствуют, а поскольку ОС не может разрешить все эти символы, она выводит исключение, а программа дает сбой, не успев запуститься.

Эти две DLL-библиотеки входят в состав пакета Intel SGX Platform Software (PSW). Без них невозможно выполнение приложений Intel SGX, написанных с помощью Intel SGX Software Development Kit (SDK). Наше приложение должно работать даже при отсутствии этих библиотек.

Программный пакет платформы


Как уже было сказано выше, рантайм библиотеки входят в состав пакета PSW. Пакет PSW, помимо этих библиотек поддержки, включает следующее.

  • Службы, поддерживающие и обслуживающие блок доверенных вычислений (TCB) в системе
  • Службы, выполняющие определенные операции Intel SGX (например, аттестацию) и управляющие ими
  • Интерфейсы к службам платформы, например к службам доверенного времени и монотонным счетчикам.

Пакет PSW должен быть установлен установщиком приложения при развертывании приложения Intel SGX, поскольку пакет PSW недоступен для прямой загрузки конечным пользователям. Разработчикам программ не следует полагаться на то, что этот пакет будет установлен в системе назначения. Более того, в лицензионном соглашении Intel SGX особо оговорено, что лицензиаты должны сами распространять PSW вместе со своими приложениями.

Мы подробнее обсудим установщик PSW в одном из следующих выпусков этой серии, посвященном упаковке и развертыванию.

Обнаружение поддержки расширений Intel Software Guard Extensions


До сих пор мы занимались лишь проблемой запуска нашего приложения в системах, не поддерживающих Intel SGX, а точнее в системах без пакета PSW. На следующем этапе необходимо уже после запуска приложения определить, поддерживаются ли расширения Intel SGX и включены ли они.

Обнаружение компонента Intel SGX, к сожалению, представляет собой не слишком простую задачу. Система поддерживает Intel SGX, если выполняются следующие четыре условия.

  1. ЦП должен поддерживать Intel SGX.
  2. BIOS должен поддерживать Intel SGX.
  3. В BIOS расширения Intel SGX должны быть либо включены явным образом, либо установлены в состояние «программное управление».
  4. На платформе должен быть установлен пакет PSW.

Обратите внимание, что одной лишь инструкции CPUID недостаточно, чтобы определить возможность поддержки Intel SGX на платформе. Эта инструкция может лишь определить, поддерживает ли ЦП расширения Intel SGX, но не может определить конфигурацию BIOS и установленное в системе программное обеспечение. Если опираться исключительно на результаты инструкции CPUID при принятии решений о поддержке Intel SGX, то при работе программы может возникнуть сбой.

Определение компонентов дополнительно затрудняется еще и тем, что анализ состояния BIOS представляет собой нетривиальную задачу, которую, как правило, невозможно осуществить из пользовательского процесса. К счастью, в пакете Intel SGX SDK предоставляется простое решение: функция sgx_enable_device проверяет наличие расширений Intel SGX и пытается их включить, если в BIOS выбрано программное управление этими расширениями (цель программного управления — предоставить приложениям возможность включать Intel SGX, не требуя от пользователей перезагружать компьютер и запускать настройку BIOS: не самая безопасная и достаточно пугающая процедура, если пользователи не слишком подкованы в техническом плане).

С функцией sgx_enable_device связана всего одна проблема: эта функция входит в состав среды выполнения Intel SGX, следовательно, для ее использования в системе должен быть установлен пакет PSW. Поэтому перед вызовом функции sgx_enable_device нужно определить наличие пакета PSW.

Реализация


Мы определились с областью проблем, требующих решения, поэтому теперь можно составить перечень действий, необходимых для того, чтобы наше приложение с двумя ветвями кода правильно работало. Вот что должно делать наше приложение.

  1. Загружаться и начинать выполнение даже без библиотек среды выполнения Intel SGX.
  2. Определять, установлен ли пакет PSW.
  3. Определять, включены ли расширения Intel SGX (и пытаться их включить).

Загрузка и выполнение без среды выполнения Intel Software Guard Extensions


Наше основное приложение зависит от библиотеки PasswordManagerCore.dll, которая зависит от библиотеки EnclaveBridge.dll, которая, в свою очередь, зависит от среды выполнения Intel SGX. Поскольку необходимо разрешить все символы при загрузке приложения, нужно каким-то образом сделать так, чтобы загрузчик не стал пытаться разрешать символы, поступающие из библиотек среды выполнения Intel SGX. Доступно два варианта.

Вариант 1. Динамическая загрузка


При динамической загрузке отсутствует явная компоновка библиотеки в проекте. Вместо этого используются системные вызовы для загрузки библиотеки во время выполнения, затем происходит поиск имен каждой функции, которые планируется использовать, чтобы получить адреса, по которым они были размещены в памяти. После этого функции, содержащиеся в библиотеке, вызываются опосредованно с помощью указателей.

Метод динамической загрузки нельзя назвать простым и удобным. Даже если нужно лишь несколько функций, может потребоваться немало усилий, чтобы создать прототипы указателей для каждой нужной функции и получать адреса их загрузки по одному. При этом также утрачиваются некоторые преимущества, предоставляемые интегрированной средой разработки (например, помощь при создании прототипов), поскольку вы больше не вызываете функции явным образом по имени.

Динамическая загрузка обычно используется в приложениях с расширяемой архитектурой (например, в подключаемых модулях).

Вариант 2. Отложенная загрузка DLL-библиотек


В этом случае все библиотеки динамически компонуются в проекте, но Windows получает команду отложить загрузку проблемных DLL-библиотек. При отложенной загрузке DLL-библиотек Windows не пытается разрешить символы, определенные библиотекой, при запуске приложения. Вместо этого система дожидается первого вызова программы к функции, определенной в библиотеке. Именно в этот момент библиотека загружается, а символ разрешается (вместе со всеми своими зависимостями). Это, по сути, означает, что библиотека не загружается, пока она не нужна приложению. Преимущество такого подхода состоит в том, что приложения могут ссылаться на библиотеки, не установленные в системе, если не происходит вызов функций, содержащихся в этих библиотеках.

Мы оказываемся именно в такой ситуации, когда флаг компонента Intel SGX отключен, поэтому используем вариант номер 2.

Отложенная загрузка DLL-библиотеки указывается в конфигурации проекта для зависимого приложения или библиотеки. Для Tutorial Password Manager лучше всего использовать отложенную загрузку для EnclaveBridge.dll, поскольку мы вызываем эту библиотеку только при включенной ветви кода Intel SGX. Если эта библиотека не загружается, не будут загружены и две библиотеки среды выполнения Intel SGX.

Соответствующий параметр настраивается на странице Компоновщик > Ввод в окне конфигурации проекта PasswordManagerCore.dll.



После повторной сборки и установки библиотеки в системе с процессором Intel Core четвертого поколения консольное тестовое приложение начинает работать нужным образом.



Обнаружение программного пакета платформы


Перед вызовом функции sgx_enable_device для проверки поддержки Intel SGX на уровне платформы необходимо убедиться, что пакет PSW установлен в системе, поскольку функция sgx_enable_device входит в состав среды выполнения Intel SGX. Для этого лучше всего попытаться загрузить библиотеки рантайма.

Из предыдущего шага мы знаем, что нельзя просто динамически скомпоновать их: это приведет к ошибке при попытке запуска программы, если система не поддерживает Intel SGX (или если не установлен пакет PSW). Но нельзя использовать и отложенную загрузку библиотек: при таком способе загрузки невозможно определить, установлена ли библиотека, поскольку, если она отсутствует, произойдет сбой приложения. Это означает, что для проверки наличие библиотек рантайма необходимо использовать динамическую загрузку.

Библиотеки рантайма PSW должны быть установлены в системную папку Windows, поэтому мы воспользуемся GetSystemDirectory для получения этого пути, а для ограничения области поиска библиотек используем вызов SetDllDirectory. И наконец, для загрузки этих библиотек мы используем функцию LoadLibrary. В случае ошибки любого из этих вызовов мы будем знать, что пакет PSW не установлен и что основному приложению не следует пытаться запускать ветвь кода Intel SGX.

Обнаружение и включение расширений Intel Software Guard Extensions.

Поскольку библиотеки среды выполнения PSW были динамически загружены на предыдущем этапе, теперь достаточно вручную найти символ sgx_enable_device и вызвать его с помощью указателя функции. В результате мы узнаем, включена ли поддержка расширений Intel SGX.

Реализация


Для реализации перечисленных возможностей в приложении Tutorial Password Manager мы создадим новую библиотеку FeatureSupport.dll. Эту библиотеку можно безопасно динамически компоновать с основным приложением, поскольку она не имеет явных зависимостей от других библиотек.

Обнаружение компонентов будет реализовано в классе C++/CLI под названием FeatureSupport. Этот класс также будет содержать некоторые высокоуровневые функции для получения дополнительной информации о состоянии Intel SGX. В редких случаях для включения Intel SGX программным способом может потребоваться перезагрузка, а в еще более редких случаях программное включение может не сработать, поэтому пользователям потребуется явным образом включить поддержку Intel SGX в BIOS.

Объявление класса для FeatureSupport показано ниже.

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);

};

Вот низкоуровневые процедуры, проверяющие наличие пакета PSW и пытающиеся обнаружить и включить 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);
}


Подведение итогов


С описанными изменениями кода мы встроили обнаружение компонентов Intel SGX в наше приложение. Теперь приложение будет правильно работать как в системах с поддержкой Intel SGX, так и без Intel SGX, выбирая соответствующую ветвь кода.

Как уже было сказано выше, в этой части предоставляется пример кода для загрузки.

Прилагаемый архив включает исходный код ядра приложения Tutorial Password Manager, включая новую библиотеку для обнаружения компонентов. Кроме того, мы добавили новую тестовую программу с графическим интерфейсом, автоматически выбирающую ветвь кода Intel SGX, но позволяющую отключить эту ветвь при необходимости (доступно только в случае, если система поддерживает Intel SGX).



Консольная тестовая программа также обновлена для обнаружения Intel SGX, хотя в этой программе невозможно настроить отключение ветви кода Intel SGX без изменения исходного кода.

В дальнейших выпусках


В седьмой части мы вернемся к работе с анклавом для дальнейшей доработки интерфейса. Следите за новостями!

Загружаемые файлы доступны на условиях лицензионного соглашения Intel Software Export Warning.
Поделиться с друзьями
-->

Комментарии (0)