あるドメインのマシンで実行し、信頼できる接続を使用して別のドメインの SQL サーバーで認証する必要があるツールを作成していたときに、同様の問題がありました。この件について私が見つけたものはすべて、それは不可能だと言いました。代わりに、ドメインに参加する、SQL 認証を使用する、Kerberos と呼ばれるチャプターに参加する、またはネットワーク担当者に信頼関係をセットアップしてもらうなど、いくつかの選択肢を挙げる必要があります。
問題は、SSMSでそれを証明したので、RUNASを使用して何らかの方法で動作させることができることを知っていたということです:
C:\WINDOWS\system32\runas.exe /netonly /savecred /user:megacorp\joe.bloggs "C:\Program Files\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\SqlWb.exe"
/netonly フラグを使用すると、ローカルの資格情報で exe を実行し、リモートの資格情報でネットワークにアクセスできるようになりました。とにかく、リモート サーバーから期待どおりの結果セットを取得したと思います。問題は、runas コマンドによってアプリケーションのデバッグが非常に難しくなり、いい匂いがしないことでした。
最終的に、Active Directory を操作するための認証について話しているコード プロジェクトに関するこの記事を見つけました。偽装を行うメイン クラスは次のとおりです。
システムを使用する;
System.Runtime.InteropServices を使用します。// DllImport
System.Security.Principal の使用; // WindowsImpersonationContext
名前空間 TestApp
{
クラスの偽装者
{
// グループ型の列挙型
enum SECURITY_IMPERSONATION_LEVEL : int
{
セキュリティ匿名 = 0、
セキュリティ識別 = 1、
セキュリティ偽装 = 2,
SecurityDelegation = 3
}
// ユーザートークンを取得
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(文字列 pszUsername、文字列 pszDomain、文字列 pszPassword、
int dwLogonType、int dwLogonProvider、ref IntPtr phToken);
// LogonUser から返された開いている手を閉じます
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
extern static bool CloseHandle(IntPtr ハンドル);
// 重複するトークン ハンドルを作成します
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL、ref IntPtr DuplicateTokenHandle);
WindowsImpersonationContext newUser;
///
/// ユーザーになりすます試み。成功した場合はリターン
/// 新しいユーザー ID の WindowsImpersonationContext。
///
/// なりすましたいユーザー名
/// ログオン ドメイン
/// ログオンに使用するユーザーのパスワード
///
public Impersonator (文字列 sUsername、文字列 sDomain、文字列 sPassword)
{
// トークンを初期化
IntPtr pExistingTokenHandle = 新しい IntPtr(0);
IntPtr pDuplicateTokenHandle = 新しい IntPtr(0);
pExistingTokenHandle = IntPtr.Zero;
pDuplicateTokenHandle = IntPtr.Zero;
// ドメイン名が空白の場合、ローカル マシンと見なす
if (sDomain == "")
sDomain = System.Environment.MachineName;
試す
{
const int LOGON32_PROVIDER_DEFAULT = 0;
// トークンを作成
// const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
//const int SecurityImpersonation = 2;
// トークンへのハンドルを取得
bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
LOGON32_LOGON_NEW_CREDENTIALS、LOGON32_PROVIDER_DEFAULT、ref pExistingTokenHandle);
// 偽装は失敗しましたか?
if (false == bImpersonated)
{
int nErrorCode = Marshal.GetLastWin32Error();
// LogonUser が失敗した理由を表示します
throw new ApplicationException("LogonUser() failed with error code: " + nErrorCode);
}
bool bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle);
// DuplicateToken は失敗しましたか?
if (false == bRetVal)
{
int nErrorCode = Marshal.GetLastWin32Error();
CloseHandle(pExistingTokenHandle); // 既存のハンドルを閉じる
// DuplicateToken が失敗した理由を表示します
throw new ApplicationException("DuplicateToken() はエラー コードで失敗しました: " + nErrorCode);
}
そうしないと
{
// 新しいプライマリ トークンを使用して新しい ID を作成します
WindowsIdentity newId = 新しい WindowsIdentity(pDuplicateTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
newUser = impersonatedUser;
}
}
最後に
{
// ハンドルを閉じる
if (pExistingTokenHandle != IntPtr.Zero)
CloseHandle(pExistingTokenHandle);
if (pDuplicateTokenHandle != IntPtr.Zero)
CloseHandle(pDuplicateTokenHandle);
}
}
public void 元に戻す()
{
newUser.Undo();
}
}
}
それを使用するには:
Impersonator impersonator = new Impersonator("username", "domain", "password");
//Connect to and use SQL server
impersonator.Undo();
Undo メソッドを追加しました。そうしないと、偽装オブジェクトがガベージ コレクションされる傾向がありました。また、LOGON32_LOGON_NEW_CREDENTIALS を使用するようにコードを変更しましたが、これは機能させるために突っ込んで実行するものでした。runas の /netonly フラグと同じような気がします。また、コンストラクターを少し分解します。