1

我们面临的问题是,不同的用户组应该能够从一个公共数据目录(例如 c:\ProgramData\xyz)读取和写入文件。

数据是从不同的来源写入的,例如,服务将文件写入其中,用户以后应该能够更改其内容。

现在的问题是,这仅在允许“每个人”读取/写入/更改该目录(和子目录)中的文件时才有效。

我想在安装程序中检查的是是否所有用户都被允许这样做。检查“每个用户”组(或德语中的“Jeder”组)是否在访问列表中。我只有关于 ACL 的基本知识,可以在资源管理器中更改它,但我需要几行代码将我推向正确的方向(在 Delphi 中)。

非常感谢迈克

4

2 回答 2

1

我认为这不是 Delphi 而是 WinAPI 问题。Delphi 没有任何特殊的设施来使这更容易 AFAIK。

从 ACL 获取信息表示您需要在打开的句柄上执行GetSecurityInfo ,然后在获得的 ACL 上执行GetEffectiveRightsFromACL

您指定一个受托人,它可以是名称,但最好使用 SID。“每个人”的名称可以更改,但它有一个特殊的 SID,它在任何 PC 上都有效,谷歌它。好的,这里是:“(S-1-1-0)”。或者您可以使用CreateWellKnownSid并给它WinWorldSid以获得相同的 SID(更合适但更长的方式)。

所有这些都来自五分钟的谷歌搜索,因此请注意错误。

好的,这里有一些代码。

function ConvertStringSidToSid(StringSid: PWideChar; var Sid: PSID): boolean; stdcall; external advapi32 name 'ConvertStringSidToSidW';

function AclGetEffectiveRights(const path, sid: string): cardinal;
var h: THandle; //handle to our directory
  err: integer;
  dacl: PACL; //access control list for the object
  secdesc: pointer;
  tr: TRUSTEE;
  bsid: PSid;
begin
  Result := 0;
 //Open directory
  h := CreateFile(PChar(path), GENERIC_READ, FILE_SHARE_READ, nil,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_BACKUP_SEMANTICS, 0);
    //we need FILE_FLAG_BACKUP_SEMANTICS to open a directory
  if h=INVALID_HANDLE_VALUE then RaiseLastOsError();
  try

    bsid := nil;
   //Query access control list for a directory -- the list you see in the properties box
    err := GetSecurityInfo(h, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
      nil, nil, @dacl, nil, secdesc);
      //GetSecurityInfo can return many things but we only need DACL,
      //and we are required to also get a security descriptor
    if err<>ERROR_SUCCESS then
      raise Exception.CreateFmt('Cannot retrieve DACL: error %d',[err]);
    try
     //Convert string sid to binary sid
      if not ConvertStringSidToSid(PChar(sid), bsid) then
        RaiseLastOsError();

     //Query effective rights for a trustee
      BuildTrusteeWithSid(@tr, bsid);
      err := GetEffectiveRightsFromAcl(dacl^, tr, Result);
      if err<>ERROR_SUCCESS then
        raise Exception.CreateFmt('Cannot calculate effective rights: error %d',[err]);
    finally
     //Documentation says to free some resources this way when we're done with it.
      LocalFree(NativeUint(bsid));
      LocalFree(NativeUint(secdesc));
    end;
  finally
    CloseHandle(h);
  end;
end;

它是这样使用的:

var rights,test: cardinal;
rights := AclGetEffectiveRights('C:\My\Folder','S-1-1-0');
//List rights you want tested
test := FILE_LIST_DIRECTORY + FILE_ADD_FILE + FILE_ADD_SUBDIRECTORY
  + FILE_READ_EA + FILE_WRITE_EA + FILE_TRAVERSE + FILE_DELETE_CHILD;
Result := (rights and test) = test;

可能行不通;在我的 PC 上给出 ACCESS_DENIED 但这可能是因为我的域情况复杂。无论如何,这是一个开始。

于 2013-03-27T13:25:01.103 回答
0

所以.. 上面的版本工作......直到 Windows Update 1903 击中我并且 GetEffectiveRightsFromAcl 总是导致错误 ERROR_NO_SUCH_DOMAIN (这是模糊的,因为这里没有涉及任何域)。所以我需要切换到以下程序:

// ###########################################
// ### translated and extended from https://docs.microsoft.com/de-de/windows/win32/api/aclapi/nf-aclapi-geteffectiverightsfromacla

