EternalWindows
セキュリティコンテキスト / ポリシーとレジストリ

前節では、ローカル セキュリティ ポリシーを使用して、規則の設定を行いました。 今回は、追加された規則の確認とポリシーの取得について説明します。 ソフトウェア制限ポリシーに関するデータは、次のレジストリキーに格納されています。

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Safer

レジストリエディタで確認すると次のようになっています。

サブキーとして0、131072、262144の3つが存在していますが、 これはセキュリティレベルを10進数で表した値です。 上から順に、「許可しない」、「基本ユーザー」、「制限しない」という意味であり、 設定した規則はこれらいずれかのサブキーとして存在することになります。 たとえば上図であれば、許可しないとして設定されたパス規則と、 基本ユーザーとして設定された証明書規則、 そして制限しないとして設定されたパス規則が存在することが分かります。

CodeIdentifiersキーに存在するエントリには、ポリシーに関する情報が含まれています。 追加した規則を動作させるためには、ポリシーが正しく適用されている必要があります。 各種エントリの意味は、次のようになります。

エントリ 意味
AuthenticodeEnabled 証明書の規則を有効するかどうか。証明書の規則を追加する場合は、このポリシーが適用されている必要がある。 適用する場合は値が1になり、しない場合は0になる。
DefaultLevel デフォルトのセキュリティレベル。
ExecutableTypes 実行可能コードと見なされるファイルの拡張子。
PolicyScope ポリシーをすべてのユーザーに適用するか、管理者を除くすべてのユーザーに適用するか。 前者の場合は0で、後者の場合は1。
TransparentEnabled ポリシーの適用をDLLなどのライブラリを除くソフトウェアのファイルすべてにするか、 ソフトウェアのファイル(DLL含む)すべてにするか。 前者の場合は1で、後者の場合は2。
LogFileName テキストファイルのフルパスを指定する。ポリシーが比較される場合、その内容がこのファイルに書き込まれる。

ユーザーからすれば、このEXEファイルやスクリプトファイルは絶対に安全だから制限の必要はない、 と見なすことができたりしますが、実際にはこれは保障できるものではありません。 これらのファイルは内部でDLLの関数を呼び出すことになっているため、 そのDLLに不正なコードがあった場合は、そのコードを実行してしまうことになるからです。 つまり、EXEファイルやスクリプトファイルの実行が安全であるかどうかは、 内部的に使用されるDLLのコードに依存しているのです。 こうした内部で動作するDLLも規制の対象にする場合はTransparentEnabledに2を指定しますが、 プロセスの起動時におけるDLLのマッピング数は非常に多いため、 ある程度のパフォーマンスの低下が起きる可能性があります。

上記したレジストリエントリの値は、SaferGetPolicyInformationで取得することができます。

BOOL WINAPI SaferGetPolicyInformation(
  DWORD dwScopeId,
  SAFER_POLICY_INFO_CLASS SaferPolicyInfoClass,
  DWORD InfoBufferSize,
  PVOID InfoBuffer,
  PDWORD InfoBufferRetSize,
  LPVOID lpReserved
);

dwScopeIdは、SAFER_SCOPEID_MACHINEかSAFER_SCOPEID_USERを指定します。 SaferPolicyInfoClassは、SAFER_POLICY_INFO_CLASS型として定義されている列挙定数を指定します。 InfoBufferSizeは、InfoBufferに指定したバッファのサイズを指定します。 InfoBufferは、指定したポリシーの値を受け取るバッファを指定します。 InfoBufferRetSizeは、InfoBufferに格納されたデータのサイズを受け取る変数のアドレスを指定します。 不要な場合は、NULLを指定することができます。 lpReservedは、予約されているためNULLを指定します。

SAFER_POLICY_INFO_CLASS型として定義されている列挙定数の一部を次に示します。

定数 意味
SaferPolicyLevelList 存在する全てのセキュリティレベルを取得する。
SaferPolicyEnableTransparentEnforcement TransparentEnabledエントリの値を取得する。dwScopeIdにSAFER_SCOPEID_MACHINEを指定しておく。
SaferPolicyDefaultLevel DefaultLevelエントリの値を取得する。
SaferPolicyScopeFlags PolicyScopeエントリの値を取得する。dwScopeIdにSAFER_SCOPEID_MACHINEを指定しておく。
SaferPolicyAuthenticodeEnabled AuthenticodeEnabledエントリの値を取得する。dwScopeIdにSAFER_SCOPEID_MACHINEを指定しておく。

