本サンプルでは、ドメインルートのディレクトリオブジェクトにバインドし、次のポリシーの値を取得してみます。

  • パスワードの長さ
  • パスワードの変更禁止期間
  • パスワードの有効期間
  • パスワードの履歴を記録する
  • パスワードは複雑さの要件を満たす必要がある
  • 暗号化を元に戻せる状態でパスワードを保存する
  • アカウントのロックアウトの閾値
  • ロックアウトカウンタのリセット
  • ロックアウト期間

動作環境

  • 動作環境 Windows 2003 (ドメイン環境)
  • 開発環境 Visual Studio 2008 Professional
  • .NET 3.5 (.NET 2.0 以上で動作します)

参考のURLです。

1 パスワードポリシーを持つディレクトリオブジェクト

パスワードのポリシーはドメインレベルのポリシーです。パスワードのポリシーを取得するには、スキーマクラスが domainDNS  (objectClass=domainDNS)  のディレクトリオブジェクトから取得します。このディレクトリオブジェクトのパスは RootDSE ディレクトリオブジェクトのdefaultNamingContext  属性値から得られます。つまり、ドメインのルートオブジェクトから取得できます。

2. パスワードの属性値を取得する

サンプルではルートオブジェクトにバインドし、パスワードのポリシーを取得します。プログラムでは次の属性値を取得します。

  • パスワードの長さ
  • パスワードの変更禁止期間
  • パスワードの有効期間
  • パスワードの履歴を記録する
  • アカウントのロックアウトの閾値
  • ロックアウトカウンタのリセット
  • ロックアウト期間
/// <summary>
/// パスワードの属性値を取得する
/// </summary>
public static void RetrievePasswordAttribute()
{
    using (DirectoryEntry rootDSE = GetDirectoryEntry("RootDSE"))
    {
       // ドメインルートオブジェクトにパスワードのプロパティは格納されている
        // ので、回りくどいことをしなくても、LDAP://crm1.localのようにドメイン
        // のルートパスをを指定すれば取得できます。
        string domainRoot = rootDSE.Properties["defaultNamingContext"].Value as string;
       using (DirectoryEntry rootEntry = GetDirectoryEntry(domainRoot))
       {
            string[] policyAttributes = new string[]{
                "maxPwdAge","minPwdAge", "minPwdLength",
                "lockoutDuration", "lockOutObservationWindow",
                "lockoutThreshold", "pwdHistoryLength"
            };
            // rootEntryはobjectClassがdomainDNSのディレクトリオブジェクト
              DirectorySearcher searcher = new DirectorySearcher(rootEntry, null, policyAttributes, SearchScope.Base);

            SearchResult result = searcher.FindOne();

            if (result != null)
            {
                // パスワードの長さ
                  if (result.Properties.Contains("minPwdLength"))
                {
                    Console.WriteLine("パスワードの長さ:" + result.Properties["minPwdLength"][0] + "文字以上");
                }
                // パスワードの変更禁止期間
                  if (result.Properties.Contains("minPwdAge"))
                {
                   // 64bit 整数型はLDAPでは負数として格納されている
                      long tick = (long)result.Properties["minPwdAge"][0];
                   if (tick == long.MinValue)
                   {
                       tick = 0L;  // 無期限
                       }
                    else
                    {
                       tick = Math.Abs(tick);
                    }
                    Console.WriteLine("パスワードの変更禁止期間:" + new TimeSpan(tick).TotalDays + "日");
                }
                // パスワードの有効期間
                  if (result.Properties.Contains("maxPwdAge"))
                {
                    // 64bit 整数型はLDAPでは負数として格納されている
                       long tick = (long)result.Properties["maxPwdAge"][0];
                    if (tick == long.MinValue)
                    {
                        tick = 0L;  // 無期限
                        }
                    else
                    {
                        tick = Math.Abs(tick);
                    }
                    Console.WriteLine("パスワードの有効期間:" + new TimeSpan(tick).TotalDays + "日");
                }
                // パスワードの履歴を記録する
                   if (result.Properties.Contains("pwdHistoryLength"))
                {
                    Console.WriteLine("パスワードの履歴を記録する:" + result.Properties["pwdHistoryLength"][0] + "回");
                }
                // パスワードは複雑さの要件を満たす必要がある
                   // 暗号化を元に戻せる状態でパスワードを保存する
                   // 上記2設定は pwdProperties 属性に格納されている
                   // アカウントのロックアウトの閾値
                  if (result.Properties.Contains("lockoutThreshold"))
                {
                    Console.WriteLine("アカウントのロックアウトの閾値:" + result.Properties["lockoutThreshold"][0] + "回ログオンに失敗");
                }
                // ロックアウトカウンタのリセット
                  if (result.Properties.Contains("lockOutObservationWindow"))
                {
                     long tick = (long)result.Properties["lockOutObservationWindow"][0];
                     if (tick == long.MinValue)
                     {
                         tick = 0L;  // 無期限
                         }
                      else
                      {
                         tick = Math.Abs(tick);
                      }
                      Console.WriteLine("ロックアウトカウンタのリセット:" + new TimeSpan(tick).TotalMinutes + "分後");
                  }
                  // ロックアウト期間
                     if (result.Properties.Contains("lockoutDuration"))
                  {
                      long tick = (long)result.Properties["lockoutDuration"][0];
                      if (tick == long.MinValue)
                      {
                          tick = 0L;  // 無期限
                          }
                       else
                      {
                          tick = Math.Abs(tick);
                      }
                      Console.WriteLine("ロックアウト期間:" + new TimeSpan(tick).TotalMinutes + "分");
                  }
             }
        }
    }
}
private static DirectoryEntry GetDirectoryEntry(string path)
{
    path = "LDAP://" + path;
    return new DirectoryEntry(path, null, null, AuthenticationTypes.Secure);
}

 

