Accessing Active Directory via Active Server Pages (Part 1)
by Marcin Policht
Access to information stored in Active Directory is available using standard Active Directory management utilities. They are convenient and fairly easy to use; however, there are situations in which you might want to develop alternative, custom solutions. For example, you might want to quickly access user or group account information from any workstation in your environment. Of course, you can use Terminal Services to accomplish this, but this has certain limitations that you need to consider:
These limitations can be eliminated by providing access to Active Directory through Web-based ASP scripts. Active Server Pages (ASP) is the Internet Information Server technology that allows dynamic generation of HTML pages. Scripts, typically written in VBScript or JScript, are executed when accessed by a client running a browser. The results of this execution are sent back to the Web client in HTML format and displayed in the browser window. Scripts running on a Web server are in some aspects unlike those invoked locally, the differences of which need to be taken into account:
Obviously, there are numerous other issues that need to be considered in both cases, but we will cover these three, since they tend to be most relevant in the situation presented in this article. In particular, we will look into execution of scripts that are using ADSI to access Active Directory information. First, I'll present a script that runs outside of ASP (locally) and then I'll describe a way to convert to a Web-based form. The script below, ListADGroups.vbs, uses Active Data Objects (and an underlying OLE DB Directory Services provider called ADsDSOObject) to extract a list of groups from Active Directory. This involves creating the following ADO objects:
For each of these objects, we set appropriate properties. More specifically, for the Connection object:
For the Command Object:
The outcome of running the Execute method of the ADO Command object is stored in the RecordSet. In order to retrieve the results, the script traverses it record by record and displays contents of each one. After all the results are listed, Recordset and Connection objects are closed and set to Nothing, which releases them from memory. In order to adapt this script to a Web Server environment, the following changes (at the very least) need to be made:
The ASP page below contains all these changes. The formatting is very crude for simiplicity reasons. The script generates a table containing four columns: group name, ADsPath, description, and members list (members list is formatted as a listbox). The page generation might take a while in larger domains, due to the fact that, by default, the value of the sGroup variable (search filter) is set to a wild card ("*"), which returns all the domain groups. If you want to narrow down your search and speed up the return of the results, replace the "*" with a character string indicating a match (e.g. to return all the groups starting with the word domain, use "Domain*"). As you can see, the content of the script has not changed much -- the main differences result from the fact that the output needs to be formatted according to HTML specifications. However, there is another significant issue that, although present in the original script, becomes more relevant in a Web environment. As you must have noticed, username and password are stored within a script. Even though such pages would be most likely secured through additional level of restrictions, such behavior should be avoided. In the next article of this series, I'll present various ways of eliminating this vulnerability.
The first article in Marcin Policht's new "Accessing Active Directory via Active Server Pages" series takes an in-depth look at accessing information stored in Active Directory via Web-based Active Server Pages scripts.
'//////////////////////////////////////////////////////////////////////////
'/// Name: ListADGroups.vbs
'/// Version: 1.0
'/// Date: 09/17/02
'/// Purpose: listing information about Active Directory groups
'/// OS: Windows 2000, XP
'/// Reqs: Account with permissions to read Active Directory groups properties
'/// Syntax: cscript /nologo ListADGroups.vbs
'//////////////////////////////////////////////////////////////////////////
Option Explicit
On Error Resume Next
'////////////////////////////////////////////////////
'/// Variable Declarations
Dim oRootDSE, oCon, oCmd, oRecordSet
Dim sDomainADsPath, sUser, sPassword, sGroup, sProperties
Dim aDescription, aMember, iCount
'////////////////////////////////////////////////////
'/// Extract domain name of the logged on user account
Set oRootDSE = GetObject("LDAP://RootDSE")
sDomainADsPath = "LDAP://" & oRootDSE.Get("defaultNamingContext")
Set oRootDSE = Nothing
'////////////////////////////////////////////////////
'/// Create, configure, and open ADO Connection object
Set oCon = CreateObject("ADODB.Connection")
oCon.Provider = "ADsDSOObject"
sUser = "UserName"
sPassword = "Password"
oCon.Open "ADProvider", sUser, sPassword
'////////////////////////////////////////////////////
'/// Create and configure ADO Command object
Set oCmd = CreateObject("ADODB.Command")
Set oCmd.ActiveConnection = oCon
sProperties = "name,ADsPath,description,member"
sGroup = "*"
oCmd.CommandText = "<" & sDomainADsPath & ">;(&(objectCategory=group)(name=" & sGroup & "));" & sProperties & ";subtree"
oCmd.Properties("Page Size") = 100
'////////////////////////////////////////////////////
'/// Populate ADO RecordSet object with AD Group info
Set oRecordSet = oCmd.Execute
'////////////////////////////////////////////////////
'/// Display results by listing all records in Recordset
WScript.Echo "Global Groups for the domain " & Replace(Mid(sDomainADsPath,11), ",DC=", ".")
While Not oRecordSet.EOF
WScript.Echo "Name: " & vbTab & oRecordSet.Fields("name")
WScript.Echo "ADsPath: " & vbTab & oRecordSet.Fields("ADsPath")
aDescription = oRecordSet.Fields("description")
If Not IsNull(aDescription) Then
WScript.Echo "Description: " & vbTab & aDescription(0)
End If
aMember = oRecordSet.Fields("member")
WScript.Echo "Members: "
If Not IsNull(aMember) Then
For icount = 0 to UBound(aMember)
WScript.Echo vbTab & vbTab & aMember(iCount)
Next
End If
oRecordSet.MoveNext
Wend
'////////////////////////////////////////////////////
'/// Close Recordset and Connection objects
oRecordSet.Close
oCon.Close
'////////////////////////////////////////////////////
'/// Clean up
Set oRecordSet = Nothing
Set oCon = Nothing
<%@ Language="VBScript" %>
<%
Option Explicit
%>
<HTML>
<HEAD>
<TITLE>Listing of Domain Groups</TITLE>
</HEAD>
<%
Dim oRootDSE, oCon, oCmd, oRecordSet
Dim sDomainADsPath, sUser, sPassword, sGroup, sProperties
Dim aDescription, aMember, iCount
Set oRootDSE = GetObject("LDAP://RootDSE")
sDomainADsPath = "LDAP://" & oRootDSE.Get("defaultNamingContext")
Set oRootDSE = Nothing
Set oCon = Server.CreateObject("ADODB.Connection")
sUser = "UserName"
sPassword = "Password"
oCon.Provider = "ADsDSOObject"
oCon.Open "ADProvider", sUser, sPassword
Set oCmd = Server.CreateObject("ADODB.Command")
Set oCmd.ActiveConnection = oCon
sProperties = "name,ADsPath,description,member"
sGroup = "*"
oCmd.CommandText = "<" & sDomainADsPath & ">;(&(objectCategory=group)(name=" & sGroup & "));" & sProperties & ";subtree"
oCmd.Properties("Page Size") = 100
Set oRecordSet = oCmd.Execute
Response.Write("<strong> Global Groups for the domain: " & Replace(Mid(sDomainADsPath,11), ",DC=", ".") & "</strong>")
Response.Write("<table border='1'>")
Response.Write("<tr><th>Name</th><th>ADsPath</th><th>Description</th><th>Members</th></tr>")
Response.Write("<font size=-2>")
While Not oRecordSet.EOF
Response.Write("<tr><td>" & oRecordSet.Fields("name") & "</td>")
Response.Write("<td>" & oRecordSet.Fields("ADsPath") & "</td>")
aDescription = oRecordSet.Fields("description")
Response.Write("<td> ")
If Not IsNull(aDescription) Then Response.Write aDescription(0)
Response.Write("</td>")
aMember = oRecordSet.Fields("member")
Response.Write("<td><select size = '5'> ")
If Not IsNull(aMember) Then
For icount = 0 to UBound(aMember)
Response.Write("<option>" & aMember(iCount))
Next
End If
Response.Write("</td></tr>")
oRecordSet.MoveNext
Wend
Response.Write("</font>")
Response.Write("</table>")
oRecordSet.Close
oCon.Close
Set oRecordSet = Nothing
Set oCon = Nothing
%>
</BODY>
</HTML>
