ユーザの偽装。

偽装処理
作ってた当事はいろいろ思うこともあったが、今は思い出せない。
とりあえずコードのみ。

/// <summary>
/// 偽装処理を行うためのスコープを提供します。
/// </summary>
public class ImpersonateScope : IDisposable {
  /// <summary>
  /// 偽装ユーザ。
  /// </summary>
  private readonly WindowsIdentity _windowsIdentity;

  /// <summary>
  /// 偽装操作を行う前のユーザ。
  /// </summary>
  private readonly WindowsImpersonationContext _impersonate;

  /// <summary>
  /// dispose が行われたか。
  /// </summary>
  private bool _disposed;

  /// <summary>
  /// ローカルコンピュータ、つまり LogonUser 関数を呼び出したコンピュータにユーザーをログオンさせようとします。リモートコンピュータへのログオンはできません。ユーザー名とドメインを使ってユーザーを指定し、平文パスワードを使ってユーザーを認証します。この関数が成功すると、既にログオンしているユーザーを表すトークンのハンドルが取得できます。次に、このトークンハンドルを使って、指定したユーザーを偽装するか、多くの場合は指定したユーザーのコンテキスト内で動作するプロセスを作成できます。
  /// </summary>
  /// <param name="lpszUsername">ユーザー名を表す、NULL で終わる文字列へのポインタを指定します。指定できるのは、既にログオンしているユーザーアカウントの名前です。</param>
  /// <param name="lpszDomain">ドメイン名、またはサーバー名を表す、NULL で終わる文字列へのポインタを指定します。このドメイン、またはサーバーのアカウントデータベースは、lpszUserName アカウントを保持しています。LogonUser 関数は、そのアカウントの検索と確認を行うようドメインコントローラ、またはサーバーに指示します。このパラメータに "." を指定すると、LogonUser 関数はローカルのアカウントデータベースのみを使ってアカウントを確認します。また、NULL を指定すると、LogonUser 関数は最初にローカルのアカウントデータベースを検索し、次に信頼される側のドメインに対して、アカウントが見つかるか、すべてのアカウントデータベースを検索し終わるまで作業を続けるよう指示します。</param>
  /// <param name="lpszPassword">lpszUsername パラメータで指定したユーザーアカウントに対応する平文のパスワードを表す、NULL で終わる文字列へのポインタを指定します。</param>
  /// <param name="dwLogonType">行したいログオン動作のタイプを指定します。</param>
  /// <param name="dwLogonProvider">ログオンプロバイダを指定します。</param>
  /// <param name="phToken">指定したユーザーを表すトークンのハンドルを受け取る、HANDLE 変数へのポインタを指定します。
  /// 返されたハンドルを使って、ImpersonateLoggedOnUser 関数が呼び出せます。 
  /// ほとんどの場合、返されたハンドルはプライマリトークンであり、CreateProcessAsUser 関数を呼び出す際に利用できます。しかし、LOGON32_LOGON_NETWORK フラグを指定した場合、LogonUser 関数は偽装トークンを返すので、DuplicateTokenEx 関数を呼び出してプライマリトークンへ変換しない限り、CreateProcessAsUser 関数では利用できません。 
  /// このハンドルが必要なくなったら、CloseHandle 関数を呼び出してこのハンドルを閉じてください。
  /// </param>
  /// <returns>関数が成功すると、0 以外の値が返ります。関数が失敗すると、0 が返ります。拡張エラー情報を取得するには、GetLastError 関数を使います。</returns>
  /// <see cref="http://msdn.microsoft.com/ja-jp/library/cc447468.aspx"/>
  /// <seealso cref="http://msdn.microsoft.com/en-us/library/aa378184.aspx"/>
  [DllImport( "advapi32.dll", SetLastError = true )]
  [return: MarshalAs( UnmanagedType.Bool )]
  private static extern bool LogonUser(
    string lpszUsername, string lpszDomain, string lpszPassword
    , LogonType dwLogonType, LogonProvider dwLogonProvider, out IntPtr phToken
    );

  /// <summary>
  /// 開いているオブジェクトハンドルを閉じます。
  /// </summary>
  /// <param name="hObject">開いているオブジェクトのハンドルを指定します。関数から制御が返ると、このハンドルに、同じオブジェクトのハンドルが格納されますが、閉じることに成功した場合はカウントが 1 減ります。</param>
  /// <returns>関数が成功すると、0 以外の値が返ります。関数が失敗すると、0 が返ります。拡張エラー情報を取得するには、GetLastError 関数を使います。</returns>
  /// <see cref="http://msdn.microsoft.com/ja-jp/library/cc429605.aspx"/>
  /// <seealso cref="http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx"/>
  [DllImport( "kernel32.dll", SetLastError = true )]
  [return: MarshalAs( UnmanagedType.Bool )]
  private static extern bool CloseHandle( IntPtr hObject );

