How can I use impersonation to manipulate files/directories on a remote machine with UNC?

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:

  1. I do not have write access (nor does the account that will run the job in UAT/Prod) to the shared drive directory.
  2. 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;
            }
        }
    }
}
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?

Ale*_*esD 0

如果帐户无法登录,则无法通过模拟进行操作。模拟要求线程在用户凭据下运行。这就是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)

您可以将此定义添加到与您的函数相同的类中。

这里还有两个旧帖子的链接,它们也使用相同的方法。

使用凭据从远程、不受信任的域访问共享文件 (UNC)

如何以编程方式访问文件共享