本サンプルでは、ドメインルートのディレクトリオブジェクトにバインドし、次のポリシーの値を取得してみます。
- パスワードの長さ
- パスワードの変更禁止期間
- パスワードの有効期間
- パスワードの履歴を記録する
- パスワードは複雑さの要件を満たす必要がある
- 暗号化を元に戻せる状態でパスワードを保存する
- アカウントのロックアウトの閾値
- ロックアウトカウンタのリセット
- ロックアウト期間
動作環境
- 動作環境 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