Tuesday, March 9, 2010

Impersonate for Windows Authentication

域环境中Winodws认证代理帐号访问
The code came from an article in CodeProject site, but I can't find the original page, so I can't paste that URL here.
Sometimes you may want to run some code under one specify account in a domain. There is one typical situation, there is a database in your SQL Server, you need user to retrieve data from this database, but you do not want to grant users direct permission to read/write. In this case, you need use another specify account to retrieve data, which means impersonation.
First, you need import some functions from native dll.
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
And you create a enum like below: 
public enum SECURITY_IMPERSONATION_LEVEL : int
{
   
SecurityAnonymous = 0,
   
SecurityIdentification = 1,
   
SecurityImpersonation = 2,
   
SecurityDelegation = 3
}
Create a function that you would be used later in your code:
  static public WindowsImpersonationContext ImpersonateUser(string sUsername, string sDomain, string sPassword)
   
{
       
IntPtr pExistingTokenHandle = new IntPtr(0);
       
IntPtr pDuplicateTokenHandle = new IntPtr(0);
        pExistingTokenHandle
= IntPtr.Zero;
        pDuplicateTokenHandle
= IntPtr.Zero;
       
if (sDomain == "")
            sDomain
= System.Environment.MachineName;
       
try
       
{
           
string sResult = null;
           
const int LOGON32_PROVIDER_DEFAULT = 0;
           
const int LOGON32_LOGON_INTERACTIVE = 2;
           
bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
                LOGON32_LOGON_INTERACTIVE
, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);
           
if (false == bImpersonated)
           
{
               
int nErrorCode = Marshal.GetLastWin32Error();
                sResult
= "LogonUser() failed with error code: " + nErrorCode + "\r\n";
           
}
            sResult
+= "Before impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";
           
bool bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle);
           
if (false == bRetVal)
           
{
               
int nErrorCode = Marshal.GetLastWin32Error();
               
CloseHandle(pExistingTokenHandle); // close existing handle
                sResult
+= "DuplicateToken() failed with error code: " + nErrorCode + "\r\n";
               
return null;
           
}
           
else
           
{
               
WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
               
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
                sResult
+= "After impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";
               
return impersonatedUser;
           
}
       
}
       
catch (Exception ex)
       
{
           
throw ex;
       
}
       
finally
       
{
           
if (pExistingTokenHandle != IntPtr.Zero)
               
CloseHandle(pExistingTokenHandle);
           
if (pDuplicateTokenHandle != IntPtr.Zero)
               
CloseHandle(pDuplicateTokenHandle);
       
}
   
}
Now, you use it to impersonate,
System.Security.Principal.WindowsImpersonationContext newUser;
newUser
= ImpersonateUser("account","domain","password");
//Your code here
newUser
.Undo();

No comments:

Post a Comment