还是请看例程,这个程序比较长,来源于MSDN,我做了一点点修改,并把自己的理解加在注释中,所以,请注意代码中的注释:
#include <windows.h>
#include <tchar.h> #include <stdio.h>
//
使用
Windows
的
HeapAlloc
函数进行动态内存分配
#define myheapalloc(x) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x)) #define myheapfree(x) (HeapFree(GetProcessHeap(), 0, x))
typedef BOOL (WINAPI *SetSecurityDescriptorControlFnPtr)(
IN PSECURITY_DESCRIPTOR pSecurityDescriptor, IN SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, IN SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
typedef BOOL (WINAPI *AddAccessAllowedAceExFnPtr)(
PACL pAcl, DWORD dwAceRevision, DWORD AceFlags, DWORD AccessMask, PSID pSid );
BOOL AddAccessRights(TCHAR *lpszFileName, TCHAR *lpszAccountName,
DWORD dwAccessMask) {
//
声明
SID
变量
SID_NAME_USE snuType;
//
声明和
LookupAccountName
相关的变量(注意,全为
0
,要在程序中动态分配)
TCHAR * szDomain = NULL; DWORD cbDomain = 0; LPVOID pUserSID = NULL; DWORD cbUserSID = 0;
//
和文件相关的安全描述符
SD
的变量
PSECURITY_DESCRIPTOR pFileSD = NULL; // 结构变量 DWORD cbFileSD = 0; // SD 的size
//
一个新的
SD
的变量,用于构造新的
ACL
(把已有的
ACL
和需要新加的
ACL
整合起来)
SECURITY_DESCRIPTOR newSD;
//
和
ACL
相关的变量
PACL pACL = NULL; BOOL fDaclPresent; BOOL fDaclDefaulted; ACL_SIZE_INFORMATION AclInfo;
//
一个新的
ACL
变量
PACL pNewACL = NULL; // 结构指针变量 DWORD cbNewACL = 0; //ACL 的 size
//
一个临时使用的
ACE
变量
LPVOID pTempAce = NULL; UINT CurrentAceIndex = 0; //ACE 在 ACL 中的位置
UINT newAceIndex = 0; //
新添的
ACE
在
ACL
中的位置
//API
函数的返回值,假设所有的函数都返回失败。
BOOL fResult; BOOL fAPISuccess;
SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;
//
下面的两个函数是新的
API
函数,仅在
Windows 2000
以上版本的操作系统支持。
// 在此将从 Advapi32.dll 文件中动态载入。如果你使用 VC++ 6.0 编译程序,而且你想 // 使用这两个函数的静态链接。则请为你的编译加上: /D_WIN32_WINNT=0x0500 // 的编译参数。并且确保你的 SDK 的头文件和 lib 文件是最新的。 SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl = NULL; AddAccessAllowedAceExFnPtr _AddAccessAllowedAceEx = NULL;
__try {
//
// STEP 1: 通过用户名取得 SID // 在这一步中 LookupAccountName 函数被调用了两次,第一次是取出所需要 // 的内存的大小,然后,进行内存分配。第二次调用才是取得了用户的帐户信息。 // LookupAccountName 同样可以取得域用户或是用户组的信息。(请参看 MSDN ) //
fAPISuccess = LookupAccountName(NULL, lpszAccountName,
pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType);
//
以上调用
API
会失败,失败原因是内存不足。并把所需要的内存大小传出。
// 下面是处理非内存不足的错误。
if (fAPISuccess)
__leave; else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { _tprintf(TEXT("LookupAccountName() failed. Error %d\n"), GetLastError()); __leave; }
pUserSID = myheapalloc(cbUserSID);
if (!pUserSID) { _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError()); __leave; }
szDomain = (TCHAR *) myheapalloc(cbDomain * sizeof(TCHAR));
if (!szDomain) { _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError()); __leave; }
fAPISuccess = LookupAccountName(NULL, lpszAccountName,
pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType); if (!fAPISuccess) { _tprintf(TEXT("LookupAccountName() failed. Error %d\n"), GetLastError()); __leave; }
//
// STEP 2: 取得文件(目录)相关的安全描述符 SD // 使用 GetFileSecurity 函数取得一份文件 SD 的拷贝,同样,这个函数也 // 是被调用两次,第一次同样是取 SD 的内存长度。注意, SD 有两种格式:自相关的 // ( self-relative )和 完全的( absolute ), GetFileSecurity 只能取到“自 // 相关的”,而 SetFileSecurity 则需要完全的。这就是为什么需要一个新的 SD , // 而不是直接在 GetFileSecurity 返回的 SD 上进行修改。因为“自相关的”信息 // 是不完整的。
fAPISuccess = GetFileSecurity(lpszFileName,
secInfo, pFileSD, 0, &cbFileSD);
//
以上调用
API
会失败,失败原因是内存不足。并把所需要的内存大小传出。
// 下面是处理非内存不足的错误。 if (fAPISuccess) __leave; else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { _tprintf(TEXT("GetFileSecurity() failed. Error %d\n"), GetLastError()); __leave; }
pFileSD = myheapalloc(cbFileSD);
if (!pFileSD) { _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError()); __leave; }
fAPISuccess = GetFileSecurity(lpszFileName,
secInfo, pFileSD, cbFileSD, &cbFileSD); if (!fAPISuccess) { _tprintf(TEXT("GetFileSecurity() failed. Error %d\n"), GetLastError()); __leave; }
//
// STEP 3: 初始化一个新的 SD // if (!InitializeSecurityDescriptor(&newSD, SECURITY_DESCRIPTOR_REVISION)) { _tprintf(TEXT("InitializeSecurityDescriptor() failed.") TEXT("Error %d\n"), GetLastError()); __leave; }
//
// STEP 4: 从 GetFileSecurity 返回的 SD 中取 DACL // if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL, &fDaclDefaulted)) { _tprintf(TEXT("GetSecurityDescriptorDacl() failed. Error %d\n"), GetLastError()); __leave; }
//
// STEP 5: 取 DACL 的内存 size // GetAclInformation 可以提供 DACL 的内存大小。只传入一个类型为 // ACL_SIZE_INFORMATION 的 structure 的参数,需 DACL 的信息,是为了 // 方便我们遍历其中的 ACE 。 AclInfo.AceCount = 0; // Assume NULL DACL. AclInfo.AclBytesFree = 0; AclInfo.AclBytesInUse = sizeof(ACL);
if (pACL == NULL)
fDaclPresent = FALSE;
//
如果
DACL
不为空,则取其信息。(大多数情况下“自关联”的
DACL
为空)
if (fDaclPresent) { if (!GetAclInformation(pACL, &AclInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) { _tprintf(TEXT("GetAclInformation() failed. Error %d\n"), GetLastError()); __leave; } }
//
// STEP 6: 计算新的 ACL 的 size // 计算的公式是:原有的 DACL 的 size 加上需要添加的一个 ACE 的 size ,以 // 及加上一个和 ACE 相关的 SID 的 size ,最后减去两个字节以获得精确的大小。 cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pUserSID) - sizeof(DWORD);
//
// STEP 7: 为新的 ACL 分配内存 // pNewACL = (PACL) myheapalloc(cbNewACL); if (!pNewACL) { _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError()); __leave; }
// // STEP 8: 初始化新的 ACL 结构 // if (!InitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) { _tprintf(TEXT("InitializeAcl() failed. Error %d\n"), GetLastError()); __leave; }
(程序未完,请看下篇)
//
// STEP 9 如果文件(目录) DACL 有数据,拷贝其中的 ACE 到新的 DACL 中 // // 下面的代码假设首先检查指定文件(目录)是否存在的 DACL ,如果有的话, // 那么就拷贝所有的 ACE 到新的 DACL 结构中,我们可以看到其遍历的方法是采用 // ACL_SIZE_INFORMATION 结构中的 AceCount 成员来完成的。在这个循环中, // 会按照默认的 ACE 的顺序来进行拷贝( ACE 在 ACL 中的顺序是很关键的),在拷 // 贝过程中,先拷贝非继承的 ACE (我们知道 ACE 会从上层目录中继承下来) //
newAceIndex = 0;
if (fDaclPresent && AclInfo.AceCount) {
for (CurrentAceIndex = 0;
CurrentAceIndex < AclInfo.AceCount; CurrentAceIndex++) {
//
// STEP 10: 从 DACL 中取 ACE // if (!GetAce(pACL, CurrentAceIndex, &pTempAce)) { _tprintf(TEXT("GetAce() failed. Error %d\n"), GetLastError()); __leave; }
//
// STEP 11: 检查是否是非继承的 ACE // 如果当前的 ACE 是一个从父目录继承来的 ACE ,那么就退出循环。 // 因为,继承的 ACE 总是在非继承的 ACE 之后,而我们所要添加的 ACE // 应该在已有的非继承的 ACE 之后,所有的继承的 ACE 之前。退出循环 // 正是为了要添加一个新的 ACE 到新的 DACL 中,这后,我们再把继承的 // ACE 拷贝到新的 DACL 中。 // if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags & INHERITED_ACE) break;
//
// STEP 12: 检查要拷贝的 ACE 的 SID 是否和需要加入的 ACE 的 SID 一样 , // 如果一样,那么就应该废掉已存在的 ACE ,也就是说,同一个用户的存取 // 权限的设置的 ACE ,在 DACL 中应该唯一。这在里,跳过对同一用户已设置 // 了的 ACE ,仅是拷贝其它用户的 ACE 。 // if (EqualSid(pUserSID, &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart))) continue;
//
// STEP 13: 把 ACE 加入到新的 DACL 中 // 下面的代码中,注意 AddAce 函数的第三个参数,这个参数的意思是 // ACL 中的索引值,意为要把 ACE 加到某索引位置之后,参数 MAXDWORD 的 // 意思是确保当前的 ACE 是被加入到最后的位置。 // if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce, ((PACE_HEADER) pTempAce)->AceSize)) { _tprintf(TEXT("AddAce() failed. Error %d\n"), GetLastError()); __leave; }
newAceIndex++;
} } |
本文转自 haoel 51CTO博客,原文链接:http://blog.51cto.com/haoel/124665,如需转载请自行联系原作者