EternalWindows
アクセスコントロール / DACLとACE

セキュリティ記述子に格納されているデータの中で、 アクセスコントロールを定義しているのはDACL(discretionary access control list)です。 DACLはACE(access control entry)が1個以上格納されているリストであり、 1個のACEは1つのアカウント(ユーザーやグループ)のアクセスを制御しています。 たとえば、Aというユーザーがsample.txtを読み取ることができたならば、 sample.txtのDACLにはAというユーザーに読み取りを許可するACEが存在したか、 Aが所属するグループに読み取りを許可するACEが存在したかのどちらかになります。 つまり、オブジェクトに対して読み取りや書き込みが成功するかどうかは、 そのオブジェクトのDACLに要求したアクセスを満たすACEが存在するかどうかで決定されます。

DACLのACEを取得できれば、そのオブジェクトが誰にアカウントを許可しているのかが分かります。 ACEを取得するためには、まずはその個数を知っておかなければなりませんが、 これはGetAclInformationで取得することができます。 ACLはACEを格納するリストであり、特にこのACEがアカウントのアクセスを制御する場合は、 そのACLはDACLと呼ばれます。

BOOL WINAPI GetAclInformation(
  PACL pAcl,
  LPVOID pAclInformation,
  DWORD nAclInformationLength,
  ACL_INFORMATION_CLASS dwAclInformationClass
);

pAclは、ACLを指定します。 pAclInformationは、ACL_SIZE_INFORMATION構造体のアドレスを指定します。 nAclInformationLengthは、pAclInformationに指定した構造体のサイズを指定します。 dwAclInformationClassは、AclSizeInformationという定数を指定します。

ACLからACEを取得するには、GetAceを呼び出します。

BOOL WINAPI GetAce(
  PACL pAcl,
  DWORD dwAceIndex,
  LPVOID *pAce
);

pAclは、ACLを指定します。 dwAceIndexは、取得するACEのインデックスを指定します。 pAceは、ACEを受け取る変数のアドレスを指定します。 通常、ACEはACCESS_ALLOWED_ACE構造体で表現します。

typedef struct _ACCESS_ALLOWED_ACE {
  ACE_HEADER  Header;
  ACCESS_MASK Mask;
  DWORD       SidStart;
} ACCESS_ALLOWED_ACE, *PACCESS_ALLOWED_ACE;

Headerは、ACE_HEADER構造体が格納されます。 Maskは、アクセスマスクが格納されます。 アクセスマスクは、アカウントに許可されている1つ以上のアクセス権です。 SidStartは、アクセスを許可するアカウントのSIDが格納されます。 ACE_HEADER構造体は、次のように定義されています。

typedef struct _ACE_HEADER {
  BYTE AceType;
  BYTE AceFlags;
  WORD AceSize;
} ACE_HEADER, *PACE_HEADER;

AceTypeは、ACEのタイプが格納されます。 通常は、ACCESS_ALLOWED_ACE_TYPEになります。 AceFlagsは、継承に関するフラグが格納されます。 ACEの継承については、後の節で取り上げます。 AceSizeは、ACEのサイズが格納されます。

今回のプログラムは、DACL内に存在する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)
{
	TCHAR                szBuf[256];
	TCHAR                szAccountName[256];
	DWORD                i;
	PACL                 pDacl;
	PACCESS_ALLOWED_ACE  pAce;
	ACL_SIZE_INFORMATION aclInformation;
	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;
	}
	
	GetAclInformation(pDacl, &aclInformation, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

	for (i = 0; i < aclInformation.AceCount; i++) {
		GetAce(pDacl, i, (LPVOID *)&pAce);
		ConvertSidToName((PSID)&pAce->SidStart, szAccountName, sizeof(szAccountName) / sizeof(TCHAR));
		wsprintf(szBuf, TEXT("アクセスマスク %08X"), pAce->Mask);
		MessageBox(NULL, szBuf, szAccountName, MB_OK);
	}
		
	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);
}