procedure DisplayAccessMask(Mask : ACCESS_MASK );
begin
{
      // This evaluation of the ACCESS_MASK is an example. 
      // Applications should evaluate the ACCESS_MASK as necessary.

}
   if (((Mask and GENERIC_ALL) = GENERIC_ALL)
      or ((Mask and FILE_ALL_ACCESS) = FILE_ALL_ACCESS)) 
   then
   begin
        OutputDebugString( 'Full control');
        exit;
   end;

   if (((Mask and GENERIC_READ) = GENERIC_READ)
      or ((Mask and FILE_GENERIC_READ) = FILE_GENERIC_READ)) 
   then
       OutputDebugString( 'Read');

   if (((Mask and GENERIC_WRITE) = GENERIC_WRITE)
      or ((Mask and FILE_GENERIC_WRITE) = FILE_GENERIC_WRITE))
   then
         OutputDebugString('Write');

   if (((Mask and GENERIC_EXECUTE) = GENERIC_EXECUTE)
      or ((Mask and FILE_GENERIC_EXECUTE) = FILE_GENERIC_EXECUTE))
   then
       OutputDebugString('Execute');

end;

function CheckMask( MASK : ACCESS_MASK; refMask : ACCESS_MASK ) : boolean;
var msk : ACCESS_MASK;
begin
     msk := 0;

     if (((Mask and GENERIC_READ) = GENERIC_READ)
      or ((Mask and FILE_GENERIC_READ) = FILE_GENERIC_READ)) 
     then
         msk := msk or FILE_GENERIC_READ;

   if (((Mask and GENERIC_WRITE) = GENERIC_WRITE)
      or ((Mask and FILE_GENERIC_WRITE) = FILE_GENERIC_WRITE))
   then
       msk := msk or FILE_GENERIC_WRITE;

   if (((Mask and GENERIC_EXECUTE) = GENERIC_EXECUTE)
      or ((Mask and FILE_GENERIC_EXECUTE) = FILE_GENERIC_EXECUTE))
   then
       msk := msk or FILE_GENERIC_EXECUTE;

   Result := (msk and refMask) = refMask;
end;

function GetAccess(hAuthzClient :AUTHZ_CLIENT_CONTEXT_HANDLE; psd : PSECURITY_DESCRIPTOR) : BOOL;
var AccessRequest : AUTHZ_ACCESS_REQUEST;
    AccessReply : AUTHZ_ACCESS_REPLY;
    buffer : Array[0..1023] of Byte;
begin
     FillChar(AccessRequest, sizeof(AccessRequest), 0);
     FillChar(AccessReply, sizeof(AccessReply), 0);
     FillChar(buffer, sizeof(buffer), 0);

     AccessRequest.DesiredAccess := MAXIMUM_ALLOWED;
     AccessRequest.PrincipalSelfSid := nil;
     AccessRequest.ObjectTypeList := nil;
     AccessRequest.ObjectTypeListLength := 0;
     AccessRequest.OptionalArguments := nil;

     AccessReply.ResultListLength := 1;
     AccessReply.GrantedAccessMask := PACCESS_MASK( LongWord(@Buffer[0])); 
     AccessReply.Error := PDWORD( LongWord( AccessReply.GrantedAccessMask ) + sizeof(Access_Mask));

     Result := AuthzAccessCheck( 0,
                          hAuthzClient,
                          @AccessRequest,
                          0,
                          psd,
                          nil,
                          0,
                          @AccessReply,
                          nil);

     if Result then
     begin
          DisplayAccessMask( AccessReply.GrantedAccessMask^ );
          Result := CheckMask( AccessReply.GrantedAccessMask^, FILE_GENERIC_WRITE or FILE_GENERIC_READ );
     end
     else
         RaiseLastOSError;
end;

function ConvertStringSidToSid(StringSid: PWideChar; var Sid: PSID): boolean; stdcall; external advapi32 name 'ConvertStringSidToSidW';

function ConvertNameToBinarySid(pAccountName : PCHAR): PSID ;
var pDomainName : PChar;
    dwDomainNameSize : DWord;
    aSID : PSID;
    dwSIDSIZE : DWORD;
    sidType : SID_NAME_USE;
