Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

LDAP query in an ASP page 4

Status
Not open for further replies.

spaulding

Technical User
Jan 10, 2001
123
0
0
US
I'm trying to write an ASP page that will return a list of Active Directory User accounts that are disabled. I've written several ASP pages and am reasonably comfortable with that, but I've yet to use LDAP in the query. Below is the script I've started to put together. Part of it is from Microsoft TechNet and part is from a thread on this forum. Unfortunately, it doesn't work and returns the following error:
Provider error '80040e14'

One or more errors occurred during processing of command.

The message refers to line 13 which is the command.execute line. I figure this means my command.text line is out of whack, but I don't know enough about the syntax to figure it out.

I'd appreciate any help I can get.



<%@language=vbscript%>
<%
Const ADS_UF_ACCOUNTDISABLE = 2

Set objConnection = CreateObject(&quot;ADODB.Connection&quot;)
objConnection.Provider=&quot;ADsDSOObject&quot;

objConnection.Open &quot;Active Directory Provider&quot;
Set objCommand = CreateObject(&quot;ADODB.Command&quot;)
objCommand.ActiveConnection = objConnection
objCommand.CommandText = &quot;select distinguishedName, userAccountControl from 'LDAP://DC=FISD, DC=org' where objectCategory=User&quot;
Set objRecordset = Server.CreateObject(&quot;ADODB.Recordset&quot;)
Set objRecordSet = objCommand.Execute

intCounter = 0
While Not objRecordset.EOF
intUAC=objRecordset.Fields(&quot;userAccountControl&quot;)
If intUAC And ADS_UF_ACCOUNTDISABLE Then
response.write objRecordset.Fields(&quot;distinguishedName&quot;) & &quot; is disabled.&quot;
intCounter = intCounter + 1
End If
objRecordset.MoveNext
Wend

response.write &quot;A total of &quot; & intCounter & &quot; accounts are disabled.&quot;

objConnection.Close

%>
 
Done a bunch more research and have modified the portion of the script setting up the connection as follows:

Set objConnection = CreateObject(&quot;ADODB.Connection&quot;)
objConnection.Provider=&quot;ADsDSOObject&quot;

objConnection.Open &quot;ADs Provider&quot;


CommandText = &quot;<LDAP://dc=adm-dc01,dc=PISD,dc=org>;(&(objectClass=user));cn,userAccountControl&quot;

Set objRecordset = objConnection.Execute(CommandText)

I've got a recordset output loop after that just to list the fields. But when I run the page, I get the following error:

Provider error '80040e37'

Table does not exist.

The error occurs on the Set recordset line.
Active Directory runs on our server named adm-dc01.PISD.org (capitalization is correct). I'm running Integrated Authentication on our intranet website (I've tried Anonymous also, no change) and am logged in as an administrator, so I don't think it's permissions. I'm stumped and I'd really appreciate some help.
 
