EternalWindows
アクセスコントロール / トラスティベースの設定

これまで述べてきたように、オブジェクトにアクセスコントロールを適応するためにはDACLを作成しなければなりませんが、 これはそれほど簡単な作業ではありません。 まず、アクセスの許可対象とするアカウントのSIDを取得しなければなりませんし、 そのSIDを考慮してDACLのサイズを計算する必要もあります。 ただし、今回紹介するSetEntriesInAclを呼び出せば、 こうした作業は全てアプリケーションから隠蔽されます。

DWORD WINAPI SetEntriesInAcl(
  ULONG cCountOfExplicitEntries,
  PEXPLICIT_ACCESS pListOfExplicitEntries,
  PACL OldAcl,
  PACL *NewAcl
);

cCountOfExplicitEntriesは、第2引数の要素数を指定します。 pListOfExplicitEntriesは、トラスティの制御情報を格納したEXPLICIT_ACCESS構造体の配列を指定します。 OldAclは、既存のDACLを指定します。 NULLを指定した場合は、第2引数のACEだけが格納されたDACLが作成されますが、 既存のDACLを指定した場合は、そのDACLのACEも新しいDACLに含まれます。 NewAclは、新しく作成したDACLを受け取る変数のアドレスを指定します。

1つのEXPLICIT_ACCESS構造体は、1人のトラスティとそのトラスティの制御情報を格納しています。 トラスティというのは、ACEに指定可能なアカウントのことであり、 制御情報はアクセスマスクやアクセスモードを意味します。 言いかえれば、EXPLICIT_ACCESS構造体は、1つのACEに関する情報を格納しています。 EXPLICIT_ACCESS構造体を初期化するには、BuildExplicitAccessWithNameを呼び出します。

VOID WINAPI BuildExplicitAccessWithName(
  PEXPLICIT_ACCESS pExplicitAccess,
  LPTSTR pTrusteeName,
  DWORD AccessPermissions,
  ACCESS_MODE AccessMode,
  DWORD Inheritance
);

pExplicitAccessは、EXPLICIT_ACCESS構造体のアドレスを指定します。 pTrusteeNameは、アカウントの名前を指定します。 AccessPermissionsは、アクセスマスクを指定します。 AccessModeは、アクセスモードを指定します。 追加するACEがアクセス許可ACEである場合は、GRANT_ACCESSを指定します。 Inheritanceは、継承に関する定数を指定します。 フォルダやレジストリキーのような階層を持つオブジェクトでない場合は、0を指定します。

今回のプログラムは、カレントディレクトリに存在するsample.txtに2つのACEを追加します。 1つのACEは、Administratorsグループに全てのアクセスを許可し、 もう1つのACEはAuthenticated Usersに読み取りを許可します。

#include <windows.h>
#include <aclapi.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	int             i;
	int             nAceCount = 2;
	LPTSTR          lpszAccountName[] = {TEXT("Administrators"), TEXT("Authenticated Users")};
	DWORD           dwAccessMask[] = {GENERIC_ALL, GENERIC_READ};
	EXPLICIT_ACCESS explicitAccess[2];
	PACL            pDacl;
	
	for (i = 0; i < nAceCount; i++) 
		BuildExplicitAccessWithName(&explicitAccess[i], lpszAccountName[i], dwAccessMask[i], GRANT_ACCESS, 0);	

	SetEntriesInAcl(nAceCount, explicitAccess, NULL, &pDacl);
	
	if (SetNamedSecurityInfo(TEXT("sample.txt"), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, pDacl, NULL) == ERROR_SUCCESS)
		MessageBox(NULL, TEXT("DACLの設定に成功しました。"), TEXT("OK"), MB_OK);
	else
		MessageBox(NULL, TEXT("DACLの設定に失敗しました。"), NULL, MB_ICONWARNING);

	LocalFree(pDacl);

	return 0;
}

複数のアカウントにアクセスを許可する関係上、 lpszAccountNameでは複数のアカウント名を宣言しています。 また、アクセスマスクに関しても、それぞれのアカウントで異なるため複数定義し、 ACE情報を表すEXPLICIT_ACCESS構造体も配列として宣言しています。 explicitAccessの各要素をBuildExplicitAccessWithNameで初期化したら、 これをSetEntriesInAclに指定し、作成されたDACLを第4引数で受け取ります。 第3引数にはNULLを指定しているため、 explicitAccessで表されるACEだけがDACLに含まれることになります。

Authenticated Usersは、認証されたユーザーのトークンに追加されます。 今回の場合、ユーザーが管理者としてログオンしていなければ、 Authenticated UsersのACEによって読み取りが許可されることになるでしょう。 Authenticated Usersの代わりにUsersグループを指定してもよいのですが、 Windows Vistaではファイルが共有されるという現象が発生します。

GetExplicitEntriesFromAclについて

EXPLICIT_ACCESS構造体を使用する関数には、 SetEntriesInAcl以外にGetExplicitEntriesFromAclがあります。 この関数は、DACLに格納されているACEの情報とACEの個数を返しますが、 明示的に設定されたACEしか考慮しないという特徴があります。 つまり、オブジェクトのDACLがコンテナオブジェクトのACEを継承している場合は、 そのようなACEに関する情報は取得されません。 既に示したSetEntriesInAclを呼び出すプログラムでは、継承ACEを含まない構成になっているため、 このプログラムを事前に実行してから、次のプログラムを実行すればACEの取得を確認できるはずです。

#include <windows.h>
#include <aclapi.h>

BOOL ConvertSidToName(PSID pSid, LPTSTR lpszName, DWORD dwSizeName);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	DWORD                i;
	TCHAR                szBuf[256];
	TCHAR                szAccountName[256];
	PACL                 pDacl;
	DWORD                dwAceCount;
	PEXPLICIT_ACCESS     pExplicitAccess;
	PSECURITY_DESCRIPTOR pSecurityDescriptor;
	
	if (GetNamedSecurityInfo(TEXT("sample.txt"), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, NULL, &pSecurityDescriptor) != ERROR_SUCCESS) {
		MessageBox(NULL, TEXT("セキュリティ記述子の取得に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}

	GetExplicitEntriesFromAcl(pDacl, &dwAceCount, &pExplicitAccess);

	for (i = 0; i < dwAceCount; i++) {
		ConvertSidToName(pExplicitAccess[i].Trustee.ptstrName, szAccountName, sizeof(szAccountName) / sizeof(TCHAR));
		wsprintf(szBuf, TEXT("アクセスマスク %08X"), pExplicitAccess[i].grfAccessPermissions);
		MessageBox(NULL, szBuf, szAccountName, MB_OK);
	}
	
	LocalFree(pExplicitAccess);
	LocalFree(pSecurityDescriptor);

	return 0;
}

BOOL ConvertSidToName(PSID pSid, LPTSTR lpszName, DWORD dwSizeName)
{
	TCHAR        szDomainName[256];
	DWORD        dwSizeDomain = sizeof(szDomainName) / sizeof(TCHAR);
	SID_NAME_USE sidName;

	return LookupAccountSid(NULL, pSid, lpszName, &dwSizeName, szDomainName, &dwSizeDomain, &sidName);
}

GetExplicitEntriesFromAclでACEの個数を取得し、 その数だけEXPLICIT_ACCESS構造体を走査します。 GetExplicitEntriesFromAclによって取得できるEXPLICIT_ACCESS.TRUSTEE.TrusteeFormはTRUSTEE_IS_SIDであるため、 TRUSTEE.ptstrNameにはアカウントのSIDが格納されています。 よって、これを自作関数でアカウント名に変換し、アクセスマスクと共に表示しています。



戻る