You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SetNamedSecurityInfo返回ERROR_SUCCESS但文件DACL未修改的解决方法

解决SetNamedSecurityInfo返回成功但文件DACL未更新的问题

问题根源

  1. 直接修改只读DACL:通过GetNamedSecurityInfo获取的安全描述符及其包含的DACL属于系统分配的只读内存,直接修改是未定义行为。SetFileSecurity对这种情况有兼容性,但SetNamedSecurityInfo要求传入的DACL必须是合法的可写内存对象,所以修改只读DACL后调用它不会生效。
  2. 错误的权限检查逻辑:代码里错误检查了WRITE_DAC权限(修改DACL的权限),但实际需求是移除FILE_WRITE_DATA,这会导致程序提前退出,根本没执行修改操作。

修正方案

  1. 用CopyAcl复制原DACL到可写内存,创建可修改的副本。
  2. 在副本DACL上修改目标ACE的权限掩码。
  3. 调用SetNamedSecurityInfo传入修改后的副本DACL。
  4. 修正权限检查逻辑,改为验证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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 04:12:12