begin
     pDomainName := nil;
     dwDomainNameSize := 0;
     aSID := nil;

     LookupAccountName( nil, pAccountName, aSID, dwSIDSIZE, pDomainName, dwDomainNameSize, sidType);
     aSid := Pointer( LocalAlloc( LPTR, dwSIDSIZE*sizeof(char)) );
     pDomainName := Pointer( LocalAlloc(LPTR, dwDomainNameSize*sizeof(char)) );
     if not LookupAccountName( nil, pAccountName, aSID, dwSIDSIZE, pDomainName, dwDomainNameSize, sidType) then
     begin
          LocalFree( Cardinal(aSID) );
          Result := nil;
     end
     else
     begin
          Result := aSid;
     end;

     LocalFree( Cardinal(pDomainName) );
end;

function GetEffectiveRightsForSID(hManager :AUTHZ_RESOURCE_MANAGER_HANDLE;
                                   psd : PSECURITY_DESCRIPTOR; 
                                   sid : PChar) : BOOL;
var asid : PSID;
    bResult : BOOL;
    unusedID : LUID;
    hAuthzClientContext : AUTHZ_CLIENT_CONTEXT_HANDLE;
begin
     Result := False;
     asid := nil;
     hAuthzClientContext := 0;
     FillChar(unusedID, sizeof(unusedID), 0);

     if not ConvertStringSidToSid(sid, asid) then
        RaiseLastOsError();
//     asid := ConvertNameToBinarySid('rabatscher');

     if asid = nil then
        RaiseLastOSError;
     try
        if asid <> nil then
        begin
             bResult := AuthzInitializeContextFromSid( 0, aSid, hManager, nil, unusedId, nil, @hAuthzClientContext );
             try
                if bResult then
                   Result := GetAccess(hAuthzClientContext, psd);
             finally
                    if hAuthzClientContext <> 0 then
                       AuthzFreeContext(hAuthzClientContext);
             end;
        end;
     finally
            if asid <> nil then
               LocalFree(LongWord(asid));
     end;
end;

function UseAuthzSolution( psd : PSECURITY_DESCRIPTOR; const sid : string = 'S-1-1-0') : boolean;
var hManager : AUTHZ_RESOURCE_MANAGER_HANDLE;
    bResult : BOOL;
    pSid : PChar;
begin
     bResult := AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT,
                nil, nil, nil, nil, @hManager);
     if bResult then
     begin
          pSid := PChar(sid);
          bResult := GetEffectiveRightsForSID(hManager, psd, psid);
          AuthzFreeResourceManager(hManager);
     end;

     Result := bResult;
end;

function GetSecurityInfo(handle: THandle; ObjectType: SE_OBJECT_TYPE;
         SecurityInfo: SECURITY_INFORMATION; ppsidOwner, ppsidGroup: PPSID; ppDacl, ppSacl: PACL;
         var pSecurityDescriptor: PSECURITY_DESCRIPTOR): DWORD; stdcall; external 'ADVAPI32.DLL' name 'GetSecurityInfo'; {use localfree to release ppSecurityDescriptor}


function CheckDirectoryAccess( path : string ) : boolean;
var dw : DWORD;
    apacl : PACL;
    psd : PSECURITY_DESCRIPTOR;
    apSID : PSID;
    h : THandle;    
begin
     try
        apSID := nil;

        //Open directory
        h := CreateFile(PChar(path), GENERIC_READ, FILE_SHARE_READ, nil,
                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_BACKUP_SEMANTICS, 0);

        //we need FILE_FLAG_BACKUP_SEMANTICS to open a directory
        if h = INVALID_HANDLE_VALUE then 
           RaiseLastOsError();
        try
           //Query access control list for a directory -- the list you see in the properties box
           dw := GetSecurityInfo(h, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION or OWNER_SECURITY_INFORMATION or GROUP_SECURITY_INFORMATION,
                                  nil, nil, @apacl, nil, psd);

           if dw <> ERROR_SUCCESS then
              RaiseLastOSError;

           try
              Result := UseAuthzSolution(psd);
           finally
                  if apSID <> nil then
                     LocalFree(NativeUint(apSID));

                  LocalFree(NativeUint(psd));
           end;
        finally
               CloseHandle(h);
        end;
     except
           on E : Exception do
           begin
                Result := False;
           end;
     end;
end;

请注意,有一些更改,因此该过程有效:GetSecurityInfo(来自上述过程)需要参数 DACL_SECURITY_INFORMATION 或 OWNER_SECURITY_INFORMATION 或 GROUP_SECURITY_INFORMATION(不仅是 DACL_SECURITY_INFORMATION),否则您会在 AuthzAccessCheck 中收到错误 87!

此外,您需要查看 jedi 库中的 JWA 标头。

希望对其他人也有帮助。

于 2019-07-04T10:10:08.263 回答