关于Win2K设置目录和文件所有者(SetFileSecurity)的问题!

时间:2021-11-08 12:25:42
请看如下代码: 可以成功执行,但有缺陷
procedure TForm1.Button1Click(Sender: TObject);
var
  SD: SECURITY_DESCRIPTOR;
  pUserSID, pGroupSID: Pointer;
  szDomain: PChar;
  snuType: SID_NAME_USE;
  cbSID: DWORD;
  cbDomain: DWORD;
begin
  if InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION) then
    ShowMessage('Initialize ok')
  else
    ShowMessage('Initialize failed');

  cbDomain := 80;
  cbSID := 1024;
  pUserSID := AllocMem(cbSID);
  szDomain := AllocMem(cbDomain);
  if LookupAccountName(nil, 'administrator', pUserSID, cbSID, szDomain, cbDomain, snuType) then
    ShowMessage('LookupAccountName:User ok')
  else
    ShowMessage('LookupAccountName:User failed');

  if SetSecurityDescriptorOwner(@SD, pUserSID, False) then
    ShowMessage('SetSecurityOwner:User ok')
  else
    ShowMessage('SetSecurityOwner:User failed');

  cbDomain := 80;
  cbSID := 1024;
  pGroupSID := AllocMem(cbSID);
  szDomain := AllocMem(cbDomain);
  if LookupAccountName(nil, 'administrators', pGroupSID, cbSID, szDomain, cbDomain, snuType) then
    ShowMessage('LookupAccountName:Group ok')
  else
    ShowMessage('LookupAccountName:Group failed');

  if SetSecurityDescriptorGroup(@SD, pGroupSID, False) then
    ShowMessage('SetSecurityGroup ok')
  else
    ShowMessage('SetSecurityGroup failed');

  if SetFileSecurity('C:\文件夹', OWNER_SECURITY_INFORMATION or GROUP_SECURITY_INFORMATION, @SD) then
    ShowMessage('SetFileSecurity ok')
  else
    ShowMessage('SetFileSecurity failed');
end;

我的问题是:但用系统管理员登录,而且对该目录拥有的权限正常的话,可以成功修改该目录的所有者,但如果对该目录的权限进行限制,连系统管理员都不能访问,即点击“安全”页,连管理员都会提示:“您无权查看或编辑目前 文件夹 的权限设置;但是,您可以取得所有权或更改审核设置”。在这样的情况下,执行上面的代码更改所有者就会失败。但是我用系统内置的“文件夹”属性功能-->“高级”却可以成功的将此目录的所有者更改,进而重新对该目录的权限重新设置。为什么系统功能可以做到,而我上面的代码就失败呢。

其实我这段代码就是为了实现,不小心修改错了某个目录的权限,结果连管理员都不能访问,并且也无法显示该目录的当前所有者的情况下,重新修改该目录的权限,使用户能够恢复正常访问,尽管系统的文件夹功能能够通过一定的方法实现,既从“高级”中重新设置所有者,然后在重新修改权限,但系统功能只能一个个来,如果目录下的文件多的话,非常费时费力,特别是在Win2003中对目录下的文件不能批量设置,只能一次一个,所以希望用代码实现批量设置。

请熟悉Windows内核功能的行家帮我解释解释。最好能指出我的代码的不足,使我能够完善并实现上面的需求。

如果确实不行,如果有其他第三方的软件可以实现,这分也可以给他。三百分可不容易赚的呀。

附:http://www.delphibbs.com/delphibbs/dispq.asp?lid=3129097

20 个解决方案

#1


帮你顶。

#2


帮你顶

#3


//初始化一个ACL
InitializeAcl
设置该用户的Access-Allowed的ACE,其权限为“所有权限
AddAccessAllowedAce
把ACL设置到SD中
SetSecurityDescriptorDacl
最后才是SetFileSecurity

#4


我有一个控件QQ:12198613,留一个地址先

#5


to: zklove(花花公子)

你说的这种方法应该不行,试过,系统提示没有权限。

#6


自己顶!

#7


我再顶!

#8


先继承一次权限或取得一次所有权,
然后清除Accesslist 再添加自己要的

#9


有没有相关代码?

#10


大富翁上的,照搬了

//添加文件、目录访问权限,对应于对象属性页中"安全" 页中的设置
function AddFileAccesRights(const FileName, UserName: string;
  dwAccessMask: DWORD): boolean;
