Wednesday, July 16, 2014

Identity Impersonation with Classic ASP

The ideal:

I have written before about using impersonation in IIS.  That was using the tools provided in .Net Framework.  This allows a web application to provide dedicated account access to a database.  Thus the application can prompt a user to login with their Windows credentials and a separate service account can be used to support all data access between that web application and the database.  This makes the application most secure as it restricts database access to a single account.  Auditing also becomes much simpler.

The reality:

Now it is great to think about how easy it is in .Net.  The reality is that there are still classic ASP applications which are simply being ported forward to IIS7/7.5.  I cringe when a customer asks for this.  They can't understand why the software has to be updated.  One issue is impersonation.

I had a customer wanting to port the application forward and just assuming that they could apply all the security "boxes" just like a .Net application.  Sadly this is not the case for classic ASP.  It does not have a native mechanism to support impersonation. 

"As most of you know ASP executes under the guise, typically, of the IUSR_ account in IIS (IIS_IUSRS group for IIS 7) which has a very few permissions to do anything on the system (in most cases anyway). "

So what can you do?  I found some great help from Doug Parsons over at Wrox.com


The fix:

You can write a ActiveX dll which you register on your IIS box.  This dll is then used by your ASP application each time you want to access the database. 

Using Statements
csharp Code:
using System; using System.Runtime.InteropServices;
Class Definition
csharp Code:
namespace Impersonate
{[indent]public class UserLogon
{[indent][DllImport("advapi32.dll", SetLastError=true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

[DllImport("advapi32.dll")]
public static extern int ImpersonateLoggedOnUser(IntPtr hToken);

[DllImport("advapi32.dll")]
public static extern bool RevertToSelf();

[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);

private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
public void Logon(string strAdminUser, string strAdminPassword, string strAdminDomain)
{
IntPtr hToken;

RevertToSelf();

LogonUser(strAdminUser, strAdminDomain, strAdminPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out hToken);

ImpersonateLoggedOnUser(hToken);

CloseHandle(hToken);
}
public void Logoff()
{
RevertToSelf();
}
[/indent]}
[/indent]}

In the Solution Explorer locate the AssemblyInfo.cs file underneath the Properties folder of your project (you may need to click the 'Show all Files' button at the top of the solution explorer) and open it. Add the following line to the file:


csharp Code:
[assembly: ComVisible(true)]
Hit Ctrl + Shift + B (Build) to create your assembly.

Start* up a Visual Studio Command Prompt which can, typically, be found in Start -- > Programs --> Visual Studio 2008 --> Visual Studio Tools and navigate to the directory where your assembly is (something like: projectDirectory\bin\debug\) and issue the following command: regasm Impersonate.dll /tlb

*if you are running Win7/8 run this prompt with Admin rights

In your ASP editor (I used Notepad) use the following code to test your assembly:

asp Code:
[size=2]<% Option Explicit Dim objLogon, oFs, oTextFile Set objLogon = Server.CreateObject("Impersonate.UserLogon") objLogon.Logon "username", "password", "domainname" Response.Write("Impersonating") set oFs = server.createobject("Scripting.FileSystemObject") set oTextFile = oFs.OpenTextFile("C:\foo.txt", 2, True) oTextFile.Write "This is some data wrote as a domain user" oTextFile.Close set oTextFile = nothing set oFS = nothing objLogon.Logoff Set objLogon = Nothing Response.Write("Done Impersonating") %> [/size]

Most of this is very basic but there are some things to point out here:

  • Make sure that the call to CreateObject for objLogon has the name of your assembly. The format for the name will be Namespace.Class. If you have followed along using my code then the correct name is Impersonate.UserLogon.
  • Provide a valid username, password, and domain. I have not tried to impersonate a Local User but, I assume, you could simply provide the PC name as the domain and have this work.
  • Make sure that you call LogOff so that the thread reverts back to running as the IUSR account.
Provided that you execute this ASP script under the guise of IUSR, under normal circumstances, the call to create a text file in the root of the C drive should fail with an Access Denied error so that is a good case to test out our impersonation assembly. As you can see above, a user is impersonated, a file is wrote, and the user is logged off. If all went according to plan you should now see a text file in the root of your C drive! Obviously I am making the assumption that the domain user you are impersonating has the right to create files in the root of the C drive so adjust this test case accordingly.

Pitfalls and Gotchas
  • If you are running IIS 7 on a x64 platform you may get a cryptic "Active X is unable to Create Object". Before you google the problem first start up inetmgr, and locate the Application Pool that your ASP script is running from, right click on it and select 'Advanced Settings' from the context menu. The second option under the General section is 'Enable 32-Bit Applications'; change the value from False to True. If you still get the error, then Google it ;]
  • For testing purposes, I suggest moving your dll out of your debug directory and into another one and then register the dll from there. The reason for this is if you have to make any changes to the dll you will not be able to rebuild your project since the OS will have a lock on the dll in your bin directory.
  • If you need to make changes to your assembly after you have already registered it issue the following command from a Visual Studio command prompt inside the directory your assembly is registered from: regasm /u Impersonate.dll tlb:Impersonate.tlb Make your changes and then re-register the assembly using the regasm from the main tutorial. If, after you have unregistered the dll you are unable to replace the file (Access is denied because the file is in use by another process) start taskmgr and kill the w3wp.exe process (you will need to tick off the 'Show porcesses from all users' checkbox). You should now be able to replace the file.




Source:
C# rework:
http://p2p.wrox.com/classic-asp-basics/77007-solution-impersonation-classic-asp.html

MSDN Original (VB6)
http://support.microsoft.com/kb/248187/en-us

No comments:

Post a Comment