需要首先获取特定用户的SID。
这是一些步骤,
- 验证输入参数。
- 为可能足够大的SID和域名创建缓冲区。
- 在循环中,调用
LookupAccountName
以检索提供的帐户名的SID。如果SID的缓冲区或域名的缓冲区不够大,则分别在cbSid
或中返回所需的缓冲区大小cchDomainName
,并在下一次调用之前分配一个新的缓冲区LookupAccountName
。请注意,当lpSystemName
参数设置为NULL时,将在本地系统上检索信息 。 - 释放分配给域名缓冲区的内存。
然后将SID传递给SetEntriesInAclA函数,
SetEntriesInAcl函数通过将新的访问控制或审核控制信息合并到现有ACL结构中来创建新的访问控制列表(ACL)。
这是传递给SetEntriesInAcl的结构参数,
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); ea.grfAccessPermissions = GENERIC_ALL; //权限设置 ea.grfAccessMode = SET_ACCESS; ea.grfInheritance = NO_INHERITANCE; ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; ea.Trustee.TrusteeType = TRUSTEE_IS_USER; //特定的对象 ea.Trustee.ptstrName = (LPTSTR)sid; //特定用户的sid // Create a new ACL that contains the new ACEs. dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
完整代码:
#pragma comment(lib, "advapi32.lib") #include <windows.h> #include <stdio.h> #include <aclapi.h> #include <tchar.h> #include <mq.h.> HRESULT GetSid( LPCWSTR wszAccName, PSID* ppSid ) { // Validate the input parameters. if (wszAccName == NULL || ppSid == NULL) { return MQ_ERROR_INVALID_PARAMETER; } // Create buffers that may be large enough. // If a buffer is too small, the count parameter will be set to the size needed. const DWORD INITIAL_SIZE = 32; DWORD cbSid = 0; DWORD dwSidBufferSize = INITIAL_SIZE; DWORD cchDomainName = 0; DWORD dwDomainBufferSize = INITIAL_SIZE; WCHAR* wszDomainName = NULL; SID_NAME_USE eSidType; DWORD dwErrorCode = 0; HRESULT hr = MQ_OK; // Create buffers for the SID and the domain name. *ppSid = (PSID) new BYTE[dwSidBufferSize]; if (*ppSid == NULL) { return MQ_ERROR_INSUFFICIENT_RESOURCES; } memset(*ppSid, 0, dwSidBufferSize); wszDomainName = new WCHAR[dwDomainBufferSize]; if (wszDomainName == NULL) { return MQ_ERROR_INSUFFICIENT_RESOURCES; } memset(wszDomainName, 0, dwDomainBufferSize * sizeof(WCHAR)); // Obtain the SID for the account name passed. for (; ; ) { // Set the count variables to the buffer sizes and retrieve the SID. cbSid = dwSidBufferSize; cchDomainName = dwDomainBufferSize; if (LookupAccountNameW( NULL, // Computer name. NULL for the local computer wszAccName, *ppSid, // Pointer to the SID buffer. Use NULL to get the size needed, &cbSid, // Size of the SID buffer needed. wszDomainName, // wszDomainName, &cchDomainName, &eSidType )) { if (IsValidSid(*ppSid) == FALSE) { wprintf(L"The SID for %s is invalid. ", wszAccName); dwErrorCode = MQ_ERROR; } break; } dwErrorCode = GetLastError(); // Check if one of the buffers was too small. if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER) { if (cbSid > dwSidBufferSize) { // Reallocate memory for the SID buffer. wprintf(L"The SID buffer was too small. It will be reallocated. "); FreeSid(*ppSid); *ppSid = (PSID) new BYTE[cbSid]; if (*ppSid == NULL) { return MQ_ERROR_INSUFFICIENT_RESOURCES; } memset(*ppSid, 0, cbSid); dwSidBufferSize = cbSid; } if (cchDomainName > dwDomainBufferSize) { // Reallocate memory for the domain name buffer. wprintf(L"The domain name buffer was too small. It will be reallocated. "); delete[] wszDomainName; wszDomainName = new WCHAR[cchDomainName]; if (wszDomainName == NULL) { return MQ_ERROR_INSUFFICIENT_RESOURCES; } memset(wszDomainName, 0, cchDomainName * sizeof(WCHAR)); dwDomainBufferSize = cchDomainName; } } else { wprintf(L"LookupAccountNameW failed. GetLastError returned: %d ", dwErrorCode); hr = HRESULT_FROM_WIN32(dwErrorCode); break; } } delete[] wszDomainName; return hr; } void main() { PSID sid; GetSid(L"strive", &sid); DWORD dwRes, dwDisposition; PACL pACL = NULL; PSECURITY_DESCRIPTOR pSD = NULL; EXPLICIT_ACCESS ea; SECURITY_ATTRIBUTES sa; HANDLE lRes = NULL; // Initialize an EXPLICIT_ACCESS structure for an ACE. // The ACE will allow Everyone read access to the key. ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); ea.grfAccessPermissions = GENERIC_ALL; ea.grfAccessMode = SET_ACCESS; ea.grfInheritance = NO_INHERITANCE; ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; ea.Trustee.TrusteeType = TRUSTEE_IS_USER; ea.Trustee.ptstrName = (LPTSTR)sid; // Create a new ACL that contains the new ACEs. dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL); if (ERROR_SUCCESS != dwRes) { _tprintf(_T("SetEntriesInAcl Error %u "), GetLastError()); goto Cleanup; } // Initialize a security descriptor. pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); if (NULL == pSD) { _tprintf(_T("LocalAlloc Error %u "), GetLastError()); goto Cleanup; } if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { _tprintf(_T("InitializeSecurityDescriptor Error %u "), GetLastError()); goto Cleanup; } // Add the ACL to the security descriptor. if (!SetSecurityDescriptorDacl(pSD, TRUE, // bDaclPresent flag pACL, FALSE)) // not a default DACL { _tprintf(_T("SetSecurityDescriptorDacl Error %u "), GetLastError()); goto Cleanup; } // Initialize a security attributes structure. sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = pSD; sa.bInheritHandle = FALSE; // Use the security attributes to set the security descriptor // when you create a key. lRes = CreateFile(_T("D:\File.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, &sa, OPEN_ALWAYS, 0, NULL); if (lRes != NULL) { _tprintf(_T("Create file success ")); } Cleanup: if (pACL) LocalFree(pACL); if (pSD) LocalFree(pSD); if (lRes) CloseHandle(lRes); return; }
结果: 创建了一个文本,我们可以检查文本的属性来验证是否成功。
文档参考: 在C ++中为新对象创建安全描述符
C-C++ Code Example: Creating a Security Descriptor
拓展:
我们可以使用SetNamedSecurityInfo函数,将访问权限限制为特定的用户帐户和/或组。 不过如果具有足够权限的合适用户帐户运行您的应用程序(或与此相关的任何应用程序),或者您的应用程序(或其他应用程序)冒充了该帐户,则它将能够访问该文件。
案例参考: 使用C ++制作具有仅用户可以查看和编辑的权限的文件
关于ACL和ACE的详细介绍: 关于Windows安全权限的学习(三)
部分内容(防止丢失):
一个安全描述符包含以下安全信息:
- 两个安全标识符(Security identifiers),简称为SID,分别是OwnerSid和GroupSid. 所谓SID就是每次当我们创建一个用户或一个组的时候,系统会分配给改用户或组一个唯一SID,当你重新安装系统后,也会得到一个唯一的SID。SID是唯一的,不随用户的删除而分配到另外的用户使用。
请记住,SID永远都是唯一的SIF是由计算机名、当前时间、当前用户态线程的CPU耗费时间的总和三个参数决定以保证它的唯一性。
例: S-1-5-21-1763234323-3212657521-1234321321-500 - 一个DACL(Discretionary Access Control List),其指出了允许和拒绝某用户或用户组的存取控制列表。 当一个进程需要访问安全对象,系统就会检查DACL来决定进程的访问权。如果一个对象没有DACL,那么就是说这个对象是任何人都可以拥有完全的访问权限。
- 一个SACL(System Access Control List),其指出了在该对象上的一组存取方式(如,读、写、运行等)的存取控制权限细节的列表。
- 还有其自身的一些控制位。SECURITY_DESCRIPTOR_CONTROL
DACL和SACL构成了整个存取控制列表Access Control List,简称ACL,ACL中的每一项,我们叫做ACE(Access Control Entry),ACL中的每一个ACE。