3

I've a custom installer program that has worked fine, but it asks the user for admin permission every time it updates an application. I'm creating a windows service that skips this part, but the windows service gives only System and Administrators permissions to the file, and the user cannot execute the new updates.

To correct this I'm trying (after the file downloads/installs to the correct place (from within the windows service, it has the account ServiceAccount.LocalSystem),

FileSecurity access = file.GetAccessControl();
SecurityIdentifier everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
access.AddAccessRule(new FileSystemAccessRule(everyone, FileSystemRights.ReadAndExecute, AccessControlType.Allow));

but the setting doesn't take effect. What should I do from here?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Chuck Savage
  • 11,775
  • 6
  • 49
  • 69
  • `.. but it asks the user for admin permission every time it updates an application.` - Isn't it supposed to? – Ritch Melton Nov 10 '11 at 18:55
  • The old way yes, but if you download via a service that is already running as admin, then no. If you use Google Chrome, it never asks you for permissions to update. Check your services for `Google Update Service` which updates Google Chrome without bothering you with requests for elevated permissions to update. – Chuck Savage Nov 10 '11 at 20:09
  • Ahh ok, that's how that works. Good to know. – Ritch Melton Nov 10 '11 at 21:10

2 Answers2

5

I figured it out. I just needed to call,

file.SetAccessControl(access);

after the above. Apparently file.GetAccessControl passes back a copy of the access control and not the one that controls the file permissions for the file, until you call file.SetAccessControl with the modified permissions.

There's another caveat I discovered with another file that the service was creating in c:\ProgramData,

  • is that the set has to occur after the file has been written. Applying the set to the file beforehand is ineffective.
Chuck Savage
  • 11,775
  • 6
  • 49
  • 69
3

I recently encountered a problem loading files from a network and wanted to be able to recreate the bug in a test and ran into the same problem.

I came up with the following little class to help out with it as the API for doing this stuff is pretty horrible and full of little pitfalls:

public class PermissionController
{
    private readonly string _file;
    private readonly FileSecurity _accessControl;
    private readonly SecurityIdentifier _id;
    private readonly List<FileSystemAccessRule> _permissionsDenied;

    public PermissionController(string file)
    {
        _file = file;
        _accessControl = File.GetAccessControl(_file);
        _id = WindowsIdentity.GetCurrent().Owner;
        _permissionsDenied = new List<FileSystemAccessRule>();
    }

    public void Allow(params FileSystemRights[] rights)
    {
        foreach (var right in rights)
            AddRule(Rule(right, AccessControlType.Allow));
    }

    public void Deny(params FileSystemRights[] rights)
    {
        foreach (var right in rights)
        {
            var rule = Rule(right, AccessControlType.Deny);
            AddRule(rule);
            _permissionsDenied.Add(rule);
        }
    }

    private void AddRule(FileSystemAccessRule rule)
    {
        _accessControl.AddAccessRule(rule);
    }

    private FileSystemAccessRule Rule(FileSystemRights right, AccessControlType type)
    {
        return new FileSystemAccessRule(_id, right, type);
    }

    public void RemoveDeniedPermissions()
    {
        foreach (var rule in _permissionsDenied)
            _accessControl.RemoveAccessRule(rule);

        Apply();
    }

    public void Apply()
    {
        File.SetAccessControl(_file,_accessControl);
    }
}

The calling code looks like:

        _permissionController = new PermissionController(_file);
        _permissionController.Allow(FileSystemRights.Read, FileSystemRights.Write);
        _permissionController.Deny(FileSystemRights.FullControl,
                                   FileSystemRights.Modify,
                                   FileSystemRights.ReadAndExecute);
        _permissionController.Apply();

where _file is the fully qualified path.

You need to be careful to call

File.SetAccessControl after adding/removing rules as otherwise there is no effect.

Unless I misunderstood the API you have to add a rule per permission because the FileSystemRights enum does not use flags.

You also need to be a bit careful as Allowing a right that you have denied is not equivalent to removing the rule that denies that right. It seems that denied rights override allowed ones.

You can eyeball the results by looking at the security tab of a file's properties in windows explorer.

JonnyRaa
  • 7,559
  • 6
  • 45
  • 49
  • That's nice - I can't use `_id = WindowsIdentity.GetCurrent().Owner;`because a service has no owner, or at least not a user. Thanks for tip on having to remove Denies, I didn't know about that. – Chuck Savage Dec 14 '11 at 16:22