SaferPolicyLevelList以外の定数を指定する場合は、 SaferGetPolicyInformationの呼び出しは非常に簡単です。 これらの値は常にDWORD型の変数で受け取ることができます。

DWORD dwValue;
SaferGetPolicyInformation(SAFER_SCOPEID_MACHINE, SaferPolicyAuthenticodeEnabled, sizeof(DWORD), &dwValue, NULL, NULL);

第2引数に取得したい値を示す定数を指定し、第4引数に値を受け取る変数のアドレスを指定します。 これはDWORD型であるため、第3引数にそのサイズを指定しています。 SaferPolicyAuthenticodeEnabledを指定する場合は、第1引数にSAFER_SCOPEID_MACHINEを指定します。

今回のプログラムは、SaferGetPolicyInformationでSaferPolicyLevelListを指定する例を示しています。 ソフトウェア制限ポリシーの関数を利用するためには、winsafer.hのインクルードが必要です。

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

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	DWORD i;
	DWORD dwSize;
	DWORD dwLevels[100];

	SaferGetPolicyInformation(SAFER_SCOPEID_USER, SaferPolicyLevelList, sizeof(dwLevels), dwLevels, &dwSize, NULL);

	for (i = 0; i < dwSize / sizeof(DWORD); i++) {
		if (dwLevels[i] == SAFER_LEVELID_DISALLOWED)
			MessageBox(NULL, TEXT("許可しない"), TEXT("OK"), MB_OK);
		else if (dwLevels[i] == SAFER_LEVELID_NORMALUSER)
			MessageBox(NULL, TEXT("基本ユーザー"), TEXT("OK"), MB_OK);
		else if (dwLevels[i] == SAFER_LEVELID_FULLYTRUSTED)
			MessageBox(NULL, TEXT("制限しない"), TEXT("OK"), MB_OK);
		else
			MessageBox(NULL, TEXT("その他のレベル"), TEXT("OK"), MB_OK);
	}

	return 0;
}

SaferPolicyLevelListを指定した場合、第4引数には存在するセキュリティレベルが配列として返ります。 そのため、受け取れるだけの大きなバッファとしてdwLevelsを宣言しています。 第5引数には第4引数に格納されたデータのサイズが格納され、 たとえばこの値が12であれば、4で割ることで配列の要素数を特定できたことになります。

実行可能コードを含むファイル

アプリケーションの実行中に何らかのファイルのパスを取得した場合、 そのファイルが実行可能コードを含むかどうかを確認したいような場合があります。 ExecutableTypesエントリには、こうしたファイルの拡張子が列挙されているため、 これをレジストリ関数で取得して比較することもできますが、 SaferiIsExecutableFileTypeを呼び出した方が効率的です。

if (SaferiIsExecutableFileType(L"sample.exe", TRUE))
	MessageBox(NULL, TEXT("実行可能コードを含むファイルです。"), TEXT("OK"), MB_OK);

SaferiIsExecutableFileTypeの第1引数は、確認したいファイルのフルパスを指定します。 この関数はファイルの拡張子がExecutableTypesエントリに含まれているかを調べるだけであるため、 指定したファイルが実際に存在している必要はありません。 第2引数にTRUEを指定した場合は、.exeファイルを含めて拡張子を確認しますが、 FALSEを指定した場合は.exeファイルを除いて拡張子を確認します。 既定でExecutableTypesエントリに含まれているのは、 .exeの他にインストール時に利用される.msiや.infがありますが、 スクリプトファイルである.vbsや.jsは含まれていません。 実際にはこれらは実行可能コードを含むわけですが、 そのように判断されるためにはExecutableTypesエントリに追加している必要があります。 なお、SaferiIsExecutableFileTypeの第2引数にFALSEを指定した場合の動作は、 AssocIsDangerousという関数と同一であると思われます。



戻る