  /// <summary>
  /// ImpersonateScope クラスの新しいインスタンスを初期化します。
  /// </summary>
  /// <param name="userName">偽装ログオンユーザ名。</param>
  /// <param name="domain">偽装ログオンドメイン名。</param>
  /// <param name="password">偽装ログオンパスワード。</param>
  public ImpersonateScope( string userName, string domain, string password ) {
    IntPtr token = GetToken( userName, domain, password );
    _windowsIdentity = new WindowsIdentity( token );
    _impersonate = _windowsIdentity.Impersonate();

    CloseHandle( token );
  }

  /// <summary>
  /// トークンを取得します。
  /// </summary>
  /// <param name="userName">ユーザ名。</param>
  /// <param name="domain">ドメイン名。</param>
  /// <param name="password">パスワード。</param>
  /// <returns>取得されたトークン。</returns>
  private static IntPtr GetToken( string userName, string domain, string password ) {
    IntPtr token;

    if (!LogonUser(
      userName, domain, password,
      LogonType.Logon32LogonInteractive, LogonProvider.Logon32ProviderDefault, out token
    )) {
      throw new Win32Exception( Marshal.GetLastWin32Error() );
    }
    return token;
  }

  ///<summary>
  /// なんか呼びたいよね。
  ///</summary>
  public void Finished() {
    Dispose();
  }

  /// <summary>
  /// ImpersonateScope で使用されるすべてのリソースを解放します。
  /// </summary>
  public void Dispose() {
    Disposed( true );
  }

  /// <summary>
  /// ImpersonateScope によって使用されているアンマネージ リソースを解放し、オプションでマネージ リソースも解放します。
  /// </summary>
  /// <param name="disposing">マネージ リソースとアンマネージ リソースの両方を解放する場合は true。アンマネージ リソースだけを解放する場合は false。</param>
  protected virtual void Disposed( bool disposing ) {
    if (_disposed) {
      return;
    }
    _disposed = true;

    if (disposing) {
      _impersonate.Dispose();
      _windowsIdentity.Dispose();
    }
  }

  /// <summary>
  /// ImpersonateScope クラスのインスタンスを破棄します。
  /// </summary>
  ~ImpersonateScope() {
    Disposed( false );
  }
}

/// <summary>
/// ログオンプロバイダを指定します。
/// </summary>
enum LogonProvider : uint {
  /// <summary>
  /// システム標準のログオンプロバイダを使います。これは、dwLogonProvider パラメータの推奨値です。Windows NT、Windows 2000 の現在、および将来のリリースに対する最大の互換性を提供します。
  /// </summary>
  Logon32ProviderDefault = 0,
  /// <summary>
  /// Windows NT 3.5 のログオンプロバイダを使います。
  /// </summary>
  Logon32ProviderWinnt35 = 1,
  /// <summary>
  /// Windows NT 4.0 のログオンプロバイダを使います。
  /// </summary>
  Logon32ProviderWinnt40 = 2,
  /// <summary>
  /// Windows 2000 のログオンプロバイダを使います。
  /// </summary>
  Logon32ProviderWinnt50 = 3,
}

/// <summary>
/// ログオンタイプを指定します。
/// </summary>
enum LogonType : uint {
  /// <summary>
  /// このログオンタイプは、コンピュータを対話形式で使うユーザーを想定しています。ターミナルサーバー、リモートシェル、または同様のプロセスを使ってログオンを行うユーザーが、これに該当します。切断された動作に関係するログオン情報をキャッシュに入れる作業が増えるので、メールサーバーといった特定のクライアント/サーバーアプリケーションには適していません。
  /// </summary>
  Logon32LogonInteractive = 2,
  /// <summary>
  /// このログオンタイプは、平文パスワード認証する高性能サーバーを想定しています。LogonUser 関数は、このログオンタイプのアカウント情報をキャッシュに入れません。これは最も高速なログオンパスですが、2つの制約があります。
  /// 第一に、この関数はプライマリトークンではなく偽装トークンを返します。CreateProcessAsUser 関数内では、このトークンを直接利用できません。代わりに、DuplicateTokenEx 関数を呼び出してこのトークンをプライマリトークンへ変換し、次に CreateProcessAsUser 関数でそのトークンを利用できます。
  /// 第二に、偽装トークンをプライマリトークンへ変換して CreateProcessAsUser 関数で利用してプロセスを開始すると、新しいプロセスはリダイレクタ経由でリモートサーバーやリモートプリンタなどの他のネットワーク資源を利用できません。
  /// </summary>
  Logon32LogonNetwork = 3,
  /// <summary>
  /// このログオンタイプは、ユーザーが直接操作する代わりにプロセスが処理を行うバッチサーバー、またはメールサーバーや Web サーバーのように平文認証を一度に数多く処理する高性能サーバーを想定しています。LogonUser 関数はこのログオンタイプのアカウント情報(認証証明)をキャッシュに入れません。
  /// </summary>
  Logon32LogonBatch = 4,
  /// <summary>
  /// サービスタイプのログオンを示します。提供されるアカウントでは、サービス特権を有効にしておかなければなりません。
  /// </summary>
  Logon32LogonService = 5,
  /// <summary>
  /// This logon type is for GINA DLLs that log on users who will be interactively using the computer. This logon type can generate a unique audit record that shows when the workstation was unlocked.
  /// </summary>
  Logon32LogonUnlock = 7,
  /// <summary>
  /// This logon type preserves the name and password in the authentication package, which allows the server to make connections to other network servers while impersonating the client. A server can accept plaintext credentials from a client, call LogonUser, verify that the user can access the system across the network, and still communicate with other servers.
  /// </summary>
  Logon32LogonNetworkCleartext = 8,
  /// <summary>
  /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identifier but uses different credentials for other network connections.
  /// This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
  /// </summary>
  Logon32LogonNewCredentials = 9,
}

