0

I am writing a C# application and I want to set (at application start) the permissions so that only the Administrator can delete a file which the application uses. All other users should not be able to delete it or modify its permissions.

The file should be protected from standard file system usage (so no one, except Admin, can delete it). Only Admin should be able to set back the file permissions. Also, other users should be able to read/write it.

Is this possible? I have found some code examples here, but none of them work. The code I'm trying :

FileSecurity fSecurity = File.GetAccessControl("database.sdf");

        AuthorizationRuleCollection rules = fSecurity.GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));
        foreach (AuthorizationRule rule in rules)
        {
            System.Security.Principal.NTAccount account =
                (System.Security.Principal.NTAccount)rule.IdentityReference.Translate(typeof(System.Security.Principal.NTAccount));
            if (account.Value != "BUILTIN\\Administrators")
            {
                fSecurity.AddAccessRule(new FileSystemAccessRule(account.Value, FileSystemRights.Delete, AccessControlType.Deny));
            }
        }

        File.SetAccessControl("database.sdf", fSecurity);    

Can I configure the file permissions and owner programmatically from the app when running it as a normal user, not admin?

Thank you!

PS The file is a SQL Server Compact database.

Cristian M
  • 715
  • 2
  • 12
  • 32
  • Can you post the code you've tried? It'll help understand what you're trying to achieve even if the code doesn't work – amura.cxg Feb 10 '16 at 15:56
  • In my opinion it's a waste of time. Even if you do set the permissions a user can in most cases just set it back again. Security through obscurity is probably a little better. – Equalsk Feb 10 '16 at 15:57
  • There are a couple of points missing here. Is the file to be protected from within the application or also from standard file system usage? Which kind of authentication you have put in place? – Mauro Feb 10 '16 at 15:58
  • I have tried these code examples: http://stackoverflow.com/questions/17347563/create-file-and-unable-users-to-delete-it-c-sharp http://stackoverflow.com/questions/8073947/set-file-permissions-for-c-program-files-company-app-file-for-all-users – Cristian M Feb 10 '16 at 16:02
  • The file should be protected from standard file system usage (so no one, except Admin, can delete it). I can connect to the database using a password. – Cristian M Feb 10 '16 at 16:04
  • That was my question. The idea is that only Admin can set back the file permissions. – Cristian M Feb 10 '16 at 16:06
  • @CristianM, when you answer someone in Comments you should preface their name with @, as I did with your name, above. This will post an "alert" in their inbox so that they know you've responded. Also, most of the information people ask for in Comments should go into your question so that anyone new sees all the information "at a glance". – Cindy Meister Feb 10 '16 at 18:20

2 Answers2

1

Before we get started, let me just say that this:

System.Security.Principal.NTAccount account =
    (System.Security.Principal.NTAccount)rule.IdentityReference.Translate(typeof(System.Security.Principal.NTAccount));
if (account.Value != "BUILTIN\\Administrators")

is entirely unnecessary.

The builtin Administrators group is a well-known security principal, and is guaranteed to always have the same Security Identifier (S-1-5-32-544), easy to compare against.


Your current procedure of explicitly granting Deny Delete for all principals that already happens to have an access rule may also backfire immensely.

Imagine that an access rule with the following characteristics already exist in the ACL:

Identity: Everyone
Access right: Read
Control type: Allow

Congratulations! You've now implicitly denied everyone the right to delete the file, including the Administrators group.

What you want to do is:

  1. Make sure the Administrators group is the Owner of the file
  2. Remove all existing access rules that grants Allow Delete (explicitly or implicitly)
  3. Add a single Access Rule granting Allow FullControl to the Administrators group
  4. Remove all inherited rules and protect the ACL from inheritance

// Way safer than string comparison against "BUILTIN\\Administrators"
IdentityReference BuiltinAdministrators = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);

// Grab ACL from file
FileSecurity FileACL = File.GetAccessControl(TargetFilePath);

// Check if correct owner is set
if (FileACL.GetOwner(typeof(SecurityIdentifier)) != BuiltinAdministrators)
{
    // If not, make it so!
    FileACL.SetOwner(BuiltinAdministrators);
}

