SetNamedSecurityInfo返回ERROR_SUCCESS但文件DACL未修改的解决方法
解决SetNamedSecurityInfo返回成功但文件DACL未更新的问题
问题根源
- 直接修改只读DACL:通过
GetNamedSecurityInfo获取的安全描述符及其包含的DACL属于系统分配的只读内存,直接修改是未定义行为。SetFileSecurity对这种情况有兼容性,但SetNamedSecurityInfo要求传入的DACL必须是合法的可写内存对象,所以修改只读DACL后调用它不会生效。 - 错误的权限检查逻辑:代码里错误检查了
WRITE_DAC权限(修改DACL的权限),但实际需求是移除FILE_WRITE_DATA,这会导致程序提前退出,根本没执行修改操作。
修正方案
- 用
CopyAcl复制原DACL到可写内存,创建可修改的副本。 - 在副本DACL上修改目标ACE的权限掩码。
- 调用
SetNamedSecurityInfo传入修改后的副本DACL。 - 修正权限检查逻辑,改为验证
FILE_WRITE_DATA权限。
修正后的完整代码
#include <windows.h> #include <aclapi.h> #include <iostream> int main() { DWORD len; HANDLE hToken = GetCurrentProcessToken(); PSECURITY_DESCRIPTOR sd = nullptr; wchar_t path[] = L"C:\\Users\\Username\\Desktop\\test_security.txt"; BYTE buffer[128]; if (::GetTokenInformation(hToken, TokenUser, buffer, sizeof(buffer), &len)) { auto data = (TOKEN_USER*)buffer; DWORD error = ::GetNamedSecurityInfo(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, nullptr, nullptr, &sd); if (error == ERROR_SUCCESS) { PACL origDacl; BOOL present; BOOL defaulted; if (::GetSecurityDescriptorDacl(sd, &present, &origDacl, &defaulted)) { if (!present) { std::cout << "DACL不存在\n"; ::LocalFree(sd); return EXIT_FAILURE; } if (!origDacl) { std::cout << "检测到NULL DACL,文件完全开放访问\n"; ::LocalFree(sd); return EXIT_FAILURE; } // 复制原DACL到可写内存,创建可修改副本 PACL newDacl = nullptr; if (!CopyAcl(ACL_REVISION, newDacl, origDacl)) { std::cout << "复制DACL失败,错误码:" << GetLastError() << std::endl; ::LocalFree(sd); return EXIT_FAILURE; } PACE_HEADER header; BOOL foundMatchingAce = FALSE; for (int i = 0; i < newDacl->AceCount; i++) { ::GetAce(newDacl, i, (PVOID*)&header); if (header->AceType == ACCESS_ALLOWED_ACE_TYPE) { auto allowed = (ACCESS_ALLOWED_ACE*)header; if (EqualSid((PSID)&allowed->SidStart, data->User.Sid)) { foundMatchingAce = TRUE; // 检查当前用户是否有FILE_WRITE_DATA权限 if ((allowed->Mask & FILE_WRITE_DATA) == 0) { std::cout << "当前用户已无FILE_WRITE_DATA权限\n"; ::LocalFree(newDacl); ::LocalFree(sd); return EXIT_FAILURE; } // 移除FILE_WRITE_DATA权限 allowed->Mask &= ~FILE_WRITE_DATA; break; // 找到目标ACE后退出循环,避免重复处理 } } } if (!foundMatchingAce) { std::cout << "未找到当前用户的ACE条目\n"; ::LocalFree(newDacl); ::LocalFree(sd); return EXIT_FAILURE; } // 调用SetNamedSecurityInfo应用修改后的DACL error = SetNamedSecurityInfo(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, newDacl, nullptr); if (error != ERROR_SUCCESS) { std::cout << "设置DACL失败,错误码:" << error << std::endl; } else { std::cout << "DACL修改成功\n"; } ::LocalFree(newDacl); } ::LocalFree(sd); } else { std::cout << "获取安全信息失败,错误码:" << error << std::endl; } } else { std::cout << "获取令牌信息失败,错误码:" << GetLastError() << std::endl; } return 0; }
额外说明
CopyAcl是关键,它确保我们操作的是可写的DACL副本,让SetNamedSecurityInfo能正确识别修改后的权限。- 新增了ACE条目未找到的处理,避免无效操作。
- 修改后立即退出循环,避免重复处理同一个用户的ACE。
内容的提问来源于stack exchange,提问作者Joe J
相关产品推荐
相关产品推荐

