Mega Code Archive

 
Categories / Delphi / Files
 

Setting NTFS FileFolder Security

Title: Setting NTFS File/Folder Security Question: I saw an article that helps setting NTFS security on file or folder. There are many problems with different NTFS (NT4, NT2000, XP) especially with inheritance. The code I am posting is too long, but works fine. Answer: Comment: some constants and type definitions are copied from JCL, some from Microsoft C++ headers, bu unit doesn't need of any additional code to be compiled. Try it on your own risk. There are additional functions that can help: IsNT, IsNT4, IsAdmin, etc. Some constants are not used. Finally, please don't blame me for my style.... unit NTSecurityU; interface Uses Windows, AclApi, AccCtrl; Const SECURITY_NULL_SID_AUTHORITY : _SID_IDENTIFIER_AUTHORITY = ( Value : (0,0,0,0,0,0)); SECURITY_WORLD_SID_AUTHORITY : _SID_IDENTIFIER_AUTHORITY = ( Value : (0,0,0,0,0,1)); SECURITY_LOCAL_SID_AUTHORITY : _SID_IDENTIFIER_AUTHORITY = ( Value : (0,0,0,0,0,2)); SECURITY_CREATOR_SID_AUTHORITY : _SID_IDENTIFIER_AUTHORITY = ( Value : (0,0,0,0,0,3)); SECURITY_NON_UNIQUE_AUTHORITY : _SID_IDENTIFIER_AUTHORITY = ( Value : (0,0,0,0,0,4)); SECURITY_NT_AUTHORITY : _SID_IDENTIFIER_AUTHORITY = ( Value : (0,0,0,0,0,5)); SECURITY_WORLD_RID :CARDINAL =$00000000; SECURITY_BUILTIN_DOMAIN_RID : CARDINAL = $00000020; DOMAIN_ALIAS_RID_ADMINS : CARDINAL = $00000220; DOMAIN_ALIAS_RID_USERS : CARDINAL = $00000221; DOMAIN_ALIAS_RID_GUESTS : CARDINAL = $00000222; STANDARD_RIGHTS_ALL : CARDINAL = $001F0000; ACL_REVISION : CARDINAL = 2; // current revision; const ACCESS_MIN_MS_ACE_TYPE = ($0); {$EXTERNALSYM ACCESS_MIN_MS_ACE_TYPE} ACCESS_ALLOWED_ACE_TYPE = ($0); {$EXTERNALSYM ACCESS_ALLOWED_ACE_TYPE} ACCESS_DENIED_ACE_TYPE = ($1); {$EXTERNALSYM ACCESS_DENIED_ACE_TYPE} SYSTEM_AUDIT_ACE_TYPE = ($2); {$EXTERNALSYM SYSTEM_AUDIT_ACE_TYPE} SYSTEM_ALARM_ACE_TYPE = ($3); {$EXTERNALSYM SYSTEM_ALARM_ACE_TYPE} ACCESS_MAX_MS_V2_ACE_TYPE = ($3); {$EXTERNALSYM ACCESS_MAX_MS_V2_ACE_TYPE} ACCESS_ALLOWED_COMPOUND_ACE_TYPE = ($4); {$EXTERNALSYM ACCESS_ALLOWED_COMPOUND_ACE_TYPE} ACCESS_MAX_MS_V3_ACE_TYPE = ($4); {$EXTERNALSYM ACCESS_MAX_MS_V3_ACE_TYPE} ACCESS_MIN_MS_OBJECT_ACE_TYPE = ($5); {$EXTERNALSYM ACCESS_MIN_MS_OBJECT_ACE_TYPE} ACCESS_ALLOWED_OBJECT_ACE_TYPE = ($5); {$EXTERNALSYM ACCESS_ALLOWED_OBJECT_ACE_TYPE} ACCESS_DENIED_OBJECT_ACE_TYPE = ($6); {$EXTERNALSYM ACCESS_DENIED_OBJECT_ACE_TYPE} SYSTEM_AUDIT_OBJECT_ACE_TYPE = ($7); {$EXTERNALSYM SYSTEM_AUDIT_OBJECT_ACE_TYPE} SYSTEM_ALARM_OBJECT_ACE_TYPE = ($8); {$EXTERNALSYM SYSTEM_ALARM_OBJECT_ACE_TYPE} ACCESS_MAX_MS_OBJECT_ACE_TYPE = ($8); {$EXTERNALSYM ACCESS_MAX_MS_OBJECT_ACE_TYPE} ACCESS_MAX_MS_V4_ACE_TYPE = ($8); {$EXTERNALSYM ACCESS_MAX_MS_V4_ACE_TYPE} ACCESS_MAX_MS_ACE_TYPE = ($8); {$EXTERNALSYM ACCESS_MAX_MS_ACE_TYPE} ACCESS_ALLOWED_CALLBACK_ACE_TYPE = $9; {$EXTERNALSYM ACCESS_ALLOWED_CALLBACK_ACE_TYPE} ACCESS_DENIED_CALLBACK_ACE_TYPE = $A; {$EXTERNALSYM ACCESS_DENIED_CALLBACK_ACE_TYPE} ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = $B; {$EXTERNALSYM ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE} ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE = $C; {$EXTERNALSYM ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE} SYSTEM_AUDIT_CALLBACK_ACE_TYPE = $D; {$EXTERNALSYM SYSTEM_AUDIT_CALLBACK_ACE_TYPE} SYSTEM_ALARM_CALLBACK_ACE_TYPE = $E; {$EXTERNALSYM SYSTEM_ALARM_CALLBACK_ACE_TYPE} SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE = $F; {$EXTERNALSYM SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE} SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE = $10; {$EXTERNALSYM SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE} ACCESS_MAX_MS_V5_ACE_TYPE = $10; {$EXTERNALSYM ACCESS_MAX_MS_V5_ACE_TYPE} SUCCESSFUL_ACCESS_ACE_FLAG = ($40); {$EXTERNALSYM SUCCESSFUL_ACCESS_ACE_FLAG} FAILED_ACCESS_ACE_FLAG = ($80); {$EXTERNALSYM FAILED_ACCESS_ACE_FLAG} Type PACE_HEADER = ^ACE_HEADER; _ACE_HEADER = record AceType: Byte; AceFlags: Byte; AceSize: Word; end; ACE_HEADER = _ACE_HEADER; TAceHeader = ACE_HEADER; PAceHeader = PACE_HEADER; //Access Allowed ACE PAccessAllowedAce = ^TAccessAllowedAce; _ACCESS_ALLOWED_ACE = record Header : ACE_HEADER; Mask : DWORD; SidStart : DWORD; end; TAccessAllowedAce = _ACCESS_ALLOWED_ACE; Type //=== ACL (Access Control List)============================== //Size information PACL_SIZE_INFORMATION = ^ACL_SIZE_INFORMATION; _ACL_SIZE_INFORMATION = record AceCount, AclBytesInUse, AclBytesFree : DWORD end; ACL_SIZE_INFORMATION =_ACL_SIZE_INFORMATION; TAclSizeInformation = ACL_SIZE_INFORMATION; PAclSizeInformation = PACL_SIZE_INFORMATION; //Revision Information PACL_REVISION_INFORMATION = ^ACL_REVISION_INFORMATION; _ACL_REVISION_INFORMATION = record AclRevision : DWORD end; ACL_REVISION_INFORMATION = _ACL_REVISION_INFORMATION; TAclRevisionInformation = ACL_REVISION_INFORMATION; PAclRevisionInformation = PACL_REVISION_INFORMATION; function IsAdmin : Boolean; stdcall; //is logged user is member of admins or //domain admins function IsNT : Boolean; stdcall; //is system NT based function IsNT4 : Boolean; stdcall; //is system NT 4 function GetEveryOneSid : Pointer; stdcall; //Security identifier of well known // group Everyone function GetAccountSID( anAccountName : String) : Pointer; stdcall; function SetFileObjectAccessRights(aFileObject: String; aSID: Pointer; anAccess: CARDINAL; isInheritedAccess : BOOLEAN): BOOLEAN; stdcall; function SetFileObjectAndSubobjectsAccessRights(aFileObject: String; aSID: Pointer; anAccess: CARDINAL): BOOLEAN; stdcall; function SetEveryoneRWEDAccessToFileOrFolder( aFileOrFolder : String) : BOOLEAN; stdcall; function SetEveryoneRWEDAccessToFileOrFolderAndSubobjects( aFileOrFolder : String) : BOOLEAN; stdcall; function VolumeSupportsPersistentACLs( aPath : String) : Boolean; stdcall; implementation Uses SysUtils, ComObj; function VolumeSupportsPersistentACLs( aPath : String) : Boolean; Var maxClen, driveFlags : Cardinal; i : Integer; VolName, FSysName : Array[0..MAX_PATH] of Char; begin aPath := ExtractFileDrive( aPath) +'\'; Result := FALSE; if GetVolumeInformation( PChar( aPath), VolName, SizeOf( VolName), nil, maxClen, driveFlags, FsysName, SizeOf( FsysName) ) then Result := driveFlags and FS_PERSISTENT_ACLS = FS_PERSISTENT_ACLS ; end; function CheckCARDINALRslt( aCardinal : DWORD) : Boolean; begin Result := aCardinal = ERROR_SUCCESS; if not Result then SetLastError( aCardinal); end; function IsAdmin : Boolean; Var ntauth : SID_IDENTIFIER_AUTHORITY; psidAdmin : Pointer; bIsAdmin : Boolean; htok : THandle; cb : DWORD; ptg : ^TOKEN_GROUPS; i : Integer; grp : PSIDAndAttributes; begin Result := FALSE; if not IsNT then Result := TRUE else begin bIsAdmin := FALSE; ntauth := SECURITY_NT_AUTHORITY; psidAdmin := nil; AllocateAndInitializeSid( ntauth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, psidAdmin ); htok := 0; OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, htok ); GetTokenInformation( htok, TokenGroups, nil, 0, cb ); GetMem( ptg, cb); GetTokenInformation( htok, TokenGroups, ptg, cb, cb ); grp := @(ptg.Groups[0]); for i := 0 to ptg.GroupCount-1 do begin if EqualSid( psidAdmin, grp.Sid ) then begin bIsAdmin := TRUE; Break; end; Inc( grp) ; //, SizeOf( TSIDAndAttributes)); end; freemem( ptg); CloseHandle(htok); FreeSid( psidAdmin); Result := bIsAdmin; end; // else of : if not IsNT end; function IsNT : Boolean; Var ovi : TOSVersionInfo; begin FillChar( ovi, SizeOf( Ovi), 0); ovi.dwOSVersionInfoSize := SizeOf( Ovi); GetVersionEx( ovi); Result := ovi.dwPlatformId = VER_PLATFORM_WIN32_NT; end; function IsNT4 : Boolean; Var ovi : TOSVersionInfo; begin FillChar( ovi, SizeOf( Ovi), 0); ovi.dwOSVersionInfoSize := SizeOf( Ovi); GetVersionEx( ovi); Result := (ovi.dwPlatformId = VER_PLATFORM_WIN32_NT) and (ovi.dwMajorVersion = 4); end; function GetEveryOneSid : Pointer; begin AllocateAndInitializeSid( SECURITY_WORLD_SID_AUTHORITY, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, Result ); end; function GetAccountSID( anAccountName : String) : Pointer; Var cb : CARDINAL; refDomainName : Array[0..1024] of Char; cbRefDomainName : Cardinal; peUse : Cardinal; SD : Pointer; begin SD := NIL; try cbRefDomainName := SizeOf(refDomainName); FillChar( refDomainName, cbRefDomainName, 0); cb := 0; LookupAccountName(nil, PChar( anAccountName), nil, cb, refDomainName, cbRefDomainName, peUse); if cb 0 then begin GetMem( SD, cb); FillChar( SD^, cb, 0); if not LookupAccountName(nil, PChar( anAccountName), SD, cb, refDomainName, cbRefDomainName, peUse) then begin FreeMem( SD, cb); SD := NIL; end; end else begin SD := NIL; end; finally Result := SD; end; end; function SetFileObjectAndSubobjectsAccessRights(aFileObject: String; aSID: Pointer; anAccess: CARDINAL): BOOLEAN; function RecursiveSet( aPath : String) : Boolean; Var F : TSearchRec; i : Integer; begin Result := SetFileObjectAccessRights(aPath, aSID, anAccess, TRUE); i := FindFirst(aPath + '\*.*', faAnyFile, F); try While i = 0 do begin if (F.Name '') and (F.Name[1] '.') then begin if F.Attr and faDirectory = faDirectory then Result := Result and RecursiveSet( aPath + '\'+F.Name) else Result := Result and SetFileObjectAccessRights(aPath + '\' + F.Name, aSID, anAccess, TRUE); if not Result then Exit; end; i := FindNext( F); end; finally FindClose(F); end; end; begin Result := FALSE; aFileObject := TRIM( aFileObject); if aFileObject '' then begin if DirectoryExists(aFileObject) then begin if aFileObject[Length( aFileObject)] = '\' then Delete( aFileObject, Length( aFileObject), 1); Result := RecursiveSet(aFileObject); Result := Result and SetFileObjectAccessRights(aFileObject, aSID, anAccess, FALSE); end else Result := SetFileObjectAccessRights(aFileObject, aSID, anAccess, FALSE); end; end; function SetEveryoneRWEDAccessToFileOrFolder( aFileOrFolder : String) : BOOLEAN; Var SID : Pointer; begin Result := FALSE; AllocateAndInitializeSid( SECURITY_WORLD_SID_AUTHORITY, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, SID ); if IsValidSid(SID) then try Result := SetFileObjectAccessRights(aFileOrFolder, SID, GENERIC_READ + GENERIC_WRITE + GENERIC_EXECUTE + _DELETE, FALSE ); finally FreeSid(SID); end; end; function SetEveryoneRWEDAccessToFileOrFolderAndSubobjects( aFileOrFolder : String) : BOOLEAN; Var SID : Pointer; begin SID := GetEveryOneSid; try Result := SetFileObjectAndSubobjectsAccessRights(aFileOrFolder, SID, GENERIC_READ + GENERIC_WRITE + GENERIC_EXECUTE + _DELETE ); finally FreeSid(SID); end; end; function SetFileObjectAccessRights(aFileObject: String; aSID: Pointer; anAccess: CARDINAL; isInheritedAccess : BOOLEAN ): BOOLEAN; var PPACL, PPACL2: PACL; newDacl: PACL; SecDescPtr, SD2: PSECURITY_DESCRIPTOR; needed : Cardinal; SD_Control : WORD; SD_Revision : Cardinal; aTrustee : TRUSTEE; expAccess : PExplicit_Access; isFile : Boolean; CurACEBr, CurACEInd : CARDINAL; OldAclSI : TAclSizeInformation; OldAclRI : TAclRevisionInformation; anACE : PAccessAllowedAce; i : Integer; oldACLSize, newACLSize, newACESize : Cardinal; bPresent, bDefaulted : LongBool; begin Result := false; if not IsValidSid( aSID) then Exit; isFile := FileExists(aFileObject); PPACL := nil; if not IsNT4 then begin if not CheckCardinalRslt( GetNamedSecurityInfo(PChar(aFileObject), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,nil, nil, PACL(@PPACL), nil, SecDescPtr) ) then Exit; end else begin GetFileSecurity(PChar(aFileObject), DACL_SECURITY_INFORMATION, nil, 0, needed); GetMem( SecDescPtr, needed); FillChar( SecDescPtr^, needed, 0); if not GetFileSecurity(PChar(aFileObject), DACL_SECURITY_INFORMATION, SecDescPtr, needed, needed) then Exit; if not GetSecurityDescriptorDacl(SecDescPtr, bPresent, PPACL, bDefaulted) then Exit; end; try if not Assigned( PPACL) then Exit; if not GetSecurityDescriptorControl(SecDescPtr, SD_Control, SD_Revision) then Exit; if SD_Control and SE_DACL_PRESENT SE_DACL_PRESENT then Exit; if not GetAclInformation(PPACL^, @oldAclSI, SizeOF( TAclSizeInformation), AclSizeInformation) then Exit; if not GetAclInformation(PPACL^,@oldAclRI, SizeOf( TAclRevisionInformation), AclRevisionInformation) then Exit; //Delete previous ACE, for a given aSID CurACEBr := oldAclSI.AceCount; For i := oldAclSI.AceCount-1 downto 0 do begin if GetAce(PPACL^, i, Pointer(anAce)) then begin if EqualSID( @(anACE.SidStart), aSID) then begin DeleteAce(PPACL^, i); CurACEBr := CurACEBr -1; end; end; end; if not GetAclInformation(PPACL^, @oldAclSI, SizeOF( TAclSizeInformation), AclSizeInformation) then Exit; if not GetAclInformation(PPACL^,@oldAclRI, SizeOf( TAclRevisionInformation), AclRevisionInformation) then Exit; NewACESize := SizeOf(TAccessAllowedACE) + GetLengthSid(aSID) - SizeOf( DWORD); OldACLSize := oldAclSI.AclBytesInUse + oldAclSI.AclBytesFree; NewACLSize := oldAclSI.AclBytesInUse + NewAceSize * 2 - oldAclSI.AclBytesFree; if NewAclSize GetMem( PPACL2, NewACLSize); try FillChar( PPACL2^, NewACLSize, 0); Move( PPACL^, PPACL2^, oldACLSize); PPACL2.AclSize := newACLSize; if not GetAclInformation(PPACL2^, @oldAclSI, SizeOF( TAclSizeInformation), AclSizeInformation) then Exit; CurACEInd := 0; if not IsNT4 then begin //Construct Our Ace GetMem( anACE, newACESize); try FillChar( anACE^, newACESize, 0); anACE.Header.AceType := ACCESS_ALLOWED_ACE_TYPE; if not isFile then //demek e folder begin anACE.Header.AceFlags := SUB_CONTAINERS_ONLY_INHERIT + SUB_OBJECTS_ONLY_INHERIT; end; if isInheritedAccess then begin if not IsNt4 then anACE.Header.AceFlags := anACE.Header.AceFlags + INHERITED_ACCESS_ENTRY; end; anACE.Header.AceSize := newACESize; anAce.Mask := anAccess; Move( aSID^, anAce.SidStart, GetLengthSid(aSID)); if not AddAce( PPACL2^, OldAclRI.AclRevision, CurACEInd, anACE, newACESize) then Exit; finally FreeMem( anACE, newACESize); end; end else begin CurACEInd := 0; if not isFile then begin GetMem( anACE, newACESize); try FillChar( anACE^, newACESize, 0); anACE.Header.AceType := ACCESS_ALLOWED_ACE_TYPE; anACE.Header.AceFlags := SUB_CONTAINERS_ONLY_INHERIT + SUB_OBJECTS_ONLY_INHERIT + INHERIT_ONLY; anACE.Header.AceSize := newACESize; anAce.Mask := anAccess; Move( aSID^, anAce.SidStart, GetLengthSid(aSID)); if not AddAce( PPACL2^, OldAclRI.AclRevision, CurACEInd, anACE, newACESize) then Exit; finally FreeMem( anACE, newACESize); end; end; //Add ACE for Files GetMem( anACE, newACESize); try FillChar( anACE^, newACESize, 0); anACE.Header.AceType := ACCESS_ALLOWED_ACE_TYPE; anACE.Header.AceFlags := 0; // Empty flags, but ACE anACE.Header.AceSize := newACESize; anAce.Mask := anAccess; Move( aSID^, anAce.SidStart, GetLengthSid(aSID)); if not AddAce( PPACL2^, OldAclRI.AclRevision, CurACEInd, anACE, newACESize) then Exit; finally FreeMem( anACE, newACESize); end; end; if not GetAclInformation(PPACL2^, @oldAclSI, SizeOF( TAclSizeInformation), AclSizeInformation) then Exit; if not IsNT4 then begin Result := CheckCARDINALRslt( SetNamedSecurityInfo(PChar(aFileObject), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nil, nil, PPACL2, nil) ); end else begin GetMem( SD2, SizeOf( TSecurityDescriptor)); try if not InitializeSecurityDescriptor(SD2, SECURITY_DESCRIPTOR_REVISION) then Exit; if not SetSecurityDescriptorDacl(SD2, bPresent, PPACL2, bDefaulted) then Exit; Result := SetFileSecurity(PChar(aFileObject), DACL_SECURITY_INFORMATION, SD2); finally FreeMem( SD2, SizeOf( TSecurityDescriptor)); end; end; finally FreeMem( PPACL2, NewACLSize); end; finally LocalFree(HLOCAL(SecDescPtr)); end; end; end.