個人使用でWindowsを普通に使う分には、セキュリティ(ユーザやアプリに応じたファイルアクセスや実行の制御)をプログラミングで調整する場面ってそれほどないかもね。
何か対処したい問題が起きたとしても、最終手段的にLOCAL_SYSTEMになりかわればどうにかなるでしょ、ってのが頭にありますし。
以下、いつもながら間違っているかもしれない内容のメモというか覚え書き。
実際にコード書く場合、書き方の流儀自体は一定なので覚えやすいですが、扱う定数が多いので嫌になります。
ざっくりとした概念の理解
- カーネルオブジェクト(HANDLE)には、セキュリティ記述子をもたせることができる
- セキュリティ記述子には所有者、グループ、アクセス制御リスト(DACL, SACL)が記述される
- アクセスする側のプロセス(スレッド)は各種操作の権利、特権が記述されたトークンをもっている
- ログインしたときに、そのログインユーザの最初のトークンを取得し、以降プロセスを作成するごとに継承される
- セキュリティチェックが必要な場面で、アクセスする側のトークンの記述内容と、必要であればアクセスされる側の保持するセキュリティ記述子とを突き合わせて実行可否を判定する
- ユーザやグループはSID (SecurityID)で管理される
セキュリティ記述子に関しては、こちらの図が大変わかりやすかったです。
目的別で使用するAPIのメモ
既知のSIDを生成する
SID_IDENTIFIER_AUTHORITY SIDAuth =SECURITY_WORLD_SID_AUTHORITY;
AllocateAndInitializeSid(&SIDAuth, 1,
SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSID_Everyone);
ConvertStringSidToSid("S-1-1-0", &pSID);
CreateWellKnowSid(WinWorldSid, NULL, pSID, &dwSIDSize);
SIDから名前を検索する
LookupAccountSid(szComputerName, pSID,
szAccountName, &dwAccountNameSize,
szDomainName, &dwDomainNameSize, &nsnu);
名前からSIDを検索する
LookupAccountName(szComputerName, szAccount,
pSID, &dwLength,
wszDomain, &dwSizeDomain, &snu);
SIDと可読文字を相互に変換する
ConvertSidToStringSid(pSID, &szSID);
ConvertStringSidToSidW(szSID, PSID);
セキュリティ記述子を新規作成
InitializeSecurityDescriptor(&SD, SECURITY_DESCRIPTOR_REVISION);
HANDLEの所有者SIDを含むセキュリティ記述子を取得する
GetKernelObjectSecurity(handle, OWNER_SECURITY_INFORMATION, NULL, 0, &dwLength);
PSECURITY_DESCRIPTOR pSD =(PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwLength);
GetKernelObjectSecurity(handle, OWNER_SECURITY_INFORMATION, pSD, dwLength, &dwLength);
セキュリティ記述子から所有者SIDを抽出する
GetSecurityDescriptorOwner(pSD, &pSID, &bOwnerDefaulted);
セキュリティ記述子に所有者SIDを設定する
SetSecurityDescriptorOwner(pSD, pSID, FALSE);
セキュリティ記述子にDACLを設定する
SetSecurityDescriptorDacl(pSD, TRUE, pDACL, FALSE);
セキュリティ記述子をCreateFile用にSeccurityAttributesでラップ
SECURITY_ATTRIBUTES SecurityAttributes{ sizeof(SECURITY_ATTRIBUTES), pSD, FALSE };
現在のプロセスのトークンを取得する
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
セッションIDのトークンを取得する
WTSQueryUserToken(SessionID, &hToken);
トークンの情報を取得する(ユーザ情報の例)
GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength);
PTOKEN_USER pTU=(PTOKEN_USER)LocalAlloc(LPTR, dwLength);
GetTokenInformation(hToken, TokenUser, pTU, dwLength, &dwLength);
トークンに情報を設定する
SetTokenInformation(hToken, TokenUser, pTU, dwLength);
トークンが含むセキュリティ記述子とDACLを取得する
GetSecurityInfo(hToken, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSD);
トークンにDACLを設定する
SetSecurityInfo(hToken, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, pDACL, NULL);
トークンからリンクトークンを取得し、それを複製する
GetTokenInformation(hTokenBase, TokenLinkedToken, &hTokenLinked, sizeof(HANDLE), &length);
DuplicateTokenEx(hTokenLinked, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hToken);
所有者とアクセス権限を保持するExpicitAccessを作成する
BuildExplicitAccessWithName(&ExplicitAccess, szAccount, FILE_ALL_ACCESS, SET_ACCESS, SUB_CONTAINERS_AND_OBJECTS_INHERIT);
ExplicitAccessをDACLに設定する(第3引数がNULLの場合、DACLを新規作成する)
SetEntriesInAcl(1, &ExplicitAccess, NULL, &pDACL);
DACLを新規作成し、ACEを追加する
static DWORD GetACESize(PSID pSID) { return sizeof(ACCESS_ALLOWED_ACE)+GetLengthSid(pSID)-sizeof(DWORD); }
DWORD dwDACLSize =GetACESize(pSID);
PACL pDACL=(PACL)LocalAlloc(LPTR, dwDACLSize);
InitializeAcl(pDACL, dwDACLSize, ACL_REVISION);
AddAccessAllowedAce(pDACL, ACL_REVISION, dwAccessMask, pSID);
ACLのサイズ情報を取得する
ACL_SIZE_INFORMATION AclSizeInfo;
GetAclInformation(pDACL, (void *)&AclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);
DWORD dwSizeACL=AclSizeInfo.AclBytesInUse;
DACLからACLを取得する
for(DWORD i=0; i<AclSizeInfo.AceCount; i++){
void *pACE;
GetAce(pDAC, i, &pACE);
}
DACLにACEを追加する
AddAce(pDAC, ACL_REVISION, MAXDWORD, pACE, ((PACE_HEADER)pACE)->AceSize);