简体   繁体   中英

How to prevent file deletion by ANY user

Problem: Need to prevent individual Windows files from being deleted, at least without Administrator privileges. Files must be readable by "Everyone". The Windows directory in which the files reside must be writeable and readable by "Everyone".

Debugging: Example Code below is my attempt to set the "Deny Delete" access control rules on an example file (string "urlFilePath" contains the full path to the actual file when this code is executed). The code runs with no errors, but the files are still marked as deletable by Everyone (and other accounts).

Details: I have a C# Service that migrates files from a soon-to-be-decommissioned FileNet WORM storage system to Windows file system storage.

The files need to be accessible to many users (effectively "Everyone"), but must be preserved against deletion - whether accidental or intentional. The folder(s) in which the files will reside must be writable (some users must be allowed to add future files), so I cannot make the folders read-only.

I thought I could just add ACL rules to "deny" delete, but the following code isn't working. Did I make a simple mistake or is there some trick I am missing?

    string urlFilePath = @"\\server\directory\anotherdir\myfile.pdf";
    List<string> fileAccounts = new List<string>();
    FileInfo fi = new FileInfo(urlFilePath);
    FileSecurity fsec = fi.GetAccessControl();
    AuthorizationRuleCollection acl = fsec.GetAccessRules(includeExplicit: true, includeInherited: true, targetType: typeof(System.Security.Principal.NTAccount));
    foreach (FileSystemAccessRule acr in acl)
    {
        if (!fileAccounts.Contains(acr.IdentityReference.Value))
            fileAccounts.Add(acr.IdentityReference.Value);
    }

    //  populate standard accounts
    foreach (string s in new string[] { "GUEST", "USERS", "DOMAIN USERS", "NETWORK", "AUTHENTICATED USERS"})
    {
        if (!fileAccounts.Contains(s))
            fileAccounts.Add(s);
    }
    FileSecurity fileSecurity = File.GetAccessControl(urlFilePath);
    int numAccounts = 0;
    foreach (string account in fileAccounts)
    {
        fileSecurity.RemoveAccessRule(new FileSystemAccessRule(account, FileSystemRights.Delete, AccessControlType.Allow));
        fileSecurity.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.Delete, AccessControlType.Deny));
        numAccounts++;
    }

In the above, I followed (I thought) the MS example of adding/removing ACL rules. I get no errors, warnings, or exceptions, but the files remain fully deleteable by everyone. I explicitly added the list of accounts, thinking maybe the ones already associated with the file weren't sufficient. When this runs, it reports the remove/add loop runs against 9 accounts - the 5 I added, plus 4 already associated, so the remove rule/add rule loop is executing against all the accounts.

How can I set the individual files to be non-deleteable? It is acceptable to allow them to be deleted if the person deleting gives themselves ownership of the individual file, as that should be sufficiently safe.

I would prefer to not allow Domain Administrators to delete without taking ownership of the file, but Administrators could be allowed to delete files (both Local and Domain Administrators) if preventing those groups is too difficult. The critical need is to prevent any non-administrators from deleting any of these files.

My question was closed as requiring "debugging details", but with no mention of what details were missing or requested. ???

After some more trial and error, I think I found a solution. Applying "Deny" to the Delete right is insufficient. The following seems to work. To delete a file set this way requires a user with sufficient privileges to remove the Deny rules.

I get the list of all current Accounts with access to the folder, add several "well known" accounts, and then iterate through the accounts, removing all the rights other than READ, and then applying DENY rights to everything except READ.

Get the list of accounts.

    DirectoryInfo dInfo = new DirectoryInfo(dir);
    DirectorySecurity dSec = dInfo.GetAccessControl();
    AuthorizationRuleCollection arc = dSec.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
    List<string> fileAccounts = new List<string>();
    foreach (FileSystemAccessRule fsar in arc)
    {
        if (!fileAccounts.Contains(fsar.IdentityReference.Value))
            fileAccounts.Add(fsar.IdentityReference.Value);
    }
    foreach (string s in new string[] { "GUEST", "USERS", "DOMAIN USERS", "NETWORK", "Authenticated Users", "SYSTEM", "Everyone" })
    {
        if (!fileAccounts.Contains(s))
            fileAccounts.Add(s);
    }

Iterate through the accounts, removing/adding rights. Note that not all accounts may exist in all cases (eg, domain/non-domain accounts), so I trap and discard errors.

    FileInfo fi = new FileInfo(fname);
    FileSecurity fsec = fi.GetAccessControl();
    int numSet = 0;
    foreach (string account in fileAccounts)
    {
        try
        {
            fsec.RemoveAccessRule(new FileSystemAccessRule(account, FileSystemRights.Delete, AccessControlType.Allow));
            fsec.RemoveAccessRule(new FileSystemAccessRule(account, FileSystemRights.FullControl, AccessControlType.Allow));
            fsec.RemoveAccessRule(new FileSystemAccessRule(account, FileSystemRights.Modify, AccessControlType.Allow));
            fsec.RemoveAccessRule(new FileSystemAccessRule(account, FileSystemRights.ReadAndExecute, AccessControlType.Allow));
            fsec.RemoveAccessRule(new FileSystemAccessRule(account, FileSystemRights.Write, AccessControlType.Allow));
            fsec.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.Delete, AccessControlType.Deny));
            fsec.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.FullControl, AccessControlType.Deny));
            fsec.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.Modify, AccessControlType.Deny));
            fsec.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.ReadAndExecute, AccessControlType.Deny));
            fsec.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.Write, AccessControlType.Deny));
        }
        catch (Exception ex)
        {
            //  log error and continue if necessare - Console.WriteLine(account + ": " + ex.Message);
        }
        numSet++;
    }
    fi.SetAccessControl(fsec);

I suspect there is a cleaner, simpler way to do this, but at least this is working for what I need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM