Dev*_*man 7 c# impersonation unc remote-access file-manipulation
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 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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
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?
如果帐户无法登录,则无法通过模拟进行操作。模拟要求线程在用户凭据下运行。这就是LogonUser失败的原因。
您可以使用WNetAddConnection2函数来建立与网络资源的连接。
CreateDirectoryIfNotExist以下是使用此方法的函数示例:
public static void CreateDirectoryIfNotExists(string directory, string sharePath, string username, string password)
{
NETRESOURCE nr = new NETRESOURCE();
nr.dwType = ResourceType.RESOURCETYPE_DISK;
nr.lpLocalName = null;
nr.lpRemoteName = sharePath;
nr.lpProvider = null;
int result = WNetAddConnection2(nr, password, username, 0);
string directoryFullPath = Path.Combine(sharePath, directory);
if (!Directory.Exists(directoryFullPath))
{
Directory.CreateDirectory(directoryFullPath);
}
}
Run Code Online (Sandbox Code Playgroud)
为了能够进行系统调用,您还需要pinvoke.net中的以下定义。
[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
public ResourceScope dwScope = 0;
public ResourceType dwType = 0;
public ResourceDisplayType dwDisplayType = 0;
public ResourceUsage dwUsage = 0;
[MarshalAs(UnmanagedType.LPStr)] public string lpLocalName = null;
[MarshalAs(UnmanagedType.LPStr)] public string lpRemoteName = null;
[MarshalAs(UnmanagedType.LPStr)] public string lpComment = null;
[MarshalAs(UnmanagedType.LPStr)] public string lpProvider;
};
public enum ResourceScope
{
RESOURCE_CONNECTED = 1,
RESOURCE_GLOBALNET,
RESOURCE_REMEMBERED,
RESOURCE_RECENT,
RESOURCE_CONTEXT
};
public enum ResourceType
{
RESOURCETYPE_ANY,
RESOURCETYPE_DISK,
RESOURCETYPE_PRINT,
RESOURCETYPE_RESERVED
};
public enum ResourceUsage
{
RESOURCEUSAGE_CONNECTABLE = 0x00000001,
RESOURCEUSAGE_CONTAINER = 0x00000002,
RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
RESOURCEUSAGE_SIBLING = 0x00000008,
RESOURCEUSAGE_ATTACHED = 0x00000010,
RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
};
public enum ResourceDisplayType
{
RESOURCEDISPLAYTYPE_GENERIC,
RESOURCEDISPLAYTYPE_DOMAIN,
RESOURCEDISPLAYTYPE_SERVER,
RESOURCEDISPLAYTYPE_SHARE,
RESOURCEDISPLAYTYPE_FILE,
RESOURCEDISPLAYTYPE_GROUP,
RESOURCEDISPLAYTYPE_NETWORK,
RESOURCEDISPLAYTYPE_ROOT,
RESOURCEDISPLAYTYPE_SHAREADMIN,
RESOURCEDISPLAYTYPE_DIRECTORY,
RESOURCEDISPLAYTYPE_TREE,
RESOURCEDISPLAYTYPE_NDSCONTAINER
};
public enum ResourceConnection
{
CONNECT_UPDATE_PROFILE = 1,
CONNECT_UPDATE_RECENT = 2,
CONNECT_TEMPORARY = 4,
CONNECT_INTERACTIVE = 8,
CONNECT_PROMPT = 0X10,
CONNECT_REDIRECT = 0X80,
CONNECT_CURRENT_MEDIA = 0X200,
CONNECT_COMMAND_LINE = 0X800,
CONNECT_CMD_SAVECRED = 0X1000,
CONNECT_CRED_RESET = 0X2000
};
[DllImport("mpr.dll", CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
private static extern int WNetAddConnection2(NETRESOURCE lpNetResource,
[MarshalAs(UnmanagedType.LPStr)] string lpPassword,
[MarshalAs(UnmanagedType.LPStr)] string lpUserName, int dwFlags);
Run Code Online (Sandbox Code Playgroud)
您可以将此定义添加到与您的函数相同的类中。
这里还有两个旧帖子的链接,它们也使用相同的方法。