var
  // SID variables
  snuType : SID_NAME_USE;
  szDomain : PChar;
  cbDomain: DWORD;
  pUserSID: Pointer;
  cbUserSID: DWORD;
  // File SD variables.
  pFileSD: PSECURITY_DESCRIPTOR;
  cbFileSD: DWORD;
  // New SD variables.
  pNewSD: PSECURITY_DESCRIPTOR;
  // ACL variables.
  p_ACL : PACL;
  fDaclPresent, fDaclDefaulted : LongBool;
  AclInfo: ACL_SIZE_INFORMATION;
  // New ACL variables.
  pNewACL : PACL;
  cbNewACL: DWORD;
  // Temporary ACE.
  pTempAce: Pointer;
  CurrentAceIndex : Cardinal;
begin
  szDomain := nil;
  cbDomain := 0;
  pUserSID := nil;
  cbUserSID := 0;
  pFileSD := nil;
  cbFileSD := 0;
  pNewSD := nil;
  p_ACL := nil;
  pNewACL := nil;
  pTempAce := nil;

  //
  // STEP 1: Get SID for given user.
  //
  Result := LookupAccountName(nil, PChar(UserName),
    pUserSID, cbUserSID, szDomain, cbDomain, snuType);

  // API should have failed with insufficient buffer.
  if (not Result) and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
    RaiseLastWin32Error;

  pUserSID := AllocMem(cbUserSID);
  szDomain := AllocMem(cbDomain);
  try
    Result := LookupAccountName(nil, PChar(UserName),
       pUserSID, cbUserSID, szDomain, cbDomain, snuType);

    if (not Result) then
      RaiseLastWin32Error;

    // STEP 2: Get security descriptor (SD) for file.
    Result := GetFileSecurity(PChar(FileName),
      DACL_SECURITY_INFORMATION, pFileSD, 0, cbFileSD);

    if (not Result) and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
      RaiseLastWin32Error;

    pFileSD := AllocMem(cbFileSD);

    Result := GetFileSecurity(PChar(FileName),
      DACL_SECURITY_INFORMATION, pFileSD, cbFileSD, cbFileSD);
    if (not Result) then
      RaiseLastWin32Error;

    // STEP 3: Initialize new SD.
    pNewSD := AllocMem(cbFileSD); // Should be same size as FileSD.

    if (not InitializeSecurityDescriptor(pNewSD,
      SECURITY_DESCRIPTOR_REVISION)) then
      RaiseLastWin32Error;

    // STEP 4: Get DACL from SD.
    if (not GetSecurityDescriptorDacl(pFileSD, fDaclPresent, p_ACL,
      fDaclDefaulted)) then
      RaiseLastWin32Error;
    // STEP 5: Get size information for DACL.
    AclInfo.AceCount := 0; // Assume NULL DACL.
    AclInfo.AclBytesFree := 0;
    AclInfo.AclBytesInUse := SizeOf(ACL);

    if (fDaclPresent and Assigned(p_ACL)) then
    begin
      if (not GetAclInformation(p_ACL^, @AclInfo,
          SizeOf(ACL_SIZE_INFORMATION), AclSizeInformation)) then
        RaiseLastWin32Error;

      // STEP 6: Compute size needed for the new ACL.
      cbNewACL := AclInfo.AclBytesInUse + SizeOf(ACCESS_ALLOWED_ACE)
        + GetLengthSid(pUserSID) - SizeOf(DWORD);

      // STEP 7: Allocate memory for new ACL.
      pNewACL := AllocMem(cbNewACL);

      // STEP 8: Initialize the new ACL.
      if (not InitializeAcl(pNewACL^, cbNewACL, ACL_REVISION2)) then
        RaiseLastWin32Error;
      // STEP 9: If DACL is present, copy it to a new DACL.
      if (fDaclPresent) then
      begin
        // STEP 10: Copy the file's ACEs to the new ACL.
        if (AclInfo.AceCount > 0) then
        begin
          for CurrentAceIndex := 0 to AclInfo.AceCount - 1 do
          begin
            // STEP 11: Get an ACE.
            if (not GetAce(p_ACL^, CurrentAceIndex, pTempAce)) then
              RaiseLastWin32Error;
            // STEP 12: Add the ACE to the new ACL.
            if (not AddAce(pNewACL^, ACL_REVISION, MAXDWORD, pTempAce,
               PACE_HEADER(pTempAce)^.AceSize)) then
              RaiseLastWin32Error;
          end
        end
      end;

      // STEP 13: Add the access-allowed ACE to the new DACL.
      {if (not AddAccessAllowedAceEx(pNewACL^, ACL_REVISION2,3, dwAccessMask,
          pUserSID)) then
        RaiseLastWin32Error; }
        if (not AddAccessAllowedAce(pNewACL^, ACL_REVISION, dwAccessMask,
          pUserSID)) then
        RaiseLastWin32Error; 

      // STEP 14: Set the new DACL to the file SD.
      if (not SetSecurityDescriptorDacl(pNewSD, True, pNewACL, False)) then
        RaiseLastWin32Error;

      // STEP 15: Set the SD to the File.
      if (not SetFileSecurity(PChar(FileName), DACL_SECURITY_INFORMATION,
          pNewSD)) then
        RaiseLastWin32Error;
      Result := True;
    end;
  finally
    // STEP 16: Free allocated memory
    if Assigned(pUserSID) then
      FreeMem(pUserSID);
    if Assigned(szDomain) then
      FreeMem(szDomain);
    if Assigned(pFileSD) then
      FreeMem(pFileSD);
    if Assigned(pNewSD) then
      FreeMem(pNewSD);
    if Assigned(pNewACL) then
      FreeMem(pNewACL);
  end;
end;

#11


“dwAccessMask”参数应该取什么值呀?不懂!

#12


谁知道呀?

#13


不知道,学习,帮你顶。

#14


顶顶顶

#15


顶顶顶

#16


我直接调的 WINDOWS 的 cacls 这个命令,写几行代码,做成批处理,
也行的啊

#17


你能提高代码吗?分就给你。

#18


你看看能不能用吧。我贴点上来。。。

#19


//对文件设置权限
//文件名称我是放到lv_File里,
function TFrmMain.MakeBat_File: string;
var
  batFile: TextFile;
  fName, user: string;
  mAll: string;
  i, j: integer;
begin
  result := '';
  mAll := '';
  AssignFile(batFile, TmpFileName);
  Rewrite(batFile);
  try
    for i := lv_File.Items.Count - 1 downto 0 do//循环设置文件
      begin
        fName := lv_File.Items[i].Caption;
        for j := 0 to ckBox_User.Count - 1 do//循环用户名称列表
          if ckBox_user.Checked[j] then
          begin
            user :=  ckBox_user.Items[j];
            if cb_dc_K.Checked then//禁止访问
            begin
              writeLn(batFile, format('echo y| cacls %s /E /D %s /C', [fName, user]));
              continue;// 直接进入下次循环
            end;
            writeLn(batFile, format('echo y| cacls %s /D /R %s /C', [fName, user]));
            if cb_dc_R.Checked then//读
            begin
              writeLn(batFile, format('echo y| cacls %s /e /g %s:R %s /C', [fName, user, mAll]));
              writeLn(batFile, format('echo y| cacls %s /e /p %s:R %s /C', [fName, user, mAll]));
            end;
            if cb_dc_W.Checked then//写
            begin
              writeLn(batFile, format('echo y| cacls %s /e /g %s:W %s /C', [fName, user, mAll]));
              writeLn(batFile, format('echo y| cacls %s /e /p %s:W %s /C', [fName, user, mAll]));
            end;
            if cb_dc_C.Checked then//更改
            begin
              writeLn(batFile, format('echo y| cacls %s /e /g %s:C %s /C', [fName, user, mAll]));
              writeLn(batFile, format('echo y| cacls %s /e /p %s:C %s /C', [fName, user, mAll]));
            end;
            if cb_dc_F.Checked then//完全控制
            begin
              writeLn(batFile, format('echo y| cacls %s /e /g %s:F %s /C', [fName, user, mAll]));
              writeLn(batFile, format('echo y| cacls %s /e /p %s:F %s /C', [fName, user, mAll]));
            end;
          end;
      end;
      result := tmpFileName;
  finally
    CloseFile(batFile);
  end;
  
end;

#20


我试试先

#1


帮你顶。

#2


帮你顶

#3


//初始化一个ACL
InitializeAcl
设置该用户的Access-Allowed的ACE,其权限为“所有权限
AddAccessAllowedAce
把ACL设置到SD中
SetSecurityDescriptorDacl
最后才是SetFileSecurity

#4


我有一个控件QQ:12198613,留一个地址先

#5


to: zklove(花花公子)

你说的这种方法应该不行,试过,系统提示没有权限。