foreach (FileSystemAccessRule fsRule in FileACL.GetAccessRules(true, false, typeof(SecurityIdentifier)))
{
    // Check if rule grants delete
    if ((fsRule.FileSystemRights & FileSystemRights.Delete) == FileSystemRights.Delete)
    {
        // If so, nuke it!
        FileACL.RemoveAccessRule(fsRule);
    }
}

// Add a single explicit rule to allow FullControl
FileACL.AddAccessRule(new FileSystemAccessRule(BuiltinAdministrators, FileSystemRights.FullControl, AccessControlType.Allow));

// Enable protection from inheritance, remove existing inherited rules
FileACL.SetAccessRuleProtection(true, false);

// Write ACL back to file
File.SetAccessControl(TargetFilePath, FileACL);
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thank you for your help. The code works if I run my app as Admin, but I have another problem. I want to let other users use the app, thus allowing them to read/write the database (excluding the possibility to delete or change permissions). With your code I can use the database only if I run the app as Admin, but I also want other users to be able to run the app. Is this possible? – Cristian M Feb 11 '16 at 09:18
  • I've tried this, but does not work `FileACL.AddAccessRule(new FileSystemAccessRule(BuiltinAdministrators, FileSystemRights.FullControl, AccessControlType.Allow)); FileACL.AddAccessRule(new FileSystemAccessRule(AuthenticatedUsers, FileSystemRights.Delete, AccessControlType.Deny)); FileACL.AddAccessRule(new FileSystemAccessRule(AuthenticatedUsers, FileSystemRights.Read, AccessControlType.Allow)); FileACL.AddAccessRule(new FileSystemAccessRule(AuthenticatedUsers, FileSystemRights.Write, AccessControlType.Allow));` – Cristian M Feb 11 '16 at 09:20
1

Using Mathias' help, I found the answer to my question:

try
{
       // Way safer than string comparison against "BUILTIN\\Administrators"
       IdentityReference BuiltinAdministrators = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
       IdentityReference AuthenticatedUsers = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);

       FileSecurity FileACL = File.GetAccessControl("database.sdf"); // Grab ACL from file

       if (FileACL.GetOwner(typeof(SecurityIdentifier)) != BuiltinAdministrators) // Check if correct owner is set
       {
             FileACL.SetOwner(BuiltinAdministrators); // If not, make it so!
       }

       foreach (FileSystemAccessRule fsRule in FileACL.GetAccessRules(true, true, typeof(SecurityIdentifier)))
       {
             if ((fsRule.FileSystemRights & FileSystemRights.Delete) == FileSystemRights.Delete ||
                    (fsRule.FileSystemRights & FileSystemRights.ChangePermissions) == FileSystemRights.ChangePermissions) // Check if rule grants delete or change permissions
             {
                  FileACL.RemoveAccessRule(fsRule); // If so, nuke it!
             }
       }

       // Add explicit rules
       FileACL.AddAccessRule(new FileSystemAccessRule(BuiltinAdministrators, FileSystemRights.FullControl, AccessControlType.Allow));
       FileACL.AddAccessRule(new FileSystemAccessRule(AuthenticatedUsers, FileSystemRights.Delete, AccessControlType.Deny));
       FileACL.AddAccessRule(new FileSystemAccessRule(AuthenticatedUsers, FileSystemRights.ChangePermissions, AccessControlType.Deny));
       FileACL.AddAccessRule(new FileSystemAccessRule(AuthenticatedUsers, FileSystemRights.Read, AccessControlType.Allow));
       FileACL.AddAccessRule(new FileSystemAccessRule(AuthenticatedUsers, FileSystemRights.Write, AccessControlType.Allow));

       FileACL.SetAccessRuleProtection(true, false); // Enable protection from inheritance, remove existing inherited rules
       File.SetAccessControl("database.sdf", FileACL); // Write ACL back to file
   }
   catch { }

And in order to work, the application has to be run once as admin.

Setting file permissions programmatically is new to me, so if someone thinks the code may not do what it's intended to do, please correct me. Thank you!

Cristian M
  • 715
  • 2
  • 12
  • 32