簡體   English   中英

LDAP 從域外重置密碼。網絡 C# 錯誤:RPC 服務器不可用。 (hresult 的異常:0x800706ba)

[英]LDAP reset password from outside the domain network C# Error: RPC server is unavailable. (exception from hresult: 0x800706ba)

我們正在嘗試重置 LDAP 密碼,它適用於開發環境,但不適用於生產環境。

我們的開發環境在域內,生產環境在域外。

在連接 LDAP 的開發中,我們使用了像abc.com這樣的域名,在生產環境中我們使用了 IP 地址IPaddress:389 ,它已經在兩種環境中用於 LDAP用戶身份驗證 但不適用於LDAP 重置密碼

Error: RPC server is unavailable. (exception from hresult: 0x800706ba)

發展:(工作)

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.

生產:(工作)我們還使用以下方法對 LDAP 用戶進行身份驗證,其在生產中工作:

檢查用戶是否有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

生產:(不工作)

// "<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)

還嘗試使用以下代碼(不工作)

// "<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)

用於修改密碼的屬性是unicodePwd 該文檔揭示了更改密碼必須滿足的一些條件。 首先,連接必須加密。

調用.Invoke("SetPassword", ...)實際上調用本機 Windows IADsUser::SetPassword方法。 該文檔顯示它會自動嘗試幾種不同的加密方式。 發生異常是因為這些方法均無效。

實際上,您可以直接修改unicodePwd屬性,而無需調用SetPassword ,我將進行介紹,但無論如何,您必須先解決加密問題。

當您從 .network 內的計算機運行它時, AuthenticationTypes.Sealing就足夠了。 正如文檔所說,效果是它使用 Kerberos 來加密連接。

但是當您從域外連接時,Kerberos 將不起作用(也許它會努力 - 我不是 Kerberos 專家)。 所以唯一可用的加密方法是SetPassword方法實際上嘗試使用 SSL,但顯然它沒有用。

我馬上看到的一個問題是您正在使用 IP 地址連接到 DC,而 SSL 將無法使用 IP 地址,因為 SSL 證書上的域名名稱必須與您用於訪問的名稱匹配到服務器,SSL 證書上將沒有 IP 地址。 因此,您必須更改它才能使用域名。 如果 DNS 無法解析該名稱,您可以將其添加到您的主機文件中。

改變它可能會解決所有問題。 如果不是,可能還有另外兩個問題:

  1. 端口 636 不可訪問(路上有防火牆)。
  2. SSL 證書在您運行此程序的計算機上不受信任

LDAP over SSL (LDAPS) 在端口 636 上工作。您可以在 PowerShell 中測試此連接:

Test-NetConnection example.com -Port 636

如果失敗,請先修復它。

接下來,檢查證書。 您可以使用此 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"

將第一行中的example.com更改為您的域名(保留https://:636 )。 然后您將在當前目錄中有一個名為certificate.cer的文件,您可以打開並檢查該文件。 如果它不受信任,它會警告您。 如果它不受信任,則您必須將根證書作為受信任的根證書安裝在服務器上。

如果它已被信任,請確保證書上的“頒發給:”域名與您用於連接的名稱相匹配。 在我們的環境中,SSL 證書位於每個域 controller ( dc1.example.com ) 的名稱中,而不是域名 ( example.com )。 所以我必須針對特定域 controller 才能使 LDAPS 工作。

一旦你把所有這些都整理好,你的代碼就應該可以工作了。

如果您想直接更改unicodePwd屬性而不是使用SetPassword (它可能會或可能不會執行得更快),您將需要通過 SSL 建立原始連接。例如:

DirectoryEntry de = new DirectoryEntry("LDAP://dc1.example.com:636","<username>", "<password>", 
    AuthenticationTypes.Secure | AuthenticationTypes.SecureSocketsLayer | AuthenticationTypes.ServerBind);

如果您的目標是特定 DC,則僅使用AuthenticationTypes.ServerBind

然后你可以用它想要的非常具體的方式更新unicodePwd屬性:

uEntry.Properties["unicodePwd"].Value = Encoding.Unicode.GetBytes("\"NewPassword\"");
uEntry.CommitChanges();

請注意,新密碼必須用引號引起來。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM