[英]LDAP reset password from outside the domain network C# Error: RPC server is unavailable. (exception from hresult: 0x800706ba)
We're trying to Reset LDAP password , its working on development environment but not working on production environment.我们正在尝试重置 LDAP 密码,它适用于开发环境,但不适用于生产环境。
Our development environment is inside the Domain and production environment is outside the Domain.我们的开发环境在域内,生产环境在域外。
In development to connect LDAP, we have used Domain name like abc.com
and production environment we use IPaddress:389
, which is already working for LDAP User authentication in both environment.在连接 LDAP 的开发中,我们使用了像
abc.com
这样的域名,在生产环境中我们使用了 IP 地址IPaddress:389
,它已经在两种环境中用于 LDAP用户身份验证。 But not working for LDAP reset password .但不适用于LDAP 重置密码。
Error: RPC server is unavailable. (exception from hresult: 0x800706ba)
Developement: (working)发展:(工作)
PrincipalContext principalContext =
new PrincipalContext(ContextType.Domain, "<domain.com>", container: "<DC=domain,DC=com>",
"<username>", "<password>");
UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, "<LdapUserName>");
// "<username>", "<password>" are Administrative credential.
bool isValid = user.ValidateCredentials("<username>", "<password>");
_logger.Log($"Is Connection: {isValid}");
**// Output: Is Connection: True**
user.UserCannotChangePassword = false;
user.SetPassword("<NewPassword>");
// Password has been successfully reset.
Production: (working) Also we are authenticate LDAP users using below method its working on Production:生产:(工作)我们还使用以下方法对 LDAP 用户进行身份验证,其在生产中工作:
Check user has LDAP account or not:检查用户是否有LDAP账号:
// "<username>", "<password>" are Administrative credential.
var entry = new DirectoryEntry($"LDAP://{"<IP:389>"}", "<username>", "<password>",
AuthenticationTypes.Secure | AuthenticationTypes.Sealing | AuthenticationTypes.ServerBind);
var search = new DirectorySearcher(entry);
var strFilter = $"(mail={"<UserEmailId>"})";
search.Filter = strFilter;
var result = await Task.Run(() => search.FindOne());
if (result != null)
{
//IsLdapUser = true;
//result.Properties["samaccountname"][0]);
}
else
{
//IsLdapUser = false;
}
// Successfully
// Authenticate LDAP user:
var ldapConnection = new LdapConnection(new LdapDirectoryIdentifier("<IP:389>", false, false));
var nc = new NetworkCredential("<LdapUserName>", "<LdapUserPassword>", "<IP:389>");
ldapConnection.Credential = nc;
ldapConnection.AuthType = AuthType.Negotiate;
ldapConnection.Bind(nc);
// Successfully
Production: (not working)生产:(不工作)
// "<username>", "<password>" are Administrative credential.
PrincipalContext principalContext =
new PrincipalContext(ContextType.Domain, "<IP:389>", container: "<DC=domain,DC=com>",
"<username>", "<password>");
UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, "<LdapUserName>");
bool isValid = user.ValidateCredentials("<username>", "<password>");
_logger.Log($"Is Connection: {isValid}");
**// Output: Is Connection: True**
user.UserCannotChangePassword = false;
user.SetPassword("<NewPassword>");
// Error: RPC server is unavailable. (exception from hresult: 0x800706ba)
Also tried with below code (not working)还尝试使用以下代码(不工作)
// "<username>", "<password>" are Administrative credential.
DirectoryEntry de = new DirectoryEntry("<IP:389>","<username>", "<password>",
AuthenticationTypes.Secure | AuthenticationTypes.Sealing | AuthenticationTypes.ServerBind);
// LDAP Search Filter
DirectorySearcher ds = new DirectorySearcher(de);
ds.Filter = "(&(objectClass=user)(|(sAMAccountName=" + "<LdapUserName>"+ ")))";
// LDAP Properties to Load
ds.PropertiesToLoad.Add("displayName");
ds.PropertiesToLoad.Add("sAMAccountName");
ds.PropertiesToLoad.Add("DistinguishedName");
ds.PropertiesToLoad.Add("CN");
// Execute Search
SearchResult result = await Task.Run(() => ds.FindOne());
string dn = result.Properties["DistinguishedName"][0].ToString();
DirectoryEntry uEntry = result.GetDirectoryEntry();
uEntry.Invoke("SetPassword", new object[] { "<NewPassword>"}); //Set New Password
uEntry.CommitChanges();
uEntry.Close();
// Error: RPC server is unavailable. (exception from hresult: 0x800706ba)
The attribute used to modify the password is unicodePwd
.用于修改密码的属性是
unicodePwd
。 That documentation reveals some conditions that must be met for the password to be changed.该文档揭示了更改密码必须满足的一些条件。 Primarily, the connection must be encrypted.
首先,连接必须加密。
Calling .Invoke("SetPassword", ...)
actually calls the native Windows IADsUser::SetPassword
method.调用
.Invoke("SetPassword", ...)
实际上调用本机 Windows IADsUser::SetPassword
方法。 That documentation shows that it automatically attempts a few different ways to encrypt.该文档显示它会自动尝试几种不同的加密方式。 The exception happens because none of these methods worked.
发生异常是因为这些方法均无效。
You can actually modify the unicodePwd
attribute directly, without calling SetPassword
, which I'll get to, but regardless, you have to resolve the issue of encryption first.实际上,您可以直接修改
unicodePwd
属性,而无需调用SetPassword
,我将进行介绍,但无论如何,您必须先解决加密问题。
When you're running this from a computer inside the.network, AuthenticationTypes.Sealing
is enough.当您从 .network 内的计算机运行它时,
AuthenticationTypes.Sealing
就足够了。 As the documentation says , the effect is that it uses Kerberos to encrypt the connection.正如文档所说,效果是它使用 Kerberos 来加密连接。
But when you're connecting from outside the domain, Kerberos won't work (maybe it will with effort - I'm no Kerberos expert).但是当您从域外连接时,Kerberos 将不起作用(也许它会努力 - 我不是 Kerberos 专家)。 So the only usable encryption method is SSL. The
SetPassword
method does actually attempt to use SSL, but clearly it didn't work.所以唯一可用的加密方法是
SetPassword
方法实际上尝试使用 SSL,但显然它没有用。
One problem I see right away is that you're using an IP address to connect to the DC, and SSL won't work using an IP address, since the domain name name on the SSL certificate must match the name you are using to access to the server, and the SSL cert will not have the IP address on it.我马上看到的一个问题是您正在使用 IP 地址连接到 DC,而 SSL 将无法使用 IP 地址,因为 SSL 证书上的域名名称必须与您用于访问的名称匹配到服务器,SSL 证书上将没有 IP 地址。 So you will have to change that to use the domain name.
因此,您必须更改它才能使用域名。 If DNS will not resolve the name, you can add it to your hosts file.
如果 DNS 无法解析该名称,您可以将其添加到您的主机文件中。
Changing that may fix everything.改变它可能会解决所有问题。 If not, there can be two other issues:
如果不是,可能还有另外两个问题:
LDAP over SSL (LDAPS) works on port 636. You can test this connection in PowerShell: LDAP over SSL (LDAPS) 在端口 636 上工作。您可以在 PowerShell 中测试此连接:
Test-NetConnection example.com -Port 636
If that fails, fix that first.如果失败,请先修复它。
Next, check the certificate.接下来,检查证书。 You can download the certificate with this PowerShell script:
您可以使用此 PowerShell 脚本下载证书:
$webRequest = [Net.WebRequest]::Create("https://example.com:636")
try { $webRequest.GetResponse() } catch {}
$cert = $webRequest.ServicePoint.Certificate
$bytes = $cert.Export([Security.Cryptography.X509Certificates.X509ContentType]::Cert)
set-content -value $bytes -encoding byte -path "certificate.cer"
Change the example.com
in the first line to your domain name (leave the https://
and :636
).将第一行中的
example.com
更改为您的域名(保留https://
和:636
)。 Then you will have a file called certificate.cer
in the current directory that you can open and inspect.然后您将在当前目录中有一个名为
certificate.cer
的文件,您可以打开并检查该文件。 It will warn you if it is not trusted.如果它不受信任,它会警告您。 If it's not trusted, then you will have to install the root certificate on the server as a Trusted Root Certificate.
如果它不受信任,则您必须将根证书作为受信任的根证书安装在服务器上。
If it is already trusted, make sure the "Issued to:" domain name on the cert matches the name you used to connect.如果它已被信任,请确保证书上的“颁发给:”域名与您用于连接的名称相匹配。 In our environment, the SSL certificates are in the name of each domain controller (
dc1.example.com
), not the domain name ( example.com
).在我们的环境中,SSL 证书位于每个域 controller (
dc1.example.com
) 的名称中,而不是域名 ( example.com
)。 So I have to target a specific domain controller for LDAPS to work.所以我必须针对特定域 controller 才能使 LDAPS 工作。
Once you get all of that sorted out, your code should work.一旦你把所有这些都整理好,你的代码就应该可以工作了。
If you want to change the unicodePwd
attribute directly instead of using SetPassword
(which may or may not perform a little faster), you will need to make the original connection via SSL. For example:如果您想直接更改
unicodePwd
属性而不是使用SetPassword
(它可能会或可能不会执行得更快),您将需要通过 SSL 建立原始连接。例如:
DirectoryEntry de = new DirectoryEntry("LDAP://dc1.example.com:636","<username>", "<password>",
AuthenticationTypes.Secure | AuthenticationTypes.SecureSocketsLayer | AuthenticationTypes.ServerBind);
Only use AuthenticationTypes.ServerBind
if you are targeting a specific DC.如果您的目标是特定 DC,则仅使用
AuthenticationTypes.ServerBind
。
Then you can update the unicodePwd
attribute in the very specific way that it wants:然后你可以用它想要的非常具体的方式更新
unicodePwd
属性:
uEntry.Properties["unicodePwd"].Value = Encoding.Unicode.GetBytes("\"NewPassword\"");
uEntry.CommitChanges();
Note that the new password must be enclosed in quotes.请注意,新密码必须用引号引起来。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.