问题
I am connecting to a Microsoft Active Directory server in a DMZ from my .net application (asp.net VB .net 4.0). I need to create a new "inetorgperson" in an orgunit called "SingleCustomerAccount".
I have had to use the System.DirectoryServices.Protocols namespace for all the work, because the ADSI classes (System.DirectoryServices namespace) wouldn't work across the DMZ properly.
Anyway it's been working fine connecting to Active Directory on Windows Server 2003 R2; however we're running tests against Active Directory on Windows Server 2008 R2 (2008r2 in native mode for forest and domain) in order to upgrade.
My existing code to create a user does not work.
System.DirectoryServices.Protocols.DirectoryOperationException: The server cannot handle directory requests. at System.DirectoryServices.Protocols.LdapConnection.ConstructResponse(Int32 messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, Boolean exceptionOnTimeOut) at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout) at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request) at Salford.LDAP.LDAPUser.SaveNewToDirectory(String UsersFirstPassword) in C:\Projects\SCA\App_Code\SCA\LDAPUser.vb:line 1059 at SCA.Web.Service.CitizenService.CreateNewAccount(String Username, String Title, String FirstName, String Surname, String Street, String City, String County, String Postcode, String EmailAddress, String HomeTel, String MobileTel, String UPRN, String SpinID, Int16 AccountLevel) in C:\Projects\SCA\App_Code\CitizenService.vb:line 255
I have found that when I remove the bit of code that adds the password attribute, the user is created, but just without a password. So the code at fault is where I am adding the password. But what has changed between 2003 and 2008 which would have stopped it from working?
Here is my code.
Using ldapConn As New LdapConnection(New LdapDirectoryIdentifier(LDAPServerAddress), credential)
ldapConn.SessionOptions.ProtocolVersion = 3
ldapConn.SessionOptions.Signing = Not _UseSecureConnection
ldapConn.SessionOptions.Sealing = Not _UseSecureConnection
ldapConn.SessionOptions.SecureSocketLayer = _UseSecureConnection
If _UseSecureConnection Then
ldapConn.SessionOptions.VerifyServerCertificate = New VerifyServerCertificateCallback(AddressOf ServerCallback)
End If
ldapConn.AuthType = AuthType.Negotiate
ldapConn.Bind()
Dim DistinguishedName As String = String.Format("CN={0},OU={1},{2}", Me.AccountName, Me.OrgUnit, Me.DCSuffix)
' Save this distinguished name to the local object; so that the group memberships addition works in a minute.
Me._DistinguishedName = DistinguishedName
Dim addRequest As New AddRequest(DistinguishedName, Me.LDAPUserObjectType)
'' Add an AccountName attribute
addRequest.Attributes.Add(New DirectoryAttribute(GetLDAPSchemaMapping(LDAPUserProperties.AccountName), AccountName))
'' Look in any derived classes, if they want any attributes adding as part of this save operation.
'' Hint: Derived classes will override the "GetDirectoryAttributesForAddNewRequest" function and return a list of anything they want adding
'' to the AD at the time of creation.
If Not GetDirectoryAttributesForAddNewRequest() Is Nothing Then
For Each kvp As KeyValuePair(Of String, String) In GetDirectoryAttributesForAddNewRequest()
addRequest.Attributes.Add(New DirectoryAttribute(kvp.Key, kvp.Value))
Next
End If
'' Hash up the password into a Unicode byte array and send this as the requried initial password.
addRequest.Attributes.Add(New DirectoryAttribute("unicodePwd", GetPasswordData(UsersFirstPassword)))
' Execute the request on the directory server.
Dim addResponse As DirectoryResponse = ldapConn.SendRequest(addRequest)
' Need to return the GUID, need to search against the ldap server:
Dim request As New SearchRequest(String.Format("OU={0},{1}", Me.OrgUnit, Me.DCSuffix), "(&(objectCategory=" & Me.LDAPUserObjectType & ")(sAMAccountName=" & Me.AccountName & "))", System.DirectoryServices.Protocols.SearchScope.Subtree)
Dim searchResponse As SearchResponse = DirectCast(ldapConn.SendRequest(request), SearchResponse)
returnedGuid = DirectCast(searchResponse.Entries(0).Attributes("objectGuid").Item(0), Byte())
' Set up the search request object so we can do searches now based on this new user:
Dim rq As SearchRequest = BuildLdapSearchRequest("sAMAccountName", Me.AccountName)
' ** Send the query to the LDAP server, and save the response into the private _SearchResponse object **
_SearchResponse = DirectCast(ldapConn.SendRequest(rq), SearchResponse)
End Using
_useSecureConnection is false for this call - the bind works fine. Like I have said, when I comment out this line, it works:
addRequest.Attributes.Add(New DirectoryAttribute("unicodePwd", GetPasswordData(UsersFirstPassword)))
The GetPasswordData method is below for completeness.
''' <summary>
''' Returns a unicode-encoded byte array based on the incoming password string.
''' </summary>
''' <param name="password">The password to turn into a byte array</param>
Public Function GetPasswordData(ByVal password As String) As Byte()
Dim formattedPassword As String
formattedPassword = String.Format("""{0}""", password)
Return Encoding.Unicode.GetBytes(formattedPassword)
End Function
I appreciate any insights...
Regards bgs264
回答1:
Is it possible that the password policies are different for the two servers? Or that there's some other policy difference?
One way to check might be to use the ldp.exe
tool and see if you can do the same operations with the exact same password with it. Here's a link that describes changing the password using that tool.
回答2:
This was fixed by a combination of using LDAPS instead of LDAP; relaxing the password policy; doing a full rebuild of the application; clearing the browser cache and rebooting all the servers.
来源:https://stackoverflow.com/questions/3138896/setting-users-password-via-system-directoryservices-protocols-in-ad-2008-r2