2012-03-28 12 views
9

Sto utilizzando il codice per impersonare un account utente per ottenere l'accesso a una condivisione file.Impersonare un utente Windows

public class Impersonator : 
    IDisposable 
{ 
    #region Public methods. 
    // ------------------------------------------------------------------ 

    /// <summary> 
    /// Constructor. Starts the impersonation with the given credentials. 
    /// Please note that the account that instantiates the Impersonator class 
    /// needs to have the 'Act as part of operating system' privilege set. 
    /// </summary> 
    /// <param name="userName">The name of the user to act as.</param> 
    /// <param name="domainName">The domain name of the user to act as.</param> 
    /// <param name="password">The password of the user to act as.</param> 
    public Impersonator(
     string userName, 
     string domainName, 
     string password) 
    { 
     ImpersonateValidUser(userName, domainName, password); 
    } 

    // ------------------------------------------------------------------ 
    #endregion 

    #region IDisposable member. 
    // ------------------------------------------------------------------ 

    public void Dispose() 
    { 
     UndoImpersonation(); 
    } 

    // ------------------------------------------------------------------ 
    #endregion 

    #region P/Invoke. 
    // ------------------------------------------------------------------ 

    [DllImport("advapi32.dll", SetLastError=true)] 
    private 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)] 
    private static extern int DuplicateToken(
     IntPtr hToken, 
     int impersonationLevel, 
     ref IntPtr hNewToken); 

    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] 
    private static extern bool RevertToSelf(); 

    [DllImport("kernel32.dll", CharSet=CharSet.Auto)] 
    private static extern bool CloseHandle(
     IntPtr handle); 

    private const int LOGON32_LOGON_INTERACTIVE = 2; 
    private const int LOGON32_PROVIDER_DEFAULT = 0; 

    // ------------------------------------------------------------------ 
    #endregion 

    #region Private member. 
    // ------------------------------------------------------------------ 

    /// <summary> 
    /// Does the actual impersonation. 
    /// </summary> 
    /// <param name="userName">The name of the user to act as.</param> 
    /// <param name="domainName">The domain name of the user to act as.</param> 
    /// <param name="password">The password of the user to act as.</param> 
    private void ImpersonateValidUser(
     string userName, 
     string domain, 
     string password) 
    { 
     WindowsIdentity tempWindowsIdentity = null; 
     IntPtr token = IntPtr.Zero; 
     IntPtr tokenDuplicate = IntPtr.Zero; 

     try 
     { 
      if (RevertToSelf()) 
      { 
       if (LogonUser(
        userName, 
        domain, 
        password, 
        LOGON32_LOGON_INTERACTIVE, 
        LOGON32_PROVIDER_DEFAULT, 
        ref token) != 0) 
       { 
        if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) 
        { 
         tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); 
         impersonationContext = tempWindowsIdentity.Impersonate(); 
        } 
        else 
        { 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        } 
       } 
       else 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 
      } 
      else 
      { 
       throw new Win32Exception(Marshal.GetLastWin32Error()); 
      } 
     } 
     finally 
     { 
      if (token!= IntPtr.Zero) 
      { 
       CloseHandle(token); 
      } 
      if (tokenDuplicate!=IntPtr.Zero) 
      { 
       CloseHandle(tokenDuplicate); 
      } 
     } 
    } 

    /// <summary> 
    /// Reverts the impersonation. 
    /// </summary> 
    private void UndoImpersonation() 
    { 
     if (impersonationContext!=null) 
     { 
      impersonationContext.Undo(); 
     } 
    } 

    private WindowsImpersonationContext impersonationContext = null; 

    // ------------------------------------------------------------------ 
    #endregion 
} 

Quindi, utilizzando:

using (new Impersonator("username", "domain", "password")) 
     { 
      Process.Start("explorer.exe", @"/root,\\server01-Prod\abc"); 
     } 

ottengo un "Accesso negato" errore.

Questo utente ha supposamente accesso a questa condivisione. Posso mappare un'unità, usare "net use" ma questo codice non funzionerà. Ora penso che sia il codice. Qualcuno vede qualcosa? C'è un modo migliore per farlo?