#6


自己顶!

#7


我再顶!

#8


先继承一次权限或取得一次所有权,
然后清除Accesslist 再添加自己要的

#9


有没有相关代码?

#10


大富翁上的,照搬了

//添加文件、目录访问权限,对应于对象属性页中"安全" 页中的设置
function AddFileAccesRights(const FileName, UserName: string;
  dwAccessMask: DWORD): boolean;
var
  // SID variables
  snuType : SID_NAME_USE;
  szDomain : PChar;
  cbDomain: DWORD;
  pUserSID: Pointer;
  cbUserSID: DWORD;
  // File SD variables.
  pFileSD: PSECURITY_DESCRIPTOR;
  cbFileSD: DWORD;
  // New SD variables.
  pNewSD: PSECURITY_DESCRIPTOR;
  // ACL variables.
  p_ACL : PACL;
  fDaclPresent, fDaclDefaulted : LongBool;
  AclInfo: ACL_SIZE_INFORMATION;
  // New ACL variables.
  pNewACL : PACL;
  cbNewACL: DWORD;
  // Temporary ACE.
  pTempAce: Pointer;
  CurrentAceIndex : Cardinal;
begin
  szDomain := nil;
  cbDomain := 0;
  pUserSID := nil;
  cbUserSID := 0;
  pFileSD := nil;
  cbFileSD := 0;
  pNewSD := nil;
  p_ACL := nil;
  pNewACL := nil;
  pTempAce := nil;

  //
  // STEP 1: Get SID for given user.
  //
  Result := LookupAccountName(nil, PChar(UserName),
    pUserSID, cbUserSID, szDomain, cbDomain, snuType);

  // API should have failed with insufficient buffer.
  if (not Result) and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
    RaiseLastWin32Error;

  pUserSID := AllocMem(cbUserSID);
  szDomain := AllocMem(cbDomain);
  try
    Result := LookupAccountName(nil, PChar(UserName),
       pUserSID, cbUserSID, szDomain, cbDomain, snuType);

    if (not Result) then
      RaiseLastWin32Error;

    // STEP 2: Get security descriptor (SD) for file.
    Result := GetFileSecurity(PChar(FileName),
      DACL_SECURITY_INFORMATION, pFileSD, 0, cbFileSD);

    if (not Result) and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
      RaiseLastWin32Error;

    pFileSD := AllocMem(cbFileSD);

    Result := GetFileSecurity(PChar(FileName),
      DACL_SECURITY_INFORMATION, pFileSD, cbFileSD, cbFileSD);
    if (not Result) then
      RaiseLastWin32Error;

    // STEP 3: Initialize new SD.
    pNewSD := AllocMem(cbFileSD); // Should be same size as FileSD.

    if (not InitializeSecurityDescriptor(pNewSD,
      SECURITY_DESCRIPTOR_REVISION)) then
      RaiseLastWin32Error;

    // STEP 4: Get DACL from SD.
    if (not GetSecurityDescriptorDacl(pFileSD, fDaclPresent, p_ACL,
      fDaclDefaulted)) then
      RaiseLastWin32Error;
    // STEP 5: Get size information for DACL.
    AclInfo.AceCount := 0; // Assume NULL DACL.
    AclInfo.AclBytesFree := 0;
    AclInfo.AclBytesInUse := SizeOf(ACL);

    if (fDaclPresent and Assigned(p_ACL)) then
    begin
      if (not GetAclInformation(p_ACL^, @AclInfo,
          SizeOf(ACL_SIZE_INFORMATION), AclSizeInformation)) then
        RaiseLastWin32Error;

      // STEP 6: Compute size needed for the new ACL.
      cbNewACL := AclInfo.AclBytesInUse + SizeOf(ACCESS_ALLOWED_ACE)
        + GetLengthSid(pUserSID) - SizeOf(DWORD);

      // STEP 7: Allocate memory for new ACL.
      pNewACL := AllocMem(cbNewACL);

      // STEP 8: Initialize the new ACL.
      if (not InitializeAcl(pNewACL^, cbNewACL, ACL_REVISION2)) then
        RaiseLastWin32Error;
      // STEP 9: If DACL is present, copy it to a new DACL.
      if (fDaclPresent) then
      begin
        // STEP 10: Copy the file's ACEs to the new ACL.
        if (AclInfo.AceCount > 0) then
        begin
          for CurrentAceIndex := 0 to AclInfo.AceCount - 1 do
          begin
            // STEP 11: Get an ACE.
            if (not GetAce(p_ACL^, CurrentAceIndex, pTempAce)) then
              RaiseLastWin32Error;
            // STEP 12: Add the ACE to the new ACL.
            if (not AddAce(pNewACL^, ACL_REVISION, MAXDWORD, pTempAce,
               PACE_HEADER(pTempAce)^.AceSize)) then
              RaiseLastWin32Error;
          end
        end
      end;

      // STEP 13: Add the access-allowed ACE to the new DACL.
      {if (not AddAccessAllowedAceEx(pNewACL^, ACL_REVISION2,3, dwAccessMask,
          pUserSID)) then
        RaiseLastWin32Error; }
        if (not AddAccessAllowedAce(pNewACL^, ACL_REVISION, dwAccessMask,
          pUserSID)) then
        RaiseLastWin32Error; 

      // STEP 14: Set the new DACL to the file SD.
      if (not SetSecurityDescriptorDacl(pNewSD, True, pNewACL, False)) then
        RaiseLastWin32Error;

      // STEP 15: Set the SD to the File.
      if (not SetFileSecurity(PChar(FileName), DACL_SECURITY_INFORMATION,
          pNewSD)) then
        RaiseLastWin32Error;
      Result := True;
    end;
  finally
    // STEP 16: Free allocated memory
    if Assigned(pUserSID) then
      FreeMem(pUserSID);
    if Assigned(szDomain) then
      FreeMem(szDomain);
    if Assigned(pFileSD) then
      FreeMem(pFileSD);
    if Assigned(pNewSD) then
      FreeMem(pNewSD);
    if Assigned(pNewACL) then
      FreeMem(pNewACL);
  end;
