[S.DS] DNを指定してAD接続するとERROR_DS_LOCAL_ERROR(0x8007203B)発生

皆さんごきげんよう。ういこうです。最近秋の長雨が面倒くさい今日この頃ですが皆さんいかがお過ごしでしょうか。雨と言えば、息子(小2)が「雨は高速に走ることにより回避可能」という奇怪な持論を展開。そんなわけないだろう!と言ったものの、風速など条件によっては…とか思うんですが取りあえず今日は ERROR_DS_LOCAL_ERROR(0x8007203B) についてのお話です。

【問題の概要】
以下のように、DirectoryEntry のコンストラクタ第 2 引数のユーザ名に DN を指定し、AuthenticationTypes.Secure をセットしたプログラムを実行すると ERROR_DS_LOCAL_ERROR (0x8007203B) が発生することがあります。

            DirectoryEntry dom = new DirectoryEntry(
                @"LDAP://uikou-dc1.uikoutest.microsoft.com/CN=Users,DC=uikoutest,DC=microsoft,DC=com",
                @"cn=Administrator,cn=Users,DC=uikoutest,DC=microsoft,DC=com", @"SQL4Ever!",
            AuthenticationTypes.Secure);
            System.Diagnostics.Debug.WriteLine(dom.Properties["cn"]);

【対処方法】 1. ユーザ名の指定方法は DN ではなく、ドメイン名\ユーザ名 (例 : uikoutest\administrator) 形式 あるいは UPN (administrator@uikoutest.microsoft.com) で実行する。
2. ユーザ名に DN を指定したい場合は、AuthenticationTypes.SecureSocketsLayer フラグを付与する。あるいは、AuthenticationTypes.None にする。

一般的に接続対象が Active Directory の場合は、Windows 認証が使用可能です。セキュリティ強度の観点から、接続先が AD の場合は AuthenticationTypes.Secure を指定することをお勧めしております。この場合対象が Windows が前提になるため、接続時に使用されるアカウント名の解決をする際、ドメイン\ユーザ名 形式、UPN (ユーザ名@ドメイン名) での指定方法が有効になります。一方、ADAM / AD LDS、他社製 LDAP サーバなどが接続先の場合は、Windows 認証が使えません。さらにアカウント名の解決をする際は DN を指定することによってのみ可能になります。それぞれどのような接続を想定しているかに合わせ、コードを変更頂くことを推奨致します。

(※) なお、以下の例のように接続先として特定のサーバ名を指定している場合は、ADS_SERVER_BIND = AuthenticationTypes.ServerBind を指定してあげてください。

-- まとめ

接続対象 おすすめの認証 ユーザ名形式
Active Directory Windows 認証など ドメイン\ユーザ名 形式、UPN (ユーザ名@ドメイン名)
その他 LDAP サーバ (ADAM/ADLDS含む) LDAP 基本認証など(※) DN

(※) ただしこの場合プレーンテキストでデータが流れてしまうので、AuthenticationTypes.SecureSocketsLayer も指定することを推奨いたします。

【検証コード例】
以下の例では、uikou-dc1.uikoutest.extest.microsoft.com に接続します。
C# Windows アプリケーションのプロジェクトに Button コントロールを二つ配置し、Click イベントを作成しその中にコードを書いています。
Button コントロールは貼りつけ時の既定のままの名前にしています。

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.DirectoryServices;

namespace WindowsFormsApplication6
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            DirectoryEntry dom = new DirectoryEntry(
                @"LDAP://uikou-dc1.uikoutest.extest.microsoft.com/CN=Users,DC=uikoutest,DC=extest,DC=microsoft,DC=com",
                @"cn=Administrator,cn=Users,DC=uikoutest,DC=extest,DC=microsoft,DC=com", @"SQL4Ever!",
            AuthenticationTypes.Secure | AuthenticationTypes.ServerBind); // エラー発生
            System.Diagnostics.Debug.WriteLine(dom.Properties["cn"]);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            DirectoryEntry dom = new DirectoryEntry(
             @"LDAP://uikou-dc1.uikoutest.extest.microsoft.com/CN=Users,DC=uikoutest,DC=extest,DC=microsoft,DC=com",
            @"uikoutest\administrator", @"SQL4Ever!",
           AuthenticationTypes.Secure | AuthenticationTypes.ServerBind); 

            System.Diagnostics.Debug.WriteLine(dom.Properties["cn"]);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            DirectoryEntry dom = new DirectoryEntry(
                @"LDAP://uikou-dc1.uikoutest.extest.microsoft.com/CN=Users,DC=uikoutest,DC=extest,DC=microsoft,DC=com",
                @"cn=Administrator,cn=Users,DC=uikoutest,DC=extest,DC=microsoft,DC=com", @"SQL4Ever!",
            AuthenticationTypes.None | AuthenticationTypes.ServerBind); // LDAP 基本認証。DN 形式でアカウント名の解決が可能
            System.Diagnostics.Debug.WriteLine(dom.Properties["cn"]);
        }
    }
}