public class NetShare {
  const int NERR_Success = 0;
  const uint MAX_PREFERRED_LENGTH = 0xFFFFFFFF;
  enum DataInformationLevel {
    /// <summary>
    /// 共有の名前を取得します。関数から制御が返ると、bufptr パラメータが指すバッファに、複数の 構造体からなる 1 つの配列が格納されます。
    /// </summary>
    Level0 = 0,
    /// <summary>
    /// リソースの名前、タイプ、リソースに関連付けられているコメントなど、共有リソースに関する情報を取得します。
    /// 関数から制御が返ると、bufptr パラメータが指すバッファに、複数の 構造体からなる 1 つの配列が格納されます。
    /// </summary>
    Level1 = 1,
    /// <summary>
    /// リソースの名前、タイプ、アクセス許可、パスワード、接続の数など、共有リソースに関する情報を取得します。関数から制御が返ると、bufptr パラメータが指すバッファに、複数の 構造体からなる 1 つの配列が格納されます。
    /// </summary>
    Level2 = 2,
    /// <summary>
    /// リソースの名前、タイプ、アクセス許可、接続の数、他の固有情報など、共有リソースに関する情報を取得します。関数から制御が返ると、bufptr パラメータが指すバッファに、複数の 構造体からなる 1 つの配列が格納されます。
    /// </summary>
    Level502 = 502,
  }

  /// <summary>
  /// 特定のサーバー上の各共有資源に関する情報を取得します。Windows 95/98 では使用できません。
  /// </summary>
  /// <param name="ServerName">この関数を実行するリモートサーバーの名前を表す、Unicode 文字列へのポインタを指定します。この文字列の先頭は "\\" でなければなりません。このパラメータで NULL を指定すると、ローカルコンピュータが使われます。</param>
  /// <param name="level">データの情報レベルを指定します。次の値のいずれかを指定します。</param>
  /// <param name="bufPtr">1 個のバッファへのポインタを指定します。関数から制御が返ると、このバッファに、指定したデータが格納されます。このデータの形式は、level パラメータの値によって異なります。このバッファはシステムによって割り当てられたものであり、NetApiBufferFree 関数を使って解放しなければなりません。この関数が失敗して ERROR_MORE_DATA が返った場合でも、このバッファを解放しなければならないことに注意してください。</param>
  /// <param name="prefmaxlen">取得するべきデータの最大の長さ(上限)をバイト単位で指定します。このパラメータが MAX_PREFERRED_LENGTH の場合、この関数はデータが必要とする量のメモリを割り当てます。このパラメータで他の値を指定すると、その値は、この関数が返すバイト数に制限を加えることがあります。バッファサイズが不足して一部のエントリを格納できない場合は、ERROR_MORE_DATA が返ります。詳細については、MSDN ライブラリの「Network Management Function Buffers」と「Network Management Function Buffer Lengths」を参照してください。</param>
  /// <param name="entriesread">1 つの DWORD 値へのポインタを指定します。関数から制御が返ると、この値に、実際に列挙された要素の数が格納されます。</param>
  /// <param name="totalentries">1 つの DWORD 値へのポインタを指定します。関数から制御が返ると、この値に、現在のレジューム位置以降で列挙できるはずのエントリの総数が格納されます。</param>
  /// <param name="resume_handle">引き続き既存の共有を検索するために使われるレジュームハンドルを保持している、1 つの DWORD 値へのポインタを指定します。このハンドルは最初の呼び出しのときに 0 であるべきで、それ以降の呼び出しでも変更しないでください。resume_handle パラメータで NULL を指定すると、レジュームハンドルは格納されません。</param>
  /// <returns>関数が成功すると、NERR_Success が返ります。関数が失敗すると、Win32 API のエラーコードが返ります。エラーコードのリストについては、MSDN ライブラリの「System Error Codes」を参照してください。</returns>
  /// <remarks>特定の共有が、DFS ルート内の DFS リンクであるかどうかを示す値を取得するには、情報レベル 1005 を指定して NetShareGetInfo 関数を呼び出してください。</remarks>
  [DllImport( "Netapi32.dll", CharSet = CharSet.Unicode )]
  static extern int NetShareEnum(
    StringBuilder ServerName,
    DataInformationLevel level,
    ref IntPtr bufPtr,
    uint prefmaxlen,
    out int entriesread,
    out int totalentries,
    ref int resume_handle
  );

