Copying Access Control List (ACL) entries between files [Kim Hamilton]

If you’re trying to transfer the access rights from one file to another, the obvious way seems to be getting the FileSecurity object from one file and applying it to another through the Get/SetAccessControl methods. The following code sample attempts to apply the ACLs from fileA to fileB using this technique.

 

string fileA = @"C:\temp\fileA.txt";

string fileB = @"C:\temp\fileB.txt";

 

// First attempt

FileSecurity securityA = File.GetAccessControl(fileA);

File.SetAccessControl(fileB, securityA);

However, this doesn’t actually apply fileB’s ACLs to fileB. The SetAccessControl method applies only the modifications made to the FileSecurity object. In the attempt above, the security object hasn’t changed and therefore fileB’s ACLs won’t change. More will be said about this in a moment.

 

The correct way to do transfer the ACLs to fileB is to use the Get/SetSecurityDescriptorBinaryForm or Get/GetSecurityDescriptorSddlForm to transfer the ACLs from one security object to another. This is elaborated on in the remarks section of https://msdn2.microsoft.com/en-us/library/system.io.file.setaccesscontrol.aspx.

 

The following code sample demonstrates this technique:

 

// Second attempt

FileSecurity securityA = File.GetAccessControl(fileA);

FileSecurity securityB = new FileSecurity();

byte[] securityDescriptor = securityA.GetSecurityDescriptorBinaryForm();

securityB.SetSecurityDescriptorBinaryForm(securityDescriptor);

File.SetAccessControl(fileB, securityB);

 

Note that for this to succeed, the caller must have the right to change permissions on fileB; otherwise he will get an UnauthorizedAccessException.

 

In the first attempt, it was mentioned that SetAccessControl operates on the changes in the FileSecurity. So, in the first scenario, if you changed the object securityA and tried to apply it to fileB, it would succeed as long as the caller has permission. But it would only persist the differences you made to securityA, so the ACLs of the files may still be different.

 

Another side effect of SetAccessControl only persisting differences is this: if you run the first code sample as a user without permission to make the change and didn’t change securityA, you won’t get an exception, but the ACLs won’t change either. This can be confusing if you rely on exceptions to know that an operation didn’t succeed. However, if you changed the FileSecurity object then tried to persist it, you’d get an exception then.

 

These strange scenarios can be avoided if you use second approach to transfer ACLs between files, and modify the FileSecurity object only in the context of one file. For example, after transferring the security descriptor to securityB, you can modify securityB with any additional rights you want to apply to fileB. The following code sample demonstrates transferring fileA’s ACLs to fileB plus an additional permission of “full control” for a new user called “AUser” to fileB.

 

The first technique is useful if you want to change the ACLs on one file.

 

FileSecurity securityA = File.GetAccessControl(fileA);

FileSecurity securityB = new FileSecurity();

byte[] securityDescriptor = securityA.GetSecurityDescriptorBinaryForm();

securityB.SetSecurityDescriptorBinaryForm(securityDescriptor);

// add new access rule

FileSystemAccessRule newAccessRule = new FileSystemAccessRule(@"ADomain\AUser",
FileSystemRights.FullControl, AccessControlType.Allow);

securityB.AddAccessRule(newAccessRule);

File.SetAccessControl(fileB, securityB);