【DACL】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;
}
  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Windows中,要删除一个文件,你需要具有适当的权限。你可以使用Windows API来删除文件,其中一个重要的函数是DeleteFile()。但是,如果文件有DACL(禁止访问控制列表)设置,则可能会阻止文件的删除。在这种情况下,你需要修改DACL以允许文件的删除。 以下是在C++中修改文件DACL以允许删除文件的示例代码: ```cpp #include <Windows.h> #include <AclAPI.h> int main() { LPCWSTR filePath = L"C:\\example\\file.txt"; // 打开文件的句柄 HANDLE fileHandle = CreateFileW(filePath, GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { printf("Failed to open file. Error code: %d\n", GetLastError()); return 1; } // 获取文件的DACL PSECURITY_DESCRIPTOR fileSd; if (!GetSecurityInfo(fileHandle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &fileSd)) { printf("Failed to get file security information. Error code: %d\n", GetLastError()); CloseHandle(fileHandle); return 1; } // 获取文件DACL PACL fileDacl; BOOL daclPresent; BOOL daclDefaulted; if (!GetSecurityDescriptorDacl(fileSd, &daclPresent, &fileDacl, &daclDefaulted)) { printf("Failed to get file DACL. Error code: %d\n", GetLastError()); LocalFree(fileSd); CloseHandle(fileHandle); return 1; } // 将当前进程的SID添加到文件的DACL中 PSID currentUserSid; if (!ConvertStringSidToSidW(L"S-1-5-20", &currentUserSid)) // 这里使用了本地服务的SID,具体可根据实际情况更改 { printf("Failed to convert string SID to SID. Error code: %d\n", GetLastError()); LocalFree(fileSd); CloseHandle(fileHandle); return 1; } EXPLICIT_ACCESSW ea; ZeroMemory(&ea, sizeof(EXPLICIT_ACCESSW)); ea.grfAccessPermissions = DELETE; ea.grfAccessMode = SET_ACCESS; ea.grfInheritance = NO_INHERITANCE; ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; ea.Trustee.TrusteeType = TRUSTEE_IS_USER; ea.Trustee.ptstrName = (LPWSTR)currentUserSid; PACL newDacl; if (ERROR_SUCCESS != SetEntriesInAclW(1, &ea, fileDacl, &newDacl)) { printf("Failed to set ACE in file DACL. Error code: %d\n", GetLastError()); LocalFree(currentUserSid); LocalFree(fileSd); CloseHandle(fileHandle); return 1; } // 将新的DACL设置为文件的安全信息 if (ERROR_SUCCESS != SetSecurityInfo(fileHandle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, newDacl, NULL)) { printf("Failed to set new file security information. Error code: %d\n", GetLastError()); LocalFree(currentUserSid); LocalFree(fileSd); CloseHandle(fileHandle); return 1; } // 关闭文件句柄,删除文件 CloseHandle(fileHandle); DeleteFileW(filePath); LocalFree(currentUserSid); LocalFree(fileDacl); LocalFree(fileSd); printf("File deleted successfully.\n"); return 0; } ``` 在上面的代码中,我们首先打开一个文件句柄,然后获取文件的DACL。接下来,我们使用ConvertStringSidToSidW()函数将当前进程的SID转换为SID结构体。然后,我们创建一个EXPLICIT_ACCESSW结构体,将当前进程的SID添加到其中,并将其设置为允许删除文件。使用SetEntriesInAclW()函数将新的ACE(访问控制项)添加到文件的DACL中。然后,我们将新的DACL设置为文件的安全信息。最后,我们关闭文件句柄并删除文件。 需要注意的是,这个例子只是在演示如何修改文件DACL以允许删除文件。在实际应用中,你需要根据实际情况来修改代码。比如,你需要使用适当的SID来代表你的进程,而不是本地服务的SID。你也需要检查函数返回值以处理错误情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值