<%@ Language=VBScript %>
<%
Option Explicit
response.buffer = true
Dim con,rs,Com,objADsPath,objDomain,objADOU,intUAC
Const ADS_UF_ACCOUNTDISABLE = 2
%>
<html>
<head>
</head>
<body topmargin=&quot;0&quot; leftmargin=&quot;0&quot; bgcolor=&quot;#CCCCCC&quot;>
<%
Set objDomain = GetObject (&quot;GC://rootDSE&quot;)
objADsPath = objDomain.Get(&quot;defaultNamingContext&quot;)
Set objDomain = Nothing
Set con = Server.CreateObject(&quot;ADODB.Connection&quot;)
con.provider =&quot;ADsDSOObject&quot;
con.open &quot;Active Directory Provider&quot;
Set Com = CreateObject(&quot;ADODB.Command&quot;)
Set Com.ActiveConnection = con
Com.CommandText =&quot;select userAccountControl,name from 'GC://&quot;+objADsPath+&quot;' WHERE objectCategory='person'&quot;
Set rs = Com.Execute
While not rs.eof
intUAC=rs.fields(&quot;userAccountControl&quot;)
If intUAC AND ADS_UF_ACCOUNTDISABLE Then
response.write rs.fields(&quot;name&quot;)&&quot; is disabled.<br>&quot;
End If
rs.movenext
Wend
rs.close
set rs=nothing
con.close
set con=nothing
%>
</body>
</html>
 
zcolton, Thanks for the help. Unfortunately, I'm getting an error at the same point in the script. In this particular instance, I get this message

Provider error '80004005'

Unspecified error

/test/test1test.asp, line 23
Line 23 is the &quot;Set rs = Com.Execute&quot;

As I read your script, it looks like I should be able to run that without modification on my network, is that correct?
If so, and since I can get a list of disabled accounts if I use a WinNT binding (albeit slow), I'm wondering if there is something preventing this in the setup of our network.

We're running W2K Server, I'm logged in to the domain controller, my intranet server (where the asp page is located) and my client machine with the same network administrator account. We migrated from WinNT about 18 months ago, is there some way we might have disabled LDAP (on not enabled it)?
 
You won't need to change the script. Is the web server a domain controller, or atleast a domain member?
You may need to update MDAC on the web server.
You will also need to confirm that at least one of the domain controllers houses the global catalog. If your not sure change GC:// to LDAP://
Besure that anonymous access is either disabled for this asp page, or the account used for anonymous access is a domain account that has the correct rights to search active directory.
 
The web server is a domain member and is running MDAC version 2.8. I've tried changing the GC:// to LDAP:// and the anonymous access is disabled for the page. Still no change.

The error message is still:

Provider error '80004005'

Unspecified error

/test/test1test.asp, line 23

The entire script is included below (just in case)

Line 23 is still the Set RS=com.execute
<%@ Language=VBScript %>
<%
Option Explicit
response.buffer = True
Dim con,rs,Com,objADsPath,objDomain,objADOU,intUAC
Const ADS_UF_ACCOUNTDISABLE = 2
%>
<html>
<head>
</head>
<body topmargin=&quot;0&quot; leftmargin=&quot;0&quot; bgcolor=&quot;#CCCCCC&quot;>
<%
Set objDomain = GetObject (&quot;LDAP://rootDSE&quot;)
response.write &quot;Test 2&quot;
objADsPath = objDomain.Get(&quot;defaultNamingContext&quot;)
Set objDomain = Nothing
Set con = Server.CreateObject(&quot;ADODB.Connection&quot;)
con.provider =&quot;ADsDSOObject&quot;
con.open &quot;Active Directory Provider&quot;
Set Com = CreateObject(&quot;ADODB.Command&quot;)
Set Com.ActiveConnection = con
Com.CommandText =&quot;select userAccountControl,name from 'LDAP://&quot;+objADsPath+&quot;' WHERE objectCategory='person'&quot;
Set rs = Com.Execute
While Not rs.eof
intUAC=rs.fields(&quot;userAccountControl&quot;)
If intUAC And ADS_UF_ACCOUNTDISABLE Then
response.write rs.fields(&quot;name&quot;)&&quot; is disabled.<br>&quot;
End If
rs.movenext
Wend
rs.close
Set rs=nothing
con.close
Set con=nothing
%>
</body>
</html>

Again, I appreciate your help.
 
spaulding,

Did you ever get an answer to this problem? I am having the exact same issue.

Thanks,
 
The problem is security.
Change the security access of the asp page (or the directory it is in) to basic or set it to anonymous using a domain account that can access active directory. First try basic and login using a domain account. If your IIS server is not a domain controller Integrated Authentication will not work. I can explain why but try the basic setting for now. The asp page I have posted on Feb 9 will work on any system.
 
zcolton,
That worked! I'm still trying to get a handle on the security/authentication issues with respect to ASP and Active Directory though. Like most projects, this one has evolved. We are now trying to give a regular user the capability to unlock these accounts. As my script is now written with your suggestions, I have to authenticate with an administrator account/password to unlock the account. If I use a user account, I get the general access denied error.
I'm trying to figure out how to address the problem and I think these are my options: 1) Figure out a way to give a regular user elevated permissions in Active Directory - a scary thought or 2) Figure out how to pass the administrator permissions through the ASP page. Can (and should) I pass the account and password when I do my binding?
I'd appreciate your advice on how to proceed from here.
 
To give you a basic (very basic) description of security with respects to IIS and Active Directory - action of authentication is as follows:
IIS has it authentication methods per site/folder/file configured in IIS. Security tokens generated from Intergrated Authentication are god for the local machine resources of the server running IIS. Active Directory is a remote resource if ther server running IIS is only a domain member and not a controller. The remote resource will request credentials. If a specific domain username and password are not passed, IIS uses a machine anonymous account. SO then authentication will fail. Microsoft has a knowledgebase article that will explain this double-hop scenario much better with more detail.
Give me a detailed description of your scenario and what you want to accomplish, and I can whip out an asp page and I will let you know what security issues that you may need to address.
 
zcolton,
I really appreciate the offer. I'm the net admin for a small school district. The students are forever forgetting their passwords or deliberately mistyping other's passwords and locking their accounts. What I'd like to do is have a web page with access controlled by NTFS permissions on the IIS server (not a domain controller) where an authorized user can go, see a list of locked accounts, select an account and unlock it. In my research so far, I've found a script and adapted it to do exactly that. With your suggestion earlier today, it works when I enter an administrator id and password on script execution. But giving an administrator account to the user is something I'm not willing to do (I'm funny that way). In the above post, you say
" If a specific domain username and password are not passed, IIS uses a machine anonymous account. SO then authentication will fail."
That's the crux of my problem, how do I pass the specific domain username and password? I'll look for the MS KB article.
 
spaulding,
I, too am the net admin for a small school district (but a big network.)1 high school, 1 middle, 3 elementary separated by grade levels. 1600 workstations 210 networked printers, 525 staff (all network users) 2300 student accounts for grades 5 and up.
Question: who do you want to grant access to have the ability to enable accounts: all staff? teachers? 1 person?
 
zcolton,
Our numbers are very similar. I'm probably looking at a person in the office at each campus and the computer lab teachers (<10 total). I'm using this script as a learning exercise, because we have AD setup to release the lock out after a few minutes. If I can make this work, it'll be a nice to have item for the labs, but...The next phase of this project will be to build a page that allows an office worker to enable (not create) a student account when all the paperwork requirements are complete. This will save me a lot of administrivial work and get the student's account turned on a lot faster. Hence, my interest.
 
Quick question: If the accounts are getting locked from user intentionaly inputting invalid passwords, and all you want to do is unlock the accounts, are you using the lockout policy as a security measure? If not, you can disable the lockout security policy.
Quick note: an account being locked-out is not the same as an account being disabled.
 
zcolton,
I'm feeling good, in that I knew both of those items. We want the lockout policy in effect to try to delay people figuring out other account passwords. It would just be nice to have the capability to unlock the account if circumstances warrant. Also, I figure if I can get the permissions right to do this, I can get them set for the enabling accounts which is a bigger pain for me. I did realize the difference and I've found some scripts which should accomplish the enabling account function (if I can get permissions right)
 
Give me a little time and I'll find the security answer. Could you post the asp pages that you have. I would like to take a look.
 
Here's the code for the unlock script:


<!-- #INCLUDE FILE="./COMMON/HEADER.INC" -->
<!-- #INCLUDE FILE="./COMMON/FUNCTIONS.INC" -->
<!-- #INCLUDE FILE="./COMMON/UserFlags.INC" -->

<!-- Chapter 4. UnlockAccount.asp -->

<html>
<body>
<H2> Account Unlocker </H2>
<HR>

<%

Dim GroupObj, Member, UserObj, Flags
'On Error Resume Next

If Request.Form("AccountName") = "" Then
Set GroupObj = GetObject("WinNT://server/Users")
If Err Then adsiErr()
Response.Write "The following accounts have been locked out <BR>"

%>
<FORM METHOD=POST ACTION="UnlockAccount.asp">
<SELECT NAME="AccountName" >
<OPTION> ** Select an Account **
<%
For Each Member In GroupObj.Members
If Member.Class = "User" Then
' response.write Member.Name & "<br>"
Set UserObj = GetObject(Member.ADsPath)
Flags = UserObj.get("UserFlags")
If (Flags And UF_LOCKOUT) <> 0 Then
Response.Write "<OPTION Value='" & Member.ADsPath & "'>" & Member.Name
End If
End If
Next

%>
</SELECT>
<INPUT TYPE=SUBMIT VALUE="UnLock This Account">
</FORM>

<%
Response.End
End If

If Request.Form("AccountName") <> "" Then
Set UserObj = GetObject(Request.Form("AccountName"))
If Err Then adsiErr()
Response.Write "Unlocking the account for <B>" & UserObj.Name & "</B>... <BR>"

Flags = UserObj.get("UserFlags")
Response.Write "User Flags On Entry" & HEX(Flags) & "<BR>"

UserObj.Put "UserFlags", Flags Xor UF_LOCKOUT
If Err Then adsiErr()
Response.Write "User Flags Applied <BR>"

UserObj.SetInfo
If Err Then adsiErr()
Response.Write "Set Info Applied <BR>"

Flags = UserObj.get("UserFlags")
Response.Write "New User Flags Retrieved: " & HEX(Flags) & "<BR>"

If (Flags And UF_LOCKOUT) <> 0 Then
Response.Write "Account Locked: " & HEX(Flags)
Else
Response.Write "Account Unlocked : " & HEX(Flags)
End If

End If

%>
</body>
</html>


I got it out of a book called "ADSI ASP" by Steven Hahn.
 
Security:
Without screwing around too much, the user accounts that you want to grant access to manipulate user accounts could just be added to the domain security group "Account Operators"
But this action will provide more access to the user account than just enable/disable
 
Hi all ... found this thread off google a while ago, and have been following closely ... thanks zcolton for your willingness to contribute ... I've followed all that was said, and am now running into a different security problem.

I'm trying to do a simple search for a user(s) based on sAMAccountName. My script will work in DOS from my own workstation, but not on remote IIS server ...

note: script combination of code from this forum & Hilltop Labs (
Here's my working DOS script for sAMAccountName search:
Code:
Option Explicit

Dim objRootDSE, strDNSDomain, objCommand, objConnection, strQuery
Dim objRecordSet, strName, strDN
Dim strBase, strFilter, strAttributes
Dim oArgs

Set oArgs = WScript.Arguments 

' Determine DNS domain name from RootDSE object.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

' Use ADO to search Active Directory.
Set objCommand = CreateObject("ADODB.Command")
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Properties("User ID") = "DOMAIN\Account"
objConnection.Properties("Password") = "xxxxxx"
objConnection.Open "Active Directory Provider"
objCommand.ActiveConnection = objConnection

' Search for all user objects. Sort recordset by DisplayName.
strBase = "<LDAP://" & strDNSDomain & ">"
strFilter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=" & oArgs(0) & "*))"
strAttributes = "distinguishedName,displayName,mail"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"