なお、上記をビルドする前、Visual Studio の参照設定で System.DirectoryEntry の設定を追加してください。参照設定に System.DirectoryEntry が無いとビルド時にエラーが発生します。

image

“エラー 型または名前空間名 'DirectoryServices' は名前空間 'System' に存在しません。アセンブリ参照が不足しています。”

1. Visual Studio のソリューション エクスプローラの [参照設定] を右クリック(1)し、[参照の追加(R)…] を選択します(2)。

image

2. [参照の追加] ダイアログが開くので、”.NET” タブを選択し、System.DirectoryServices を選択し、[OK] ボタンを押します。

image

3. 以下のように追加されていれば OK です。

image

下記の例では、Button1 はエラー、それ以外は正常に動作し、[出力] ウインドウに型が表示されます。

Button1 を押した場合

image

※例外の詳細

System.DirectoryServices.DirectoryServicesCOMException はハンドルされませんでした。

Message="ローカル エラーが発生しました。\r\n"

Source="System.DirectoryServices"

ErrorCode=-2147016645

ExtendedError=-2146893052

ExtendedErrorMessage=""

StackTrace:

場所 System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)

場所 System.DirectoryServices.DirectoryEntry.Bind()

場所 System.DirectoryServices.DirectoryEntry.get_AdsObject()

場所 System.DirectoryServices.PropertyValueCollection.PopulateList()

場所 System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName)

場所 System.DirectoryServices.PropertyCollection.get_Item(String propertyName)

場所 WindowsFormsApplication6.Form1.button1_Click(Object sender, EventArgs e) 場所 C:\temp\WindowsFormsApplication6\WindowsFormsApplication6\Form1.cs:行 26

場所 System.Windows.Forms.Control.OnClick(EventArgs e)

場所 System.Windows.Forms.Button.OnClick(EventArgs e)

場所 System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)

場所 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)

場所 System.Windows.Forms.Control.WndProc(Message& m)

場所 System.Windows.Forms.ButtonBase.WndProc(Message& m)

場所 System.Windows.Forms.Button.WndProc(Message& m)

場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)

場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)

場所 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

場所 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)

場所 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)

場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)

場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)

場所 System.Windows.Forms.Application.Run(Form mainForm)

場所 WindowsFormsApplication6.Program.Main() 場所 C:\temp\WindowsFormsApplication6\WindowsFormsApplication6\Program.cs:行 18

場所 System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)

場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)

場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)

場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

場所 System.Threading.ThreadHelper.ThreadStart()

InnerException:

Button 2 および 3 を押した場合

例外は発生せず、以下のように、System.DirectoryServices.PropertyValueCollection と出力されます。

image

【参考資料】

IADsOpenDSObject::OpenDSObject Method

https://msdn.microsoft.com/en-us/library/aa706065(VS.85).aspx

※ 上記ドキュメントからの引用

Distinguished Name, such as "CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=Com". To use a DN, the lnReserved parameter must be zero or it must include the ADS_USE_SSL flag

.NET Framework System.DirectoryServices 名前空間は、実際にはアンマネージの ADSI の層に多くの処理をリダイレクトしています。そのため、ADSI のドキュメントの内容も併用頂くことが非常に有用です。意外に System.DirectoryServices 名前空間のドキュメントに書いていない動作などについて ADSI のドキュメントを見ることで腑に落ちることもしばしばです。

*****

おまけ : ねこ

うちのチームの「お父さん」のところの猫です。メインクーンという種類で、成猫になるまで 1 年半くらいかかるとか。とにかくでかい…まだ生後一年たたないのに、もう襟巻にできるしまつ。写真の女の子はお父さんの娘さん(小学校五年) です。

image

なんとなくブログに彩りを加えてみようと思い、載せてみました。

それではまた。

ういこう@B型同盟って未完だったの!?