检查每个人是否可以读/写目录

时间:2013-03-27 13:00:37

标签: delphi acl

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

数据来自不同的来源,例如服务将文件写入其中,用户以后可以更改其内容。

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

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

非常感谢   麦克

2 个答案:

答案 0 :(得分:1)

我认为它不是Delphi而是WinAPI问题。德尔福没有任何特殊的设施来使AFAIK变得更容易。

Getting information from an ACL表示您需要在打开的句柄上执行GetSecurityInfo,然后在您获得的ACL上执行GetEffectiveRightsFromACL

您指定trustee,可以按名称,但最好使用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;

可能不起作用;在我的电脑上提供ACCESS_DENIED,但这可能是因为我的complicated domain situation。无论如何,这是一个开始的事情。

答案 1 :(得分: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标头。

希望也能帮助其他人。