To read the article online, visit http://www.4GuysFromRolla.com/webtech/061202-1.shtml

How to Validate a User Exists in a Windows NT Domain

By Douglas Setzer, II


Introduction
In a recent project I worked on I needed to be able to verify that a Web visitor was indeed a user on our company's Windows NT domain. Everything that I read pointed me to use the Challenge/Response Authentication within IIS. If you've NT Challenge/Response for authentication you know that when an unauthenticated user visits a page that requires authorization the user is prompted with a login box asking for their username/password. (If you've not worked with NT Challenge/Response, be sure to read Using NT Challenge/Response and Authentication Methods in IIS.) The problem with the NT Challenge/Response login box is that I work with very particular graphic designers who have their own specific ideas on the way the login window should look. So I needed to be able to create a means to have a custom login Web page that collected a user's username and password and then determined if the entered credentials matched the credentials for a user on the Windows NT domain.

I found that ADSI (Active Directory Services Interface) seemed like it should be my answer. First a quick background on ADSI. ADSI services can be used to enumerate and manage resources in a directory service. Microsoft products that currently support the ADSI programming interface are Windows NT 4.0 Server, Windows 2000, Exchange, IIS, and Site Server. More ADSI information can be found at: ADSI Article Archive and the 15Second.com ADSI Focus Section.

Using ADSI to Retrieve Information
When using ADSI and the Windows NT provider, you are able to retrieve information about the Domain, Computers, Groups and Users. To get information about a particular user, you could use the following code, which retrieves information about the user dsetzer on the NT Domain MyDomain:

'Specify the domain and username
strNTDomain = "MyDomain"
strUsername = "dsetzer"

'Get information about the user
Dim objIADSUser
Set objIADSUser = GetObject("WinNT://" & strNTDomain & "/" & strUsername & ",user")

The above code retrieves information about a specific user. Normally, you wouldn't hard-code the domain and username values, but, for this example I do. The last line is the important one that does all the work. It calls ADSI and creates an ADSI user object. Note that the variables get translated into the request: WinNT://MyDomain/dsetzer,user, and returns a User object.

Changing User Information through ADSI
The user object give you access to the user's information such as their full name, the groups they are in, if their account is disabled and if their account is locked out. In addition to finding out information about the user, it allows you to make changes to their account. To change their password, you execute the following code:

'specify the old and new passwords
strOldPassword = "THEPWD"
strNewPassword = "ILOVEMOM"

'change the user's password
objIADSUser.ChangePassword strOldPassword,strNewPassword
objIADSUser.SetInfo

This code defines what the user's current password is and what the password should be changed to. It then calls a method on the ADSI user object to change the password for the user. Anytime you make changes, you need to call the SetInfo method to do the actual update in the provider. This is because ADSI locally caches the information about the user to speed up response times and minimize network traffic.

In order for the change password code above to work, administrator rights are required. Because ADSI was written with network administrators in mind, it gives you a way to run scripts on behalf of another user. For instance, say a company wanted users to be able to change their password from the company's intranet. Anonymous requests to the intranet are likely going to be configured to be run under the IUSR_machinename account, which is generally a normal user with very limited permissions. To allow for ADSI actions to occur for an intranet request, one can essentially use ADSI to create a "container" object that encapsulates the Administrator account. Using this container you can then change a user's password through an ASP Web page. Some code may help clarify things here:

'Specify the domain we're working with
strNTDomain = "MyDomain"

'Specify the username/password for the administrator account
strAdminUsername = "Administrator"
strAdminPassword = "password"

'Specify the name of the user whose password you wish to change
strClientUsername = "dsetzer"

'Specify the desired old and new password for the user
strOldPassword = "THEPWD"
strNewPassword = "ILOVEMOM"

'Create a container for the Administrator account
Const ADS_SECURE_AUTHENTICATION = 1
Set objIADS = GetObject("WinNT:").OpenDSObject("WinNT://" & _
                        strNTDomain, strAdminUserame, _
                        strAdminPassword, _
                        ADS_SECURE_AUTHENTICATION)

'Change the user's password through the Administrator container
Set objIADSUser = objIADS.GetObject("user", strClientUsername)
    objIADSUser.ChangePassword strOldPassword,strNewPassword
    objIADSUser.SetInfo

'Clean up
Set objIADSUser = Nothing
Set objIADS = Nothing

While this code is a little longer that the previous example, it does, essentially, the same thing: it changes a specified user's password from an old password to a new one. Recall that the script to change the user's password must run under the Administrator account. Since anonymous Web requests are treated as requests from the IUSR_machinename account, to perform an Administrator-level ADSI task through an ASP page you will need to create a container object that impersonates the Administrator account. Once this container is created, you can use the container to perform the password changing task.

Take a quick minute to examine the code. Note that there is a constant called ADS_SECURE_AUTHENTICATION, which is used when authenticating the Administrator account. Instead of creating the user object directly, as was done in the previous example, I first create a container object on behalf of the Administrator. When I create this object, ADSI securely verifies that the Administrator user exists in the domain, that the password matches, and that the account isn't disabled or locked out. Using that container, I create a User object with which I'm then able to change the client user's password using the same code as before.

Authenticating a User with ADSI
Changing a password is all fine and dandy, but, you are probably saying that you don't want to change the password, you want to check if a given username/password that has been entered into a login form matches the user's account on the Windows domain. As I mentioned earlier, with ADSI you can get various pieces of information about a user once you create an ADSI user object. One piece of information you can't get the user's password from this object -- even as an administrator. This is because when you enter or change your password in Windows, it doesn't store what you type in. Instead, it encrypts the password using a one-way hash encryption and stores the encrypted value. It can't tell you what the password is because it can't decrypt it. But, you aren't really interested in what the user's password is. Instead, you want to know that it matches whatever the user enters as his or her password.

To do this, you need to be able to create an object that verifies that the account exists, the password matches and that it isn't disabled or locked out. Sound familiar? The ability to run scripts on behalf of another user isn't limited to just Administrators. If you are able to create the container object, without an error, you know that the account exists, the password matches and that it isn't disabled or locked out. If you receive an error, you can check the Err.Description property to find out why it failed.

In my particular implementation of this, I created a COM+ object in VB6 that had a UserLogin method that I passed a username, password and domain name. If it returned True, I knew the account was good, if it returned False, I knew the account didn't exist. I set the COM+ object to be ran under an Administrator account. (You can download the complete code for the COM+ component.)

This code has been tested on Windows 2000. I used Component Services to register the COM+ object and within that, I specified that the object run under an Administrator account. From the testing that I have done, this is the only way that I've gotten this to work. I haven't tested it, but I think the code would work directly from ASP if the virtual directory was configured to run under an administrator account.

A final note, you should definitely be using SSL to secure the transmission of the username and password from the browser to the server. Windows NT passwords are one thing that you don't want to let loose!

Happy Programming!

  • By Douglas Setzer, II
    27 Seconds, Inc., President


    Attachments

  • Download the complete code for the COM+ Component (in ZIP format)


  • Article Information
    Article Title: How to Validate a User Exists in a Windows NT Domain
    Article Author: Douglas Setzer, II
    Published Date: Wednesday, June 12, 2002
    Article URL: http://www.4GuysFromRolla.com/webtech/061202-1.shtml


    Copyright 2017 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers