Sometimes it is necessary to change the security settings of a file or folder for getting or denying write access. With JWSCL this task is made very easy. However there are some pitfalls to avoid.

The following code will also be available in the example section of the source code. The application gets a file or folder name as parameter and tries to add the user with full access control. It even tries to get ownership if it can’t change the access control list.

First of all we need some JWSCL classes:

  • TJwSecurityDescriptor
    A security descriptor contains all information about security of an object. It contains the owner and the access control list (also some other thing, we don’t need here)
  • TJwSecureFileObject
    This class provides methods to read and write security information on a file or folder. Despite its name it does also support folders. It even supports inheritance.
    You can access a file or folder through its name, a handle or the VCL class TFileStream.
  • TJwDAccessControlList
    This class contains methods to maintain a discreationary access control list (DACL). A DACL contains a list of users and their possible access on the object.
  • TJwSecurityId
    Every user is identified by a unique number which is maintained by this class.
  • TJwSecurityToken
    Every logged on user gets a security pass which contains information what she can do or not. We mainly use it to retrieve the user’s SID (TJwSecurityID)

These classes are stored in the JWSCL units. We use the following ones:

uses
 JwaWindows,
 JwsclSid,
 JwsclToken,
 JwsclACl,
 JwsclDescriptor,
 JwsclSecureObjects,
 JwsclKnownSid;

The units above are necessary and contain all the classes described earlier Of course we have to declare the classes:

var
  UserToken : TJwSecurityToken;
  SD : TJwSecurityDescriptor;
  FileObject : TJwSecureFileObject;
  Owner : TJwSecurityId;
  DACL : TJwDAccessControlList;
begin
  if not FileExists(ParamStr(1)) then
    exit;

This example also shows how we can add well known Security Identifiers (SID) to a secured object. We have to initialize them. The variable JwWorldSID will then contain the correct SID for group Everyone. If we didn’t call it, we would get nil instead.
JwInitWellKnownSIDs;

The next steps are creating the classes. We get the user name through her token and save the SID into Owner.
Later we will use the Owner instance to add it into the security information of the object.

UserToken := TJwSecurityToken.CreateTokenEffective(MAXIMUM_ALLOWED);
Owner := UserToken.GetTokenOwner;
try
  FileObject := TJwSecureFileObject.Create(ParamStr(1));

The actual class which does all the work on the file/folder is TJwSecureFileObject. We just apply the first parameter.

Notice: A user can only change security information of an object if she has the right to do it. There are two options to allow it.

  1. The user is listed in the DACL. Additionally the right WRITE_DAC is granted for her.
  2. The user is the owner. In this case she don’t need to be listed and allowed in the DACL. It is automatically granted

We can check both version in one call.

 try
  if not FileObject.AccessCheck(WRITE_DAC)
  begin

This call is very easy. If we can’t change the DACL, we can try to become the owner. The only way to become an owner is to enable a privilege called SE_TAKE_OWNERSHIP_NAME. It is usually only granted to Administrators.

    JwEnablePrivilege(SE_TAKE_OWNERSHIP_NAME, pst_Enable);
    FileObject.Owner := Owner;
  end;

JwEnablePrivilege will fail, if it can’t activate the privilege. Otherwise we can set the file/folder’s owner to the token user.

The main work is done here. We get the default DACL from the existing object and adapt it.

   DACL := FileObject.DACL;

Adaption is done by adding the user to the DACL with full control. We additionally allow the Everyone group to demonstrate the well known Sids initialized by JwInitWellKnownSIDs. The last parameters (false) define that we don’t want the list to free the given SIDs (Owner and JwWorldSid) automatically.

   DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create
        (nil, [], GENERIC_ALL, Owner, false));
    DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create
        (nil, [], GENERIC_READ, JwWorldSID, false));

And finally we reset the DACL.

   FileObject.SetDACL(DACL);

The DACL of the file or folder will receive the newly created control entries in addition to its existing ones. If it contains inherited entries (entries from a parent folder) they will be conserved. However if you don’t retrieve the DACL and just use an empty one, all previously existing entries which are not inherited will be removed. Of course the inherited entries will still remain intact.

And of course we free all allocated resources

 finally
    FileObject.Free;
  end;

finally
  Owner.Free;
  UserToken.Free;
end;
end.

Since I cut the source code into pieces, I’ll show it here in full glory

program SetFileSecurity;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  JwaWindows,
  JwsclSid,
  JwsclToken,
  JwsclAcl,
  JwsclDescriptor,
  JwsclSecureObjects,
  JwsclKnownSid;

var
  UserToken : TJwSecurityToken;
  SD : TJwSecurityDescriptor;
  FileObject : TJwSecureFileObject;
  Owner : TJwSecurityId;
  DACL : TJwDAccessControlList;
begin
  if not FileExists(ParamStr(1)) then
    exit;

  JwInitWellKnownSIDs;

  UserToken := TJwSecurityToken.CreateTokenEffective(MAXIMUM_ALLOWED);
  Owner := UserToken.GetTokenOwner;
  try
    FileObject := TJwSecureFileObject.Create(ParamStr(1));
    try
      //Make me owner if we cant access DACL
      if not FileObject.AccessCheck(WRITE_DAC) then
      begin
        //try to become owner
        JwEnablePrivilege(SE_TAKE_OWNERSHIP_NAME, pst_Enable);
        FileObject.Owner := Owner;
      end;

      DACL := FileObject.DACL;
      DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(nil, [], GENERIC_ALL, Owner, false));
      DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(nil, [], GENERIC_READ, JwWorldSID, false));

      FileObject.SetDACL(DACL);
    finally
      FileObject.Free;
    end;

  finally
    Owner.Free;
    UserToken.Free;
  end;
end.