最近看了一下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: 0
User: S-1-5-21-2233661284-422353248-3399546490-500
Groups:
00 S-1-5-21-2233661284-422353248-3399546490-513
Attributes - Mandatory Default Enabled
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-32-544
Attributes - Mandatory Default Enabled Owner
03 S-1-5-32-545
Attributes - Mandatory Default Enabled
04 S-1-5-4
Attributes - Mandatory Default Enabled
05 S-1-5-11
Attributes - Mandatory Default Enabled
06 S-1-5-15
Attributes - Mandatory Default Enabled
07 S-1-5-5-0-48147
Attributes - Mandatory Default Enabled LogonId
08 S-1-2-0
Attributes - Mandatory Default Enabled
09 S-1-5-64-10
Attributes - Mandatory Default Enabled
Primary Group: S-1-5-21-2233661284-422353248-3399546490-513
Privs:
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:c32f
Impersonation Level: Anonymous
TokenType: Primary
Is 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;
// 为管理员组创建一个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允许管理员组完全控制这个对象的权力
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,就返回。