ディレクトリの属性値について
パスワードのポリシーを未定義にしても、パスワードポリシーのプロパティ値がとれます。ポリシーが未定義とは、定義しないというのではなく、現在のポリシーを上書きしないという意味。パスワードに関するポリシーはドメインレベルでのみ定義できます。そのため、ポリシーを定義してから未定義にすると、定義時の値が有効になります。

3. パスワードのポリシーを取得する

ルートディレクトリオブジェクトの pwdProperties 属性値からパスワードのプロパティを取得し、次のパスワードのポリシーを取得するサンプルを掲載します。

  • パスワードは複雑さの要件を満たす必要がある
  • 暗号化を元に戻せる状態でパスワードを保存する
/// <summary>
/// プロパティの説明は以下のリンク参照
/// DOMAIN_PASSWORD_INFORMATION Structure
/// http://msdn.microsoft.com/en-us/library/aa375371(VS.85).aspx
/// </summary>
[Flags]
public enum PasswordProperty
{
    DOMAIN_PASSWORD_COMPLEX = 1,
    DOMAIN_PASSWORD_NO_ANON_CHANGE = 2,
    DOMAIN_PASSWORD_NO_CLEAR_CHANGE = 4,
    DOMAIN_LOCKOUT_ADMINS = 8,
    DOMAIN_PASSWORD_STORE_CLEARTEXT = 16,
    DOMAIN_REFUSE_PASSWORD_CHANGE = 32
}
public static void RetrievePasswordProperties()
{
    using (DirectoryEntry rootDSE = GetDirectoryEntry("RootDSE"))
    {
        // ドメインルートオブジェクトにパスワードのプロパティは格納されている
         // ので、回りくどいことをしなくても、LDAP://crm1.localのようにドメイン
         // のルートパスをを指定すれば取得できます。
         string domainRoot = rootDSE.Properties["defaultNamingContext"].Value as string;
        using (DirectoryEntry rootEntry = GetDirectoryEntry(domainRoot))
        {
            string[] passwordProperties = new string[] { "pwdProperties" };
            // rootEntryはobjectClassがdomainDNSのディレクトリオブジェクト
              DirectorySearcher searcher = new DirectorySearcher(rootEntry, null, passwordProperties, SearchScope.Base);

            SearchResult result = searcher.FindOne();

            if (result != null)
            {
                if (result.Properties.Contains("pwdProperties"))
                {
                    PasswordProperty property = (PasswordProperty) result.Properties["pwdProperties"][0];
                    Console.WriteLine(property);
                    if ((property & PasswordProperty.DOMAIN_PASSWORD_COMPLEX) == PasswordProperty.DOMAIN_PASSWORD_COMPLEX)
                    {
                        Console.WriteLine("パスワードは、複雑さの要件を満たす必要がある:有効");
                    }
                    else
                    {
                        Console.WriteLine("パスワードは、複雑さの要件を満たす必要がある:無効");
                    }
                    if ((property & PasswordProperty.DOMAIN_PASSWORD_STORE_CLEARTEXT) == PasswordProperty.DOMAIN_PASSWORD_STORE_CLEARTEXT)
                    {
                        Console.WriteLine("暗号化を元に戻せる状態でパスワードを保存する:有効");
                    }
                    else
                    {
                        Console.WriteLine("暗号化を元に戻せる状態でパスワードを保存する:無効");
                    }

                }
            }
        }
    }
}

ADSIを使用するサンプルはスクリプトですが、次のサイトが参考になります。

スクリプト一覧 : Active Directory
http://www.microsoft.com/japan/technet/scriptcenter/scripts/ad/default.mspx