Windows 文件用户权限备份与恢复
前几天遇到一个客户使用我我们备份产品时提出一个需求,想把win下面文件的用户访问控制权限也进行备份,因为如果备份后恢复出来的 文件不带权限,那所有用户都可以访问,这不符合客户原始文件权限标准,所以我就对文件访问控制权限进行了解。
DACL简介
说到DACL 就要从ACL说起,访问控制列表(Access Control List,ACL),其中的每一项叫做访问控制条目(Access Control Entries,ACE)。
访问控制列表是属于安全对象的安全描述符的,根据文档可以看出来,安全描述符中包含了两个跟 ACL 相关的信息,DACL(discretionary access control list)和 SACL(system access control list)
DACL 可以对发起请求的用户或者组进行权限控制,允许或者拒绝它们的访问
SACL 使监视对受保护对象的访问成为可能,其信息会在安全日志中被记录
备份涉及函数介绍
GetNamedSecurityInfo
GetNamedSecurityInfo 函数检索由 name 指定的对象的安全描述符的副本
DWORD GetNamedSecurityInfoW(
[in] LPCWSTR pObjectName,
[in] SE_OBJECT_TYPE ObjectType,
[in] SECURITY_INFORMATION SecurityInfo,
[out, optional] PSID *ppsidOwner,
[out, optional] PSID *ppsidGroup,
[out, optional] PACL *ppDacl,
[out, optional] PACL *ppSacl,
[out, optional] PSECURITY_DESCRIPTOR *ppSecurityDescriptor
);
函数具体说明可点击查阅GetNamedSecurityInfoW 函数 (aclapi.h)
getAclInformation
GetAclInformation 函数 (ACL) 检索有关访问控制列表的信息。
语法
BOOL GetAclInformation(
[in] PACL pAcl,
[out] LPVOID pAclInformation,
[in] DWORD nAclInformationLength,
[in] ACL_INFORMATION_CLASS dwAclInformationClass
);
函数具体说明可点击查阅getAclInformation 函数 (securitybaseapi.h)
GetAce
GetAce 函数获取一个指针,该指针指向访问控制列表中的访问控制项 (ACE) (ACL) 。
BOOL GetAce(
[in] PACL pAcl,
[in] DWORD dwAceIndex,
[out] LPVOID *pAce
);
函数具体说明可点击查阅GetAce 函数 (securitybaseapi.h)
LookupAccountSidW
LookupAccountSid 函数接受 SID) (安全标识符作为输入。 它检索此 SID 的帐户名称和找到此 SID 的第一个域的名称。
BOOL LookupAccountSidW(
[in, optional] LPCWSTR lpSystemName,
[in] PSID Sid,
[out, optional] LPWSTR Name,
[in, out] LPDWORD cchName,
[out, optional] LPWSTR ReferencedDomainName,
[in, out] LPDWORD cchReferencedDomainName,
[out] PSID_NAME_USE peUse
);
函数具体说明可点击查阅LookupAccountSidW 函数 (winbase.h)
通过以上四个函数就可以获取到 恢复时添加用户权限所用到的 用户名,允许或拒绝类型 ,继承,读写改,这四个关键参数 。
实际备份代码
bool BackupProcWIN::GetDACL(const std::wstring& filePath) {
DWORD dw;
PACL pacl;
PSECURITY_DESCRIPTOR psd;
PSID psid = NULL;
dw = GetNamedSecurityInfo(filePath.c_str(), SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION, NULL, NULL, &pacl, NULL, &psd);
if (dw != ERROR_SUCCESS)
{
return false;
}
ACL_SIZE_INFORMATION AclInfo;
GetAclInformation(pacl, &AclInfo, sizeof(AclInfo), AclSizeInformation);
list< FileAttr::Dacl> daclInfo;
for (DWORD i = 0; i < AclInfo.AceCount; i++) {
ACCESS_ALLOWED_ACE* pAce;
if (GetAce(pacl, i, (LPVOID*)&pAce)) {
wchar_t szUser[256], DomainBuffer[256];
DWORD dwRes = sizeof(szUser);
bool result = false;
SID_NAME_USE eUse;
LookupAccountSidW(NULL, &pAce->SidStart, szUser, &dwRes, DomainBuffer, &dwRes, &eUse);
FileAttr::Dacl dacl;
dacl.m_UserName=DATools::UtoA(szUser);//用户名
dacl.m_grfInheritance= pAce->Header.AceFlags;//是否继承
dacl.m_grfAccessPermissions = pAce->Mask;//读写权限
dacl.m_grfAccessMode = pAce->Header.AceType;//允许或拒绝类型
daclInfo.push_back(dacl);
}
}
...
return true;
}
恢复涉及函数介绍
InitializeAcl
InitializeAcl 函数初始化新的 ACL 结构。
BOOL InitializeAcl(
[out] PACL pAcl,
[in] DWORD nAclLength,
[in] DWORD dwAclRevision
);
函数具体说明可点击查阅InitializeAcl 函数 (securitybaseapi.h)
SetEntriesInAclW
SetEntriesInAcl 函数通过将新的访问控制或审核控制信息合并到现有 ACL 结构 (ACL) 创建新的访问控制列表。
DWORD SetEntriesInAclW(
[in] ULONG cCountOfExplicitEntries,
[in, optional] PEXPLICIT_ACCESS_W pListOfExplicitEntries,
[in, optional] PACL OldAcl,
[out] PACL *NewAcl
);
函数具体说明可点击查阅SetEntriesInAclW 函数 (aclapi.h)
SetNamedSecurityInfo
SetNamedSecurityInfo 函数在指定对象的安全描述符中设置指定的安全信息。 调用方按名称标识对象。
DWORD SetNamedSecurityInfoA(
[in] LPSTR pObjectName,
[in] SE_OBJECT_TYPE ObjectType,
[in] SECURITY_INFORMATION SecurityInfo,
[in, optional] PSID psidOwner,
[in, optional] PSID psidGroup,
[in, optional] PACL pDacl,
[in, optional] PACL pSacl
);
函数具体说明可点击查阅SetNamedSecurityInfoA 函数 (aclapi.h)
实际恢复代码
bool RestoreProcWIN::SetFileDACL(std::wstring& filePath) {
list< FileAttr::Dacl> daclInfo;
...
if (daclInfo.size() != 0) {
PACL pNewDACL = nullptr;
InitializeAcl(pNewDACL, daclInfo.size(), ACL_REVISION);
for (auto da : daclInfo) {
// 创建新的访问控制列表(ACL)
EXPLICIT_ACCESS ea;
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = da.m_grfAccessPermissions; // 设置控制权限
if (da.m_grfAccessMode == 0) { //允许访问
ea.grfAccessMode = SET_ACCESS;
}
else if (da.m_grfAccessMode == 1)
{
ea.grfAccessMode = DENY_ACCESS;//拒绝访问
}
ea.grfInheritance = da.m_grfInheritance; // 继承权限至子对象
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
wchar_t * ptstrName = new wchar_t[da.m_UserName.size()];
swprintf(ptstrName, 100, L"%S", da.m_UserName.c_str()); //注意大写
ea.Trustee.ptstrName = ptstrName; // 用户名
//加入新的访问列表
SetEntriesInAclW(1, &ea, pNewDACL, &pNewDACL);
}
if (SetNamedSecurityInfo(const_cast<wchar_t*>(filePath.c_str()), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,nullptr, nullptr, pNewDACL, nullptr) == ERROR_SUCCESS) {
printf("SetNamedSecurityInfo ERROR_SUCCESS \n");
}
}
...
return true;
}