【问题标题】:How to prevent DirectoryOperationException - The server cannot handle directory requests如何防止 DirectoryOperationException - 服务器无法处理目录请求
【发布时间】:2012-11-21 19:17:26
【问题描述】:

我正在尝试编写一个实用方法来更新 C# 中的 AD 属性(现在只是单值字符串属性)。这是一个不依赖 IIS 的独立实用程序。此方法将用于将数据从我们的 HR 系统加载到我们的 AD 中。

我能够使用 System.DirectoryServices.Protocols 有效地读取对象和属性。但是当我调用 ModifyRequest 方法时,我得到一个 DirectoryOperationException 消息“服务器无法处理目录请求”。

基于另一个堆栈溢出问题: .Net's Directory Services throws a strange exception

我尝试将端口 636 用于 SSL LDAP,但它并没有改变行为。

我没有使用 IIS,我使用的是 .NET 4.5,因此不应该应用适用于 .NET/IIS 的 Microsoft 补丁。

谷歌搜索没有结果。

如果你知道为什么会出现这个错误,以及如何解决它,我将非常感激。

下面的代码.. 请假设 Conn 包含来自封闭实用程序类的有效且经过身份验证的 LDAP 连接——如果需要,我可以提供封闭实用程序类的完整源代码。

异常发生在ModifyStringAttributeValues 中的SendRequest 行:

using System;
using System.Collections.Generic;
using System.DirectoryServices.Protocols;
using System.Net;

namespace MyOrganization.Common.Ldap
{
    public class LdapSession
    {
        public bool UseKerberos { set; get; }
        public String Host { set; get; }
        public String UserId { set; get; }
        public String Password { set; get; }
        public String Tag { set; get; }
        public int Port { set; get; }

        public const int DefaultLdapPort = 389;

        protected LdapConnection Conn;

        public void EstablishV2()
        {

        }

        public void Establish()
        {

            var effectivePort = Port == 0 ? DefaultLdapPort : Port;

            Console.WriteLine("EffectivePort={0}", effectivePort);

            var identifier = new LdapDirectoryIdentifier(Host, effectivePort);

            if (UseKerberos)
            {
                Conn = new LdapConnection(identifier)
                {
                    AuthType = AuthType.Kerberos,
                    Timeout = new TimeSpan(0, 10, 0, 0),
                    SessionOptions =
                    {
                        ProtocolVersion = 3,
                        VerifyServerCertificate =
                            new VerifyServerCertificateCallback((con, cer) => true),
                        SecureSocketLayer = true
                    }
                };
            }
            else
            {
                Conn = new LdapConnection(identifier)
                {
                    AuthType = AuthType.Basic,
                    Timeout = new TimeSpan(0, 10, 0, 0)
                };

                // Console.WriteLine("LPA:  Binding with {0}, {1}", UserId, Password); // QUARTZ

                Conn.Bind(new NetworkCredential(UserId, Password));
            }


        }

        public IEnumerable<SearchResultEntry> Search(string cx, string filter, SearchScope searchScope, params string[] attrib)
        {
            var s = new SearchRequest(cx, filter, searchScope, attrib)
            {
                SizeLimit = 0,
                TimeLimit = new TimeSpan(1, 0, 0) // One hour, zero minutes, zero seconds
            };

            var raw = Conn.SendRequest(s);

            if (raw == null)
            {
                throw new Exception("null response");
            }

            var r = raw as SearchResponse;

            if (r != null)
            {
                // Console.WriteLine(Tag + "Search response entries: {0}", r.Entries.Count); // QUARTZ

                foreach (SearchResultEntry e in r.Entries)
                {
                    yield return e;
                }
            }
            else
            {
                // Console.WriteLine(Tag + "Search response was null" ); // QUARTZ
            }

            yield break;
        }


        public ResultCode ModifyStringAttributeValues(string dn, IDictionary<string, string> modifications)
        {
            // declare the request and response objects here
            // they are used in two blocks
            ModifyRequest modRequest;
            ModifyResponse modResponse;

            try
            {
                // initialize the modRequest object 
                modRequest =
                    new ModifyRequest(dn);

                modRequest.Controls.Add(new PermissiveModifyControl());

                var mods = new DirectoryAttributeModification[modifications.Count];

                int z = 0;
                foreach (var pair in modifications)
                {
                    var mod = new DirectoryAttributeModification();
                    mod.Operation = DirectoryAttributeOperation.Replace;
                    mod.Name = pair.Key;
                    mod.Add(pair.Value);

                    mods[z] = mod;

                    z += 1;
                }

                // cast the returned directory response into a ModifyResponse type 
                // named modResponse
                modResponse =
                    (ModifyResponse)Conn.SendRequest(modRequest);

                return modResponse.ResultCode;
            }

            catch (Exception e)
            {
                Console.WriteLine("\nUnexpected exception occured:\n\t{0}: {1}",
                                  e.GetType().Name, e.Message);

                return ResultCode.Unavailable;
            }
        }
    }
}