  /// <summary>
  /// NetApiBufferAllocate が割り当てたメモリを解放します。Windows NT/2000 上で他のネットワーク管理関数が返したメモリを解放するには、この関数を使ってください。
  /// </summary>
  /// <param name="Buffer">既に他のネットワーク管理関数が返したバッファへのポインタを指定します。</param>
  /// <returns>関数が成功すると、NERR_Success が返ります。</returns>
  [DllImport( "Netapi32.dll", SetLastError = true )]
  static extern int NetApiBufferFree( IntPtr Buffer );

  /// <summary>
  /// The SHARE_INFO_1 structure contains information about the shared resource, including the name and type of the resource, and a comment associated with the resource.
  /// </summary>
  [StructLayout( LayoutKind.Sequential, CharSet = CharSet.Unicode )]
  public struct SHARE_INFO_1 {
    /// <summary>
    /// Pointer to a Unicode string specifying the share name of a resource. Calls to the NetShareSetInfo function ignore this member.
    /// </summary>
    public string shi1_netname;
    /// <summary>
    /// A bitmask of flags that specify the type of the shared resource. Calls to the NetShareSetInfo function ignore this member.
    /// </summary>
    public uint shi1_type;
    /// <summary>
    /// Pointer to a Unicode string specifying an optional comment about the shared resource.
    /// </summary>
    public string shi1_remark;

    public SHARE_INFO_1( string sharename, uint sharetype, string remark ) {
      shi1_netname = sharename;
      shi1_type = sharetype;
      shi1_remark = remark;
    }

    public override string ToString() {
      return shi1_netname;
    }
  }

  /// <summary>
  /// サーバ上の共有ファイル一覧を取得。
  /// </summary>
  /// <param name="Server">対象サーバ。</param>
  /// <returns></returns>
  public static List<SHARE_INFO_1> GetNetShares( string Server ) {
    int entriesread;
    IntPtr bufPtr = GetBufPtr( Server, out entriesread );
    try {
      return GetShareInfos( bufPtr, entriesread );
    } finally {
      NetApiBufferFree( bufPtr );
    }
  }

  private static List<SHARE_INFO_1> GetShareInfos( IntPtr bufPtr, int entriesread ) {
    IntPtr currentPtr = bufPtr;
    List<SHARE_INFO_1> shareInfos = new List<SHARE_INFO_1>();
    int nStructSize = Marshal.SizeOf( typeof( SHARE_INFO_1 ) );

    for (int i = 0; i < entriesread; i++) {
      SHARE_INFO_1 shi1 = (SHARE_INFO_1)Marshal.PtrToStructure( currentPtr, typeof( SHARE_INFO_1 ) );
      shareInfos.Add( shi1 );
      currentPtr = new IntPtr( currentPtr.ToInt32() + nStructSize );
    }
    return shareInfos;
  }

  private static IntPtr GetBufPtr( string Server, out int entriesread ) {
    int totalentries;
    int resume_handle = 0;
    IntPtr bufPtr = IntPtr.Zero;

    StringBuilder server = new StringBuilder( Server );
    int result = NetShareEnum(
      server, DataInformationLevel.Level1, ref bufPtr
      , MAX_PREFERRED_LENGTH, out entriesread, out totalentries, ref resume_handle
    );

    if (result != NERR_Success) {
      throw new Win32Exception( result );
    }
    return bufPtr;
  }
}

使い方

using ( new ImpersonateScope( "user", "domain", "password" ) ) {
  // 偽装中
  // なんか処理
}
//偽装終わり

おまけ。

public object Execute( Delegate func, params object[] args ) {
  try {
    ImpersonateScope scope = GetScope();

    try {
      return func.DynamicInvoke( args );
    } finally {
      if (scope != null) {
        scope.Dispose();
      }
    }
  } catch (Exception ex) {
    MessageBox.Show( ex.ToString() );
    return null;
  }
}