GetNamedSecurityInfoの第3引数にDACL_SECURITY_INFORMATIONを指定しているのが重要です。 これにより、第6引数にDACLが返ることになります。 実際には、pSecurityDescriptorからGetSecurityDescriptorDaclを呼び出してもDACLを取得できるのですが、 GetNamedSecurityInfoの呼び出しで取得した方が簡単といえます。 GetAclInformationでACL_SIZE_INFORMATION構造体を初期化したら、 ACEの数だけGetAceを呼び出します。

for (i = 0; i < aclInformation.AceCount; i++) {
	GetAce(pDacl, i, (LPVOID *)&pAce);
	ConvertSidToName((PSID)&pAce->SidStart, szAccountName, sizeof(szAccountName) / sizeof(TCHAR));
	wsprintf(szBuf, TEXT("アクセスマスク %08X"), pAce->Mask);
	MessageBox(NULL, szBuf, szAccountName, MB_OK);
}

まず、GetAceを呼び出してACCESS_ALLOWED_ACE構造体を取得します。 次に、SidStartメンバをConvertSidToNameという自作関数に指定することで、 SIDに対応するアカウント名を取得しています。 そして、最後にMessageBoxで、アカウント名とそのアカウントに対するアクセスマスクを表示しています。 このアクセスマスクを調べれば、そのアカウントにどのようなアクセスが許可されているのかが分かります。 取得したACEやDACLは、セキュリティ記述子を開放すれば共に開放されます。

デフォルトDACLについて

セキュリティ記述子を指定せずにオブジェクトを作成した場合、 プロセス(またはスレッド)に割り当てられているトークンのデフォルトDACLがオブジェクトに設定されることになります。 次のコードは、デフォルトDACLに格納されている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)
{
	TCHAR                szBuf[256];
	TCHAR                szAccountName[256];
	DWORD                i;
	DWORD                dwLength;
	HANDLE               hToken;
	PACCESS_ALLOWED_ACE  pAce; 
	ACL_SIZE_INFORMATION aclInformation;
	PTOKEN_DEFAULT_DACL  pDefaultDacl;
	
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
		MessageBox(NULL, TEXT("トークンのハンドルの取得に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}
	
	GetTokenInformation(hToken, TokenDefaultDacl, NULL, 0, &dwLength);
	pDefaultDacl = (PTOKEN_DEFAULT_DACL)LocalAlloc(LPTR, dwLength);
	GetTokenInformation(hToken, TokenDefaultDacl, pDefaultDacl, dwLength, &dwLength);
	
	GetAclInformation(pDefaultDacl->DefaultDacl, &aclInformation, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

	for (i = 0; i < aclInformation.AceCount; i++) {
		GetAce(pDefaultDacl->DefaultDacl, i, (LPVOID *)&pAce);
		ConvertSidToName((PSID)&pAce->SidStart, szAccountName, sizeof(szAccountName) / sizeof(TCHAR));
		wsprintf(szBuf, TEXT("アクセスマスク %08X"), pAce->Mask);
		MessageBox(NULL, szBuf, szAccountName, MB_OK);
	}
		
	LocalFree(pDefaultDacl);

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

まず、OpenProcessTokenでトークンを取得し、GetTokenInformationを呼び出します。 このとき、デフォルトDACLを取得するためにTokenDefaultDaclを指定します。 1回目の呼び出しではサイズを取得することに専念し、メモリを確保してから2回目の呼び出しでデフォルトDACLを取得します。 デフォルトDACLには、現在のユーザーとシステムに全てのアクセスを許可するACEが含まれているはずです。

ファイルやレジストリキーのような階層上に存在するオブジェクトは、 セキュリティ記述子を指定せずに作成した場合でも、デフォルトDACLが設定されるとは限りません。 このようなオブジェクトは、コンテナオブジェクト(上位のオブジェクト)がACEの継承を指定していれば、 そのACEを継承することになるからです。 コンテナオブジェクトにACEの継承が指定されていない場合は、 下位のオブジェクトにデフォルトDACLが設定されることになります。



戻る