除了使用 IIS 整合驗證跟呼叫 Windows API,.NET 還有一種流傳甚廣的 LDAP 帳號密碼驗證程式寫法:

var username = "Child\\someone";
var password = "P@ssW0rd";
// web Domain DC = 10.0.0.7
// child Domain DC = 10.0.0.6
var ldapPath = "LDAP://10.0.0.7";
AuthByLdap();

void AuthByLdap()
{
    try
    {
        Console.WriteLine($"Connect {ldapPath} with {username}...");
        DirectoryEntry entry = new DirectoryEntry(ldapPath, username, password);
        // Bind NativeAdsObject 時將觸發身分驗證
        object obj = entry.NativeObject;
        Console.WriteLine("Login Successful");

        // 執行到此沒出錯即代表帳號密碼有效,以下加碼搜尋 AD 所有名為 someone 的項目
        DirectorySearcher search = new DirectorySearcher(entry);
        // 搜尋 AD 帳號名稱為 someone 的項目 (child 跟 web 網域各有一位)
        search.Filter = "(SAMAccountName=someone)";
        foreach (SearchResult r in search.FindAll())
        {
            Console.WriteLine(r.Properties["distinguishedName"][0].ToString());
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Login failed: " + ex.Message);
    }
}

面對這段程式碼,我有幾個疑問:

  1. 在 AD 架構中,DC 就是 LDAP 主機,若套用前幾天跨子網域登入 IIS的情境。 我是否可以用 child\someone 登入 web LDAP 主機(DC)?
  2. 若可以用 child\someone 登入網域 web LDAP,過程需要連到網域 child LDAP 嗎?
  3. DC 主機若具有 GC 角色,同時提供 389 (LDAP) 及 3268 (GC) 兩種 Port,LDAP 協定可選擇其一連接,依先前 PowerShell Get-ADUser/C# 跨網域搜尋 AD 帳號使用 3268 可跨網域搜索,在這段程式是否也會產生相同差異?

現在來一一驗證。先說測試環境:

  • 程式在 10.0.0.8 主機上執行,機器加入 web 網域,以 web\someone 登入 Windows 執行程式
  • web 網域 DC/LDAP 伺服器 IP = 10.0.0.7
  • child 網域 DC/LDAP 伺服器 IP = 10.0.0.6
  • web 網域與 child 網域都有一個名為 someone 的使用者

測試四次:

  1. 使用 web\someone 連 LDAP://10.0.0.7
    成功登入,查到 web 的 someone
  2. 使用 child\someone 連 LDAP://10.0.0.7
    成功登入,查到 web 的 someone
  3. 使用 web\someone 連 GC://10.0.0.7
    成功登入,查到 web 跟 child 的 someone
  4. 使用 child\someone 連 GC://10.0.0.7
    成功登入,查到 web 跟 child 的 someone

使用 Wireshark 觀察封包,發現 .NET DirectoryEntry 底層是用 NTLMSSP Challenge/Response 完成認證,不需要直接傳輸密碼,而第 15 列 NTMLSSP_NEGOTIATEsasl 的 SASL 及第 17 列的 GSS-API(整合 Kerberos) 都是 LDAP 協定的安全認證框架。參考

既然是用 NTLM,web DC (10.0.0.7) 便能直接驗證跨網域的 child\someone 帳號,不需接觸 child DC (10.0.0.6),就 bindResponse success:

將 LDAP://10.0.0.7 改成 GC://10.0.0.7,封包過程幾乎一模一樣,只差在連接埠由 389 Port 變成 3268 Port,而查詢對象為 GC,故可查到 web 跟 child 的 someone 共兩筆:

那如果將 GC://10.0.0.7 改成 GC://web.contoso.com,會跟 IIS 一樣改用 Kerberos 嗎?會!

此時 10.0.0.8 會與 child DC 10.0.0.6 建立 CLDAP (UDP 389 Port) 及 KRB5 (88 Port) 連線:

不過我的環境疑似 DNS SPN 沒設好,出現 KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN 錯誤,最後仍是走 NTLM: 參考

依據微軟文件,當條件被滿足時,AD DS 是可以支援 Kerberos 的,至於是哪裡沒設好?感覺愈游水愈深,這段就先不深究了,容我游回岸邊喘一會兒。

最後做個小結:

  1. 使用 DirectoryEntry 連線 AD DC/LDAP 主機時,在 Bind 階段會觸發帳號驗證,類似 IIS 可協商使用 NTLM 或 Kerberos。
  2. 若走 NTLM,由 DC 直接驗證跨網域帳號;當條件滿足時則會啟動 Kerberos,此時客戶端需連跨網域 DC 的 LDAP (389 UDP) 及 Kerberos (88 TCP)。
  3. DC 同時支援 389 及 3268,ldap:// 預設連 389、gc:// 預設連 3268,前者只能搜覽自己網域的項目,但資訊較完整;後者可查詢全網域所有物件,但屬目錄性質,完整資料需接洽該網域 DC 取得。

Experimenets about athenticating cross-domain account with AD DS.


Comments

# by player

關於以LDAP進行AD驗證帳密 記得小朱在ASP.NET 2.0時期寫的那本書 就有了 忘了是Q幾了,應該還能用吧? https://dotblogs.com.tw/regionbbs/2012/07/30/asp_net_q_and_a_in_practices_available_full_book_for_download

Post a comment