最近看了一下msdn上關於windows的存取控制模型。存取控制就是“適當人使用適當的資源right persons use right resources.”
所以,
第一步,作業系統要認證Authentication, 驗證是否是使用者聲明的使用者身份。驗證通過,以後,
第二步,作業系統要授權Authorization, 使用者a要讀取一個檔案,作業系統檢查檔案的安全性描述元security descriptor;
如果“擁有讀取許可權的使用者列表”裡面儲存了“使用者a”,那麼,作業系統繼續開始檔案的讀取操作;
這個過程就是“授權” authorization;
這個文章主要講一下授權(Authorization),授權裡面主要看看存取控制(Access Control Model);
1) 使用者token
使用者登入以後,作業系統給使用者一個access token,裡面主要存放了使用者的許可權privileges, 比如關機,調試,備份系統等等。
使用者建立進程,這個進程就複製這個使用者的token到進程資訊中,進程建立線程(包括主線程),線程自動得到這個token,另外,線程可以“假冒”某個客戶身份,impersonate,線程得到其他使用者的token‘(登入,或者參數傳遞進來),就可以代表這個使用者去訪問只有這個使用者可以訪問的資源。
token裡面還儲存了使用者的安全性識別碼security identifier.
=======================
使用者token資訊的列印
=======================
void DumpTokenInfo(void){ HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) return ; DWORD retLen = 0; GetTokenInformation(hToken, TokenGroupsAndPrivileges, NULL, 0, &retLen); TOKEN_GROUPS_AND_PRIVILEGES * pTgap = (TOKEN_GROUPS_AND_PRIVILEGES * )LocalAlloc(0, retLen); GetTokenInformation(hToken, TokenGroupsAndPrivileges, pTgap, retLen, &retLen); // 查詢sid數組資訊 printf("\n===============Sids=====================\n"); for (DWORD i = 0; i < pTgap->SidCount; ++i) { PSID pSid = pTgap->Sids[i].Sid; SID_NAME_USE sidType; char szName [MAX_PATH] = ""; DWORD dwNameLen = MAX_PATH; char szDomain [MAX_PATH] = ""; DWORD dwDomainLen = MAX_PATH; LookupAccountSidA(NULL , pSid , szName, &dwNameLen , szDomain, &dwDomainLen , &sidType); printf("Sids[%d] = %s@%s\n", i, szName, szDomain); } // 查詢許可權資訊 printf("\n===============Priveleges=====================\n"); for (DWORD i = 0; i < pTgap->PrivilegeCount; ++i) { char szName [MAX_PATH] = ""; DWORD dwNameLen = MAX_PATH; LookupPrivilegeNameA(NULL, &pTgap->Privileges[i].Luid, szName, &dwNameLen); printf("Privileges[%d] = %s\n", i, szName); }}/************************************************************************0:001> !token <<<<windbg列印出來的token資訊>>>>/////////////////////////////////////////////////////////////////////////Thread is not impersonating. Using process token...TS Session ID: 0User: S-1-5-21-2233661284-422353248-3399546490-500Groups: 00 S-1-5-21-2233661284-422353248-3399546490-513Attributes - Mandatory Default Enabled 01 S-1-1-0Attributes - Mandatory Default Enabled 02 S-1-5-32-544Attributes - Mandatory Default Enabled Owner 03 S-1-5-32-545Attributes - Mandatory Default Enabled 04 S-1-5-4Attributes - Mandatory Default Enabled 05 S-1-5-11Attributes - Mandatory Default Enabled 06 S-1-5-15Attributes - Mandatory Default Enabled 07 S-1-5-5-0-48147Attributes - Mandatory Default Enabled LogonId 08 S-1-2-0Attributes - Mandatory Default Enabled 09 S-1-5-64-10Attributes - Mandatory Default Enabled Primary Group: S-1-5-21-2233661284-422353248-3399546490-513Privs: 00 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default 01 0x000000008 SeSecurityPrivilege Attributes - Enabled 02 0x000000011 SeBackupPrivilege Attributes - 03 0x000000012 SeRestorePrivilege Attributes - 04 0x00000000c SeSystemtimePrivilege Attributes - Enabled 05 0x000000013 SeShutdownPrivilege Attributes - Enabled 06 0x000000018 SeRemoteShutdownPrivilege Attributes - Enabled 07 0x000000009 SeTakeOwnershipPrivilege Attributes - Enabled 08 0x000000014 SeDebugPrivilege Attributes - Enabled 09 0x000000016 SeSystemEnvironmentPrivilege Attributes - Enabled 10 0x00000000b SeSystemProfilePrivilege Attributes - Enabled 11 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled 12 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled 13 0x00000000a SeLoadDriverPrivilege Attributes - Enabled 14 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled 15 0x000000005 SeIncreaseQuotaPrivilege Attributes - Enabled 16 0x000000019 SeUndockPrivilege Attributes - Enabled 17 0x00000001c SeManageVolumePrivilege Attributes - Enabled 18 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default 19 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default Auth ID: 0:c32fImpersonation Level: AnonymousTokenType: PrimaryIs restricted token: no.************************************************************************/
=======================
模仿客戶存取特殊許可權檔案
=======================
#include <sys/stat.h>#include <fcntl.h>#include <io.h>int main(int argc, char * argv[]){ char szCmd [0x20]; printf("ImpersonateLoggedOnUser? [Y/N]>"); gets(szCmd); // 模仿使用者 if (!_strnicmp(szCmd, "Y", 1)) { HANDLE hToken = INVALID_HANDLE_VALUE; if (!LogonUserA("test2" , "." , "test2" , LOGON32_LOGON_INTERACTIVE , LOGON32_PROVIDER_DEFAULT , &hToken)) { printf("fail to LogonUserA, error %d\n", GetLastError()); return -1; } if (!ImpersonateLoggedOnUser(hToken)) { printf("fail to ImpersonateLoggedOnUser, error %d\n", GetLastError()); return -1; } } // 實際上是訪問檔案所在的檔案夾資訊 struct _stat _st; if (_stat("f:\\test.txt", &_st)) return -1; printf("fsize=%d\n", _st.st_size); // 真正訪問檔案 int fd = open("f:\\test.txt", O_RDONLY); if (fd > 0) { printf("open file ok.\n"); close(fd); } else { printf("open failed %d.\n", GetLastError()); return -1; } getchar(); return 0;}
2) 安全性描述元
作業系統中的所有資來源物件(進程,線程,檔案,訊號量。。。)都有一個安全性描述元,來描述這個對象可以被哪些使用者訪問,安全性描述元存放2個列表,discretionary access control list
(DACL),sacl。
dacl中存放這ace(access control entries),一個ace包含一個sid和這個sid相關的的訪問權(拒絕,允許,審計)。
========================
安全性描述元資訊的列印
========================
#include "accctrl.h"#include "aclapi.h"#pragma comment(lib, "advapi32.lib")/*調整許可權*/BOOL SetPrivilege( HANDLE hToken, // access token handle LPCTSTR lpszPrivilege, // name of privilege to enable/disable BOOL bEnablePrivilege // to enable or disable privilege ) { TOKEN_PRIVILEGES tp; LUID luid; if ( !LookupPrivilegeValue( NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid ) ) // receives LUID of privilege { printf("LookupPrivilegeValue error: %u\n", GetLastError() ); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if (bEnablePrivilege) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if ( !AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) { printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); return FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { printf("The token does not have the specified privilege. \n"); return FALSE; } return TRUE;}int main(void){ DWORD dwRtnCode = 0; PSID pSidOwner = NULL, pSidGroup=NULL; PACL pDacl = NULL, pSacl = NULL; BOOL bRtnBool = TRUE; LPTSTR AcctName = NULL; LPTSTR DomainName = NULL; DWORD dwAcctName = 1, dwDomainName = 1; SID_NAME_USE eUse = SidTypeUnknown; HANDLE hFile; PSECURITY_DESCRIPTOR pSD = NULL; HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) return -1; SetPrivilege(hToken, SE_SECURITY_NAME, TRUE); // Get the handle of the file object. hFile = CreateFile( TEXT("h:\\workspace\\Test\\test2\\test2\\test2.cpp"), GENERIC_READ | READ_CONTROL | ACCESS_SYSTEM_SECURITY, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // Check GetLastError for CreateFile error code. if (hFile == INVALID_HANDLE_VALUE) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); _tprintf(TEXT("CreateFile error = %d\n"), dwErrorCode); return -1; } // Get the owner SID of the file. dwRtnCode = GetSecurityInfo( hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION , &pSidOwner, &pSidGroup, &pDacl, &pSacl, &pSD); // Check GetLastError for GetSecurityInfo error condition. if (dwRtnCode != ERROR_SUCCESS) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); _tprintf(TEXT("GetSecurityInfo error = %d\n"), dwErrorCode); return -1; } // First call to LookupAccountSid to get the buffer sizes. bRtnBool = LookupAccountSid( NULL, // local computer pSidOwner, AcctName, (LPDWORD)&dwAcctName, DomainName, (LPDWORD)&dwDomainName, &eUse); // Reallocate memory for the buffers. AcctName = (LPTSTR)GlobalAlloc( GMEM_FIXED, dwAcctName); // Check GetLastError for GlobalAlloc error condition. if (AcctName == NULL) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); _tprintf(TEXT("GlobalAlloc error = %d\n"), dwErrorCode); return -1; } DomainName = (LPTSTR)GlobalAlloc( GMEM_FIXED, dwDomainName); // Check GetLastError for GlobalAlloc error condition. if (DomainName == NULL) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); _tprintf(TEXT("GlobalAlloc error = %d\n"), dwErrorCode); return -1; } // Second call to LookupAccountSid to get the account name. bRtnBool = LookupAccountSid( NULL, // name of local or remote computer pSidOwner, // security identifier AcctName, // account name buffer (LPDWORD)&dwAcctName, // size of account name buffer DomainName, // domain name (LPDWORD)&dwDomainName, // size of domain name buffer &eUse); // SID type // Check GetLastError for LookupAccountSid error condition. if (bRtnBool == FALSE) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); if (dwErrorCode == ERROR_NONE_MAPPED) _tprintf(TEXT("Account owner not found for specified SID.\n")); else _tprintf(TEXT("Error in LookupAccountSid.\n")); return -1; } else if (bRtnBool == TRUE) // Print the account name. _tprintf(TEXT("Account owner = %s\n"), AcctName); // 列印dacl的資訊 if (NULL == pDacl || !IsValidAcl(pDacl)) return -1; ACL_SIZE_INFORMATION asi; if (!GetAclInformation(pDacl, &asi, sizeof(asi), AclSizeInformation)) return -1; for (int i = 0; i < asi.AceCount; ++i) { void * pAce = NULL; GetAce(pDacl, i, &pAce); printf("Access Credential Entries[%d] is at %p\n", i, pAce); } return 0;}
=======================
建立帶安全屬性的對象-檔案
=======================
void CreateKeyWithSecDesc(char * filePath){ DWORD dwRes, dwDisposition; LONG lRes; // 為EveryOne組建立一個知名sid SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; PSID pEveryoneSID = NULL; if(!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID)) { _tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError()); goto Cleanup; } // 為ACE初始化一個"顯示訪問"結構 // 這個ACE允許EveryOne讀取這個對象 EXPLICIT_ACCESS ea[2]; ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS)); ea[0].grfAccessPermissions = FILE_GENERIC_READ | FILE_GENERIC_EXECUTE; ea[0].grfAccessMode = SET_ACCESS; ea[0].grfInheritance= NO_INHERITANCE; ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID; // 為Administrator 群組建立一個sid SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY; PSID pAdminSID = NULL; if(! AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID)) { _tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError()); goto Cleanup; } // 為ACE初始化一個"顯示訪問"結構 // 這個ACE允許Administrator 群組完全控制這個對象的權力 ea[1].grfAccessPermissions = FILE_ALL_ACCESS; ea[1].grfAccessMode = SET_ACCESS; ea[1].grfInheritance= NO_INHERITANCE; ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; ea[1].Trustee.ptstrName = (LPTSTR) pAdminSID; // 建立一個新的acl,包含以上設定的2個ACE // ACE機器可讀的,所以用“顯示訪問”結構 // 去建立ACL PACL pACL = NULL; dwRes = SetEntriesInAcl(2, ea, NULL, &pACL); if (ERROR_SUCCESS != dwRes) { _tprintf(_T("SetEntriesInAcl Error %u\n"), GetLastError()); goto Cleanup; } // 分配一個最小"安全性描述元". PSECURITY_DESCRIPTOR pSD = NULL; pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); if (NULL == pSD) { _tprintf(_T("LocalAlloc Error %u\n"), GetLastError()); goto Cleanup; } // 初始化"安全性描述元" if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { _tprintf(_T("InitializeSecurityDescriptor Error %u\n"), GetLastError()); goto Cleanup; } // 把ACL添加到“安全性描述元 ” if (!SetSecurityDescriptorDacl(pSD, TRUE, // bDaclPresent flag pACL, FALSE)) // not a default DACL { _tprintf(_T("SetSecurityDescriptorDacl Error %u\n"), GetLastError()); goto Cleanup; } // 把安全性描述元設定到安全屬性結構中 SECURITY_ATTRIBUTES sa; sa.nLength = sizeof (SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = pSD; sa.bInheritHandle = FALSE; // 使用安全屬性結構作為參數, // 把安全性描述元資訊設定到新建立對象中 HANDLE hFile = CreateFileA(filePath , FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE , FILE_SHARE_READ | FILE_SHARE_WRITE , &sa , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL); if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); } else _tprintf(_T("CreateFile result %u\n"), lRes );Cleanup: if (pEveryoneSID) FreeSid(pEveryoneSID); if (pAdminSID) FreeSid(pAdminSID); if (pACL) LocalFree(pACL); if (pSD) LocalFree(pSD); return;}
3) 授權的過程
一個線程訪問一個資源,系統首先檢查這個線程的impersionate token,如果沒有就是用進程的access token,從裡面擷取到sid,然後遍曆dacl的ace,查看和這個sid相關的每一個ace,知道找的一個拒絕或者允許的ace,就返回。
http://msdn.microsoft.com/en-us/library/windows/desktop/aa374860(v=VS.85).aspx