CustomAuth modification to use user identities from an INI file

I recently got the following question about CustomAuth behavior:

Question:

Dear Mr. Wang,

I have read in one of your previous posts that you had posted instructions on how to set up CustomAuth to read usernames and passwords from a text file. Alas, I have not been able to locate this post. Would it be possible for you to point me in the right direction or email the sample if you have it handy?

Best regards,

Ryan

Answer:

Ok, I found that post, made on July 8, 2004 to microsoft.public.inetserver.iis newsgroup. I have cut/paste the relevant portion below.

What you should be aware is that you can also wildcard scriptmap ASP.Net 2.0 on IIS6 and use its excellent membership/roles system to do way better Custom Auth without needing to do this code modification. The caveat is that this inserts managed code into your request pipeline whereas it is not there with CustomAuth.

----->8---------->8---------->8---------->8---------->8---------->8---------->8-----

Now, with IIS6, we have a custom authentication sample ISAPI that should work for you after you write a little bit of C code to customize it for your specific needs.

Download the Platform SDK and get the IIS portion.  The sample code is called CustomAuth, and the Platform SDK includes necessary tools/compiler to build it (if you do not have Visual Studio or comparable development tool).

https://www.microsoft.com/msdownload/platformsdk/sdkupdate/default.htm

It can be configured for any URL namespace, and it implements custom authentication where you enter username/password in an HTML page.  So, you can easily configure CustomAuth to protect only content under the /Files vdir and no-where else.  The CustomAuth.INI file explains how to do this, conceptually.  Basically, the /Files vdir will have only anonymous authentication enabled and this CustomAuth module loaded to implement custom authentication.

What you'd need to do with this sample code is to implement verification of the username/password entered from the HTML page as well as set the user token for IIS to impersonate -- which is EXACTLY all you're trying to do in the ASP page.

You will find in CustomAuth.cpp a single line containing "LogonUser", and this is where you'll inject your code.

  • pstrUser->QueryStr()  contains the username from the form:
  • strPassword.QueryStr()   contains the password from the form
  • The hToken that is created by the LogonUser call is the impersonation
    token that will be used by IIS to execute the subsequent request.

So, all you need to do is:

  1. implement a username/password validation scheme using the values from
    pstrUser and strPassword.  If there's not a lot of users, I suggest using an
    INI file -- one single call to GetPrivateProfileString  should be sufficient
    to lookup a username/password inside a plain-text INI file.
  2. hardcode the LogonUser call to log in UserX
  3. If username/password authenticates, then make the LogonUser call in #2
    let the rest of the code use the hToken for UserX.  If the username/password
    fails to authenticate, then set fResult to FALSE and you're done.

Here's a code snippet for you to get started in CustomAuth.cpp.  I haven't compiled nor tested it, but it should get you most of the way there.

For example, suppose your INI file containing usernames/passwords are in C:\passwords.txt  (make sure that this file is accessible to the anonymous user configured in IIS, since it's the user identity used to execute the
CustomAuth code.

passwords.txt looks like:

[passwords]
user1=password1
user2=password2
...

The code snippet looks something like this (I've added leading/trailing comments so it is clear where to inject this bit of code in CustomAuth.cpp):

//
// Log on the user
//

DWORD dwPasswordSize;
CHAR szStoredPassword[MAX_PATH];

//
// lookup the username key under the "passwords" section
// user passwords are limited to MAX_PATH-1 in length.
// Read MSDN to figure out how to allow larger passwords
//
dwPasswordSize = GetPrivateProfileString(
"passwords",
pstrUser->QueryStr(),
"",
szStoredPassword,
MAX_PATH,
"C:\\passwords.txt" );

if ( dwPasswordSize == 0 )
{
//
// Did not find username in password.txt, so deny access
//
fResult = FALSE;
SetLastError( ERROR_NO_SUCH_USER );
}
else if ( strcmp( strPassword.QueryStr(), szStoredPassword ) == 0 )
{
//
// The password from the form matches password for
// username in password.txt, so allow access
// with impersonation token of UserX
//
fResult = LogonUser( "UserX",
NULL,
"UserX's password",
g_LogonType,
LOGON32_PROVIDER_DEFAULT,
&hToken );
}
else
{
//
// username was found, but password did not match,
// so deny access
//
fResult = FALSE;
SetLastError( ERROR_INVALID_PASSWORD );
}

//
// Regardless of the result, we are done with the password
//

Good luck.

//David