別のドメインのマシンで共有ファイルを読み取る必要があるC#コンソールアプリケーションがあります。アプリケーションがファイルにアクセスしようとすると、ローカルユーザーが共有リソースにアクセスする権限を持っていないため、例外が発生します。
現在、実行から共有フォルダーを開き、ユーザー名とパスワードをWindows認証ダイアログに入力してからアプリケーションを実行することにより、この問題を手動で克服しています。
プログラムでそれを行うにはどうすればよいですか?
別のドメインのマシンで共有ファイルを読み取る必要があるC#コンソールアプリケーションがあります。アプリケーションがファイルにアクセスしようとすると、ローカルユーザーが共有リソースにアクセスする権限を持っていないため、例外が発生します。
現在、実行から共有フォルダーを開き、ユーザー名とパスワードをWindows認証ダイアログに入力してからアプリケーションを実行することにより、この問題を手動で克服しています。
プログラムでそれを行うにはどうすればよいですか?
a)p / invokeLogonUser
を使用して、新しいトークンを使用してLOGON32_LOGON_NEW_CREDENTIALS
newを作成してから、通常のファイルアクセスを使用します。WindowsIdentity
b)WNetAddConnection3をp/invokeします。これにより、リモート共有がマシン上の他のすべてのプロセスにアクセスできるようになることに注意してください。
c)System.Management
およびを介したWMI CIM_DataFile
; p/invokeも必要ありません。System.Management
リモートマシンの資格情報を指定できます。
Anton が提案したように、ポイント "a" を使用しました。1 つのクラスに対して 2 つのバージョンを開発しました。1 つ目は Win32 API を使用し、2 つ目はWindowsIdentity
クラスを使用します。
バージョン 1:
class UserImpersonation : IDisposable
{
[DllImport("advapi32.dll")]
public static extern int LogonUser(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
WindowsImpersonationContext wic;
string _userName;
string _domain;
string _passWord;
public UserImpersonation(string userName, string domain, string passWord)
{
_userName = userName;
_domain = domain;
_passWord = passWord;
}
public bool ImpersonateValidUser()
{
WindowsIdentity wi;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUser(_userName, _domain, _passWord, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
wi = new WindowsIdentity(tokenDuplicate);
wic = wi.Impersonate();
if (wic != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
return false;
}
#region IDisposable Members
public void Dispose()
{
if (wic != null)
{
wic.Dispose();
}
RevertToSelf();
}
#endregion
}
Version2 (小さな変更を加えたMSDNから):
class UserImpersonation2 : IDisposable
{
[DllImport("advapi32.dll")]
public static extern bool LogonUser(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
WindowsImpersonationContext wic;
IntPtr tokenHandle;
string _userName;
string _domain;
string _passWord;
public UserImpersonation2(string userName, string domain, string passWord)
{
_userName = userName;
_domain = domain;
_passWord = passWord;
}
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
public bool ImpersonateValidUser()
{
bool returnValue = LogonUser(_userName, _domain, _passWord,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
Console.WriteLine("LogonUser called.");
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine("LogonUser failed with error code : {0}", ret);
return false;
}
Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No"));
Console.WriteLine("Value of Windows NT token: " + tokenHandle);
// Check the identity.
Console.WriteLine("Before impersonation: "
+ WindowsIdentity.GetCurrent().Name);
// Use the token handle returned by LogonUser.
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
wic = newId.Impersonate();
// Check the identity.
Console.WriteLine("After impersonation: "
+ WindowsIdentity.GetCurrent().Name);
return true;
}
#region IDisposable Members
public void Dispose()
{
if(wic!=null)
{
wic.Undo();
}
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
#endregion
}
使い方(どちらも同じ):
const string file = @"\\machine\test\file.txt";
using (UserImpersonation user = new UserImpersonation("user", "domain", "password"))
{
if (user.ImpersonateValidUser())
{
StreamReader reader = new StreamReader(file);
Console.WriteLine(reader.ReadToEnd());
reader.Close();
}
}
メモリから、Windows API 呼び出しを使用し、他のドメインのユーザーとしてログインする必要があります。例については、このリンクを参照してください。
もう 1 つのアイデアは、RunAs コマンド ライン引数を使用してファイルを読み取り、ローカル ドメイン/サーバー上のファイルに保存することです。