+0

Da dove viene questo? Se si tratta di un'app ospitata su IIS, l'utente predefinito di IIS potrebbe non avere i diritti per impersonare – oleksii

+0

Sì. app Web ospitata in IIS –

+0

Ricordo che avevo un problema simile, provate a eseguire questo codice in una semplice applicazione di console usando l'utente amministratore. Non sono sicuro che lo si possa fare da un'applicazione Web in esecuzione su IIS. Questo è qualcosa a che fare con le autorizzazioni dell'utente ASP.NET (per quanto mi ricordo) – oleksii

risposta

5

provare questo:

[DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool LogonUser(
      string lpszUsername, 
      string lpszDomain, 
      string lpszPassword, 
      int dwLogonType, 
      int dwLogonProvider, 
      out IntPtr phToken); 

Usage:

IntPtr userToken = IntPtr.Zero; 

bool success = External.LogonUser(
    "john.doe", 
    "domain.com", 
    "MyPassword", 
    (int) AdvApi32Utility.LogonType.LOGON32_LOGON_INTERACTIVE, //2 
    (int) AdvApi32Utility.LogonProvider.LOGON32_PROVIDER_DEFAULT, //0 
    out userToken); 

if (!success) 
{ 
    throw new SecurityException("Logon user failed"); 
} 

using (WindowsIdentity.Impersonate(userToken)) 
{ 
Process.Start("explorer.exe", @"/root,\\server01-Prod\abc"); 
} 
+0

Sto usando lo stesso pezzo di codice e vedo questo comportamento che non riesco a spiegare: a volte LogonUser restituisce il codice di uscita 0 e Win32Exceptionis: "System.ComponentModel.Win32Exception: il nome della directory non è valido". Ma poi rieseguire la stessa funzione 10 minuti dopo funziona. Sai perché?Potrei fornirti più contesto se necessario. –

1

Invece di utilizzare la classe Impersonator, cosa succede quando si chiama Process.Start e passa in un'istanza ProcessStartInfo che contiene il nome utente, la password e il dominio che si desidera eseguire il processo come?

Forse, se funziona, la classe Impersonator dovrebbe creare un'istanza ProcessStartInfo e utilizzarla per creare nuovi processi (racchiuderlo all'interno della classe stessa).

var psi = new ProcessStartInfo("explorer.exe", @"/root,\\server01-Prod\abc"); 
psi.Domain = domain; 
psi.UserName = username; 
psi.Password = password; 
psi.WorkingDirectory = workingDir; 

Process.Start(psi); 

Inoltre, per il MSDN docs ...

Impostazione del dominio di, nome utente e password le proprietà di un oggetto ProcessStartInfo è la pratica consigliata per avviare un processo con le credenziali dell'utente.

È inoltre necessario impostare la directory di lavoro quando si avvia un processo con credenziali utente diverse.

+0

Non ho ricevuto un errore ma mi aspettavo che le finestre di Explorer si aprissero. psi.Domain = "dominio"; psi.UserName = "usera"; psi.Password = pwd; psi.FileName = "explorer.exe"; psi.Arguments = @ "/ root, \\ server01-pv \ abc"; psi.ErrorDialog = true; psi.UseShellExecute = false; Process.Start (psi); –

+0

Cosa succede quando si imposta 'UseShellExecute = true'? –

+0

Inoltre, se si imposta 'ErrorDialog = true', ho pensato di dover impostare' UseShellExecute = true'. E imposta anche 'WorkingDirectory' sull'istanza' ProcessStartInfo'. –

2

Se sto capendo correttamente, l'intenzione è quella di eseguire il processo nel contesto di rappresentazione.

Il documento di CreateProcess (utilizzato da Process.Start) dice: Se il processo chiamante sta impersonando un altro utente, il nuovo processo utilizza il token per il processo chiamante, non il token di rappresentazione. Per eseguire il nuovo processo nel contesto di sicurezza dell'utente rappresentato dal token di rappresentazione, utilizzare la funzione CreateProcessAsUser o CreateProcessWithLogonW.

Quindi, stai usando l'API sbagliata per farlo.

Problemi correlati