I need to download files from a server to a shared drive directory, creating the directory if it doesn't exist. There are a few things making this more complicated:
- I do not have write access (nor does the account that will run the job in UAT/Prod) to the shared drive directory.
- The Service account that does have write access does not have any privileges anywhere but the shared drive directory.
I attempt to impersonate, as so:
class Impersonation
{
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON_TYPE_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_WINNT50 = 3;
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public static void Impersonate(string domain, string user, string password, Action act)
{
//if no user specified, don't impersonate
if (user.Trim() == "")
{
act();
return;
}
WindowsImpersonationContext impersonationContext = null;
IntPtr token = IntPtr.Zero;
try
{
//if no domain specified, default it to current machine
if (domain.Trim() == "")
{
domain = System.Environment.MachineName;
}
bool result = LogonUser(user, domain, password, LOGON_TYPE_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, ref token);
WindowsIdentity wi = new WindowsIdentity(token);
impersonationContext = WindowsIdentity.Impersonate(token);
act();
}
catch (Exception ex)
{
if (impersonationContext != null)
{
impersonationContext.Undo();
impersonationContext = null;
}
//if something went wrong, try it as the running user just in case
act();
}
finally
{
if (impersonationContext != null)
{
impersonationContext.Undo();
impersonationContext = null;
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
token = IntPtr.Zero;
}
}
}
}
And a piece of the the actual calling code is (in another class):
private static void CreateDirectoryIfNotExist(string directory, string domain, string username, string password)
{
Impersonation.Impersonate(domain, username, password, () => CreateIfNotExist(directory));
}
private static void CreateIfNotExist(string dir)
{
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
}
If I run it with the proper login info for the service account, I get an Exception on the Directory.CreateDirectory(string) call:
{System.IO.IOException: This user isn't allowed to sign in to this computer.}
I'm guessing this means the service account isn't allowed to log in to the executing machine, which I already knew. But really, there's no reason it needs to log in to the executing machine. Is there a way I can use impersonation to log on to a remote machine and execute the commands from there?