Изменение пароля Active Directory LDAP через C# ASP.NET: не удается прочитать конфигурацию

Мы пытаемся изменить пароли пользователей в Active Directory (мы будем использовать «Джейн» в качестве примера пользователя) через приложение C# ASP.NET 4.5, хотя мы получаем ошибку, указанную ниже.

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)

Подключение к удаленному (стороннему) серверу AD может быть установлено без проблем, и наш пользователь службы аутентифицирован. Запись в каталоге Джейн также может быть получена без проблем.

Мы попытались изменить / установить пароль при аутентификации как Джейн, так и нашего пользователя службы, оба дали одну и ту же ошибку (см. Выше).

Ниже приведен пример используемого кода.

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();
}

Журнал системных событий указывает на сбой аудита при изменении / установке пароля с указанием причины как «Произошла ошибка во время входа в систему». Пакет аутентификации перечисляет «MICROSOFT_AUTHENTICATION_PACKAGE_V1_0». До этого у нас есть событие Audit Success для нашего пользователя службы.

Стоит отметить, что это ок. За 20 секунд до того, как мы получим ошибку, указывающую, что это может быть проблема с соединением / тайм-аутом, однако исходящие соединения не ограничены, и наш IP-адрес внесен в белый список на удаленном конце (опять же, мы можем подключиться и получить запись пользователя). Это также может занять много времени для аутентификации в качестве пользователя (что по-прежнему успешно работает).

У нашего пользователя службы есть права на изменение / установку паролей других пользователей, но не на прямое изменение пользовательских атрибутов, как нам посоветовали.

Если есть какая-либо другая информация, которая может помочь в диагностике проблемы, дайте мне знать.

В вашем конструкторе PrincipalContext вы принудительно используете его для определенного контроллера домена (используя имя контроллера домена, а не просто имя домена)? а может установка номера порта?

Gabriel Luci 18.12.2018 23:56

Да, ServiceDefaultLocation - это имя DC, в частности DC=TheName,DC=local.

Shane 19.12.2018 12:55

Я имел в виду ServiceDomain. Это просто имя домена, или вы указали там конкретный контроллер домена?

Gabriel Luci 19.12.2018 14:15
ServiceDomain - это исключительно IP-адрес
Shane 19.12.2018 14:36

Итак, вы нацеливаетесь на конкретный DC. По какой-то причине? Есть ли в домене более одного DC?

Gabriel Luci 19.12.2018 15:14

Да, есть несколько контроллеров домена. Мы будем нацеливаться на это по инструкции от стороны, управляющей доменом.

Shane 19.12.2018 16:09
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
2 455
3

Ответы 3

Вы пробовали DirectoryEntry? С этим легче отлаживать проблемы, связанные с подключением.

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;
        }

    }

Я также однажды забыл, что плагин ZenMate VPN работает в Google Chrome в фоновом режиме. Поэтому я не смог подключиться к контроллеру домена.

Мы попытались это сделать, но, к сожалению, это приводит к той же ошибке, что и выше. Первоначальное подключение и поиск пользователя - это нормально, хотя в Invoke.

Shane 18.12.2018 16:11

У меня была аналогичная проблема с веб-страницей сервера сброса пароля, чтобы пользователи могли сбросить свой собственный пароль. Я обнаружил, что домен был настроен не слишком разрешить это, даже если учетной записи службы были даны разрешения просто сбрасывать пароли. Вы пытались дать учетной записи службы права администратора домена просто в качестве проверки того, что это не проблема с разрешениями?

Итак, UserPrincipal.ChangePassword() будет вызов:

de.Invoke("ChangePassword", new object[] { oldPassword, newPassword });

на DirectoryEntry он используется негласно. DirectoryEntry.Invoke() используется для вызова «метода на собственном объекте доменных служб Active Directory», что означает, что в конечном итоге будет использован собственный метод Windows IADsUser::ChangePassword. Замечания для этого говорит, что использует один из трех методов:

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.

Примечания IADsUser::SetPassword говорит немного больше:

  • 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.

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

Я действительно не знаю, как работает Kerberos или NetUserSetInfo, но я знаю, как работает LDAP через SSL, и это то, что вы и попробуйте посмотреть, подойдет ли он для вас.

должно быть так же просто, как поместить :636 в конец вашего ServerDomain для подключения к порту LDAPS (при условии, что это DNS-имя домена):

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();
}

Но у вас могут возникнуть проблемы, если сервер использует сертификат, которому компьютер, с которого вы его запускаете, не доверяет (вам придется установить корневой сертификат на машине, с которой вы его запускаете). Также возможно, что LDAPS просто не работает на DC.

Но попробовать стоит.

Спасибо за это. Указание порта 636 возвращает ошибку «Сервер LDAP недоступен», поэтому я отправлю это обратно контроллеру AD, чтобы проверить доступность LDAP через SSL.

Shane 19.12.2018 12:57

Другие вопросы по теме