end;

#11


“dwAccessMask”参数应该取什么值呀?不懂!

#12


谁知道呀?

#13


不知道,学习,帮你顶。

#14


顶顶顶

#15


顶顶顶

#16


我直接调的 WINDOWS 的 cacls 这个命令,写几行代码,做成批处理,
也行的啊

#17


你能提高代码吗?分就给你。

#18


你看看能不能用吧。我贴点上来。。。

#19


//对文件设置权限
//文件名称我是放到lv_File里,
function TFrmMain.MakeBat_File: string;
var
  batFile: TextFile;
  fName, user: string;
  mAll: string;
  i, j: integer;
begin
  result := '';
  mAll := '';
  AssignFile(batFile, TmpFileName);
  Rewrite(batFile);
  try
    for i := lv_File.Items.Count - 1 downto 0 do//循环设置文件
      begin
        fName := lv_File.Items[i].Caption;
        for j := 0 to ckBox_User.Count - 1 do//循环用户名称列表
          if ckBox_user.Checked[j] then
          begin
            user :=  ckBox_user.Items[j];
            if cb_dc_K.Checked then//禁止访问
            begin
              writeLn(batFile, format('echo y| cacls %s /E /D %s /C', [fName, user]));
              continue;// 直接进入下次循环
            end;
            writeLn(batFile, format('echo y| cacls %s /D /R %s /C', [fName, user]));
            if cb_dc_R.Checked then//读
            begin
              writeLn(batFile, format('echo y| cacls %s /e /g %s:R %s /C', [fName, user, mAll]));
              writeLn(batFile, format('echo y| cacls %s /e /p %s:R %s /C', [fName, user, mAll]));
            end;
            if cb_dc_W.Checked then//写
            begin
              writeLn(batFile, format('echo y| cacls %s /e /g %s:W %s /C', [fName, user, mAll]));
              writeLn(batFile, format('echo y| cacls %s /e /p %s:W %s /C', [fName, user, mAll]));
            end;
            if cb_dc_C.Checked then//更改
            begin
              writeLn(batFile, format('echo y| cacls %s /e /g %s:C %s /C', [fName, user, mAll]));
              writeLn(batFile, format('echo y| cacls %s /e /p %s:C %s /C', [fName, user, mAll]));
            end;
            if cb_dc_F.Checked then//完全控制
            begin
              writeLn(batFile, format('echo y| cacls %s /e /g %s:F %s /C', [fName, user, mAll]));
              writeLn(batFile, format('echo y| cacls %s /e /p %s:F %s /C', [fName, user, mAll]));
            end;
          end;
      end;
      result := tmpFileName;
  finally
    CloseFile(batFile);
  end;
  
end;

#20


我试试先

#21