Reputation: 5670
I'm using the code below to change the owner of a file. This code runs in a Windows Service written in C#, version 4.0. It's running on a local server, running Windows Server 2008 R2 Standard, Service Pack 1.
I need to change the owner of a file that is received via FTP to a domain account. I can log into the box and do it manually using Explorer, but when I try and run this via code, I get an InvalidOperation exception. I can change the owner to the Local System account, but not a network account. Any help on this would be greatly appreciated.
I'm working with some bizarre Microsoft Dynamics AX code that handles EDI files. The process requires the owner of the file be a valid DAX user, in this case a Domain User. We have Vendors that send us EDI data via FTP. Our DAX application checks the FTP directory every 10 minutes and processes the files. The process currently fails, because the owner is invalid. So, I've written a service to change the owner of the file when it arrives. However, the code below fails with the exception show below the code example.
var ediFileOwner = new NTAccount("MyDomain", _ediEndpointUserAccount);
var fileSecurity = File.GetAccessControl(fileName);
var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
fileSecurity.AddAccessRule(new FileSystemAccessRule(everyone, FileSystemRights.FullControl, AccessControlType.Allow));
fileSecurity.AddAccessRule(new FileSystemAccessRule(ediFileOwner, FileSystemRights.TakeOwnership, AccessControlType.Allow));
fileSecurity.SetOwner(ediFileOwner); //Change our owner from to our desired User
File.SetAccessControl(fileName, fileSecurity);
Here is the full Exception:
System.InvalidOperationException: The security identifier is not allowed to be the owner of this object.
at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle handle, AccessControlSections includeSections, Object exceptionContext)
at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, AccessControlSections includeSections, Object exceptionContext)
at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, AccessControlSections includeSections)
at System.Security.AccessControl.FileSystemSecurity.Persist(String fullPath)
at System.IO.File.SetAccessControl(String path, FileSecurity fileSecurity)
UPDATE
If I change the account the service run under to the account I'm trying to change to owner to, I get a different exception.
Unexpected Exception: System.UnauthorizedAccessException: Attempted to perform an unauthorized operation. at System.Security.AccessControl.Win32.SetSecurityInfo(ResourceType type, String name, SafeHandle handle, SecurityInfos securityInformation, SecurityIdentifier owner, SecurityIdentifier group, GenericAcl sacl, GenericAcl dacl)
Upvotes: 1
Views: 3204
Reputation: 5670
I ended up using some code I found here, http://www.codeproject.com/Articles/10090/A-small-C-Class-for-impersonating-a-User
I had to jump through a few hoops in order to get everything done, but it worked. In order to avoid the errors I was getting, I had to use the Impersonate stuff I found in addition to switching between users throughout.
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
// ...
//Copy the file. This allows our service account to take ownership of the copied file
var tempFileName = Path.Combine(Path.GetDirectoryName(file.FileName), "TEMP_" + file.FileNameOnly);
File.Copy(file.FileName, tempFileName);
var windowID = WindowsIdentity.GetCurrent();
var currUserName = windowID.User.Translate(typeof(NTAccount)).Value;
var splitChar = new[] { '\\' };
//var name = currUserName.Split(splitChar)[1];
//var domain = currUserName.Split(splitChar)[0];
var ediFileOwner = new NTAccount("TricorBraun", _radleyEDIEndpointUserAccount);
//We have to give Access to the service account to delete the original file
var fileSecurity = File.GetAccessControl(file.FileName);
var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
fileSecurity.AddAccessRule(new FileSystemAccessRule(everyone, FileSystemRights.FullControl, AccessControlType.Allow));
File.SetAccessControl(file.FileName, fileSecurity);
File.Delete(file.FileName);
//We rename our file to get our original file name back
File.Move(tempFileName, file.FileName);
//The following give our desired user permissions to take Ownership of the file.
//We have to do this while running under the service account.
fileSecurity = File.GetAccessControl(file.FileName);
var aosSID = (SecurityIdentifier) ediFileOwner.Translate(typeof(SecurityIdentifier));
fileSecurity.AddAccessRule(new FileSystemAccessRule(aosSID, FileSystemRights.FullControl, AccessControlType.Allow));
File.SetAccessControl(file.FileName, fileSecurity);
//Now we user the Impersonator (http://www.codeproject.com/Articles/10090/A-small-C-Class-for-impersonating-a-User)
//This allows us to manage the file as the Account we wish to change ownership to.
//It makes itself the owner.
using (new Impersonator(_radleyEDIEndpointUserAccount, "MyDomain", "password")) {
_logger.Debug(string.Format("Attempting changing owner to Tricorbraun\\{0}", _radleyEDIEndpointUserAccount));
fileSecurity = File.GetAccessControl(file.FileName);
fileSecurity.SetOwner(ediFileOwner); //Change our owner from LocalAdmin to our chosen DAX User
_logger.Debug(string.Format("Setting owner to Tricorbraun - {0}", _radleyEDIEndpointUserAccount));
File.SetAccessControl(file.FileName, fileSecurity);
}
Upvotes: 2