We're attempting to change user passwords in Active Directory (we'll use "Jane" as an example user), via a C# ASP.NET 4.5 app, though we're receiving the error below.
Configuration information could not be read from the domain controller, either because the machine is unavailable, or access has been denied. (Exception from HRESULT: 0x80070547)
A connection to the remote (3rd party) AD server can be established without issue and our service user is authenticated. Jane's directory entry can also be retrieved without issue.
We've attempted a password change/set whilst authenticated as both Jane and our service user, both yield the same error (above).
Below is an example of the code in use.
using (var context = new PrincipalContext(ContextType.Domain, ServiceDomain, ServiceDefaultLocation, ContextOptions.Negotiate, ServiceUser, ServicePassword))
using (var identity = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "Jane"))
{
// identity.SetPassword("SomeNewPassword"); // also tried
identity.ChangePassword("TheOldPassword", "SomeNewPassword"); // this is the error line
identity.Save();
}
The system event log indicates an Audit Failure on the change/set password, with the reason listed as "An Error occured during logon.". The Authentication Package lists "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0". Prior to this, we have an Audit Success event against our service user.
It's worth noting that it's approx. 20 seconds before we receive the error, indicating that it may be a connection/timeout issue, however outgoing connections are unrestricted and our IP has been whitelisted on the remote end (again, we can connect and retrieve the user entry). It can also take this long for authenticating as a user too though (which still works successfully).
Our service user has permissions set to change/set other users' passwords, though not to directly alter user attributes, so we've been advised.
If there is any other info that may help diagnose the issue, please let me know.
Have you tried DirectoryEntry? It is more easy to debug connection releated problems with this.
var entry = new DirectoryEntry{
Path = "LDAP://yourDCname";
Username = yourUsername;
Password = yourPassword;
};
using(var searcher = new DirectorySearcher(entry))
{
searcher.Filter = "(sAMAccountName=Jane)";
var result = searcher.FindOne();
var user = result.GetDirectoryEntry();
try
{
user.Invoke("ChangePassword", new object[] { oldPassword, newPassword});
user.CommitChanges();
}
catch
{
throw;
}
}
I also once forgot the ZenMate VPN plug-in running in Google Chrome on background. So I couldn't connect to the Domain Controller.
I had a similar issue with a password reset server web page for users to be able to reset their own password. I found that the domain was setup not too allow this even if the service account was given the permissions to just reset passwords. Have you tried to give the service account domain admin rights just as a test that it is not a permission issue?
So UserPrincipal.ChangePassword()
will call:
de.Invoke("ChangePassword", new object[] { oldPassword, newPassword });
on the DirectoryEntry
it uses behind the scenes. DirectoryEntry.Invoke()
is used to call "a method on the native Active Directory Domain Services object", which means this will end up using the native Windows IADsUser::ChangePassword
method. The Remarks for that says it uses one of 3 methods:
IADsUser::ChangePassword functions similarly to IADsUser::SetPassword in that it will use one of three methods to try to change the password. Initially, the LDAP provider will attempt an LDAP change password operation, if a secure SSL connection to the server is established. If this attempt fails, the LDAP provider will next try to use Kerberos (see IADsUser::SetPassword for some problems that may result on Windows with cross-forest authentication), and if this also fails, it will finally call the Active Directory specific network management API, NetUserChangePassword.
The Remarks of IADsUser::SetPassword
says a little more:
- First, the LDAP provider attempts to use LDAP over a 128-bit SSL connection. For LDAP SSL to operate successfully, the LDAP server must have the appropriate server authentication certificate installed and the clients running the ADSI code must trust the authority that issued those certificates. Both the server and the client must support 128-bit encryption.
- Second, if the SSL connection is unsuccessful, the LDAP provider attempts to use Kerberos.
- Third, if Kerberos is unsuccessful, the LDAP provider attempts a NetUserSetInfo API call. In previous releases, ADSI called NetUserSetInfo in the security context in which the thread was running, and not the security context specified in the call to IADsOpenDSObject::OpenDSObject or ADsOpenObject. In later releases, this was changed so that the ADSI LDAP provider would impersonate the user specified in the OpenDSObject call when it calls NetUserSetInfo.
So if this is not working at all for you, it means all three methods are failing.
I don't really know how Kerberos or NetUserSetInfo works, but I do know how LDAP over SSL works, and it's something you and try an see if it will work for you.
It should be as easy as putting :636
at the end of your ServerDomain
to connect to the LDAPS port (assuming that's the DNS name of the domain):
using (var context = new PrincipalContext(ContextType.Domain, $"{ServiceDomain}:636", ServiceDefaultLocation, ContextOptions.Negotiate, ServiceUser, ServicePassword))
using (var identity = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "Jane"))
{
// identity.SetPassword("SomeNewPassword"); // also tried
identity.ChangePassword("TheOldPassword", "SomeNewPassword"); // this is the error line
identity.Save();
}
But you may run into trouble if the server uses a certificate that the computer you run this from doesn't trust (you'd have to install the root cert on the machine you run this from). And it's also possible that LDAPS just isn't running on the DC.
But it's worth a try.
User contributions licensed under CC BY-SA 3.0