Как проверить creds Active Directory через LDAP + SSL?

Я пытаюсь использовать .NET 3.5 System.DirectoryServices.AccountManagement пространство имен для проверки учетных данных пользователя на нашем сервере LDAP Active Directory через SSL зашифрованное соединение LDAP. Вот пример кода:

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:389", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate))
{
    return pc.ValidateCredentials(_username, _password);
}

этот код отлично работает над незащищенным LDAP (порт 389), однако я бы предпочел не передавать комбинацию user/pass в ясном тексте. Но когда я перехожу на LDAP + SSL (порт 636), я получаю следующее исключение:

System.DirectoryServices.Protocols.DirectoryOperationException: The server cannot handle directory requests.
  at System.DirectoryServices.Protocols.ErrorChecking.CheckAndSetLdapError(Int32 error)
  at System.DirectoryServices.Protocols.LdapSessionOptions.FastConcurrentBind()
  at System.DirectoryServices.AccountManagement.CredentialValidator.BindLdap(NetworkCredential creds, ContextOptions contextOptions)
  at System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password)
  at System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName, String password)
  at (my code)

порт 636 работает для других действия, такие как поиск непарольной информации для этой записи LDAP/AD...

UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, _username)

...поэтому я знаю, что это не настройка SSL моего сервера LDAP, так как он работает через SSL для других запросов.

кто-нибудь получил ValidateCredentials(...) вызов для работы по SSL? Можете объяснить как? Или есть другой / лучший способ безопасной проверки учетных данных AD/LDAP?

4 ответов


я смог проверить учетные данные, используя System.DirectoryServices.Protocols пространство имен, благодаря коллеге. Вот код:

// See http://support.microsoft.com/kb/218185 for full list of LDAP error codes
const int ldapErrorInvalidCredentials = 0x31;

const string server = "sd.example.com:636";
const string domain = "sd.example.com";

try
{
    using (var ldapConnection = new LdapConnection(server))
    {
        var networkCredential = new NetworkCredential(_username, _password, domain);
        ldapConnection.SessionOptions.SecureSocketLayer = true;
        ldapConnection.AuthType = AuthType.Negotiate;
        ldapConnection.Bind(networkCredential);
    }

    // If the bind succeeds, the credentials are valid
    return true;
}
catch (LdapException ldapException)
{
    // Invalid credentials throw an exception with a specific error code
    if (ldapException.ErrorCode.Equals(ldapErrorInvalidCredentials))
    {
        return false;
    }

    throw;
}

Я не в восторге от использования блока try / catch для управления логикой принятия решений, но это то, что работает. :/


может быть, это другой путь. В проверке полномочий нет ничего необычного. ContextOptions должны быть установлены правильно.

значение по умолчанию:

ContextOptions.Переговоры | ContextOptions.Подписание | ContextOptions.Запечатывание

Добавить Ssl:

ContextOptions.Переговоры | ContextOptions.Подписание | ContextOptions.Уплотнение | ContextOptions.SecureSocketLayer

ContextOptions.Вести переговоров или ContextOptions.Требуется SimpleBind. Или независимо от того, что ваш сервер должен выполнить аутентификацию. ContextOptions поддерживает только бит или бит.

можно попробовать также установить ContextOptions напрямую этот способ В способ ValidateCredentials.

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:636", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate | ContextOptions.SecureSocketLayer))
{
    return pc.ValidateCredentials(_username, _password);
}

или

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:636", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate))
{
    return pc.ValidateCredentials(_username, _password, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer);
}

для меня, метод ValidateCredentials работает просто отлично. Проблема, как я обнаружил, была на сервере, на котором размещалось объявление (я использую AD LDS). Необходимо связать сертификат сервера с экземпляром AD. Поэтому, если ваш экземпляр назывался "MyAD" (или ActiveDirectoryWebService), вам нужно было открыть MMC, щелкнуть в модуле "сертификаты", выбрать "учетная запись службы", а затем выбрать "MyAD" из списка. Оттуда вы можете добавить сертификат SSL в личный магазин "MyAD". Этот наконец, запустил обработку SSL.

подозреваю, из того, что я знаю о LdapConnection способ и тот факт, что вы опустили функцию обратного вызова, что вы не проверять сертификат сервера. Это грязная работа и ValidateCredentials это бесплатно. Возможно, ничего особенного, но все же дыра в системе безопасности.


Я знаю, что это старо, но для любого, кто снова столкнется с этим:

PrincipalContext.ValidateCredentials(...) по умолчанию пытается открыть SSL-соединение (ldap_init(NULL, 636)) с последующей установкой опции LDAP_OPT_FAST_CONCURRENT_BIND.

если a (доверенный?) клиентский сертификат присутствует, однако соединение LDAP неявно связано, и быстрая привязка больше не может быть включена. PrincipalContext не рассмотреть это дело и выдает неожиданный DirectoryOperationException.

решение: Для поддержки SSL, где это возможно, но есть запасной, вызова ValidateCredentials(...) с параметрами по умолчанию (т. е. без вариантов). Если это не удается с DirectoryOperationException, повторить задание ContextOptions (переговоры / печать / подписание), который является то, что ValidateCredentials внутренне делает для ожидаемого LdapException в любом случае.