ADユーザのパスワードの変更許可、拒否を設定するサンプルを掲載します。ローカル環境のユーザの場合、WinNTプロバイダを使用して、パスワードの変更を許可されているか拒否されているかを参照し、設定することができます。

AD環境のユーザではLDAPプロバイダを使用してWinNTプロバイダで使用した場合のプロパティにアクセスしてもパスワード変更の許可、拒否設定を参照したり設定することはできません。

ローカル ユーザーのパスワードが変更されないようにするにはどうすればよいでしょうか。
http://www.microsoft.com/japan/technet/scriptcenter/resources/qanda/dec04/hey1202.mspx

AD環境のユーザの場合は、LDAPプロバイダを使用してもWinNTプロバイダと同じように処理することはできません。 userAccountControl にアクセスしてもパスワードの変更許可に関しては有効な値が設定されていないためです。AD環境の場合は、ActiveDirectoryAccessRuleを使用する必要があります。

動作確認

  • 動作環境:WIndows Server 2003 AD 環境で実施。実行ユーザはDomain Admin権限
  • 開発環境:Visual Studio 2008 Professional
  • .NET 3.5

AD内で単純にパスワードの変更を拒否するコードサンプルを知りたい場合は、次のMSDNのリンク先が参考になります。リンク先にはCOM相互運用機能を利用した場合のサンプルも掲載されています。

ユーザー パスワードの管理
http://msdn.microsoft.com/ja-jp/library/ms180915.aspx

1. ADユーザのパスワード変更を許可、拒否する

Visual Studio を起動して.NET 3.5 用のコンソールプロジェクトを作成します。今回のサンプルは.NET 2.0 以上で動作しますが、サンプルソースでLINQ を使用しているため、ターゲットを.NET 3.5 にしています。

次に、ソリューションエクスプローラでプロジェクトを右クリックし参照の追加を行います。System.DirectoryServices.dll の参照を追加します。

あとは、プロジェクトを右クリック→追加→クラス を選択してChangePasswordAccessControlクラスを作成し、次のサンプルのようにコードを記述します。

注意点は、パスワードの変更を許可する、拒否するを設定するには、ActiveDirectorySecurityのActiveDirectoryAccessRule において、セキュリティ識別子が Everyone と Self のオブジェクトタイプがChangePassword (AB721A53-1E2F-11D0-9819-00AA0040529B)となっているアクセス制御エントリを変更する必要があることです。つまり、2つのアクセス制御エントリを修正する必要があります。

参考になりそうなリンクもソースのChangePasswordAccessControlクラスのコメントに記載しておきました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
using System.Security.Principal;
using System.Security.AccessControl;

namespace CannotChangePassword
{
    /// <summary>
    /// COM相互運用機能と、DirectoryService を使用した場合のパスワードの変更を拒否するサンプル
    /// が掲載されています。
    /// ユーザー パスワードの管理
    /// http://msdn.microsoft.com/ja-jp/library/ms180915.aspx
    /// 
    /// パスワードの変更を許可、拒否する場合はntSecurityDescriptorのObjectTypeが
    /// {AB721A53-1E2F-11D0-9819-00AA0040529B}のActiveDirectoryAccessRuleを変更
    /// します。
    /// 
    /// そのほかの情報
    /// Modifying User Cannot Change Password (LDAP Provider)
    /// http://msdn.microsoft.com/en-us/library/aa746398.aspx
    /// 
    /// UserAccountControl フラグを使用してユーザー アカウント プロパティを操作する方法
    /// http://support.microsoft.com/kb/305144/ja
    /// </summary>
    class ChangePasswordAccessControl
    {
        // パスワードの変更を許可,拒否する
        static Guid changePasswordGuid = new Guid("AB721A53-1E2F-11D0-9819-00AA0040529B");

        // Everyoneのセキュリティ識別子.
        static SecurityIdentifier everyoneSid =
            new SecurityIdentifier(WellKnownSidType.WorldSid, null);

        // NT AUTHORITY\SELFのセキュリティ識別子.
        static SecurityIdentifier selfSid =
            new SecurityIdentifier(WellKnownSidType.SelfSid, null);

        ActiveDirectoryAccessRule allowEveryone = new ActiveDirectoryAccessRule(everyoneSid,
             ActiveDirectoryRights.ExtendedRight, AccessControlType.Allow, changePasswordGuid);
        ActiveDirectoryAccessRule denyEveryone = new ActiveDirectoryAccessRule(everyoneSid,
            ActiveDirectoryRights.ExtendedRight, AccessControlType.Deny, changePasswordGuid);

        ActiveDirectoryAccessRule allowSelf = new ActiveDirectoryAccessRule(selfSid,
            ActiveDirectoryRights.ExtendedRight, AccessControlType.Allow, changePasswordGuid);
        ActiveDirectoryAccessRule denySelf = new ActiveDirectoryAccessRule(selfSid,
            ActiveDirectoryRights.ExtendedRight, AccessControlType.Deny, changePasswordGuid);

        public void DenyChangePassword()
        {
            string path = "CN=ChangePasswordUser,CN=Users,DC=crm2,DC=local";
            SetCannotChangePassword(path, false);
        }
        public void AllowChangePassword()
        {
            string path = "CN=ChangePasswordUser,CN=Users,DC=crm2,DC=local";
            SetCannotChangePassword(path, true);
        }
        private void SetCannotChangePassword(string path, bool allowChangePassword)
        {
            using (DirectoryEntry user = GetDirectoryEntry(path))
            {
                ActiveDirectorySecurity ads = user.ObjectSecurity;

                AuthorizationRuleCollection accessRules = ads.GetAccessRules(true, false, typeof(NTAccount));
                var query = from rule in accessRules.OfType<ActiveDirectoryAccessRule>()
                            where rule.ObjectType == changePasswordGuid
                            select rule;

                foreach (ActiveDirectoryAccessRule rule in query)
                {
                    bool modified = false;
                    if (rule.IdentityReference.Value == @"NT AUTHORITY\SELF")
                    {
                        if (allowChangePassword && rule.AccessControlType == AccessControlType.Deny)
                        {
                            ads.ModifyAccessRule(AccessControlModification.Reset, allowSelf, out modified);
                        }
                        else if (!allowChangePassword && rule.AccessControlType == AccessControlType.Allow)
                        {
                            ads.ModifyAccessRule(AccessControlModification.Reset, denySelf, out modified);
                        }
                    }
                    else if (rule.IdentityReference.Value == @"Everyone")
                    {
                        if (allowChangePassword && rule.AccessControlType == AccessControlType.Deny)
                        {
                            ads.ModifyAccessRule(AccessControlModification.Reset, allowEveryone, out modified);
                        }
                        else if (!allowChangePassword && rule.AccessControlType == AccessControlType.Allow)
                        {
                            ads.ModifyAccessRule(AccessControlModification.Reset, denyEveryone, out modified);
                        }

                    }
                }
                user.CommitChanges();
            }
        }
        private DirectoryEntry GetDirectoryEntry(string path)
        {
            path = "LDAP://" + path;
            DirectoryEntry entry = new DirectoryEntry(path, null, null, AuthenticationTypes.Secure);
            object o = entry.NativeObject;

            return entry;
        }
    }
}

ソースのDenyChangePassword()メソッドを呼び出すとユーザChangePasswordUserがパスワードを変更することが拒否され、AllowChangePassword()メソッドを使用するとユーザChangePasswordUserがパスワードを変更することを許可されるように設定されます。

2. まとめ

説明は以上です。間違い、指摘点などがあればご連絡ください。