_DotNetStuff and more...

Wednesday, March 02, 2005

Impersonation for data access with LogonUser

Using the example code from URL below and a little modification.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemSecurityPrincipalWindowsIdentityClassImpersonateTopic.asp

I made myself a class that would call LogonUser.
See below:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;

[assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum, UnmanagedCode=true)]
[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name = "FullTrust")]
namespace Impersonation
{

public class Impersonation
{
[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

[DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource,
int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr *Arguments);

[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);


// GetErrorMessage formats and returns an error message
// corresponding to the input errorCode.
public unsafe static string GetErrorMessage(int errorCode)
{
int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

int messageSize = 255;
String lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER FORMAT_MESSAGE_FROM_SYSTEM FORMAT_MESSAGE_IGNORE_INSERTS;

IntPtr ptrlpSource = IntPtr.Zero;
IntPtr prtArguments = IntPtr.Zero;

int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &prtArguments);
if (0 == retVal)
{
throw new Exception("Failed to format message for error code " + errorCode + ". ");
}

return lpMsgBuf;
}

// Test harness.
// If you incorporate this code into a DLL, be sure to demand FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void impersonateThis(string username, string password, string domainname)
{

IntPtr tokenHandle = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
try
{
string userName, domainName;

domainName = domainname;

userName = username;

const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
const int SecurityImpersonation = 2;

tokenHandle = IntPtr.Zero;
dupeTokenHandle = IntPtr.Zero;

bool returnValue = LogonUser(userName, domainName, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);


if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine("LogonUser failed with error code : {0}", ret);
Console.WriteLine("\nError: [{0}] {1}\n", ret, GetErrorMessage(ret));
int errorCode = 0x5; //ERROR_ACCESS_DENIED
throw new System.ComponentModel.Win32Exception(errorCode);
}

bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle);
if (false == retVal)
{
CloseHandle(tokenHandle);
Console.WriteLine("Exception thrown in trying to duplicate token.");
return;
}

// The token that is passed to the following constructor must
// be a primary token in order to use it for impersonation.
WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();

// Free the tokens.
if (tokenHandle != IntPtr.Zero)
CloseHandle(tokenHandle);
if (dupeTokenHandle != IntPtr.Zero)
CloseHandle(dupeTokenHandle);
}
catch(Exception ex)
{
}
finally
{
}
}
}
}
Once I had my class created I called the impersonateThis funtion when opening my connection inside my data access class.
See below:

public void openConnection(String connectionString)
{

try
{
Impersonation mi = new Impersonation();
mi.impersonateThis("user","password","domainname");

sqlConn = new SqlConnection(connectionString);
sqlConn.Open();

}
catch(Exception ex)
{
}
finally
{
}
}
The result of this is that when calling these classes from ASP.NET the code runs under aspnet account but when the openConnection function is called the impersonation kicks in and you are running under the impersonated account; which in my case happens to be a user setup in my SQL Server database and magic happens. When the code block is done and returns to the .aspx code behind page the impersonation is released and you are now running under the aspnet account again. To test this you can call WindowsIdentity.GetCurrent().Name to see what account you are running under when.

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]



<< Home