objCommand.CommandText = strQuery
objCommand.Properties("Page Size") = 100
objCommand.Properties("Timeout") = 30
objCommand.Properties("Cache Results") = False
objCommand.Properties("Sort On") = "displayName"
Set objRecordSet = objCommand.Execute

If objRecordSet.EOF Then
	Wscript.Echo "User not found"
	Wscript.Quit
End If

' Loop through results
Do Until objRecordSet.EOF
	Wscript.Echo "Display Name: " & objRecordSet.Fields("displayName")
	Wscript.Echo "Email Address: " & objRecordSet.Fields("mail")
	Wscript.Echo "Distinguished Name: " & objRecordSet.Fields("distinguishedName")
	objRecordSet.MoveNext
Loop

' Clean up.
objConnection.Close
Set objRootDSE = Nothing
Set objCommand = Nothing
Set objConnection = Nothing
Set objRecordSet = Nothing
</font>

And here's my ASP function :
Code:
Function getUserInfo(ntUsername)
	
	Dim objRootDSE, strDNSDomain, objCommand, objConnection, strQuery
	Dim objRecordSet, strName, strDN
	Dim strBase, strFilter, strAttributes
	
	' Determine DNS domain name from RootDSE object.
	Set objRootDSE = GetObject("LDAP://RootDSE")
	strDNSDomain = objRootDSE.Get("defaultNamingContext")
	
	' Use ADO to search Active Directory.
	Set objCommand = CreateObject("ADODB.Command")
	Set objConnection = CreateObject("ADODB.Connection")
	objConnection.Provider = "ADsDSOObject"
	objConnection.Properties("User ID") = "DOMAIN\account"
	objConnection.Properties("Password") = "xxxxxx"
	objConnection.Open "Active Directory Provider"
	objCommand.ActiveConnection = objConnection
	
	' Search for all user objects. Sort recordset by DisplayName.
	strBase = "<LDAP://" & strDNSDomain & ">"
	strFilter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=" & ntUsername & "*))"
	strAttributes = "distinguishedName,displayName,mail"
	strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
	
	objCommand.CommandText = strQuery
	objCommand.Properties("Page Size") = 100
	objCommand.Properties("Timeout") = 30
	objCommand.Properties("Cache Results") = False
	objCommand.Properties("Sort On") = "displayName"	
	Set objRecordSet = objCommand.Execute
	
	If objRecordSet.EOF Then
		Response.Write "User not found"
		Exit Function
	End If
	

	' Loop through results
	Do Until objRecordSet.EOF
		Response.Write "Display Name: " & objRecordSet.Fields("displayName")
		Response.Write "Email Address: " & objRecordSet.Fields("mail")
		Response.Write "Distinguished Name: " & objRecordSet.Fields("distinguishedName")
		objRecordSet.MoveNext
	Loop
	
	' Clean up.
	objConnection.Close
	Set objRootDSE = Nothing
	Set objCommand = Nothing
	Set objConnection = Nothing
	Set objRecordSet = Nothing
		
End Function


This will work from DOS, but I get "Provider (0x80004005)
Unspecified error" from my page - it's choking on the "objCommand.Execute" line.

The IIS Server is NOT a domain controller, and I believe it's set to basic authentication (pretty sure). I've hard coded the username/pass, but it's not working.

Any ideas ?

Thanks much ...
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top