问题描述
我们遇到了一个需要解决的有趣情况,而我的搜索结果为零.因此,我向 SO 社区寻求帮助.
We've run into an interesting situation that needs solving, and my searches have turned up nill. I therefore appeal to the SO community for help.
问题是这样的:我们需要通过远程文件共享/UNC 以编程方式访问不在我们域中且不在受信任外部域内的共享文件.当然,我们需要向远程机器提供凭据.
The issue is this: we have a need to programmatically access a shared file that is not in our domain, and is not within a trusted external domain via remote file sharing / UNC. Naturally, we need to supply credentials to the remote machine.
通常,可以通过以下两种方式之一解决此问题:
Typically, one solves this problem in one of two ways:
- 将文件共享映射为驱动器并提供当时的凭据.这通常使用
NET USE
命令或复制NET USE
的 Win32 函数来完成. - 使用 UNC 路径访问文件,就像远程计算机在域中一样,并确保在远程计算机上以本地用户身份运行程序的帐户(包括密码)是重复的.基本上利用这样一个事实,即当用户尝试访问共享文件时,Windows 将自动提供当前用户的凭据.
- 不要使用远程文件共享.使用 FTP(或其他方式)传输文件,在本地处理它,然后将其传回.
- Map the file share as a drive and supply the credentials at that time. This is typically done using the
NET USE
command or the Win32 functions that duplicateNET USE
. - Access the file with a UNC path as if the remote computer were on the domain and ensure that the account under which the program runs is duplicated (including password) on the remote machine as a local user. Basically leverage the fact that Windows will automatically supply the current user's credentials when the user attempts to access a shared file.
- Don't use remote file sharing. Use FTP (or some other means) to transfer the file, work on it locally, then transfer it back.
出于各种原因,我们的安全/网络架构师拒绝了前两种方法.第二种方法显然是一个安全漏洞;如果远程计算机受到威胁,则本地计算机现在处于危险之中.第一种方法并不令人满意,因为新安装的驱动器是本地计算机上其他程序在程序访问文件期间可用的共享资源.尽管这很可能是暂时的,但在他们看来,这仍然是一个漏洞.
For various and sundry reasons, our security / network architects have rejected the first two approaches. The second approach is obviously a security hole; if the remote computer is compromised, the local computer is now at risk. The first approach is unsatisfactory because the newly mounted drive is a shared resource available to other programs on the local computer during file access by the program. Even though it's quite possible to make this temporary, it's still a hole in their opinion.
他们对第三种选择持开放态度,但远程网络管理员坚持使用 SFTP 而不是 FTPS,而 FtpWebRequest 仅支持 FTPS.SFTP 是 对防火墙更友好的选项,我可以将几个库用于这种方法,但如果可以的话,我更愿意减少我的依赖项.
They're open to the third option, but the remote network admins insist on SFTP rather than FTPS, and FtpWebRequest only supports FTPS. SFTP is the more firewall-friendly option and there are a couple libraries I could use for that approach, but I'd prefer to reduce my dependencies if I can.
我已经在 MSDN 上搜索了使用远程文件共享的托管或 win32 方法,但是我没有找到任何有用的东西.
I've searched MSDN for either a managed or a win32 means of using remote file sharing, but I have failed to come up with anything useful.
所以我问:还有其他方法吗?我是否错过了一个可以完成我想要的超级秘密的 win32 函数?还是我必须追求选项 3 的某种变体?
And so I ask: Is there another way? Did I miss a super-secret win32 function that does what I want? Or must I pursue some variant of option 3?
推荐答案
解决问题的方法是使用名为 WNetUseConnection.
使用此函数连接到带有身份验证的 UNC 路径,而不是映射驱动器.
The way to solve your problem is to use a Win32 API called WNetUseConnection.
Use this function to connect to a UNC path with authentication, NOT to map a drive.
这将允许您连接到远程计算机,即使它不在同一个域中,即使它具有不同的用户名和密码.
This will allow you to connect to a remote machine, even if it is not on the same domain, and even if it has a different username and password.
使用 WNetUseConnection 后,您将能够通过 UNC 路径访问该文件,就像您在同一个域中一样.最好的方法可能是通过管理内置共享.
示例:\计算机名c$program filesFolderfile.txt
Once you have used WNetUseConnection you will be able to access the file via a UNC path as if you were on the same domain. The best way is probably through the administrative built in shares.
Example: \computernamec$program filesFolderfile.txt
这是一些使用 WNetUseConnection 的示例 C# 代码.
请注意,对于 NetResource,您应该为 lpLocalName 和 lpProvider 传递 null.dwType 应该是 RESOURCETYPE_DISK.lpRemoteName 应该是 \ComputerName.
Here is some sample C# code that uses WNetUseConnection.
Note, for the NetResource, you should pass null for the lpLocalName and lpProvider. The dwType should be RESOURCETYPE_DISK. The lpRemoteName should be \ComputerName.
using System;
using System.Runtime.InteropServices ;
using System.Threading;
namespace ExtremeMirror
{
public class PinvokeWindowsNetworking
{
#region Consts
const int RESOURCE_CONNECTED = 0x00000001;
const int RESOURCE_GLOBALNET = 0x00000002;
const int RESOURCE_REMEMBERED = 0x00000003;
const int RESOURCETYPE_ANY = 0x00000000;
const int RESOURCETYPE_DISK = 0x00000001;
const int RESOURCETYPE_PRINT = 0x00000002;
const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;
const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
const int RESOURCEUSAGE_CONTAINER = 0x00000002;
const int CONNECT_INTERACTIVE = 0x00000008;
const int CONNECT_PROMPT = 0x00000010;
const int CONNECT_REDIRECT = 0x00000080;
const int CONNECT_UPDATE_PROFILE = 0x00000001;
const int CONNECT_COMMANDLINE = 0x00000800;
const int CONNECT_CMD_SAVECRED = 0x00001000;
const int CONNECT_LOCALDRIVE = 0x00000100;
#endregion
#region Errors
const int NO_ERROR = 0;
const int ERROR_ACCESS_DENIED = 5;
const int ERROR_ALREADY_ASSIGNED = 85;
const int ERROR_BAD_DEVICE = 1200;
const int ERROR_BAD_NET_NAME = 67;
const int ERROR_BAD_PROVIDER = 1204;
const int ERROR_CANCELLED = 1223;
const int ERROR_EXTENDED_ERROR = 1208;
const int ERROR_INVALID_ADDRESS = 487;
const int ERROR_INVALID_PARAMETER = 87;
const int ERROR_INVALID_PASSWORD = 1216;
const int ERROR_MORE_DATA = 234;
const int ERROR_NO_MORE_ITEMS = 259;
const int ERROR_NO_NET_OR_BAD_PATH = 1203;
const int ERROR_NO_NETWORK = 1222;
const int ERROR_BAD_PROFILE = 1206;
const int ERROR_CANNOT_OPEN_PROFILE = 1205;
const int ERROR_DEVICE_IN_USE = 2404;
const int ERROR_NOT_CONNECTED = 2250;
const int ERROR_OPEN_FILES = 2401;
private struct ErrorClass
{
public int num;
public string message;
public ErrorClass(int num, string message)
{
this.num = num;
this.message = message;
}
}
// Created with excel formula:
// ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"),
new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"),
new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"),
new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"),
new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"),
new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"),
new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"),
new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"),
new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"),
new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"),
new ErrorClass(ERROR_MORE_DATA, "Error: More Data"),
new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"),
new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"),
new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"),
new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"),
new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"),
new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"),
new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"),
new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"),
new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"),
};
private static string getErrorForNumber(int errNum)
{
foreach (ErrorClass er in ERROR_LIST)
{
if (er.num == errNum) return er.message;
}
return "Error: Unknown, " + errNum;
}
#endregion
[DllImport("Mpr.dll")] private static extern int WNetUseConnection(
IntPtr hwndOwner,
NETRESOURCE lpNetResource,
string lpPassword,
string lpUserID,
int dwFlags,
string lpAccessName,
string lpBufferSize,
string lpResult
);
[DllImport("Mpr.dll")] private static extern int WNetCancelConnection2(
string lpName,
int dwFlags,
bool fForce
);
[StructLayout(LayoutKind.Sequential)] private class NETRESOURCE
{
public int dwScope = 0;
public int dwType = 0;
public int dwDisplayType = 0;
public int dwUsage = 0;
public string lpLocalName = "";
public string lpRemoteName = "";
public string lpComment = "";
public string lpProvider = "";
}
public static string connectToRemote(string remoteUNC, string username, string password)
{
return connectToRemote(remoteUNC, username, password, false);
}
public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser)
{
NETRESOURCE nr = new NETRESOURCE();
nr.dwType = RESOURCETYPE_DISK;
nr.lpRemoteName = remoteUNC;
// nr.lpLocalName = "F:";
int ret;
if (promptUser)
ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
else
ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
if (ret == NO_ERROR) return null;
return getErrorForNumber(ret);
}
public static string disconnectRemote(string remoteUNC)
{
int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
if (ret == NO_ERROR) return null;
return getErrorForNumber(ret);
}
}
}
这篇关于使用凭据从远程、不受信任的域访问共享文件 (UNC)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!