EternalWindows
セキュリティコンテキスト / 基本ユーザー

ソフトウェア制限ポリシーによって規則が設定されたファイルは、 基本ユーザーとしてコードを実行することがあります。 この事を考えると、アプリケーションを配布する前に基本ユーザーとしての動作確認も 行っておいた方がよいため、今回はこの点について見ていきます。 まず、SaferCreateLevelでセキュリティレベルを表すハンドルを取得します。

BOOL WINAPI SaferCreateLevel(
  DWORD dwScopeId,
  DWORD dwLevelId,
  DWORD OpenFlags,
  SAFER_LEVEL_HANDLE *pLevelHandle,
  LPVOID lpReserved
)

dwScopeIdは、SAFER_SCOPEID_MACHINEまたはSAFER_SCOPEID_USERを指定します。 dwLevelIdは、定義されているセキュリティレベルを指定します。 OpenFlagsは、0で問題ないと思われます。 pLevelHandleは、セキュリティレベルのハンドルを受けとる変数のアドレスを指定します。 lpReservedは、予約されているためNULLを指定します。

dwLevelIdに指定できる定数を次に示します。

セキュリティレベル 効果
SAFER_LEVELID_FULLYTRUSTED トークンは制限されない。
SAFER_LEVELID_NORMALUSER Administratorsが無効SIDとなり、SeChangeNotifyPrivilege以外の特権が削除される。
SAFER_LEVELID_CONSTRAINED 上記の効果に加え、Administrators以外が制限SIDとなる。トークンユーザーも無効になっているように思える。
SAFER_LEVELID_UNTRUSTED 上記の効果に加え、ログオンSIDやLOCAL、独自のグループが無効になる。デスクトップへのアクセスが許可されない。
SAFER_LEVELID_DISALLOWED SaferComputeTokenFromLevelで失敗することになる。

基本ユーザーを表すのはSAFER_LEVELID_NORMALUSERですが、それ以外のレベルを指定することもできます。 SAFER_LEVELID_CONSTRAINEDとSAFER_LEVELID_UNTRUSTEDに相当する制限は、 ローカル セキュリティ ポリシーに存在しません。

セキュリティレベルを表すハンドルを取得すれば、 SaferComputeTokenFromLevelでトークンを作成することができます。 このトークンは制限付きトークンであり、指定したセキュリティレベルによって制限の内容も異なります。

BOOL WINAPI SaferComputeTokenFromLevel(
  SAFER_LEVEL_HANDLE LevelHandle,
  HANDLE InAccessToken,
  PHANDLE OutAccessToken,
  DWORD dwFlags,
  LPVOID lpReserved
);

LevelHandleは、セキュリティレベルのハンドルを指定します。 InAccessTokenは、制限付きトークンの元とするトークンのハンドルを指定します。 NULLを指定した場合は、現在のスレッドのトークンが参照されます。 OutAccessTokenは、作成された制限付きトークンを受け取る変数のアドレスを指定します。 dwFlagsは、0で問題ありません。 lpReservedは、予約されているためNULLを指定します。

不要になったセキュリティレベルのハンドルは、SaferCloseLevelで開放することになります。

BOOL WINAPI SaferCloseLevel(
  SAFER_LEVEL_HANDLE hLevelHandle
);

LevelHandleは、セキュリティレベルのハンドルを指定します。

今回のプログラムは、基本ユーザーとしてコードを実行する例を示しています。

#include <windows.h>
#include <winsafer.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	HANDLE             hTokenRestricted;
	HANDLE             hFile;
	SAFER_LEVEL_HANDLE hLevel;

	SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, 0, &hLevel, NULL);

	if (!SaferComputeTokenFromLevel(hLevel, NULL, &hTokenRestricted, 0, NULL)) {
		MessageBox(NULL, TEXT("制限付きトークンの作成に失敗しました。"), NULL, MB_ICONWARNING);
		SaferCloseLevel(hLevel);
		return 0;
	}

	ImpersonateLoggedOnUser(hTokenRestricted);

	hFile = CreateFile(TEXT("C:\\sample.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		if (GetLastError() == ERROR_ACCESS_DENIED)
			MessageBox(NULL, TEXT("アクセスが拒否されました。"), NULL, MB_ICONWARNING);
		else
			MessageBox(NULL, TEXT("ファイルの作成に失敗しました。"), NULL, MB_ICONWARNING);
		CloseHandle(hTokenRestricted);
		SaferCloseLevel(hLevel);
		return 0;
	}
	
	RevertToSelf();

	MessageBox(NULL, TEXT("ファイルを作成しました。"), TEXT("OK"), MB_OK);

	CloseHandle(hFile);
	CloseHandle(hTokenRestricted);
	SaferCloseLevel(hLevel);

	return 0;
}

SaferCreateLevelにSAFER_LEVELID_NORMALUSERを指定しているため、 基本ユーザーを表すセキュリティレベルが作成されることになります。 これをSaferComputeTokenFromLevelに指定すれば、 基本ユーザーを表す制限付きトークンを作成することができ、 ImpersonateLoggedOnUserで偽装することで、 スレッドは基本ユーザーとして動作することになります。 Cドライブ直下へのファイルの作成は管理者でなければ行えませんが、 管理者として実行しても基本ユーザーとして実行することになるため、 アクセスは必ず拒否されることになります。 ちなみに、UACが有効である場合はスレッドが既に制限されているので、 今回のような処理を行う必要はありません。

SaferComputeTokenFromLevelで基本ユーザーを表す制限付きトークンを作成することは、 CreateRestrictedTokenにLUA_TOKENを指定するのと同じ効果が得られるように思えますが、 実際には両者のトークンは異なっています。 まず、基本ユーザーはSE_CHANGE_NOTIFY_NAME以外の特権が削除されていますが、 LUAは一部の特権しか削除されていません。 また、基本ユーザーの整合性レベルはMediumですが、 LUAでは元にしたトークンの整合性レベルがHighであれば、 作成されるトークンの整合性レベルもHighになります。 この結果、LUAよりも基本ユーザーの方が制限されているといえます。


戻る