#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++;
}
}
|