我知道代码有点笨拙,而且充满了奇怪的 cmets——它是在我开始运行时从 Microsoft 网站上的示例代码剪切、粘贴和修改的。

【问题讨论】:

  • 我还想知道为什么您需要 PermissiveModifyControl。你试过没有它吗? stackoverflow.com/q/3450732/1236044
  • 您找到问题的答案了吗? (完成一个 C# - redhat LDAP DS,我可能有一个 AD LDAP 项目进来,所以我正在收集一些信息 ;-) 有些人建议如果可能的话,将 AD 服务器上的日志级别提高为“服务器无法处理目录请求”似乎是一般的 AD 错误

标签: c# directoryservices


【解决方案1】:

如果有人再次遇到这个问题,这是我的解决方案。删除重复用户证书的操作为我解决了这个问题。以下是步骤

  1. 运行 > certmgr.msc
  2. 进入personal文件夹,找到相关证书
  3. 最后删除所有重复的证书

【讨论】:

    【解决方案2】:

    我的问题与不满足约束的属性值有关。我试图在不满足所有要求(大写、包含数字等...)的情况下为帐户设置密码

    【讨论】:

      【解决方案3】:

      我也遇到了这个问题,可能是加强域的原因。

      解决方案是使用域的协商设置而不是简单的调用。这可能是 ValidateCredentials 中的一个错误,因为它只检查 LdapException。见Validate内部类函数CredentialValidator中的https://github.com/dotnet/runtime/blob/main/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Context.cs

      应该可以工作的代码: ctx.ValidateCredentials(用户名、密码、ContextOptions.Negotiate | ContextOptions.Sealing | ContextOptions.Signing);

      附加的,也许是正确的信息:

      https://living-sun.com/143080-how-do-i-validate-active-directory-creds-over-ldap-ssl-c-net-active-directory-ldap-directoryservices.html

      编辑: 它应该在 .NET 6.0 中修复: https://github.com/dotnet/runtime/commit/ce95c98fbbf7592b1b74127a4b87fcd607a25c1a#diff-5ec762bfbfb222fe7cb9500c16e615227b7ab971f4118bb9be168c6a4716683e

      【讨论】:

        【解决方案4】:

        这可能是服务器证书检查的问题(例如,如果您的服务器是自动签名的)

        这是一些绕过服务器证书检查建立 SSL 连接的测试代码:

            public static LdapConnection GetLdapConnection(string login, string password)
            {
                var serverName = /*myServerNameFromConfig*/;
                var port = /*myPortFromConfig*/;
                LdapDirectoryIdentifier ldi = new LdapDirectoryIdentifier(string.Format("{0}:{1}", serverName, port));
        
                NetworkCredential nc = new NetworkCredential(login, password);
        
                LdapConnection connection = new LdapConnection(ldi, nc, System.DirectoryServices.Protocols.AuthType.Basic);
                connection.SessionOptions.ProtocolVersion = 3;
                connection.SessionOptions.VerifyServerCertificate =
                        new VerifyServerCertificateCallback((con, cer) => true);
                connection.SessionOptions.SecureSocketLayer = true;
                return connection;
            }
        

        【讨论】:

        • 谢谢...我的火鸡现在在烤箱里...不过我明天试试。
        • 我更新了类的身份验证部分(其完整代码现在在问题中,时间不长)以包含您推荐的 SessionOptions。这可以在 Establish 方法中找到。行为没有变化。 (请注意,此类适用于 Microsoft 和非 Microsoft LDAP 源 - 此用例将 UseKerberos 选项设置为 true,Port 设置为 636)。
        • @SAJ14SAJ 好的,谢谢跟进。我会尝试删除我的答案,因为它无关紧要。因此,您的问题将显示为未回答,希望有人尝试一下。
        • 我不会删除您的答案,这是一次诚实的尝试,我从中学到了一些东西。我做了UsePermissive,因为该类上的 Microsoft 文档表明它更能容忍属性可能存在或可能不存在的条件,并且对我的用例没有不利影响。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多