ADMembershipProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / xsp / System / Web / Security / ADMembershipProvider.cs / 2 / ADMembershipProvider.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.Security 
{ 
    using  System.Net;
    using  System.Web; 
    using  System.Text;
    using  System.Text.RegularExpressions;
    using  System.Security;
    using  System.Collections; 
    using  System.Globalization;
    using  System.Configuration; 
    using  System.DirectoryServices; 
    using  System.DirectoryServices.ActiveDirectory;
    using  System.DirectoryServices.Protocols; 
    using  System.Web.Hosting;
    using  System.Security.Cryptography;
    using  System.Web.Configuration;
    using  System.Security.Permissions; 
    using  System.Collections.Specialized;
    using  System.Runtime.InteropServices; 
    using  System.Security.Principal; 
    using  System.Web.DataAccess;
    using  System.Web.Util; 
    using  System.Reflection;
    using  System.Configuration.Provider;
    using  System.Web.Management;
 
    public enum ActiveDirectoryConnectionProtection
    { 
        None		= 0, 
        Ssl			= 1,
        SignAndSeal	= 2 
    }

    internal enum DirectoryType
    { 
        AD = 0,
        ADAM = 1, 
        Unknown = 2 
    }
 
    internal enum CredentialsType
    {
        Windows = 0,
        NonWindows = 1 
    }
 
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)] 
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [DirectoryServicesPermission(SecurityAction.LinkDemand, Unrestricted=true)] 
    [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
    public class ActiveDirectoryMembershipProvider : MembershipProvider
    {
 
        //
        // keeps track of whether the provider has already been initialized 
        // 
        private bool initialized = false;
 
        //
        // configuration parameters common to all membership providers
        //
 
        private string  adConnectionString;
        private bool enablePasswordRetrieval = false; 
        private bool enablePasswordReset; 
        private bool enableSearchMethods;
        private bool requiresQuestionAndAnswer; 
        private string appName;
        private bool requiresUniqueEmail;
        private int maxInvalidPasswordAttempts;
        private int passwordAttemptWindow; 
        private int passwordAnswerAttemptLockoutDuration;
        private int minRequiredPasswordLength; 
        private int minRequiredNonalphanumericCharacters; 
        private string passwordStrengthRegularExpression;
 
        //
        // configuration parameters specific to the AD membership provider
        // and related to the directory connection are stored within the DirectoryInformation class
        // 
        DirectoryInformation directoryInfo = null;
 
        // 
        // custom schema mappings (and their default values)
        // 
        private string attributeMapUsername = "userPrincipalName";
        private string attributeMapEmail = "mail";
        private string attributeMapPasswordQuestion = null;
        private string attributeMapPasswordAnswer = null; 
        private string attributeMapFailedPasswordAnswerCount = null;
        private string attributeMapFailedPasswordAnswerTime= null; 
        private string attributeMapFailedPasswordAnswerLockoutTime = null; 

        // 
        // maximum lengths for the different string properties
        //
        private int maxUsernameLength = 256;
        private int maxUsernameLengthForCreation = 64; 
        private int maxPasswordLength = 128;
        private int maxCommentLength = 1024; 
        private int maxEmailLength = 256; 
        private int maxPasswordQuestionLength = 256;
        private int maxPasswordAnswerLength = 128; 

        //
        // user account flags
        // 
        private const int UF_ACCOUNT_DISABLED =0x2;
        private const int UF_LOCKOUT=0x10; 
        private readonly DateTime DefaultLastLockoutDate = new DateTime(1754, 1, 1, 0, 0, 0, DateTimeKind.Utc); 
        private const int AD_SALT_SIZE_IN_BYTES = 16;
 
        //
        // table containing the valid syntaxes for various attribute mappings
        //
        Hashtable syntaxes = new Hashtable(); 
        Hashtable attributesInUse = new Hashtable(StringComparer.OrdinalIgnoreCase);
        Hashtable userObjectAttributes = null; 
 
        //
        // auth type to be used for validation 
        //
        AuthType authTypeForValidation;
        LdapConnection connection;
        bool usernameIsSAMAccountName = false; 
        bool usernameIsUPN = true;
 
        // 
        // password size for autogenerating password
        // 
        private const int PASSWORD_SIZE      = 14;

        public override string ApplicationName
        { 
            get
            { 
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return appName;
            }
            set
            { 
                throw new NotSupportedException(SR.GetString(SR.ADMembership_Setting_ApplicationName_not_supported));
            } 
        } 

        public ActiveDirectoryConnectionProtection CurrentConnectionProtection 
        {
            get
            {
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return directoryInfo.ConnectionProtection; 
            }
        } 

        public override MembershipPasswordFormat PasswordFormat
        {
            get 
            {
                // 
                // AD membership provider does not support password retrieval 
                // (regardless of the settings). As a result the provider operates as
                // if the password was effectively hashed. 
                //
                return MembershipPasswordFormat.Hashed;
            }
        } 

        public override bool  EnablePasswordRetrieval 
        { 
            get
            { 
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

                return enablePasswordRetrieval; 
             }
        } 
 
        public override bool  EnablePasswordReset
        { 
            get
            {
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

                return enablePasswordReset; 
            } 
        }
 
        public bool  EnableSearchMethods
        {
            get
            { 
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 
 
                return enableSearchMethods;
            } 
        }

        public override bool  RequiresQuestionAndAnswer
        { 
            get
            { 
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return requiresQuestionAndAnswer;
            }
        }
 
        public override bool  RequiresUniqueEmail
        { 
            get 
            {
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

                return requiresUniqueEmail;
            } 
        }
 
        public override int MaxInvalidPasswordAttempts 
        {
            get 
            {
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return maxInvalidPasswordAttempts;
            } 
        } 

        public override int PasswordAttemptWindow 
        {
            get
            {
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return passwordAttemptWindow; 
            }
        } 

        public int PasswordAnswerAttemptLockoutDuration
        {
            get 
            {
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

                return passwordAnswerAttemptLockoutDuration; 
            }
        }

        public override int MinRequiredPasswordLength 
        {
            get 
            { 
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

                return minRequiredPasswordLength;
            }
        } 

        public override int MinRequiredNonAlphanumericCharacters 
        { 
            get
            { 
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

                return minRequiredNonalphanumericCharacters; 
            }
        } 
 
        public override string PasswordStrengthRegularExpression
        { 
            get
            {
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

                return passwordStrengthRegularExpression; 
            } 
        }
 
        //
        // NOTE: In every method of the provider we need to demand DirectoryServicesPermission (irrespective of
        //           whether the underlying calls to S.DS/S.DS.Protocols result in full demand or link demand for that permission.
        //           Moreover, once we demand the permission, we should also assert it so that S.DS/S.DS.Protocols does not make the 
        //           same demand (if we do not assert then in the case of S.DS/S.DS.Protocols making a full demand we would have two stack walks)
        // 
        #pragma warning disable 0688 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override void Initialize(string name, NameValueCollection config)
        {
            if (System.Web.Hosting.HostingEnvironment.IsHosted) 
                HttpRuntime.CheckAspNetHostingPermission (AspNetHostingPermissionLevel.Low, SR.Feature_not_supported_at_this_level);
 
            if (initialized) 
                return;
 
            if (config == null)
                throw new ArgumentNullException("config");

            if (String.IsNullOrEmpty(name)) 
                name = "AspNetActiveDirectoryMembershipProvider";
 
            if (string.IsNullOrEmpty(config["description"])) 
            {
                config.Remove("description"); 
                config.Add("description", SR.GetString(SR.ADMembership_Description));
            }

            base.Initialize(name, config); 

            appName = config["applicationName"]; 
 
            if (string.IsNullOrEmpty(appName))
                appName = SecUtility.GetDefaultAppName(); 

            if( appName.Length > 256 )
                throw new ProviderException(SR.GetString(SR.Provider_application_name_too_long));
 
            string temp = config["connectionStringName"];
            if (String.IsNullOrEmpty(temp)) 
                throw new ProviderException(SR.GetString(SR.Connection_name_not_specified)); 

            adConnectionString = GetConnectionString(temp, true); 
            if (String.IsNullOrEmpty(adConnectionString))
                throw new ProviderException(SR.GetString(SR.Connection_string_not_found, temp));

            // 
            // Get the provider specific configuration settings
            // 
 
            // connectionProtection
            string connProtection = config["connectionProtection"]; 
            if (connProtection == null)
                connProtection = "Secure";
            else
            { 
                if ((String.Compare(connProtection, "Secure", StringComparison.Ordinal) != 0) &&
                    (String.Compare(connProtection, "None", StringComparison.Ordinal) != 0)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_InvalidConnectionProtection, connProtection)); 
            }
 
            //
            // credentials
            // username and password if specified must not be empty, moreover if one is specified the other must
            // be specified as well 
            //
            string username = config["connectionUsername"]; 
            if (username != null && username.Length == 0) 
                throw new ProviderException(SR.GetString(SR.ADMembership_Connection_username_must_not_be_empty));
 
            string password = config["connectionPassword"];
            if (password != null && password.Length == 0)
                throw new ProviderException(SR.GetString(SR.ADMembership_Connection_password_must_not_be_empty));
 
            if ((username != null && password == null) || (password != null && username == null))
                throw new ProviderException(SR.GetString(SR.ADMembership_Username_and_password_reqd)); 
 
            NetworkCredential credential = new NetworkCredential(username, password);
 
            int clientSearchTimeout = SecUtility.GetIntValue(config, "clientSearchTimeout", -1, false, 0);
            int serverSearchTimeout = SecUtility.GetIntValue(config, "serverSearchTimeout", -1, false, 0);

            enableSearchMethods = SecUtility.GetBooleanValue(config, "enableSearchMethods", false); 
            requiresUniqueEmail = SecUtility.GetBooleanValue(config, "requiresUniqueEmail", false);
            enablePasswordReset = SecUtility.GetBooleanValue(config, "enablePasswordReset", false); 
            requiresQuestionAndAnswer = SecUtility.GetBooleanValue(config, "requiresQuestionAndAnswer", false); 
            minRequiredPasswordLength = SecUtility.GetIntValue( config, "minRequiredPasswordLength", 7, false, 128 );
            minRequiredNonalphanumericCharacters = SecUtility.GetIntValue( config, "minRequiredNonalphanumericCharacters", 1, true, 128 ); 

            passwordStrengthRegularExpression = config["passwordStrengthRegularExpression"];
            if( passwordStrengthRegularExpression != null )
            { 
                passwordStrengthRegularExpression = passwordStrengthRegularExpression.Trim();
                if( passwordStrengthRegularExpression.Length != 0 ) 
                { 
                    try
                    { 
                        Regex regex = new Regex( passwordStrengthRegularExpression );
                    }
                    catch( ArgumentException e )
                    { 
                        throw new ProviderException( e.Message, e );
                    } 
                } 
            }
            else 
            {
                passwordStrengthRegularExpression = string.Empty;
            }
            if (minRequiredNonalphanumericCharacters > minRequiredPasswordLength) 
                throw new HttpException(SR.GetString(SR.MinRequiredNonalphanumericCharacters_can_not_be_more_than_MinRequiredPasswordLength));
 
 
            using (new ApplicationImpersonationContext())
            { 
                //
                //  This will make some checks regarding whether the connectionProtection is valid (choose the right
                //  connectionprotection if necessary, make sure credentials are valid, container exists and the directory is
                //  either AD or ADAM) 
                //
                directoryInfo = new DirectoryInformation(adConnectionString, credential, connProtection, clientSearchTimeout, serverSearchTimeout, enablePasswordReset); 
 
                //
                // initialize the syntaxes table 
                //
                syntaxes.Add("attributeMapUsername", "DirectoryString");
                syntaxes.Add("attributeMapEmail", "DirectoryString");
                syntaxes.Add("attributeMapPasswordQuestion", "DirectoryString"); 
                syntaxes.Add("attributeMapPasswordAnswer", "DirectoryString");
                syntaxes.Add("attributeMapFailedPasswordAnswerCount", "Integer"); 
                syntaxes.Add("attributeMapFailedPasswordAnswerTime", "Integer8"); 
                syntaxes.Add("attributeMapFailedPasswordAnswerLockoutTime", "Integer8");
 
                //
                // initialize the in use attributes list
                //
                attributesInUse.Add("objectclass", null); 
                attributesInUse.Add("objectsid", null);
                attributesInUse.Add("comment", null); 
                attributesInUse.Add("whencreated", null); 
                attributesInUse.Add("pwdlastset", null);
                attributesInUse.Add("msds-user-account-control-computed", null); 
                attributesInUse.Add("lockouttime", null);
                if (directoryInfo.DirectoryType == DirectoryType.AD)
                    attributesInUse.Add("useraccountcontrol", null);
                else 
                    attributesInUse.Add("msds-useraccountdisabled", null);
 
                // 
                // initialize the user attributes list
                // 
                userObjectAttributes = GetUserObjectAttributes();

                //
                // get the username/email schema mappings 
                //
                int maxLength; 
                string attrMapping = GetAttributeMapping(config, "attributeMapUsername", out maxLength); 
                if (attrMapping != null)
                { 
                    attributeMapUsername = attrMapping;
                    if (maxLength != -1)
                    {
                        if (maxLength < maxUsernameLength) 
                            maxUsernameLength = maxLength;
                        if (maxLength < maxUsernameLengthForCreation) 
                            maxUsernameLengthForCreation = maxLength; 
                    }
                } 
                attributesInUse.Add(attributeMapUsername, null);
                if (StringUtil.EqualsIgnoreCase(attributeMapUsername, "sAMAccountName"))
                {
                    usernameIsSAMAccountName = true; 
                    usernameIsUPN = false;
                } 
 
                attrMapping = GetAttributeMapping(config, "attributeMapEmail", out maxLength);
                if (attrMapping != null) 
                {
                    attributeMapEmail = attrMapping;
                    if (maxLength != -1 && maxLength < maxEmailLength)
                        maxEmailLength = maxLength; 
                }
                attributesInUse.Add(attributeMapEmail, null); 
 
                //
                // get max length of "comment" attribute 
                //
                maxLength = GetRangeUpperForSchemaAttribute("comment");
                if (maxLength != -1 && maxLength < maxCommentLength)
                    maxCommentLength = maxLength; 

                // 
                // enablePasswordReset and requiresQuestionAndAnswer should match 
                //
                if (enablePasswordReset) 
                {
                    //
                    // AD membership provider does not support password reset without question and answer
                    // 
                    if (!requiresQuestionAndAnswer)
                        throw new ProviderException(SR.GetString(SR.ADMembership_PasswordReset_without_question_not_supported)); 
 
                    //
                    // Other password reset related attributes 
                    //
                    maxInvalidPasswordAttempts = SecUtility.GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0);
                    passwordAttemptWindow = SecUtility.GetIntValue(config, "passwordAttemptWindow", 10, false, 0);
                    passwordAnswerAttemptLockoutDuration = SecUtility.GetIntValue(config, "passwordAnswerAttemptLockoutDuration", 30, false, 0); 

                    // 
                    // some more schema mappings that must be specified for Password Reset 
                    //
                    attributeMapFailedPasswordAnswerCount = GetAttributeMapping(config, "attributeMapFailedPasswordAnswerCount", out maxLength /* ignored */); 
                    if (attributeMapFailedPasswordAnswerCount != null)
                        attributesInUse.Add(attributeMapFailedPasswordAnswerCount, null);

                    attributeMapFailedPasswordAnswerTime = GetAttributeMapping(config, "attributeMapFailedPasswordAnswerTime", out maxLength /* ignored */); 
                    if (attributeMapFailedPasswordAnswerTime != null)
                        attributesInUse.Add(attributeMapFailedPasswordAnswerTime, null); 
 
                    attributeMapFailedPasswordAnswerLockoutTime = GetAttributeMapping(config, "attributeMapFailedPasswordAnswerLockoutTime", out maxLength /* ignored */);
                    if (attributeMapFailedPasswordAnswerLockoutTime != null) 
                        attributesInUse.Add(attributeMapFailedPasswordAnswerLockoutTime, null);

                    if (attributeMapFailedPasswordAnswerCount == null || attributeMapFailedPasswordAnswerTime == null ||
                            attributeMapFailedPasswordAnswerLockoutTime == null) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_BadPasswordAnswerMappings_not_specified));
                } 
 
                //
                // Password Q&A mappings 
                //
                attributeMapPasswordQuestion = GetAttributeMapping(config, "attributeMapPasswordQuestion", out maxLength);
                if (attributeMapPasswordQuestion != null)
                { 
                    if (maxLength != -1 && maxLength < maxPasswordQuestionLength)
                        maxPasswordQuestionLength = maxLength; 
 
                    attributesInUse.Add(attributeMapPasswordQuestion, null);
                } 

                attributeMapPasswordAnswer = GetAttributeMapping(config, "attributeMapPasswordAnswer", out maxLength);
                if (attributeMapPasswordAnswer != null)
                { 
                    if (maxLength != -1 && maxLength < maxPasswordAnswerLength)
                        maxPasswordAnswerLength = maxLength; 
 
                    attributesInUse.Add(attributeMapPasswordAnswer, null);
                } 

                if (requiresQuestionAndAnswer)
                {
                    // 
                    // We also need to check that the password question and answer attributes are mapped
                    // 
                    if (attributeMapPasswordQuestion == null || attributeMapPasswordAnswer == null) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_PasswordQuestionAnswerMapping_not_specified));
                } 

                //
                // the auth type to be used for validation is determined as follows:
                // if directory is ADAM: authType = AuthType.Basic 
                // if directory is AD: authType is based on connectionProtection (None, SSL: AuthType.Basic; SignAndSeal: AuthType.Negotiate)
                // 
                if (directoryInfo.DirectoryType == DirectoryType.ADAM) 
                    authTypeForValidation = AuthType.Basic;
                else 
                    authTypeForValidation = directoryInfo.GetLdapAuthenticationTypes(directoryInfo.ConnectionProtection, CredentialsType.NonWindows);

                if (directoryInfo.DirectoryType == DirectoryType.AD)
                { 
                    //
                    // if password reset is enabled we should perform all operations on a single server 
                    // 
                    if (enablePasswordReset)
                        directoryInfo.SelectServer(); 

                    //
                    // if the username is mapped to upn we need to do  forest wide search to check the uniqueness of the upn.
                    // and if the username is mapped to samAccountName then we need to append the domain name in the username for reliable validation 
                    //
                    directoryInfo.InitializeDomainAndForestName(); 
 
                }
            } 

            //
            // Create a new common ldap connection for validation
            // 
            connection = directoryInfo.CreateNewLdapConnection(authTypeForValidation);
 
            config.Remove("name"); 
            config.Remove("applicationName");
            config.Remove("connectionStringName"); 
            config.Remove("requiresUniqueEmail");
            config.Remove("enablePasswordReset");
            config.Remove("requiresQuestionAndAnswer");
            config.Remove("attributeMapPasswordQuestion"); 
            config.Remove("attributeMapPasswordAnswer");
            config.Remove("attributeMapUsername"); 
            config.Remove("attributeMapEmail"); 
            config.Remove("connectionProtection");
            config.Remove("connectionUsername"); 
            config.Remove("connectionPassword");
            config.Remove("clientSearchTimeout");
            config.Remove("serverSearchTimeout");
            config.Remove("enableSearchMethods"); 
            config.Remove("maxInvalidPasswordAttempts");
            config.Remove("passwordAttemptWindow"); 
            config.Remove("passwordAnswerAttemptLockoutDuration"); 
            config.Remove("attributeMapFailedPasswordAnswerCount");
            config.Remove("attributeMapFailedPasswordAnswerTime"); 
            config.Remove("attributeMapFailedPasswordAnswerLockoutTime");
            config.Remove("minRequiredPasswordLength");
            config.Remove("minRequiredNonalphanumericCharacters");
            config.Remove("passwordStrengthRegularExpression"); 

            if (config.Count > 0) 
            { 
                string attribUnrecognized = config.GetKey(0);
                if (!String.IsNullOrEmpty(attribUnrecognized)) 
                    throw new ProviderException(SR.GetString(SR.Provider_unrecognized_attribute, attribUnrecognized));
            }

            initialized = true; 
        }
        #pragma warning restore 0688 
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override MembershipUser CreateUser(string username,
                                                        string password,
                                                        string email, 
                                                        string passwordQuestion,
                                                        string passwordAnswer, 
                                                        bool   isApproved, 
                                                        object providerUserKey,
                                                        out    MembershipCreateStatus status) 
        {
            status = (MembershipCreateStatus) 0;
            MembershipUser user = null;
 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 
 
            if (providerUserKey != null)
                throw new NotSupportedException(SR.GetString(SR.ADMembership_Setting_UserId_not_supported)); 

            if ((passwordQuestion != null) && (attributeMapPasswordQuestion == null))
                throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordQ_not_supported));
 
            if ((passwordAnswer != null) && (attributeMapPasswordAnswer == null))
                throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordA_not_supported)); 
 
            if(!SecUtility.ValidateParameter(ref username, true, true, true, maxUsernameLengthForCreation))
            { 
                status = MembershipCreateStatus.InvalidUserName;
                return null;
            }
 
            //
            // if username is mapped to UPN, it should not contain '\' 
            // 
            if (usernameIsUPN && (username.IndexOf('\\') != -1))
            { 
                status = MembershipCreateStatus.InvalidUserName;
                return null;
            }
 
            if(!ValidatePassword(password, maxPasswordLength))
            { 
                status = MembershipCreateStatus.InvalidPassword; 
                return null;
            } 

            if(!SecUtility.ValidateParameter(ref email, RequiresUniqueEmail, true, false, maxEmailLength))
            {
                status = MembershipCreateStatus.InvalidEmail; 
                return null;
            } 
 
            if(!SecUtility.ValidateParameter(ref passwordQuestion, RequiresQuestionAndAnswer, true, false, maxPasswordQuestionLength))
            { 
                status = MembershipCreateStatus.InvalidQuestion;
                return null;
            }
 
            // validate the parameter before encoding the password answer
            if(!SecUtility.ValidateParameter(ref passwordAnswer, RequiresQuestionAndAnswer, true, false, maxPasswordAnswerLength)) 
            { 
                status = MembershipCreateStatus.InvalidAnswer;
                return null; 
            }

            string encodedPasswordAnswer;
            if (!string.IsNullOrEmpty(passwordAnswer)) 
            {
                encodedPasswordAnswer = Encrypt(passwordAnswer); 
 
                // check length of encoded password answer
                if (maxPasswordAnswerLength > 0 && encodedPasswordAnswer.Length > maxPasswordAnswerLength) 
                {
                    status = MembershipCreateStatus.InvalidAnswer;
                    return null;
                } 
            }
            else 
                encodedPasswordAnswer = passwordAnswer; 

            if( password.Length < MinRequiredPasswordLength ) 
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            } 

            int count = 0; 
 
            for( int i = 0; i < password.Length; i++ )
            { 
                if( !char.IsLetterOrDigit( password, i ) )
                {
                    count++;
                } 
            }
 
            if( count < MinRequiredNonAlphanumericCharacters ) 
            {
                status = MembershipCreateStatus.InvalidPassword; 
                return null;
            }

            if( PasswordStrengthRegularExpression.Length > 0 ) 
            {
                if( !Regex.IsMatch( password, PasswordStrengthRegularExpression ) ) 
                { 
                    status = MembershipCreateStatus.InvalidPassword;
                    return null; 
                }
            }

            ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, password, true); 
            OnValidatingPassword(e);
 
            if(e.Cancel) 
            {
                status = MembershipCreateStatus.InvalidPassword; 
                return null;
            }

            try 
            {
                // 
                // Get the directory entry for the container and create a user object under it 
                //
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.CreationContainerDN, true /* revertImpersonation */); 

                DirectoryEntry containerEntry = null;
                DirectoryEntry userEntry = null;
 
                try
                { 
                    containerEntry = connection.DirectoryEntry; 
                    // to avoid unnecessary searches (for better performance)
                    containerEntry.AuthenticationType |= AuthenticationTypes.FastBind; 

                    //
                    // we set the username as the cn
                    // 
                    userEntry = containerEntry.Children.Add(GetEscapedRdn("CN=" + username), "user");
 
                    // 
                    // if we are talking to Active Directory
                    // set the sAMAccountName (if username is not mapped to this attribute, we need to autogenerate it) 
                    // (NOTE: We do not need to do this if the domain controller functionality is Windows 2003 (dcLevel = 2))
                    //
                    if (directoryInfo.DirectoryType == DirectoryType.AD)
                    { 
                        string sAMAccountName= null;
                        bool setSAMAccountName = false; 
 
                        if (usernameIsSAMAccountName)
                        { 
                            sAMAccountName = username;
                            setSAMAccountName = true;
                        }
                        else 
                        {
                            int dcLevel = GetDomainControllerLevel(containerEntry.Options.GetCurrentServerName()); 
 
                            if (dcLevel != 2)
                            { 
                                sAMAccountName = GenerateAccountName();
                                setSAMAccountName = true;
                            }
                        } 

                        if (setSAMAccountName) 
                            userEntry.Properties["sAMAccountName"].Value = sAMAccountName; 
                    }
 
                    //
                    // if username is mapped to userPrincipalName and we are talking to AD, we need to do
                    // a GC search to find if the same upn already exists or not
                    // On ADAM, uniqueness of userPrincipalName is enforced on the server itself across all partitions 
                    //
                    if (usernameIsUPN) 
                    { 
                        if (directoryInfo.DirectoryType == DirectoryType.AD && !IsUpnUnique(username))
                        { 
                            status = MembershipCreateStatus.DuplicateUserName;
                            return null;
                        }
 
                        userEntry.Properties["userPrincipalName"].Value = username;
                    } 
 
                    //
                    // set other attributes 
                    //
                    if (email != null)
                    {
                        if (RequiresUniqueEmail && !IsEmailUnique(containerEntry, username, email, false /* existing */)) 
                        {
                            status = MembershipCreateStatus.DuplicateEmail; 
                            return null; 
                        }
                        userEntry.Properties[attributeMapEmail].Value = email; 
                    }

                    if (passwordQuestion != null)
                        userEntry.Properties[attributeMapPasswordQuestion].Value = passwordQuestion; 

                    if (passwordAnswer != null) 
                        userEntry.Properties[attributeMapPasswordAnswer].Value = encodedPasswordAnswer; 

                    // 
                    // commit the user object
                    //
                    try
                    { 
                        userEntry.CommitChanges();
                    } 
                    catch (COMException e1) 
                    {
                        if ((e1.ErrorCode == unchecked((int) 0x80071392)) || (e1.ErrorCode == unchecked((int) 0x8007200d))) 
                        {
                            status = MembershipCreateStatus.DuplicateUserName;
                            return null;
                        } 
                        else if ((e1.ErrorCode == unchecked((int) 0x8007001f)) && (e1 is DirectoryServicesCOMException))
                        { 
                            // 
                            // this error corresponds to LDAP_OTHER
                            // if username was mapped to sAMAccountName and the name is too long 
                            // then the extended error should be 1315 (ERROR_INVALID_ACCOUNT_NAME)
                            //
                            DirectoryServicesCOMException dsce = e1 as DirectoryServicesCOMException;
                            if (dsce.ExtendedError == 1315) 
                            {
                                status = MembershipCreateStatus.InvalidUserName; 
                                return null; 
                            }
                            else 
                                throw;
                        }
                        else
                            throw; 
                    }
 
                    // 
                    // set the password
                    // 
                    try
                    {
                        SetPasswordPortIfApplicable(userEntry);
 
                        //
                        // Set the password 
                        // 
                        userEntry.Invoke("SetPassword", new object[]{ password });
 
                        //
                        // if the user is approved then we need to enable the account (disabled dy default)
                        //
                        if (isApproved) 
                        {
                            if (directoryInfo.DirectoryType ==  DirectoryType.AD) 
                            { 
                                const int UF_ACCOUNT_DISABLED =0x2;
                                const int UF_PASSWD_NOTREQD = 0x20; 

                                int val = (int)PropertyManager.GetPropertyValue(userEntry, "userAccountControl");
                                val &= ~(UF_ACCOUNT_DISABLED | UF_PASSWD_NOTREQD);
                                userEntry.Properties["userAccountControl"].Value = val; 
                            }
                            else 
                            { 
                                // ADAM case
                                userEntry.Properties["msDS-UserAccountDisabled"].Value = false; 
                            }
                            userEntry.CommitChanges();
                        }
                        else 
                        {
                            // 
                            // For ADAM the user may be created as enabled in some cases 
                            // so we need to explicitly disable it
                            // 
                            if (directoryInfo.DirectoryType ==  DirectoryType.ADAM)
                            {
                                userEntry.Properties["msDS-UserAccountDisabled"].Value = true;
                                userEntry.CommitChanges(); 
                            }
                        } 
 
                        //
                        // For ADAM users, we need to add the user to the Readers group in that 
                        // partition
                        //
                        if (directoryInfo.DirectoryType == DirectoryType.ADAM)
                        { 
                            DirectoryEntry readersEntry = new DirectoryEntry(directoryInfo.GetADsPath("CN=Readers,CN=Roles," + directoryInfo.ADAMPartitionDN), directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes);
                            readersEntry.Properties["member"].Add(PropertyManager.GetPropertyValue(userEntry, "distinguishedName")); 
                            readersEntry.CommitChanges(); 
                        }
                    } 
                    //
                    // At this point we have already created the user object in AD/ADAM but we
                    // have failed in either SetPassword or while enabling/disabling the user, so we try to delete the user object
                    // 
                    catch (COMException)
                    { 
                        containerEntry.Children.Remove(userEntry); 
                        throw;
                    } 
                    catch (ProviderException)
                    {
                        containerEntry.Children.Remove(userEntry);
                        throw; 
                    }
                    catch (TargetInvocationException tie) 
                    { 
                        containerEntry.Children.Remove(userEntry);
 
                        if (tie.InnerException is COMException)
                        {
                            COMException ce = (COMException) tie.InnerException;
                            int errorCode = ce.ErrorCode; 

                            // 
                            // if the exception is due to password not meeting complexity requirements, then return 
                            // status as InvalidPassword
                            // 
                            if ((errorCode == unchecked((int) 0x800708c5)) || (errorCode == unchecked((int) 0x8007202f)) || (errorCode == unchecked((int) 0x8007052d)) || (errorCode == unchecked((int) 0x8007052f)))
                            {
                                status = MembershipCreateStatus.InvalidPassword;
                                return null; 
                            }
                            // if the target is ADAM and the exception is due to property not found, this indicates that a secure 
                            // connection could not be setup for changing the password and ADSI is falling back to kerberos which does not work for ADAM 
                            // so we will provide a clearer exception
                            // 
                            else if ((errorCode == unchecked((int) 0x8000500d) && (directoryInfo.DirectoryType == DirectoryType.ADAM)))
                                throw new ProviderException(SR.GetString(SR.ADMembership_No_secure_conn_for_password));
                            else
                                throw; 
                        }
                        else 
                            throw; 
                    }
 
                    //
                    // Create a user object
                    //
                    DirectoryEntry dummyEntry = null; 
                    bool dummyFlag = false;
                    string dummyString; 
                    user = FindUser(userEntry, "(objectClass=*)", System.DirectoryServices.SearchScope.Base, false /*retrieveSAMAccountName */, out dummyEntry, out dummyFlag, out dummyString); 
                }
                finally 
                {
                    if (userEntry != null)
                        userEntry.Dispose();
 
                    connection.Close();
                } 
            } 
            catch
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                //
                throw; 
            }
 
            return user; 
        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override bool ChangePasswordQuestionAndAnswer(string username, 
                                                        string password,
                                                        string newPasswordQuestion, 
                                                        string newPasswordAnswer) 
        {
            if (!initialized) 
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            //
            // if there are no mappings for password question and answer, we should throw a NotSupportedException 
            //
            if ((newPasswordQuestion != null) && (attributeMapPasswordQuestion == null)) 
                throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordQ_not_supported)); 

            if ((newPasswordAnswer != null) && (attributeMapPasswordAnswer == null)) 
                throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordA_not_supported));


            CheckUserName( ref username, maxUsernameLength, "username" ); 
            CheckPassword(password, maxPasswordLength, "password");
 
            SecUtility.CheckParameter( 
                            ref newPasswordQuestion,
                            RequiresQuestionAndAnswer, 
                            true,
                            false,
                            maxPasswordQuestionLength,
                            "newPasswordQuestion" ); 

            // validate the parameter before encoding the password answer 
            CheckPasswordAnswer(ref newPasswordAnswer, RequiresQuestionAndAnswer, maxPasswordAnswerLength, "newPasswordAnswer"); 

            string encodedPasswordAnswer; 
            if (!string.IsNullOrEmpty(newPasswordAnswer))
            {
                encodedPasswordAnswer = Encrypt(newPasswordAnswer);
 
                // check length of encoded password answer
                if (maxPasswordAnswerLength > 0 && encodedPasswordAnswer.Length > maxPasswordAnswerLength) 
                    throw new ArgumentException(SR.GetString(SR.ADMembership_Parameter_too_long, "newPasswordAnswer"), "newPasswordAnswer"); 
            }
            else 
                encodedPasswordAnswer = newPasswordAnswer;

            try
            { 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry; 
                DirectoryEntry userEntry = null; 
                bool resetBadPasswordAnswerAttributes = false;
                string usernameForAuthentication = null; 

                try
                {
                    if (EnablePasswordReset) 
                    {
                        // 
                        // get the user's directory entry 
                        // NOTE: If the username is mapped to userPrincipalName and the username does not contain '@' in it, then simple bind will fail as it needs domain information.
                        //           To workaround this whenever we are talking to AD, username is mapped to userPrincipalName and does not contain '@', we will get the sAMAccountName 
                        //           while getting the user object and use that for authenticating the user.
                        //
                        MembershipUser user = null;
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1)) 
                        {
                            string sAMAccountName = null; 
                            user = FindUserAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes, out sAMAccountName); 
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        } 
                        else
                        {
                            user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes);
                            usernameForAuthentication = username; 
                        }
 
                        // 
                        // user does not exist, return false
                        // 
                        if (user == null)
                            return false;

                        // 
                        // here we want to check if the user is already unlocked due to bad password answer (or bad password)
                        // 
                        if (user.IsLockedOut) 
                            return false;
 
                    }
                    else
                    {
                        // 
                        // get the user's directory entry
                        // 
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1)) 
                        {
                            string sAMAccountName = null; 
                            userEntry = FindUserEntryAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out sAMAccountName);
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        }
                        else 
                        {
                            userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")"); 
                            usernameForAuthentication = username; 
                        }
 
                        //
                        // user does not exist, return false
                        //
                        if (userEntry == null) 
                            return false;
                    } 
 
                    //
                    // validate the user's credentials 
                    //
                    if (!ValidateCredentials(usernameForAuthentication, password))
                        return false;
 
                    if (EnablePasswordReset && resetBadPasswordAnswerAttributes)
                    { 
                        // 
                        // user supplied correct password, so we need to reset the password answer tracking info
                        // (NOTE: The reason we do not call the Reset method here is so that we can make all the modifications in one transaction) 
                        //
                        userEntry.Properties[attributeMapFailedPasswordAnswerCount].Value = 0;
                        userEntry.Properties[attributeMapFailedPasswordAnswerTime].Value = 0;
                        userEntry.Properties[attributeMapFailedPasswordAnswerLockoutTime].Value = 0; 
                    }
 
                    if (newPasswordQuestion == null) 
                    {
                        // set it to null only if it already exists 
                        if ((attributeMapPasswordQuestion != null) && (userEntry.Properties.Contains(attributeMapPasswordQuestion)))
                            userEntry.Properties[attributeMapPasswordQuestion].Clear();
                    }
                    else 
                        userEntry.Properties[attributeMapPasswordQuestion].Value = newPasswordQuestion;
 
                    if (newPasswordAnswer == null) 
                    {
                        // set it to null only if it already exists 
                        if ((attributeMapPasswordAnswer != null) && (userEntry.Properties.Contains(attributeMapPasswordAnswer)))
                            userEntry.Properties[attributeMapPasswordAnswer].Clear();
                    }
                    else 
                        userEntry.Properties[attributeMapPasswordAnswer].Value = encodedPasswordAnswer;
 
                    userEntry.CommitChanges(); 

                } 
                finally
                {
                    if (userEntry != null)
                        userEntry.Dispose(); 
                    connection.Close();
                } 
            } 
            catch
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                //
                throw; 
            }
 
            // 
            // Password question and answer changed successfully
            // 
            return true;
        }

        public override string GetPassword(string username, string passwordAnswer) 
        {
            // 
            // ADMembership Provider does not support password retrieval 
            //
            throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordRetrieval_not_supported_AD)); 
        }

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override bool ChangePassword(string username, 
                                                        string oldPassword, 
                                                        string newPassword)
        { 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            CheckUserName(ref username, maxUsernameLength, "username" ); 

            CheckPassword(oldPassword, maxPasswordLength, "oldPassword"); 
 
            CheckPassword(newPassword, maxPasswordLength, "newPassword");
 
            if( newPassword.Length < MinRequiredPasswordLength )
            {
                throw new ArgumentException(SR.GetString(
                              SR.Password_too_short, 
                              "newPassword",
                              MinRequiredPasswordLength.ToString(CultureInfo.InvariantCulture))); 
            } 

            int count = 0; 

            for( int i = 0; i < newPassword.Length; i++ )
            {
                if( !char.IsLetterOrDigit( newPassword, i ) ) 
                {
                    count++; 
                } 
            }
 
            if( count < MinRequiredNonAlphanumericCharacters )
            {
                throw new ArgumentException(SR.GetString(
                              SR.Password_need_more_non_alpha_numeric_chars, 
                              "newPassword",
                              MinRequiredNonAlphanumericCharacters.ToString(CultureInfo.InvariantCulture))); 
            } 

            if( PasswordStrengthRegularExpression.Length > 0 ) 
            {
                if( !Regex.IsMatch( newPassword, PasswordStrengthRegularExpression ) )
                {
                    throw new ArgumentException(SR.GetString(SR.Password_does_not_match_regular_expression, 
                                                             "newPassword"));
                } 
            } 

            ValidatePasswordEventArgs e = new ValidatePasswordEventArgs( username, newPassword, false ); 
            OnValidatingPassword(e);

            if(e.Cancel)
            { 
                if(e.FailureInformation != null)
                    throw e.FailureInformation; 
                else 
                    throw new ArgumentException(SR.GetString( SR.Membership_Custom_Password_Validation_Failure), "newPassword");
            } 

            try
            {
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                DirectoryEntry userEntry = null; 
                bool resetBadPasswordAnswerAttributes = false; 
                string usernameForAuthentication = null;
 
                try
                {
                    if (EnablePasswordReset)
                    { 
                        //
                        // get the user's directory entry 
                        // NOTE: If the username is mapped to userPrincipalName and the username does not contain '@' in it, the S.DS(adsi) will pass NULL 
                        //           domain name to the underlying wldap32 layer. This results in authentication failure even for valid credentials. To workaround this
                        //           whenever we are talking to AD, username is mapped to userPrincipalName and does not contain '@', we will get the sAMAccountName 
                        //           while getting the user object and use that for changing the password.
                        //
                        MembershipUser user = null;
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1)) 
                        {
                            string sAMAccountName = null; 
                            user = FindUserAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes, out sAMAccountName); 
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        } 
                        else
                        {
                            user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes);
                            usernameForAuthentication = username; 
                        }
 
                        // 
                        // user does not exist, return false
                        // 
                        if (user == null)
                            return false;

                        // 
                        // here we want to check if the user is already unlocked due to bad password answer (or bad password)
                        // 
                        if (user.IsLockedOut) 
                            return false;
                    } 
                    else
                    {
                        //
                        // get the user's directory entry (Also get sAMAccountName if needed) 
                        //
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1)) 
                        { 
                            string sAMAccountName = null;
                            userEntry = FindUserEntryAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out sAMAccountName); 
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        }
                        else
                        { 
                            userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")");
                            usernameForAuthentication = username; 
                        } 

                        // 
                        // user does not exist, return false
                        //
                        if (userEntry == null)
                            return false; 
                    }
 
                    // 
                    // associate the user's context with the directory entry
                    // 
                    userEntry.Username = (usernameIsSAMAccountName) ? directoryInfo.DomainName + "\\" + usernameForAuthentication : usernameForAuthentication;
                    userEntry.Password = oldPassword;
                    userEntry.AuthenticationType = directoryInfo.GetAuthenticationTypes(directoryInfo.ConnectionProtection, (directoryInfo.DirectoryType == DirectoryType.AD) ? CredentialsType.Windows : CredentialsType.NonWindows);
 
                    try
                    { 
                        SetPasswordPortIfApplicable(userEntry); 

                        // 
                        // Change the password
                        //
                        userEntry.Invoke("ChangePassword", new object[]{ oldPassword, newPassword });
                    } 
                    catch (COMException e2)
                    { 
                        if (e2.ErrorCode == unchecked((int) 0x8007052e)) 
                            return false;
                        else 
                            throw;
                    }
                    catch (TargetInvocationException tie)
                    { 
                        if (tie.InnerException is COMException)
                        { 
                            COMException ce = (COMException) tie.InnerException; 
                            int errorCode = ce.ErrorCode;
 
                            //
                            // if the exception is due to password not meeting complexity requirements, then return
                            // MembershipPasswordException
                            // 
                            if ((errorCode == unchecked((int) 0x800708c5)) || (errorCode == unchecked((int) 0x8007202f))  || (errorCode == unchecked((int) 0x8007052d)) || (errorCode == unchecked((int) 0x8007052f)))
                                throw new MembershipPasswordException(SR.GetString(SR.Membership_InvalidPassword), ce); 
                            // 
                            // if the target is ADAM and the exception is due to property not found, this indicates that a secure
                            // connection could not be setup for changing the password and ADSI is falling back to kerberos which does not work for ADAM 
                            // so we will provide a clearer exception
                            //
                            else if ((errorCode == unchecked((int) 0x8000500d) && (directoryInfo.DirectoryType == DirectoryType.ADAM)))
                                throw new ProviderException(SR.GetString(SR.ADMembership_No_secure_conn_for_password)); 
                            else
                                throw; 
                        } 
                        else
                            throw; 
                    }

                    if (EnablePasswordReset && resetBadPasswordAnswerAttributes)
                    { 
                        //
                        // associate the process context with the directory entry 
                        // 
                        userEntry.Username = directoryInfo.GetUsername();
                        userEntry.Password = directoryInfo.GetPassword(); 
                        userEntry.AuthenticationType = directoryInfo.AuthenticationTypes;

                        //
                        // user supplied correct password, so we need to reset the password answer tracking info 
                        //
                        ResetBadPasswordAnswerAttributes(userEntry); 
                    } 
                }
                finally 
                {
                    if (userEntry != null)
                        userEntry.Dispose();
                    connection.Close(); 
                }
            } 
            catch 
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                //
                throw;
            } 

            // 
            // Password changed successfully 
            //
            return true; 
        }

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override string ResetPassword(string username, string passwordAnswer) 
        { 
            string newPassword = null;
 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            if (!EnablePasswordReset) 
                throw new NotSupportedException(SR.GetString(SR.Not_configured_to_support_password_resets));
 
            CheckUserName(ref username, maxUsernameLength, "username"); 

            CheckPasswordAnswer(ref passwordAnswer, RequiresQuestionAndAnswer, maxPasswordAnswerLength, "passwordAnswer"); 

            try
            {
                // 
                // validate the password answer
                // 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                DirectoryEntry userEntry = null; 
                bool resetBadPasswordAnswerAttributes = false;

                try
                { 
                    //
                    // get the user's directory entry 
                    // 
                    MembershipUser user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes);
 
                    //
                    // user does not exist, throw exception
                    //
                    if (user == null) 
                        throw new ProviderException(SR.GetString(SR.Membership_UserNotFound));
 
                    // 
                    // if user is locked, throw an exception
                    // 
                    if (user.IsLockedOut)
                        throw new MembershipPasswordException(SR.GetString(SR.Membership_AccountLockOut));

                    string storedPasswordAnswer = Decrypt((string) PropertyManager.GetPropertyValue(userEntry, attributeMapPasswordAnswer)); 
                    if (!StringUtil.EqualsIgnoreCase(passwordAnswer, storedPasswordAnswer))
                    { 
                        UpdateBadPasswordAnswerAttributes(userEntry); 
                        throw new MembershipPasswordException(SR.GetString(SR.Membership_WrongAnswer));
                    } 
                    else
                    {
                        if (resetBadPasswordAnswerAttributes)
                            ResetBadPasswordAnswerAttributes(userEntry); 
                    }
 
                    SetPasswordPortIfApplicable(userEntry); 

                    // 
                    // Reset  the password (generating a random new password)
                    //
                    newPassword = GeneratePassword();
 
                    ValidatePasswordEventArgs e = new ValidatePasswordEventArgs( username, newPassword, false );
                    OnValidatingPassword(e); 
 
                    if(e.Cancel)
                    { 
                        if(e.FailureInformation != null)
                            throw e.FailureInformation;
                        else
                            throw new ProviderException(SR.GetString( SR.Membership_Custom_Password_Validation_Failure)); 
                    }
 
                    userEntry.Invoke("SetPassword", new object[]{ newPassword }); 

                } 
                catch (TargetInvocationException tie)
                {
                    if (tie.InnerException is COMException)
                    { 
                        COMException ce = (COMException) tie.InnerException;
                        int errorCode = ce.ErrorCode; 
 
                        //
                        // if the exception is due to password not meeting complexity requirements, then return 
                        // ProviderException
                        //
                        if ((errorCode == unchecked((int) 0x800708c5)) || (errorCode == unchecked((int) 0x8007202f))  || (errorCode == unchecked((int) 0x8007052d)) || (errorCode == unchecked((int) 0x8007052f)))
                            throw new ProviderException(SR.GetString(SR.ADMembership_Generated_password_not_complex), ce); 
                        //
                        // if the target is ADAM and the exception is due to property not found, this indicates that a secure 
                        // connection could not be setup for changing the password and ADSI is falling back to kerberos which does not work for ADAM 
                        // so we will provide a clearer exception
                        // 
                        if ((errorCode == unchecked((int) 0x8000500d) && (directoryInfo.DirectoryType == DirectoryType.ADAM)))
                            throw new ProviderException(SR.GetString(SR.ADMembership_No_secure_conn_for_password));
                        else
                            throw; 
                    }
                    else 
                        throw; 
                }
                finally 
                {
                    if (userEntry != null)
                        userEntry.Dispose();
                    connection.Close(); 
                }
            } 
            catch 
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                //
                throw;
            } 

            // 
            // Password was reset successfully, return the generated password 
            //
            return newPassword; 
        }

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override bool UnlockUser(string username) 
        { 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

            CheckUserName( ref username, maxUsernameLength, "username" );

            try 
            {
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry; 
                DirectoryEntry userEntry = null;
 
                try
                {
                    //
                    // get the user's directory entry 
                    //
                    userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")"); 
 
                    //
                    // user does not exist, return false 
                    //
                    if (userEntry == null)
                        return false;
 
                    userEntry.Properties["lockoutTime"].Value = 0;
 
                    if (EnablePasswordReset) 
                    {
                        userEntry.Properties[attributeMapFailedPasswordAnswerCount].Value = 0; 
                        userEntry.Properties[attributeMapFailedPasswordAnswerTime].Value = 0;
                        userEntry.Properties[attributeMapFailedPasswordAnswerLockoutTime].Value = 0;
                    }
 
                    userEntry.CommitChanges();
                } 
                finally 
                {
                    if (userEntry != null) 
                        userEntry.Dispose();
                    connection.Close();
                }
            } 
            catch
            { 
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                // 
                throw;
            }

            // 
            // user unlocked successfully, return true
            // 
            return true; 
        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override void UpdateUser(MembershipUser user) 
        {
            bool emailModified = true; 
            bool commentModified = true; 
            bool isApprovedModified = true;
 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            if( user == null ) 
            {
                throw new ArgumentNullException("user" ); 
            } 

            ActiveDirectoryMembershipUser adUser = user as ActiveDirectoryMembershipUser; 

            if (adUser != null)
            {
                // 
                // check which fields have really been modified
                // 
                emailModified = adUser.emailModified; 
                commentModified = adUser.commentModified;
                isApprovedModified = adUser.isApprovedModified; 
            }

            string temp = user.UserName;
            CheckUserName( ref temp, maxUsernameLength, "UserName" ); 

            string email = user.Email; 
            if (emailModified) 
                SecUtility.CheckParameter( ref email, RequiresUniqueEmail, true, false, maxEmailLength, "Email");
 
            if (commentModified && user.Comment != null)
            {
                if (user.Comment.Length == 0)
                    throw new ArgumentException(SR.GetString(SR.Parameter_can_not_be_empty, "Comment"), "Comment"); 

                if (maxCommentLength > 0 && user.Comment.Length > maxCommentLength) 
                    throw new ArgumentException(SR.GetString(SR.Parameter_too_long, "Comment", maxCommentLength.ToString(CultureInfo.InvariantCulture)), "Comment"); 
            }
 
            try
            {

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                DirectoryEntry userEntry = null; 
 
                try
                { 
                    //
                    // get the user's directory entry
                    //
                    userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(user.UserName) + ")"); 

                    if (userEntry == null) 
                        throw new ProviderException(SR.GetString(SR.Membership_UserNotFound)); 

                    if (!((emailModified) || (commentModified) || (isApprovedModified))) 
                        // nothing has been modified
                        return;

                    // 
                    // update the email
                    // if enableUniqueEmail is specified, we need to ensure that the email is unique 
                    // 
                    if (emailModified)
                    { 
                        if (email == null)
                        {
                            // set the email to null only if email already exists
                            if (userEntry.Properties.Contains(attributeMapEmail)) 
                                userEntry.Properties[attributeMapEmail].Clear();
                        } 
                        else 
                        {
                            if (RequiresUniqueEmail && !IsEmailUnique(null, user.UserName, email, true /* existing */)) 
                                throw new ProviderException(SR.GetString(SR.Membership_DuplicateEmail));

                            userEntry.Properties[attributeMapEmail].Value = email;
                        } 
                    }
 
                    // 
                    // update the comment
                    // 
                    if (commentModified)
                    {
                        if (user.Comment == null)
                        { 
                            // set the comment to null only if comment already exists
                            if (userEntry.Properties.Contains("comment")) 
                                userEntry.Properties["comment"].Clear(); 
                        }
                        else 
                        {
                            //
                            // we use the original value ("user.Comment") to preserve all white space
                            // (including leading and trailing white space) 
                            userEntry.Properties["comment"].Value = user.Comment;
                        } 
                    } 

                    // 
                    // update the IsApproved field
                    //
                    if (isApprovedModified)
                    { 
                        if (directoryInfo.DirectoryType == DirectoryType.AD)
                        { 
                            // userAccountControl attribute 
                            const int UF_ACCOUNT_DISABLED =0x2;
 
                            int val = (int)PropertyManager.GetPropertyValue(userEntry, "userAccountControl");

                            if (user.IsApproved)
                                val &= ~UF_ACCOUNT_DISABLED; 
                            else
                                val |= UF_ACCOUNT_DISABLED; 
                            userEntry.Properties["userAccountControl"].Value = val; 
                        }
                        else 
                        {
                            // different attribute for ADAM
                            userEntry.Properties["msDS-UserAccountDisabled"].Value = !(user.IsApproved);
                        } 
                    }
 
                    userEntry.CommitChanges(); 

                    if (adUser != null) 
                    {
                        adUser.emailModified = false;
                        adUser.commentModified = false;
                        adUser.isApprovedModified = false; 
                    }
 
                } 
                finally
                { 
                    if (userEntry != null)
                        userEntry.Dispose();
                    connection.Close();
                } 
            }
            catch 
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                //
                throw;
            }
 
            return;
        } 
 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.LinkDemand, Unrestricted=true)] 
        public override bool ValidateUser(string username, string password)
        {
            if( ValidateUserCore(username, password))
            { 
                PerfCounters.IncrementCounter(AppPerfCounter.MEMBER_SUCCESS);
                WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditMembershipAuthenticationSuccess, username); 
                return true; 
            } else {
                PerfCounters.IncrementCounter(AppPerfCounter.MEMBER_FAIL); 
                WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditMembershipAuthenticationFailure, username);
                return false;
            }
        } 

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        private bool ValidateUserCore(string username, string password) 
        {
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
            if(!SecUtility.ValidateParameter(ref username, true, true, true, maxUsernameLength))
            { 
                return false; 
            }
 
            //
            // if username is mapped to UPN, it should not contain '\'
            //
            if (usernameIsUPN && (username.IndexOf('\\') != -1)) 
            {
                return false; 
            } 

            if( !ValidatePassword(password, maxPasswordLength)) 
            {
                return false;
            }
 
            bool result = false;
            try 
            { 

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                DirectoryEntry userEntry = null;
                bool resetBadPasswordAnswerAttributes = false;
                string usernameForAuthentication = null; 

                try 
                { 
                    if (EnablePasswordReset)
                    { 
                        //
                        // get the user's directory entry
                        // NOTE: If the username is mapped to userPrincipalName and the username does not contain '@' in it, then simple bind will fail as it needs domain information.
                        //           To workaround this whenever we are talking to AD, username is mapped to userPrincipalName and does not contain '@', we will get the sAMAccountName 
                        //           while getting the user object and use that for authenticating the user.
                        // 
                        MembershipUser user = null; 
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1))
                        { 
                            string sAMAccountName = null;
                            user = FindUserAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes, out sAMAccountName);
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        } 
                        else
                        { 
                            user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes); 
                            usernameForAuthentication = username;
                        } 

                        //
                        // user does not exist, return false
                        // 
                        if (user == null)
                            return false; 
 
                        //
                        // here we want to check if the user is already unlocked due to bad password answer (or bad password) 
                        //
                        if (user.IsLockedOut)
                            return false;
                    } 
                    else
                    { 
                        // 
                        // get the user's directory entry
                        // 
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1))
                        {
                            string sAMAccountName = null;
                            userEntry = FindUserEntryAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out sAMAccountName); 
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        } 
                        else 
                        {
                            userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")"); 
                            usernameForAuthentication = username;
                        }

                        // 
                        // user does not exist, return false
                        // 
                        if (userEntry == null) 
                            return false;
                    } 

                    result = ValidateCredentials(usernameForAuthentication, password);

                    if (EnablePasswordReset && result && resetBadPasswordAnswerAttributes) 
                    {
                        // 
                        // user supplied correct password, so we need to reset the password answer tracking info 
                        //
                        ResetBadPasswordAnswerAttributes(userEntry); 
                    }

                }
                finally 
                {
                    if (userEntry != null) 
                        userEntry.Dispose(); 
                    connection.Close();
                } 
            }
            catch
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                // 
                throw; 
            }
 
            return result;

        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] 
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        { 
            MembershipUser user = null;

            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

            if( providerUserKey == null ) 
            { 
                throw new ArgumentNullException( "providerUserKey" );
            } 

            if ( !( providerUserKey is SecurityIdentifier) )
            {
                throw new ArgumentException( SR.GetString( SR.ADMembership_InvalidProviderUserKey ), "providerUserKey" ); 
            }
 
            try 
            {
 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry;

                try 
                {
                    // 
                    // Search for the user and return a MembershipUser object 
                    //
                    SecurityIdentifier sid = providerUserKey as SecurityIdentifier; 
                    StringBuilder sidHexValueStr = new StringBuilder();
                    int binaryLength = sid.BinaryLength;
                    byte[] sidBinaryForm = new byte[binaryLength];
                    sid.GetBinaryForm(sidBinaryForm, 0); 

                    for (int i = 0; i < binaryLength; i++) 
                    { 
                        sidHexValueStr.Append("\\");
                        sidHexValueStr.Append(sidBinaryForm[i].ToString("x2", NumberFormatInfo.InvariantInfo)); 
                    }

                    DirectoryEntry dummyEntry;
                    bool resetBadPasswordAnswerAttributes = false; 
                    user = FindUser(containerEntry, "(" + attributeMapUsername + "=*)(objectSid=" + sidHexValueStr.ToString() + ")", out dummyEntry /* ignored */, out resetBadPasswordAnswerAttributes /* ignored */);
                } 
                finally 
                {
                    connection.Close(); 
                }
            }
            catch
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                // 
                throw;
            } 

            return user;
        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] 
        public override MembershipUser GetUser(string username, bool userIsOnline)
        { 
            MembershipUser user = null;

            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

            CheckUserName(ref username, maxUsernameLength, "username" ); 
 
            try
            { 

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry;
 
                try
                { 
                    // 
                    // Search for the user and return a MembershipUser object
                    // 
                    DirectoryEntry dummyEntry;
                    bool resetBadPasswordAnswerAttributes = false;
                    user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out dummyEntry /*ignored */, out resetBadPasswordAnswerAttributes /* ignored */);
                } 
                finally
                { 
                    connection.Close(); 
                }
            } 
            catch
            {
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                //
                throw; 
            } 

            return user; 
        }

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override string GetUserNameByEmail(string email) 
        { 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

            SecUtility.CheckParameter(ref email, false, true, false, maxEmailLength,  "email");

            string username = null; 
            try
            { 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                SearchResultCollection resCol = null; 

                try
                {
                    DirectorySearcher searcher = new DirectorySearcher(containerEntry); 
                    if (email != null)
                        searcher.Filter = "(&(objectCategory=person)(objectClass=user)(" + attributeMapUsername + "=*)(" + attributeMapEmail + "=" + GetEscapedFilterValue(email) +"))"; 
                    else 
                        searcher.Filter = "(&(objectCategory=person)(objectClass=user)(" + attributeMapUsername + "=*)(!(" + attributeMapEmail + "=" +"*)))";
                    searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree; 
                    searcher.PropertiesToLoad.Add(attributeMapUsername);

                    if (directoryInfo.ClientSearchTimeout != -1)
                        searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0); 
                    if (directoryInfo.ServerSearchTimeout != -1)
                        searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0); 
 
                    resCol = searcher.FindAll();
                    bool userFound = false; 

                    foreach (SearchResult res in resCol)
                    {
                        if (!userFound) 
                        {
                            username = (string) PropertyManager.GetSearchResultPropertyValue(res, attributeMapUsername); 
                            userFound = true; 

                            if (!RequiresUniqueEmail) 
                                break;
                        }
                        else
                        { 
                            if (RequiresUniqueEmail)
                            { 
                                // there is a duplicate entry, so we need to throw an ProviderException 
                                throw new ProviderException(SR.GetString(SR.Membership_more_than_one_user_with_email));
                            } 
                            else
                                // we should never get here
                                break;
                        } 
                    }
                } 
                finally 
                {
                    if (resCol != null) 
                        resCol.Dispose();
                    connection.Close();
                }
            } 
            catch
            { 
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                // 
                throw;
            }

            return username; 
        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] 
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {

            if (!initialized) 
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
            CheckUserName(ref username, maxUsernameLength, "username"); 

            try 
            {
                //
                // Get the Directory Entry for the container
                // 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.CreationContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry; 
                // to avoid unnecessary searches (for better performance) 
                containerEntry.AuthenticationType |= AuthenticationTypes.FastBind;
                DirectoryEntry userEntry = null; 

                try
                {
                    // 
                    // Get the directory entry for the user
                    // 
                    string dummyString; 
                    userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", System.DirectoryServices.SearchScope.OneLevel, false /* retrieveSAMAccountName */, out dummyString);
 
                    if (userEntry == null)
                        return false;

                    // 
                    // Remove the entry from the container
                    // 
                    containerEntry.Children.Remove(userEntry); 

                } 
                catch (COMException e)
                {
                    if (e.ErrorCode == unchecked((int) 0x80072030))
                    { 
                        //
                        // incase some one else deleted the object just before this 
                        // 
                        return false;
                    } 
                    else
                        throw;
                }
                finally 
                {
                    if (userEntry != null) 
                        userEntry.Dispose(); 
                    connection.Close();
                } 
            }
            catch
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                // 
                throw; 
            }
 
            return true;
        }

        public virtual string GeneratePassword() 
        {
            // 
            // 

 


            return Membership.GeneratePassword(
                      MinRequiredPasswordLength < PASSWORD_SIZE ? PASSWORD_SIZE : MinRequiredPasswordLength, 
                      MinRequiredNonAlphanumericCharacters);
        } 
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override MembershipUserCollection GetAllUsers(int pageIndex,
                                                        int pageSize,
                                                        out int totalRecords) 
        {
            return FindUsersByName("*", pageIndex, pageSize, out totalRecords); 
        } 

        public override int GetNumberOfUsersOnline() 
        {
            //
            // ADMembershipProvider does not support the notion of online users
            // 
            throw new NotSupportedException(SR.GetString(SR.ADMembership_OnlineUsers_not_supported));
        } 
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override MembershipUserCollection FindUsersByName(string usernameToMatch,
                                                        int pageIndex,
                                                        int pageSize, 
                                                        out int totalRecords)
        { 
            if (!initialized) 
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
            if (!EnableSearchMethods)
                throw new NotSupportedException(SR.GetString(SR.ADMembership_Provider_SearchMethods_not_supported));

            SecUtility.CheckParameter( ref usernameToMatch, true, true, true, maxUsernameLength, "usernameToMatch" ); 

            if ( pageIndex < 0 ) 
                throw new ArgumentException(SR.GetString(SR.PageIndex_bad), "pageIndex"); 
            if ( pageSize < 1 )
                throw new ArgumentException(SR.GetString(SR.PageSize_bad), "pageSize"); 

            long upperBound = (long)pageIndex * pageSize + pageSize - 1;
            if ( upperBound > Int32.MaxValue )
                throw new ArgumentException(SR.GetString(SR.PageIndex_PageSize_bad), "pageIndex and pageSize"); 

            try 
            { 

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;

                try
                { 
                    totalRecords = 0;
                    return FindUsers(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(usernameToMatch, false) + ")", attributeMapUsername, pageIndex, pageSize, out totalRecords); 
                } 
                finally
                { 
                    connection.Close();
                }
            }
            catch 
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                //
                throw; 
            }

        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] 
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        { 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            if (!EnableSearchMethods) 
                throw new NotSupportedException(SR.GetString(SR.ADMembership_Provider_SearchMethods_not_supported));
 
            SecUtility.CheckParameter(ref emailToMatch, false, true, false, maxEmailLength, "emailToMatch"); 

            if ( pageIndex < 0 ) 
                throw new ArgumentException(SR.GetString(SR.PageIndex_bad), "pageIndex");
            if ( pageSize < 1 )
                throw new ArgumentException(SR.GetString(SR.PageSize_bad), "pageSize");
 
            long upperBound = (long)pageIndex * pageSize + pageSize - 1;
            if ( upperBound > Int32.MaxValue ) 
                throw new ArgumentException(SR.GetString(SR.PageIndex_PageSize_bad), "pageIndex and pageSize"); 

            try 
            {

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry; 

                try 
                { 
                    totalRecords = 0;
                    string filter = null; 
                    if (emailToMatch != null)
                        filter = "(" + attributeMapUsername + "=*)(" + attributeMapEmail + "=" + GetEscapedFilterValue(emailToMatch, false) +")";
                    else
                        filter = "(" + attributeMapUsername + "=*)(!(" + attributeMapEmail + "=" +"*))"; 
                    return FindUsers(containerEntry, filter, attributeMapEmail, pageIndex, pageSize, out totalRecords);
                } 
                finally 
                {
                    connection.Close(); 
                }
            }
            catch
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                // 
                throw;
            } 

        }

        private bool ValidateCredentials(string username, string password) 
        {
            bool result = false; 
            NetworkCredential credentialForValidation = (usernameIsSAMAccountName) ? new NetworkCredential(username, password, directoryInfo.DomainName) 
                                                                                                                                    : DirectoryInformation.GetCredentialsWithDomain(new NetworkCredential(username, password));
 
            //
            // NOTE: we do not need to revert context here since this method is always
            //           called with explicit credentials
            // 

            // 
            // if this is concurrent bind (use the common connection) 
            //
 
            if (directoryInfo.ConcurrentBindSupported)
            {
                try
                { 
                    connection.Bind(credentialForValidation);
                    result = true; 
                } 
                catch (LdapException e)
                { 
                    if (e.ErrorCode == 0x31)
                    {
                        //
                        // authentication failure, invalid user 
                        //
                        result = false; 
                    } 
                    else
                    { 
                        //
                        // some other failure
                        //
                        throw; 
                    }
                } 
            } 
            else
            { 
                //
                // create a new ldap connection
                //
                LdapConnection newConnection = directoryInfo.CreateNewLdapConnection(authTypeForValidation); 

                try 
                { 
                    newConnection.Bind(credentialForValidation);
                    result = true; 
                }
                catch (LdapException e2)
                {
                    if (e2.ErrorCode == 0x31) 
                    {
                        // 
                        // authentication failure, invalid user 
                        //
                        result = false; 
                    }
                    else
                    {
                        // 
                        // some other failure
                        // 
                        throw; 
                    }
                } 
                finally
                {
                    newConnection.Dispose();
                } 
            }
 
            return result; 
        }
 
        private DirectoryEntry FindUserEntryAndSAMAccountName(DirectoryEntry containerEntry, string filter, out string sAMAccountName)
        {
            return FindUserEntry(containerEntry, filter, System.DirectoryServices.SearchScope.Subtree, true /*retrieveSAMAccountName */, out sAMAccountName);
        } 

        private DirectoryEntry FindUserEntry(DirectoryEntry containerEntry, string filter) 
        { 
            string dummyString;
            return FindUserEntry(containerEntry, filter, System.DirectoryServices.SearchScope.Subtree, false /*retrieveSAMAccountName */, out dummyString); 
        }

        private DirectoryEntry FindUserEntry(DirectoryEntry containerEntry, string filter, System.DirectoryServices.SearchScope searchScope, bool retrieveSAMAccountName, out string sAMAccountName)
        { 
            Debug.Assert(containerEntry != null);
            DirectorySearcher searcher = new DirectorySearcher(containerEntry); 
 
            searcher.SearchScope = searchScope;
            searcher.Filter = "(&(objectCategory=person)(objectClass=user)" + filter + ")"; 

            if (directoryInfo.ClientSearchTimeout != -1)
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0);
            if (directoryInfo.ServerSearchTimeout != -1) 
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0);
 
            if (retrieveSAMAccountName) 
                searcher.PropertiesToLoad.Add("sAMAccountName");
 
            SearchResult res = searcher.FindOne();

            sAMAccountName = null;
            if (res != null) 
            {
                if (retrieveSAMAccountName) 
                    sAMAccountName = (string) PropertyManager.GetSearchResultPropertyValue(res, "sAMAccountName"); 
                return res.GetDirectoryEntry();
            } 
            else
                return null;

        } 

        private MembershipUser FindUserAndSAMAccountName(DirectoryEntry containerEntry, string filter, out DirectoryEntry userEntry, out bool resetBadPasswordAnswerAttributes, out string sAMAccountName) 
        { 
            return FindUser(containerEntry, filter, System.DirectoryServices.SearchScope.Subtree, true /* retrieveSAMAccountName */, out userEntry, out resetBadPasswordAnswerAttributes, out sAMAccountName);
        } 

        private MembershipUser FindUser(DirectoryEntry containerEntry, string filter, out DirectoryEntry userEntry, out bool resetBadPasswordAnswerAttributes)
        {
            string dummyString; 
            return FindUser(containerEntry, filter, System.DirectoryServices.SearchScope.Subtree, false /* retrieveSAMAccountName */, out userEntry, out resetBadPasswordAnswerAttributes, out dummyString);
        } 
 
        private MembershipUser FindUser(DirectoryEntry containerEntry, string filter, System.DirectoryServices.SearchScope searchScope,  bool retrieveSAMAccountName, out DirectoryEntry userEntry, out bool resetBadPasswordAnswerAttributes, out string sAMAccountName)
        { 
            Debug.Assert(containerEntry != null);
            MembershipUser user = null;
            DirectorySearcher searcher = new DirectorySearcher(containerEntry);
 
            searcher.SearchScope = searchScope;
            searcher.Filter = "(&(objectCategory=person)(objectClass=user)" + filter + ")"; 
 
            if (directoryInfo.ClientSearchTimeout != -1)
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0); 
            if (directoryInfo.ServerSearchTimeout != -1)
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0);

            // 
            // load all the attributes needed to create a MembershipUser object
            // 
            searcher.PropertiesToLoad.Add(attributeMapUsername); 
            searcher.PropertiesToLoad.Add("objectSid");
            searcher.PropertiesToLoad.Add(attributeMapEmail); 
            searcher.PropertiesToLoad.Add("comment");
            searcher.PropertiesToLoad.Add("whenCreated");
            searcher.PropertiesToLoad.Add("pwdLastSet");
            searcher.PropertiesToLoad.Add("msDS-User-Account-Control-Computed"); 
            searcher.PropertiesToLoad.Add("lockoutTime");
 
            if (retrieveSAMAccountName) 
                searcher.PropertiesToLoad.Add("sAMAccountName");
 
            if (attributeMapPasswordQuestion != null)
                searcher.PropertiesToLoad.Add(attributeMapPasswordQuestion);

            if (directoryInfo.DirectoryType == DirectoryType.AD) 
                searcher.PropertiesToLoad.Add("userAccountControl");
            else 
                searcher.PropertiesToLoad.Add("msDS-UserAccountDisabled"); 

            if (EnablePasswordReset) 
            {
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerCount);
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerTime);
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerLockoutTime); 
            }
 
 
            SearchResult res = searcher.FindOne();
            resetBadPasswordAnswerAttributes = false; 
            sAMAccountName = null;
            if (res != null)
            {
                user = GetMembershipUserFromSearchResult(res); 
                userEntry = res.GetDirectoryEntry();
 
                if (retrieveSAMAccountName) 
                    sAMAccountName = (string) PropertyManager.GetSearchResultPropertyValue(res, "sAMAccountName");
 
                if ((EnablePasswordReset) && res.Properties.Contains(attributeMapFailedPasswordAnswerCount))
                    resetBadPasswordAnswerAttributes = ((int) PropertyManager.GetSearchResultPropertyValue(res, attributeMapFailedPasswordAnswerCount) > 0);
            }
            else 
            {
                userEntry = null; 
            } 

            return user; 

        }

        private MembershipUserCollection FindUsers(DirectoryEntry containerEntry, string filter, string sortKey, int pageIndex, int pageSize, out int totalRecords) 
        {
            Debug.Assert(containerEntry != null); 
            MembershipUserCollection col = new MembershipUserCollection(); 
            int lastOffset = (pageIndex + 1) * pageSize;
            int startOffset = lastOffset -pageSize + 1; 

            DirectorySearcher searcher = new DirectorySearcher(containerEntry);
            searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
            searcher.Filter = "(&(objectCategory=person)(objectClass=user)" + filter + ")"; 

            if (directoryInfo.ClientSearchTimeout != -1) 
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0); 
            if (directoryInfo.ServerSearchTimeout != -1)
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0); 

            //
            // load all the attributes needed to create a MembershipUser object
            // 
            searcher.PropertiesToLoad.Add(attributeMapUsername);
            searcher.PropertiesToLoad.Add("objectSid"); 
            searcher.PropertiesToLoad.Add(attributeMapEmail); 
            searcher.PropertiesToLoad.Add("comment");
            searcher.PropertiesToLoad.Add("whenCreated"); 
            searcher.PropertiesToLoad.Add("pwdLastSet");
            searcher.PropertiesToLoad.Add("msDS-User-Account-Control-Computed");
            searcher.PropertiesToLoad.Add("lockoutTime");
 
            if (attributeMapPasswordQuestion != null)
                searcher.PropertiesToLoad.Add(attributeMapPasswordQuestion); 
 
            if (directoryInfo.DirectoryType == DirectoryType.AD)
                searcher.PropertiesToLoad.Add("userAccountControl"); 
            else
                searcher.PropertiesToLoad.Add("msDS-UserAccountDisabled");

            if (EnablePasswordReset) 
            {
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerCount); 
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerTime); 
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerLockoutTime);
            } 

            //
            // turn on paging
            // 
            searcher.PageSize = 512;
 
            // 
            // need to sort the users based on the attribute that is mapped to the username
            // 
            searcher.Sort = new SortOption(sortKey, SortDirection.Ascending);

            SearchResultCollection resCol = searcher.FindAll();
 
            try
            { 
                int count = 0; 
                totalRecords = 0;
 
                foreach(SearchResult res in resCol)
                {
                    count++;
 
                    //
                    // add only the requested window of the result set 
                    // 
                    if (count >= startOffset && count <= lastOffset)
                    { 
                        col.Add(GetMembershipUserFromSearchResult(res));
                    }
                }
                totalRecords = count; 
            }
            finally 
            { 
                resCol.Dispose();
            } 

            return col;

        } 

        private void CheckPasswordAnswer(ref string passwordAnswer, bool checkForNull, int maxSize,string paramName) 
        { 
            if (passwordAnswer == null)
            { 
                if (checkForNull)
                    throw new ArgumentNullException(paramName);
                return;
            } 

            passwordAnswer = passwordAnswer.Trim(); 
 
            if (passwordAnswer.Length < 1)
                throw new ArgumentException(SR.GetString(SR.Parameter_can_not_be_empty, paramName), paramName); 

            if (maxSize > 0 && passwordAnswer.Length > maxSize)
                throw new ArgumentException(SR.GetString(SR.ADMembership_Parameter_too_long, paramName), paramName);
        } 

        private bool ValidatePassword(string password, int maxSize) 
        { 
            if (password == null)
                return false; 

            if (password.Trim().Length < 1)
                return false;
 
            if (maxSize > 0 && password.Length > maxSize)
                return false; 
 
            return true;
        } 

        private void CheckPassword(string password, int maxSize, string paramName)
        {
            if (password == null) 
                throw new ArgumentNullException(paramName);
 
            if (password.Trim().Length < 1) 
                throw new ArgumentException(SR.GetString(SR.Parameter_can_not_be_empty, paramName), paramName);
 
            if (maxSize > 0 && password.Length > maxSize)
                throw new ArgumentException(SR.GetString(SR.Parameter_too_long, paramName, maxSize.ToString(CultureInfo.InvariantCulture)), paramName);
        }
 
        private void CheckUserName(ref string username, int maxSize, string paramName)
        { 
            SecUtility.CheckParameter( ref username, true, true, true, maxSize, paramName ); 

            // 
            // if username is mapped to UPN, it should not contain '\'
            //
            if (usernameIsUPN && (username.IndexOf('\\') != -1))
                throw new ArgumentException(SR.GetString(SR.ADMembership_UPN_contains_backslash, paramName), paramName); 
        }
 
        private int GetDomainControllerLevel(string serverName) 
        {
            int dcLevel = 0; 

            DirectoryEntry rootdse = new DirectoryEntry("LDAP://" + serverName + "/RootDSE", directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes);
            string dcLevelString = (string) rootdse.Properties["domainControllerFunctionality"].Value;
            if (dcLevelString != null) 
                dcLevel = Int32.Parse(dcLevelString, NumberFormatInfo.InvariantInfo);
 
            return dcLevel; 
        }
 
        private void UpdateBadPasswordAnswerAttributes(DirectoryEntry userEntry)
        {

            // 
            // get the password answer tracking related attributes to determine if we are still in an
            // active window for bad password answer attempts 
            // 
            int badPasswordAttemptCount = 0;
            bool inActiveWindow = false; 


            DateTime currentTime = DateTime.UtcNow;
            if (userEntry.Properties.Contains(attributeMapFailedPasswordAnswerTime)) 
            {
                DateTime lastBadPasswordAnswerTime = GetDateTimeFromLargeInteger((NativeComInterfaces.IAdsLargeInteger) PropertyManager.GetPropertyValue(userEntry, attributeMapFailedPasswordAnswerTime)); 
                TimeSpan diffTime = currentTime.Subtract(lastBadPasswordAnswerTime); 
                inActiveWindow = (diffTime <= new TimeSpan(0, PasswordAttemptWindow, 0));
            } 

            // get the current bad password count
            int currentBadPasswordAttemptCount = 0;
            if (userEntry.Properties.Contains(attributeMapFailedPasswordAnswerCount)) 
                currentBadPasswordAttemptCount = (int) PropertyManager.GetPropertyValue(userEntry, attributeMapFailedPasswordAnswerCount);
 
            if (inActiveWindow && (currentBadPasswordAttemptCount > 0)) 
            {
                // within an active window for bad password answer attempts (increment count, if greater than 0) 
                badPasswordAttemptCount =  currentBadPasswordAttemptCount + 1;
            }
            else
            { 
                // start a new active window (set count = 1)
                badPasswordAttemptCount = 1; 
            } 

            // set the bad password attempt count and time 
            userEntry.Properties[attributeMapFailedPasswordAnswerCount].Value = badPasswordAttemptCount;
            userEntry.Properties[attributeMapFailedPasswordAnswerTime].Value = GetLargeIntegerFromDateTime(currentTime);

            if (badPasswordAttemptCount >= maxInvalidPasswordAttempts) 
            {
                // 
                // user needs to be locked out due to too many bad password answer attempts 
                //
                userEntry.Properties[attributeMapFailedPasswordAnswerLockoutTime].Value = GetLargeIntegerFromDateTime(currentTime); 
            }

            userEntry.CommitChanges();
        } 

 
        private void ResetBadPasswordAnswerAttributes(DirectoryEntry userEntry) 
        {
            // 
            // clear the password answer tracking related attributes (reset the window)
            //
            userEntry.Properties[attributeMapFailedPasswordAnswerCount].Value = 0;
            userEntry.Properties[attributeMapFailedPasswordAnswerTime].Value = 0; 
            userEntry.Properties[attributeMapFailedPasswordAnswerLockoutTime].Value = 0;
 
            userEntry.CommitChanges(); 
        }
 
        private MembershipUser GetMembershipUserFromSearchResult(SearchResult res)
        {
            // username
            string username = (string) PropertyManager.GetSearchResultPropertyValue(res, attributeMapUsername); 

            // providerUserKey is the SID of the user 
            byte[] sidBinaryForm = (byte[]) PropertyManager.GetSearchResultPropertyValue(res, "objectSid"); 
            object providerUserKey = new SecurityIdentifier(sidBinaryForm, 0);
 
            // email (optional)
            string email = (res.Properties.Contains(attributeMapEmail)) ? (string) res.Properties[attributeMapEmail][0] : null;

            // passwordQuestion 
            string passwordQuestion = null;
            if ((attributeMapPasswordQuestion != null) && (res.Properties.Contains(attributeMapPasswordQuestion))) 
                passwordQuestion = (string) PropertyManager.GetSearchResultPropertyValue(res, attributeMapPasswordQuestion); 

            //comment (optional) 
            string comment = (res.Properties.Contains("comment")) ? (string) res.Properties["comment"][0] : null;

            //isApproved and isLockedOut
            bool isApproved; 
            bool isLockedOut = false;
            if (directoryInfo.DirectoryType == DirectoryType.AD) 
            { 
                int val = (int) PropertyManager.GetSearchResultPropertyValue(res, "userAccountControl");
                if ((val & UF_ACCOUNT_DISABLED) == 0) 
                    isApproved = true;
                else
                    isApproved = false;
 
                //
                // the "msDS-User-Account-Control-Computed" is the correct attribute to determine if  the 
                // user is locked out or not. This attribute does not exist in W2K schema, so if we do not see this attribute in the result set 
                // we will use the "lockoutTime". Note, if the user is not locked out and the schema is W2K3, this attribute will exist in the result
                // and have value 0 (since it's constructed), therefore absence of the attribute signifies that schema is W2K. 
                //
                if (res.Properties.Contains("msDS-User-Account-Control-Computed"))
                {
                    int val2 = (int) PropertyManager.GetSearchResultPropertyValue(res, "msDS-User-Account-Control-Computed"); 
                    if ((val2 & UF_LOCKOUT) != 0)
                        isLockedOut = true; 
                } 
                else if (res.Properties.Contains("lockoutTime"))
                { 
                    // NOTE: all date-time computation is done in UTC time though the values returned are in local time
                    DateTime lockoutTime = DateTime.FromFileTimeUtc((Int64) PropertyManager.GetSearchResultPropertyValue(res, "lockoutTime"));
                    DateTime currentTime = DateTime.UtcNow;
                    TimeSpan diffTime = currentTime.Subtract(lockoutTime); 
                    isLockedOut = (diffTime <= directoryInfo.ADLockoutDuration);
                } 
 
            }
            else 
            {
                isApproved = true; // if the msDS-UserAccountDisabled attribute if not present then the user is enabled

                if (res.Properties.Contains("msDS-UserAccountDisabled")) 
                    isApproved = !((bool) PropertyManager.GetSearchResultPropertyValue(res, "msDS-UserAccountDisabled"));
 
                // 
                // ADAM schema contains the "msDS-User-Account-Control-Computed" attribute, therefore it is used to determine the
                // lockout status of the user 
                //
                int val2 = (int) PropertyManager.GetSearchResultPropertyValue(res, "msDS-User-Account-Control-Computed");
                if ((val2 & UF_LOCKOUT) != 0)
                    isLockedOut = true; 
            }
 
            // lastLockoutDate (DateTime.FromFileTime cnoverts to Local time) 
            DateTime lastLockoutDate = DefaultLastLockoutDate;
            if (isLockedOut) 
                lastLockoutDate = DateTime.FromFileTime((Int64) PropertyManager.GetSearchResultPropertyValue(res, "lockoutTime"));

            //
            // if password reset is enabled, we need to check if user is locked out due to bad password answer (and set/change the last lockout date) 
            //
            if ((EnablePasswordReset) && (res.Properties.Contains(attributeMapFailedPasswordAnswerLockoutTime))) 
            { 
                // NOTE: all date-time computation is done in UTC time though the values returned are in local time
                DateTime badPasswordAnswerLockoutTime = DateTime.FromFileTimeUtc((Int64) PropertyManager.GetSearchResultPropertyValue(res, attributeMapFailedPasswordAnswerLockoutTime)); 
                DateTime currentTime = DateTime.UtcNow;
                TimeSpan diffTime = currentTime.Subtract(badPasswordAnswerLockoutTime);
                bool isLockedOutByBadPasswordAnswer = (diffTime <= new TimeSpan(0, PasswordAnswerAttemptLockoutDuration, 0));
 
                if (isLockedOutByBadPasswordAnswer)
                { 
                    if (isLockedOut) 
                    {
                        // 
                        // The account is locked both due to bad password and bad password answer, so we have two lockout dates
                        // Taking the later one.
                        //
                        if (DateTime.Compare(badPasswordAnswerLockoutTime, DateTime.FromFileTimeUtc((Int64) PropertyManager.GetSearchResultPropertyValue(res, "lockoutTime"))) > 0) 
                            lastLockoutDate = DateTime.FromFileTime((Int64) PropertyManager.GetSearchResultPropertyValue(res, attributeMapFailedPasswordAnswerLockoutTime));
                    } 
                    else 
                    {
                        // 
                        // Account is locked out only due to bad password answer
                        //
                        isLockedOut = true;
                        lastLockoutDate = DateTime.FromFileTime((Int64) PropertyManager.GetSearchResultPropertyValue(res, attributeMapFailedPasswordAnswerLockoutTime)); 
                    }
                } 
            } 

            //createTimeStamp 
            DateTime whenCreated =  ((DateTime) PropertyManager.GetSearchResultPropertyValue(res, "whenCreated")).ToLocalTime();

            //lastLogon (not supported)
            DateTime lastLogon = DateTime.MinValue; 

            //lastActivity (not supported) 
            DateTime lastActivity = DateTime.MinValue; 

            //lastpwdchange (DateTime.FromFileTime cnoverts to Local time) 
            DateTime lastPasswordChange = DateTime.FromFileTime((Int64) PropertyManager.GetSearchResultPropertyValue(res, "pwdLastSet"));

            return new ActiveDirectoryMembershipUser(Name, username, sidBinaryForm, providerUserKey, email, passwordQuestion, comment, isApproved, isLockedOut, whenCreated, lastLogon, lastActivity, lastPasswordChange, lastLockoutDate, true /* valuesAreUpdated */);
        } 

        private string GetEscapedRdn(string rdn) 
        { 
            NativeComInterfaces.IAdsPathname pathCracker = (NativeComInterfaces.IAdsPathname) new NativeComInterfaces.Pathname();
            return pathCracker.GetEscapedElement(0, rdn); 
        }

        //
        // Generates an escaped name that may be used in an LDAP query. The characters 
        // ( ) * \ must be escaped when used in an LDAP query per RFC 2254.
        // 
 
        internal string GetEscapedFilterValue(string filterValue)
        { 
            return GetEscapedFilterValue(filterValue, true /* escapeWildChar */);
        }

        internal string GetEscapedFilterValue(string filterValue, bool escapeWildChar) 
        {
            int index = -1; 
            char[] specialCharacters = new char[] { '(', ')', '*', '\\' }; 
            char[] specialCharactersWithoutWildChar = new char[] { '(', ')', '\\' };
 
            index = escapeWildChar ? filterValue.IndexOfAny(specialCharacters) : filterValue.IndexOfAny(specialCharactersWithoutWildChar);
            if (index != -1)
            {
 
                //
                // if it contains any of the special characters then we 
                // need to escape those 
                //
 
                StringBuilder str = new StringBuilder(2 * filterValue.Length);
                str.Append(filterValue.Substring(0, index));

                for (int i = index; i < filterValue.Length; i++) { 

                switch (filterValue[i]) { 
                    case ('(') : { 
                        str.Append("\\28");
                        break; 
                    }

                    case (')') : {
                        str.Append("\\29"); 
                        break;
                    } 
 
                    case ('*') : {
                        if (escapeWildChar) 
                            str.Append("\\2A");
                        else
                            str.Append("*");
                        break; 
                    }
 
                    case ('\\') : { 
                        // this may be the escaped version of '*', i.e. "\2A" or "\2a"
                        if ((escapeWildChar) || (!(((filterValue.Length - i) >= 3) && (filterValue[i + 1] == '2') && ((filterValue[i + 2] == 'A') || (filterValue[i + 2] == 'a'))))) 
                            str.Append("\\5C");
                        else
                            str.Append("\\");
                        break; 
                    }
 
                    default : { 
                        str.Append(filterValue[i]);
                        break; 
                    }
                }
            }
 
            return str.ToString();
            } 
            else 
            {
                // 
                // just return the original string
                //

                return filterValue; 
            }
        } 
 
        //
        // 



        private string GenerateAccountName() 
        {
            char[] accountNameEncodingTable = new char[] {'0','1','2','3','4','5','6','7', 
                                                                                '8','9','A','B','C','D','E','F', 
                                                                                'G','H','I','J','K','L','M','N',
                                                                                'O','P','Q','R','S','T','U','V' }; 
            //
            // account name will be 20 characters long;
            //
            char[] accountName = new char[20]; 

            // 
            // Generate a 64 bit random quantity 
            //
            byte[] random = new byte[12]; 

            //RNGCryptoServiceProvider is an implementation of a random number generator.
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            rng.GetBytes(random); // The array is now filled with cryptographically strong random bytes. 

            // create a 32 bit random numbers from this 
            uint random32a = 0; 
            uint random32b = 0;
            uint random32c = 0; 

            for (int i = 0; i < 4; i++)
            {
                random32a = random32a | unchecked((uint)(random[i] << (8 * i))); 
            }
            for (int i = 0; i < 4; i++) 
            { 
                random32b = random32b | unchecked((uint)(random[4 + i] << (8 * i)));
            } 
            for (int i = 0; i < 4; i++)
            {
                random32c = random32c | unchecked((uint)(random[8 + i] << (8 * i)));
            } 

            // 
            // The first character in the account name is a $ sign 
            //
 
            accountName[0] = '$';

            //
            // The next 6 chars are the least 30 bits of random32a (base 32 encoded) 
            //
            for (int i=1;i<=6;i++) 
            { 

                 // 
                 // Lookup the char corresponding to the last 5 bits of
                 // random32a
                 //
 
                 accountName[i] = accountNameEncodingTable[(random32a & 0x1F)];
 
                 // 
                 // Shift random32a right by 5 places
                 // 

                 random32a = random32a >> 5;
            }
 
            //
            // The next char is a "-" to make the name more readable 
            // 

            accountName[7] = '-'; 

            //
            // The next 12 chars are formed by base 32 encoding the last 30
            // bits of random32b and random32c. 
            //
 
            for (int i=8;i<=13;i++) 
            {
                 // 
                 // Lookup the char corresponding to the last 5 bits
                 //

                 accountName[i] = accountNameEncodingTable[(random32b & 0x1F)]; 

                 // 
                 // Shift  right by 5 places 
                 //
                 random32b = random32b >> 5; 
            }

            for (int i=13;i<=19;i++)
            { 
                 //
                 // Lookup the char corresponding to the last 5 bits 
                 // 

                 accountName[i] = accountNameEncodingTable[(random32c & 0x1F)]; 

                 //
                 // Shift  right by 5 places
                 // 
                 random32c = random32c >> 5;
            } 
 
            return new String(accountName);
        } 

        private void SetPasswordPortIfApplicable(DirectoryEntry userEntry)
        {
            // 
            // For ADAM, if the port is specified and we are using Ssl for connection protection,
            // we should set the password port. 
            // 
            if (directoryInfo.DirectoryType == DirectoryType.ADAM)
            { 

                try
                {
                    if ((directoryInfo.ConnectionProtection == ActiveDirectoryConnectionProtection.Ssl) && (directoryInfo.PortSpecified)) 
                    {
                        userEntry.Options.PasswordPort = directoryInfo.Port; 
                        userEntry.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingSsl; 
                    }
                    else if ((directoryInfo.ConnectionProtection == ActiveDirectoryConnectionProtection.SignAndSeal) || (directoryInfo.ConnectionProtection == ActiveDirectoryConnectionProtection.None)) 
                    {
                        userEntry.Options.PasswordPort = directoryInfo.Port;
                        userEntry.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingClear;
                    } 
                }
                catch (COMException e) 
                { 

                    if (e.ErrorCode == unchecked((int) 0x80005008)) 
                    {
                        //
                        // If ADSI returns E_ADS_BAD_PARAMETER, it means we are running
                        // on a platform where ADSI does not support setting of the password port 
                        // Since ADSI will set the password port to 636 and password method to Ssl, we can
                        // ignore this error only if that is what we are trying to set 
                        // 
                        if (!((directoryInfo.Port == DirectoryInformation.SSL_PORT) &&
                            (directoryInfo.ConnectionProtection == ActiveDirectoryConnectionProtection.Ssl))) 
                            throw new ProviderException(SR.GetString(SR.ADMembership_unable_to_set_password_port));
                    }
                    else
                        throw; 

                } 
            } 
        }
 
        private bool IsUpnUnique(string username)
        {

            // 
            // NOTE: we do not need to revert context here since this method is always
            //           called after reverting any impersonated context 
            // 

            DirectoryEntry rootEntry = new DirectoryEntry("GC://" + directoryInfo.ForestName, directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes); 

            DirectorySearcher searcher = new DirectorySearcher(rootEntry);
            searcher.Filter = "(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + GetEscapedFilterValue(username) + "))";
            searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree; 

            if (directoryInfo.ClientSearchTimeout != -1) 
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0); 
            if (directoryInfo.ServerSearchTimeout != -1)
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0); 

            bool result;
            try
            { 
                result = (searcher.FindOne() == null);
            } 
            finally 
            {
                rootEntry.Dispose(); 
            }

            return result;
 
        }
 
        private bool IsEmailUnique(DirectoryEntry containerEntry, string username, string email, bool existing) 
        {
            bool disposeContainerEntry = false; 

            if (containerEntry == null)
            {
                // 
                // NOTE: we do not need to revert context here since this method is always
                //           called after reverting any impersonated context 
                // 
                containerEntry = new DirectoryEntry(directoryInfo.GetADsPath(directoryInfo.ContainerDN), directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes);
                disposeContainerEntry = true; 
            }

            DirectorySearcher searcher = new DirectorySearcher(containerEntry);
            if (existing) 
                searcher.Filter = "(&(objectCategory=person)(objectClass=user)(" + attributeMapUsername + "=*)(" + attributeMapEmail + "=" + GetEscapedFilterValue(email) + ")(!(" + GetEscapedRdn("cn=" + GetEscapedFilterValue(username)) + ")))";
            else 
                searcher.Filter = "(&(objectCategory=person)(objectClass=user)(" + attributeMapUsername + "=*)(" + attributeMapEmail + "=" + GetEscapedFilterValue(email) + "))"; 
            searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
 
            if (directoryInfo.ClientSearchTimeout != -1)
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0);
            if (directoryInfo.ServerSearchTimeout != -1)
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0); 

            bool result; 
            try 
            {
                result = (searcher.FindOne() == null); 
            }
            finally
            {
                if (disposeContainerEntry) 
                {
                    containerEntry.Dispose(); 
                    containerEntry = null; 
                }
            } 

            return result;
        }
 
        private string GetConnectionString(string connectionStringName, bool appLevel)
        { 
            if (String.IsNullOrEmpty(connectionStringName)) 
                return null;
 
            RuntimeConfig config = (appLevel) ? RuntimeConfig.GetAppConfig() : RuntimeConfig.GetConfig();
            ConnectionStringSettings connObj = config.ConnectionStrings.ConnectionStrings[connectionStringName];

            if (connObj == null) 
            {
                // 
                // No connection string by the specified name 
                //
                throw new ProviderException(SR.GetString(SR.Connection_string_not_found, connectionStringName)); 
            }

            return connObj.ConnectionString;
        } 

        private string GetAttributeMapping(NameValueCollection config, string valueName, out int maxLength) 
        { 
            string sValue = config[valueName];
            maxLength = -1; 

            if (sValue == null)
                return null;
 
            sValue = sValue.Trim();
 
            if (sValue.Length == 0) 
                throw new ProviderException(SR.GetString(SR.ADMembership_Schema_mappings_must_not_be_empty, valueName));
 
            return GetValidatedSchemaMapping(valueName, sValue, out maxLength);
        }

        private string GetValidatedSchemaMapping(string valueName, string attributeName, out int maxLength) 
        {
            if (String.Compare(valueName, "attributeMapUsername", StringComparison.Ordinal) == 0) 
            { 
                if (directoryInfo.DirectoryType == DirectoryType.AD)
                { 
                    //
                    // username can only be mapped to "sAMAccountName", "userPrincipalName"
                    //
 
                    if ((!StringUtil.EqualsIgnoreCase(attributeName, "sAMAccountName"))
                        && (!StringUtil.EqualsIgnoreCase(attributeName, "userPrincipalName"))) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_Username_mapping_invalid)); 
                }
                else 
                {
                    //
                    // for ADAM, username can only be mapped to "userPrincipalName"
                    // 
                    if (!StringUtil.EqualsIgnoreCase(attributeName, "userPrincipalName"))
                        throw new ProviderException(SR.GetString(SR.ADMembership_Username_mapping_invalid_ADAM)); 
 
                }
            } 
            else
            {
                //
                // ensure that we are not already using this attribute 
                //
                if (attributesInUse.Contains(attributeName)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_mapping_not_unique, valueName, attributeName)); 

                // 
                // ensure that the attribute exists on the user object
                //
                if (!userObjectAttributes.Contains(attributeName))
                    throw new ProviderException(SR.GetString(SR.ADMembership_MappedAttribute_does_not_exist_on_user, attributeName, valueName)); 
            }
 
            try 
            {
                // 
                // verify that this is an existing property and it's syntax is correct
                //
                DirectoryEntry propertyEntry = new DirectoryEntry(directoryInfo.GetADsPath("schema") + "/"  + attributeName, directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes);
 
                //
                // to get the syntax we need to invoke the "syntax" property 
                // 
                string syntax = (string) propertyEntry.InvokeGet("Syntax");
 
                //
                // check that the syntax is as per the syntaxes table
                //
                if (!StringUtil.EqualsIgnoreCase(syntax, (string) syntaxes[valueName])) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_Wrong_syntax, valueName, (string) syntaxes[valueName]));
 
                // 
                // if the type is "DirectoryString", then set the maxLength value if any
                // 
                maxLength = -1;
                if (StringUtil.EqualsIgnoreCase(syntax, "DirectoryString"))
                {
                    try 
                    {
                        maxLength = (int) propertyEntry.InvokeGet("MaxRange"); 
                    } 
                    catch (TargetInvocationException e)
                    { 
                        //
                        // if the inner exception is a comexception with error code 0x8007500d, then the max range is not set
                        // so we ignore that exception
                        // 
                        if (!((e.InnerException is COMException) && (((COMException)e.InnerException).ErrorCode == unchecked((int) 0x8000500d))))
                            throw; 
                    } 
                }
 
                //
                // unless this is the username (which we already know is mapped
                // to a single valued attribute), the attribute should be single valued
                // 
                if (String.Compare(valueName, "attributeMapUsername", StringComparison.Ordinal) != 0)
                { 
                    bool isMultiValued = (bool) propertyEntry.InvokeGet("MultiValued"); 

                    if (isMultiValued) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_attribute_not_single_valued, valueName));
                }

            } 
            catch (COMException e)
            { 
                if (e.ErrorCode == unchecked((int) 0x80005000)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_MappedAttribute_does_not_exist, attributeName, valueName), e);
                else 
                    throw;
            }

            // 
            // add the attribute name (lower cased) to the in use attributes list
            // 
            return attributeName; 
        }
 
        private int GetRangeUpperForSchemaAttribute(string attributeName)
        {
            int rangeUpper = -1;
            DirectoryEntry propertyEntry = new DirectoryEntry(directoryInfo.GetADsPath("schema") + "/"  + attributeName, directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes); 

            try 
            { 
                rangeUpper = (int) propertyEntry.InvokeGet("MaxRange");
            } 
            catch (TargetInvocationException e)
            {
                //
                // if the inner exception is a comexception with error code 0x8007500d, then the max range is not set 
                // so we ignore that exception
                // 
                if (!((e.InnerException is COMException) && (((COMException)e.InnerException).ErrorCode == unchecked((int) 0x8000500d)))) 
                    throw;
            } 

            return rangeUpper;
        }
 
        private Hashtable GetUserObjectAttributes()
        { 
            DirectoryEntry de = new DirectoryEntry(directoryInfo.GetADsPath("schema") + "/user", directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes); 
            object value = null;
            bool listEmpty = false; 
            Hashtable attributes = new Hashtable(StringComparer.OrdinalIgnoreCase);

            try
            { 
                value = de.InvokeGet("MandatoryProperties");
            } 
            catch (COMException e) 
            {
                if (e.ErrorCode == unchecked((int) 0x8000500D)) 
                {
                    listEmpty = true;
                }
                else 
                    throw;
            } 
 
            if (!listEmpty)
            { 
                if (value is ICollection)
                {
                    foreach (string attribute in (ICollection) value)
                    { 
                        if (!attributes.Contains(attribute))
                            attributes.Add(attribute, null); 
                     } 
                }
                else 
                {
                    // single value

                    if (!attributes.Contains(value)) 
                        attributes.Add(value, null);
                } 
            } 

            listEmpty = false; 
            try
            {
                value = de.InvokeGet("OptionalProperties");
            } 
            catch (COMException e)
            { 
                if (e.ErrorCode == unchecked((int) 0x8000500D)) 
                {
                    listEmpty = true; 
                }
                else
                    throw;
            } 

            if (!listEmpty) 
            { 
                if (value is ICollection)
                { 
                    foreach (string attribute in (ICollection) value)
                    {
                        if (!attributes.Contains(attribute))
                            attributes.Add(attribute, null); 
                     }
                } 
                else 
                {
                    // single value 
                    if (!attributes.Contains(value))
                        attributes.Add(value, null);
                }
            } 

            return attributes; 
 
        }
 
        private DateTime GetDateTimeFromLargeInteger(NativeComInterfaces.IAdsLargeInteger largeIntValue)
        {
            //
            // Convert large integer to int64 value 
            //
            Int64 int64Value = largeIntValue.HighPart * 0x100000000 + (uint) largeIntValue.LowPart; 
 
            //
            // Return the DateTime in utc 
            //
            return DateTime.FromFileTimeUtc(int64Value);

        } 

        private NativeComInterfaces.IAdsLargeInteger GetLargeIntegerFromDateTime(DateTime dateTimeValue) 
        { 
            //
            // Convert DateTime value to utc file time 
            //
            Int64 int64Value = dateTimeValue.ToFileTimeUtc();

            // 
            // convert to large integer
            // 
            NativeComInterfaces.IAdsLargeInteger largeIntValue = (NativeComInterfaces.IAdsLargeInteger) new NativeComInterfaces.LargeInteger(); 
            largeIntValue.HighPart = (int) (int64Value >> 32);
            largeIntValue.LowPart = (int) (int64Value & 0xFFFFFFFF); 

            return largeIntValue;
        }
 
        private string Encrypt(string clearTextString)
        { 
            // we should never be getting null input here 
            Debug.Assert(clearTextString != null);
 
            byte[] bIn = Encoding.Unicode.GetBytes(clearTextString);

            byte[] bSalt = new byte[AD_SALT_SIZE_IN_BYTES];
            (new RNGCryptoServiceProvider()).GetBytes(bSalt); 

            byte[] bAll = new byte[bSalt.Length + bIn.Length]; 
            Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length); 
            Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
 
            return Convert.ToBase64String(EncryptPassword(bAll));
        }

        private string Decrypt(string encryptedString) 
        {
            // we should never be getting null input here 
            Debug.Assert(encryptedString != null); 

            byte[] bEncryptedData = Convert.FromBase64String(encryptedString); 

            byte[] bAll = DecryptPassword(bEncryptedData);

            return Encoding.Unicode.GetString(bAll, AD_SALT_SIZE_IN_BYTES, bAll.Length - AD_SALT_SIZE_IN_BYTES); 
        }
 
    } 

    internal sealed class DirectoryInformation 
    {
        private string serverName = null;
        private string containerDN = null;
        private string creationContainerDN = null; 
        private string adspath = null;
        private int port = 389; 
        private bool portSpecified = false; 
        private DirectoryType directoryType = DirectoryType.Unknown;
        private ActiveDirectoryConnectionProtection connectionProtection = ActiveDirectoryConnectionProtection.None; 
        private bool concurrentBindSupported = false;
        private int clientSearchTimeout = -1;
        private int serverSearchTimeout = -1;
        private DirectoryEntry rootdse = null; 
        private NetworkCredential credentials = null;
        private AuthenticationTypes authenticationType = AuthenticationTypes.None; 
        private AuthType ldapAuthType = AuthType.Basic; 
        private string adamPartitionDN = null;
        private TimeSpan adLockoutDuration; 
        private string forestName = null;
        private string domainName = null;
        private bool isServer = false;
 
        private const string LDAP_CAP_ACTIVE_DIRECTORY_ADAM_OID ="1.2.840.113556.1.4.1851";
        private const string LDAP_CAP_ACTIVE_DIRECTORY_OID ="1.2.840.113556.1.4.800"; 
        private const string LDAP_SERVER_FAST_BIND_OID = "1.2.840.113556.1.4.1781"; 
        internal const int SSL_PORT = 636;
        private const int GC_PORT = 3268; 
        private const int GC_SSL_PORT = 3269;
        private const string GUID_USERS_CONTAINER_W = "a9d1ca15768811d1aded00c04fd8d5cd";

        // 
        // authentication types for S.DS and S.DS.Protocols (rows are indexed by connection protection
        // columns are indexed by type of credentials (see CredentialType enum) 
        // 
        AuthenticationTypes[,] authTypes = new AuthenticationTypes[,]
                    {{AuthenticationTypes.None, AuthenticationTypes.None}, 
                      {AuthenticationTypes.Secure | AuthenticationTypes.SecureSocketsLayer , AuthenticationTypes.SecureSocketsLayer },
                      {AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing, AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing}};

        AuthType[,] ldapAuthTypes = new AuthType[,] 
                     {{AuthType.Negotiate, AuthType.Basic},
                      {AuthType.Negotiate, AuthType.Basic}, 
                      {AuthType.Negotiate, AuthType.Negotiate}}; 

        internal DirectoryInformation(string adspath, 
                                                            NetworkCredential credentials,
                                                            string connProtection,
                                                            int clientSearchTimeout,
                                                            int serverSearchTimeout, 
                                                            bool enablePasswordReset)
        { 
 
           //
           // all parameters have already been validated at this point 
           //

            this.adspath = adspath;
            this.credentials = credentials; 
            this.clientSearchTimeout = clientSearchTimeout;
            this.serverSearchTimeout = serverSearchTimeout; 
 
            Debug.Assert(adspath != null);
            Debug.Assert(adspath.Length > 0); 

            //
            // Provider must be LDAP
            // 
            if (!(adspath.StartsWith("LDAP", StringComparison.Ordinal)))
                throw new ProviderException(SR.GetString(SR.ADMembership_OnlyLdap_supported)); 
 
            //
            // Parse out the server/domain information 
            //
            NativeComInterfaces.IAdsPathname pathCracker = (NativeComInterfaces.IAdsPathname) new NativeComInterfaces.Pathname();

            try { 
                pathCracker.Set(adspath, NativeComInterfaces.ADS_SETTYPE_FULL);
            } 
            catch (COMException e) 
            {
                if (e.ErrorCode == unchecked((int) 0x80005000)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_invalid_path));
                else
                    throw;
            } 

            // Get the server and container names 
            try 
            {
                serverName = pathCracker.Retrieve(NativeComInterfaces.ADS_FORMAT_SERVER); 
            }
            catch (COMException e)
            {
                if (e.ErrorCode == unchecked((int) 0x80005000)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_ServerlessADsPath_not_supported));
                else 
                    throw; 
            }
            Debug.Assert(serverName != null); 

            creationContainerDN = containerDN = pathCracker.Retrieve(NativeComInterfaces.ADS_FORMAT_X500_DN);

            // 
            // Parse out the port number if specified
            // 
            int index = serverName.IndexOf(':'); 
            if (index != -1)
            { 
                string tempStr = serverName;

                serverName = tempStr.Substring(0, index);
 
                Debug.Assert(tempStr.Length > index);
                port = Int32.Parse(tempStr.Substring(index + 1), NumberFormatInfo.InvariantInfo); 
                portSpecified = true; 
            }
 
            if (String.Compare(connProtection, "Secure", StringComparison.Ordinal) == 0)
            {
                //
                // The logic is as follows: 
                // 1. Try Ssl first and check if concurrent binds are possible for validating users
                // 2. If Ssl is not supported, try signing and sealing 
                // 3. If both the above are not supported, then we will fail 
                //
 
                bool trySignAndSeal = false;
                bool trySslWithSecureAuth = false;

                // first try with simple bind 
                if (!IsDefaultCredential())
                { 
 
                    authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.NonWindows);
                    ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.NonWindows); 

                    try
                    {
                        rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); 
                        // this will force a bind
                        rootdse.RefreshCache(); 
                        this.connectionProtection = ActiveDirectoryConnectionProtection.Ssl; 
                        if (!portSpecified)
                        { 
                            port = SSL_PORT;
                            portSpecified = true;
                        }
                    } 
                    catch (COMException ce)
                    { 
 
                        if (ce.ErrorCode == unchecked((int) 0x8007052e))
                        { 
                            //
                            // this could be an ADAM target with windows user (in that case simple bind will not work)
                            //
                            trySslWithSecureAuth = true; 
                        }
                        else if (ce.ErrorCode == unchecked((int) 0x8007203a)) 
                        { 
                            // server is not operational error, do nothing, we need to fall back to SignAndSeal
                            trySignAndSeal = true; 
                        }
                        else
                            throw;
                     } 
                }
                else 
                { 
                    // default credentials, so we have to do secure bind
                    trySslWithSecureAuth = true; 
                }

                if (trySslWithSecureAuth)
                { 

                    authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.Windows); 
                    ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.Windows); 

                    try 
                    {
                        rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType);
                        // this will force a bind
                        rootdse.RefreshCache(); 
                        this.connectionProtection = ActiveDirectoryConnectionProtection.Ssl;
                        if (!portSpecified) 
                        { 
                            port = SSL_PORT;
                            portSpecified = true; 
                        }

                    }
                    catch (COMException ce) 
                    {
                        if (ce.ErrorCode == unchecked((int) 0x8007203a)) 
                        { 
                            // server is not operational error, do nothing, we need to fall back to SignAndSeal
                            trySignAndSeal = true; 
                        }
                        else
                            throw;
                     } 

                } 
 
                if (trySignAndSeal)
                { 
                    authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.SignAndSeal, CredentialsType.Windows);
                    ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.SignAndSeal, CredentialsType.Windows);

                    try 
                    {
                        rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); 
                        rootdse.RefreshCache(); 
                        this.connectionProtection = ActiveDirectoryConnectionProtection.SignAndSeal;
                    } 
                    catch (COMException e)
                    {
                        throw new ProviderException(SR.GetString(SR.ADMembership_Secure_connection_not_established, e.Message), e);
                    } 
                }
            } 
            else 
            {
                // 
                // No connection protection
                //

                // 
                // we will do a simple bind but we must ensure that the credentials are explicitly specified
                // since in the case of default credentials we cannot honor it (default credentials become anonymous in the case of 
                // simple bind) 
                //
                if (IsDefaultCredential()) 
                    throw new NotSupportedException(SR.GetString(SR.ADMembership_Default_Creds_not_supported));

                // simple bind
                authenticationType = GetAuthenticationTypes(connectionProtection, CredentialsType.NonWindows); 
                ldapAuthType = GetLdapAuthenticationTypes(connectionProtection, CredentialsType.NonWindows);
 
                rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); 

            } 

            //
            // Determine whether this is AD or ADAM by binding to the rootdse and
            // checking the supported capabilities 
            //
            if (rootdse == null) 
                rootdse = new DirectoryEntry(GetADsPath("RootDSE"), GetUsername(), GetPassword(), authenticationType); 
            directoryType = GetDirectoryType();
 
            //
            // if the directory type is ADAM and the conntectionProtection was selected
            // as sign and seal, then we should throw an ProviderException. This is becuase validate user will always fail for ADAM
            // because ADAM does not support secure authentication for ADAM users. 
            //
            if ((directoryType == DirectoryType.ADAM) && (this.connectionProtection == ActiveDirectoryConnectionProtection.SignAndSeal)) 
                throw new ProviderException(SR.GetString(SR.ADMembership_Ssl_connection_not_established)); 

            // 
            // for AD, we need to block the GC ports
            //
            if ((directoryType == DirectoryType.AD) && ((port == GC_PORT) || (port == GC_SSL_PORT)))
                throw new ProviderException(SR.GetString(SR.ADMembership_GCPortsNotSupported)); 

            // 
            // if container dn is null, we need to get the default naming context 
            // (containerDN cannot be null for ADAM)
            // 
            if (String.IsNullOrEmpty(containerDN))
            {
                if (directoryType == DirectoryType.AD)
                { 
                    containerDN = (string)rootdse.Properties["defaultNamingContext"].Value;
                    if (containerDN == null) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_DefContainer_not_specified)); 

                    // 
                    // we will create users in the default users container, check that it exists
                    //
                    string wkUsersContainerPath = GetADsPath("");
                    DirectoryEntry containerEntry = new DirectoryEntry(wkUsersContainerPath, GetUsername(), GetPassword(), authenticationType); 

                    try 
                    { 
                        creationContainerDN = (string) PropertyManager.GetPropertyValue(containerEntry, "distinguishedName");
                    } 
                    catch (COMException ce)
                    {
                        if (ce.ErrorCode == unchecked((int) 0x80072030))
                            throw new ProviderException(SR.GetString(SR.ADMembership_DefContainer_does_not_exist)); 
                        else
                            throw; 
                    } 
                }
                else 
                {
                    // container must be specified for ADAM
                    throw new ProviderException(SR.GetString(SR.ADMembership_Container_must_be_specified));
                } 
            }
            else 
            { 
                //
                // Normalize the container name (incase it was specified as GUID or WKGUID) 
                //
                DirectoryEntry containerEntry = new DirectoryEntry(GetADsPath(containerDN), GetUsername(), GetPassword(), authenticationType);

                try 
                {
                    creationContainerDN = containerDN = (string) PropertyManager.GetPropertyValue(containerEntry, "distinguishedName"); 
                } 
                catch (COMException ce)
                { 
                    if (ce.ErrorCode == unchecked((int) 0x80072030))
                        throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist));
                    else
                        throw; 
                }
            } 
 
            //
            // Check if the specified path(container) exists on the specified server/domain 
            // (NOTE: We need to do this using S.DS.Protocols rather than S.DS because we need to
            //            bypass the referral chasing which is automatic in S.DS)
            //
 
            LdapConnection tempConnection = new LdapConnection(new LdapDirectoryIdentifier(serverName + ":" + port), GetCredentialsWithDomain(credentials), ldapAuthType);
            tempConnection.SessionOptions.ProtocolVersion = 3; 
 
            try
            { 
                tempConnection.SessionOptions.ReferralChasing = System.DirectoryServices.Protocols.ReferralChasingOptions.None;
                SetSessionOptionsForSecureConnection(tempConnection, false /*useConcurrentBind */);
                tempConnection.Bind();
 

                SearchRequest request = new SearchRequest(); 
                request.DistinguishedName = containerDN; 
                request.Filter = "(objectClass=*)";
                request.Scope = System.DirectoryServices.Protocols.SearchScope.Base; 
                request.Attributes.Add("distinguishedName");
                request.Attributes.Add("objectClass");

                if (ServerSearchTimeout != -1) 
                    request.TimeLimit = new TimeSpan(0, ServerSearchTimeout, 0);
 
                SearchResponse response; 
                try
                { 
                    response = (SearchResponse) tempConnection.SendRequest(request);
                    if (response.ResultCode == ResultCode.Referral || response.ResultCode ==  ResultCode.NoSuchObject)
                        throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist));
                    else if (response.ResultCode != ResultCode.Success) 
                        throw new ProviderException(response.ErrorMessage);
                } 
                catch (DirectoryOperationException oe) 
                {
                    SearchResponse errorResponse = (SearchResponse) oe.Response; 
                    if (errorResponse.ResultCode == ResultCode.NoSuchObject)
                        throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist));
                    else throw;
                } 

                // 
                // check that the container is of an object type that can be a superior of a user object 
                //
                DirectoryAttribute objectClass = response.Entries[0].Attributes["objectClass"]; 
                if (!ContainerIsSuperiorOfUser(objectClass))
                    throw new ProviderException(SR.GetString(SR.ADMembership_Container_not_superior));

                // 
                // Determine whether concurrent bind is supported
                // 
                if ((connectionProtection == ActiveDirectoryConnectionProtection.None) || (connectionProtection == ActiveDirectoryConnectionProtection.Ssl)) 
                {
                    this.concurrentBindSupported = IsConcurrentBindSupported(tempConnection); 
                }

            }
            finally 
            {
                tempConnection.Dispose(); 
            } 

            // 
            // if this is ADAM, get the partition DN
            //
            if (directoryType == DirectoryType.ADAM)
            { 
                adamPartitionDN = GetADAMPartitionFromContainer();
            } 
            else 
            {
                if (enablePasswordReset) 
                {
                    // for AD, get the lockout duration for user account auto unlock
                    DirectoryEntry de = new DirectoryEntry(GetADsPath((string) PropertyManager.GetPropertyValue(rootdse, "defaultNamingContext")), GetUsername(), GetPassword(), AuthenticationTypes);
                    NativeComInterfaces.IAdsLargeInteger largeIntValue = (NativeComInterfaces.IAdsLargeInteger) PropertyManager.GetPropertyValue(de, "lockoutDuration"); 
                    Int64 int64Value = largeIntValue.HighPart * 0x100000000 + (uint) largeIntValue.LowPart;
 
                    // int64Value is the negative of the number of 100 nanoseconds interval that makes up the lockout duration 
                    adLockoutDuration = new TimeSpan(-int64Value);
                } 
            }
        }

        internal bool ConcurrentBindSupported 
        {
            get { return concurrentBindSupported; } 
        } 

        internal string ContainerDN 
        {
            get { return containerDN; }
        }
 
        internal string CreationContainerDN
        { 
            get { return creationContainerDN; } 
        }
 
        internal int Port
        {
            get { return port; }
        } 

        internal bool PortSpecified 
        { 
            get { return portSpecified; }
        } 

#if UNUSED_CODE
        internal NetworkCredential Credential
        { 
            get { return credentials; }
        } 
#endif 

        internal DirectoryType DirectoryType 
        {
            get { return directoryType; }
        }
 
        internal ActiveDirectoryConnectionProtection ConnectionProtection
        { 
            get { return connectionProtection; } 
        }
 
        internal AuthenticationTypes AuthenticationTypes
        {
            get { return authenticationType; }
        } 

        internal int ClientSearchTimeout 
        { 
            get { return clientSearchTimeout; }
        } 

        internal int ServerSearchTimeout
        {
            get { return serverSearchTimeout; } 
        }
 
        internal string ADAMPartitionDN 
        {
            get { return adamPartitionDN; } 
        }

        internal TimeSpan ADLockoutDuration
        { 
            get { return adLockoutDuration; }
        } 
 
        internal string ForestName
        { 
            get { return forestName; }
        }

        internal string DomainName 
        {
            get { return domainName; } 
        } 

        internal void InitializeDomainAndForestName() 
        {
            if (!isServer)
            {
                DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, serverName, GetUsername(), GetPassword()); 
                try
                { 
                    Domain domain = Domain.GetDomain(context); 
                    domainName = GetNetbiosDomainNameIfAvailable(domain.Name);
                    forestName = domain.Forest.Name; 
                }
                catch (ActiveDirectoryObjectNotFoundException)
                {
                    // the serverName may be the name of the server rather than domain 
                    isServer = true;
                } 
            } 

            if (isServer) 
            {
                DirectoryContext context = new DirectoryContext(DirectoryContextType.DirectoryServer, serverName, GetUsername(), GetPassword());
                try
                { 
                    Domain domain = Domain.GetDomain(context);
                    domainName = GetNetbiosDomainNameIfAvailable(domain.Name); 
                    forestName = domain.Forest.Name; 
                }
                catch (ActiveDirectoryObjectNotFoundException) 
                {
                    // we were unable to contact the domain or server
                    throw new ProviderException(SR.GetString(SR.ADMembership_unable_to_contact_domain));
                } 
            }
        } 
 
        internal void SelectServer()
        { 
            //
            // if the name specified in the target is a domain name, then we should
            // perform all operations on the PDC. If the name is not a domain name
            // then it would be the name of a server. In that case we perform all 
            // operations on that server
            // 
            serverName = GetPdcIfDomain(serverName); 
            isServer = true;
        } 

        //
        // Creates a new ldap connection with the specified auth types
        // (the session options are set based on the connection protection that was 
        // determined during the initialize method)
        // 
        internal LdapConnection CreateNewLdapConnection(AuthType authType) 
        {
            LdapConnection newConnection = null; 

            newConnection = new LdapConnection(new LdapDirectoryIdentifier(serverName + ":" + port));
            newConnection.AuthType = authType;
            newConnection.SessionOptions.ProtocolVersion = 3; 
            SetSessionOptionsForSecureConnection(newConnection, true /* useConcurrentBind */);
 
            return newConnection; 
        }
 
        //
        // this method returns the ADsPath for the given DN
        //
        internal string GetADsPath(string dn) 
        {
            string path = null; 
 
            //
            // provider and server information 
            //
            Debug.Assert(serverName != null);
            path = "LDAP://" + serverName;
 
            //
            // port info if specified 
            // 
            if (portSpecified)
                path = path + ":" + port; 

            //
            // DN of the object
            // 
            Debug.Assert(dn != null);
            NativeComInterfaces.IAdsPathname pathCracker = (NativeComInterfaces.IAdsPathname) new NativeComInterfaces.Pathname(); 
            pathCracker.Set(dn, NativeComInterfaces.ADS_SETTYPE_DN); 
            pathCracker.EscapedMode = NativeComInterfaces.ADS_ESCAPEDMODE_ON;
            path = path + "/" + pathCracker.Retrieve(NativeComInterfaces.ADS_FORMAT_X500_DN); 

            return path;

        } 

        internal void SetSessionOptionsForSecureConnection(LdapConnection connection, bool useConcurrentBind) 
        { 

            if (connectionProtection == ActiveDirectoryConnectionProtection.Ssl) { 
                connection.SessionOptions.SecureSocketLayer = true;
            }
            else if (connectionProtection == ActiveDirectoryConnectionProtection.SignAndSeal)
            { 
                connection.SessionOptions.Signing = true;
                connection.SessionOptions.Sealing = true; 
            } 

            if (useConcurrentBind && this.concurrentBindSupported) 
            {
                try
                {
                    connection.SessionOptions.FastConcurrentBind(); 
                }
                catch (PlatformNotSupportedException) 
                { 
                    //
                    // concurrent bind is not supported by the client, (continue without it and don't try to set it next time) 
                    //
                    this.concurrentBindSupported = false;
                }
                catch (DirectoryOperationException) 
                {
                    // Dev10 Bug# 623663: 
                    // concurrent bind is not supported when a client certificate is specified, (continue without it and don't try to set it next time) 

                    this.concurrentBindSupported = false; 
                }
            }
        }
 
        [EnvironmentPermission(SecurityAction.Assert, Read="USERNAME")]
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] 
        internal string GetUsername() 
        {
            if (credentials == null) 
                return null;

            if (credentials.UserName == null)
                return null; 

            if (credentials.UserName.Length == 0 && (credentials.Password == null || credentials.Password.Length == 0)) 
                return null; 

            return this.credentials.UserName; 
        }

        [EnvironmentPermission(SecurityAction.Assert, Read="USERNAME")]
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] 
        internal string GetPassword()
        { 
            if (credentials == null) 
                return null;
 
            if (credentials.Password == null)
                return null;

            if (credentials.Password.Length == 0 && (credentials.UserName == null || credentials.UserName.Length == 0)) 
                return null;
 
            return this.credentials.Password; 
        }
 
        internal AuthenticationTypes GetAuthenticationTypes(ActiveDirectoryConnectionProtection connectionProtection, CredentialsType type)
        {
            return authTypes[(int) connectionProtection, (int) type];
        } 

        internal AuthType GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection connectionProtection, CredentialsType type) 
        { 
            return ldapAuthTypes[(int) connectionProtection, (int) type];
        } 

        [EnvironmentPermission(SecurityAction.Assert, Read="USERNAME")]
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)]
        internal bool IsDefaultCredential() 
        {
            if ((credentials.UserName == null || credentials.UserName.Length == 0) && (credentials.Password == null || credentials.Password.Length == 0)) 
                return true; 

            return false; 
        }

        [EnvironmentPermission(SecurityAction.Assert, Read="USERNAME")]
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] 
        internal static NetworkCredential GetCredentialsWithDomain(NetworkCredential credentials)
        { 
            NetworkCredential credentialsWithDomain; 

            if (credentials == null) 
                credentialsWithDomain = new NetworkCredential(null, null);
            else
            {
                string tempUsername = credentials.UserName; 
                string username = null;
                string password = null; 
                string domainName = null; 

                if (!String.IsNullOrEmpty(tempUsername)) 
                {
                    int index = tempUsername.IndexOf('\\');
                    if (index != -1)
                    { 
                        domainName = tempUsername.Substring(0, index);
                        username = tempUsername.Substring(index + 1); 
                    } 
                    else
                        username = tempUsername; 

                    password = credentials.Password;
                }
                credentialsWithDomain = new NetworkCredential(username, password, domainName); 
            }
 
            return credentialsWithDomain; 
        }
 
        private bool IsConcurrentBindSupported(LdapConnection ldapConnection)
        {
            bool result = false;
 
            Debug.Assert(ldapConnection != null);
 
            // 
            // supportedExtension is a constructed attribute so we need to search and load that attribute explicitly
            // 
            SearchRequest request = new SearchRequest();
            request.Scope = System.DirectoryServices.Protocols.SearchScope.Base;
            request.Attributes.Add("supportedExtension");
 
            if (ServerSearchTimeout != -1)
                request.TimeLimit = new TimeSpan(0, ServerSearchTimeout, 0); 
 
            SearchResponse response = (SearchResponse) ldapConnection.SendRequest(request);
            if (response.ResultCode != ResultCode.Success) 
                throw new ProviderException(response.ErrorMessage);

            foreach (string supportedExtension in response.Entries[0].Attributes["supportedExtension"].GetValues(typeof(string)))
            { 
                if (StringUtil.EqualsIgnoreCase(supportedExtension, LDAP_SERVER_FAST_BIND_OID))
                { 
                    result = true; 
                    break;
                } 
            }

            return result;
        } 

        // 
        // This function goes through each of the naming contexts on the server 
        // and determines which one is the longest postfix of the container DN.
        // That will give the DN of partition that the container lives in. 
        //
        //
        private string GetADAMPartitionFromContainer()
        { 
            string partitionName = null;
            int startsAt = Int32.MaxValue; 
 
            foreach(string namingContext in rootdse.Properties["namingContexts"])
            { 
                bool endsWith = containerDN.EndsWith(namingContext, StringComparison.Ordinal);
                int lastIndexOf = containerDN.LastIndexOf(namingContext, StringComparison.Ordinal);

                if (endsWith && (lastIndexOf != -1) && (lastIndexOf < startsAt)) 
                {
                    partitionName = namingContext; 
                    startsAt = lastIndexOf; 
                }
            } 

            if (partitionName == null)
                throw new ProviderException(SR.GetString(SR.ADMembership_No_ADAM_Partition));
 
            return partitionName;
        } 
 
        //
        // This function goes through each of the object class values for the container to determine 
        // whether the object class is one of the possible superiors of the user object
        //
        private bool ContainerIsSuperiorOfUser(DirectoryAttribute objectClass)
        { 
            ArrayList possibleSuperiorsList = new ArrayList();
 
            // 
            // first get a list of all the classes from which the user class is derived
            // 
            DirectoryEntry de = new DirectoryEntry(GetADsPath("schema") + "/user", GetUsername(), GetPassword(), AuthenticationTypes);
            ArrayList classesList = new ArrayList();
            bool derivedFromlistEmpty = false;
            object value = null; 

            try 
            { 
                value = de.InvokeGet("DerivedFrom");
            } 
            catch (COMException e)
            {
                if (e.ErrorCode == unchecked((int) 0x8000500D))
                { 
                    derivedFromlistEmpty = true;
                } 
                else 
                    throw;
            } 

            if (!derivedFromlistEmpty)
            {
                if (value is ICollection) 
                {
                    classesList.AddRange((ICollection) value); 
                } 
                else
                { 
                    // single value
                    classesList.Add((string) value);
                }
            } 

            // 
            // we will use this list to create a filter of all the classSchema objects that we need to determine the recursive list 
            // of "possibleSecuperiors". We need to add the user class also.
            // 
            classesList.Add("user");

            //
            // Now search under the schema naming context for all these classes and get the "possSuperiors" and "systemPossSuperiors" attributes 
            //
            DirectoryEntry schemaNC = new DirectoryEntry(GetADsPath((string) rootdse.Properties["schemaNamingContext"].Value), GetUsername(), GetPassword(), AuthenticationTypes); 
            DirectorySearcher searcher = new DirectorySearcher(schemaNC); 

            searcher.Filter = "(&(objectClass=classSchema)(|"; 
            foreach(string supClass in classesList)
                searcher.Filter += "(ldapDisplayName=" + supClass + ")";
            searcher.Filter += "))";
 
            searcher.SearchScope = System.DirectoryServices.SearchScope.OneLevel;
            searcher.PropertiesToLoad.Add("possSuperiors"); 
            searcher.PropertiesToLoad.Add("systemPossSuperiors"); 

            SearchResultCollection resCol = searcher.FindAll(); 

            try
            {
                foreach (SearchResult res in resCol) 
                {
                    possibleSuperiorsList.AddRange(res.Properties["possSuperiors"]); 
                    possibleSuperiorsList.AddRange(res.Properties["systemPossSuperiors"]); 
                }
            } 
            finally
            {
                resCol.Dispose();
            } 

            // 
            // Now we have the list of all the possible superiors, check if the objectClass that was specified as a parameter 
            // to this function is one of these values, if so, return true else false
            // 
            foreach (string objectClassValue in objectClass.GetValues(typeof(string)))
            {
                if (possibleSuperiorsList.Contains(objectClassValue))
                    return true; 
            }
 
            return false; 
        }
 
        //
        // This method determines whether the server we are talking to
        // is an AD domain controller or an ADAM instance
        // 
        private DirectoryType GetDirectoryType()
        { 
            DirectoryType directoryType = DirectoryType.Unknown; 

            foreach (string supportedCapability in rootdse.Properties["supportedCapabilities"]) 
            {
                if (StringUtil.EqualsIgnoreCase(supportedCapability, LDAP_CAP_ACTIVE_DIRECTORY_ADAM_OID))
                {
                    directoryType = DirectoryType.ADAM; 
                    break;
                } 
                else if (StringUtil.EqualsIgnoreCase(supportedCapability, LDAP_CAP_ACTIVE_DIRECTORY_OID)) 
                {
                    directoryType = DirectoryType.AD; 
                    break;
                }
            }
 
            if (directoryType == DirectoryType.Unknown)
                throw new ProviderException(SR.GetString(SR.ADMembership_Valid_Targets)); 
 
            return directoryType;
        } 

        //
        // This method returns the dns name of the primary domain controller if the specified name is a domain,
        // else is just returns the name as is 
        //
        internal string GetPdcIfDomain(string name) 
        { 
            IntPtr pDomainControllerInfo = IntPtr.Zero;
 
            /* DS_DIRECTORY_SERVICE_REQUIRED   0x00000010
                 DS_RETURN_DNS_NAME              0x40000000
                 DS_PDC_REQUIRED                 0x00000080 */
            uint flags = 0x00000010 | 0x40000000 | 0x00000080; 
            string pdc = null;
 
            int ERROR_NO_SUCH_DOMAIN = 1355; 

            int result = NativeMethods.DsGetDcName(null, name, IntPtr.Zero, null,  flags, out pDomainControllerInfo); 

            try {
                if (result == 0)
                { 
                    // success case
                    DomainControllerInfo domainControllerInfo = new DomainControllerInfo(); 
                    Marshal.PtrToStructure(pDomainControllerInfo, domainControllerInfo); 

                    Debug.Assert(domainControllerInfo != null); 
                    Debug.Assert(domainControllerInfo.DomainControllerName != null);
                    Debug.Assert(domainControllerInfo.DomainControllerName.Length > 2);

                    // domain controller name is in the format "\\server", so we need to strip the back slashes 
                    pdc = domainControllerInfo.DomainControllerName.Substring(2);
                } 
                else if (result == ERROR_NO_SUCH_DOMAIN) 
                    pdc = name;
                else 
                    throw new ProviderException(GetErrorMessage(result));
            }
            finally
            { 
                // free the buffer
                if (pDomainControllerInfo != IntPtr.Zero) { 
                    NativeMethods.NetApiBufferFree(pDomainControllerInfo); 
                }
            } 

            return pdc;
        }
 
        internal string GetNetbiosDomainNameIfAvailable(string dnsDomainName)
        { 
            string result = null; 

            // 
            // Get the netbios name from the "nETBIOSName" attribute on the crossRef object for this domain
            //
            DirectoryEntry partitionsEntry = new DirectoryEntry(GetADsPath("CN=Partitions," + (string) PropertyManager.GetPropertyValue(rootdse, "configurationNamingContext")), GetUsername(), GetPassword());
            DirectorySearcher searcher = new DirectorySearcher(partitionsEntry); 
            searcher.SearchScope = System.DirectoryServices.SearchScope.OneLevel;
 
            StringBuilder str = new StringBuilder(15); 
            str.Append("(&(objectCategory=crossRef)(dnsRoot=");
            str.Append(dnsDomainName); 
            str.Append(")(systemFlags:1.2.840.113556.1.4.804:=1)(systemFlags:1.2.840.113556.1.4.804:=2))");

            searcher.Filter = str.ToString();
            searcher.PropertiesToLoad.Add("nETBIOSName"); 

            SearchResult res = searcher.FindOne(); 
            if ((res == null) || (!(res.Properties.Contains("nETBIOSName")))) 
                // return the dns name
                result = dnsDomainName; 
            else
                // return the netbios name
                result = (string) PropertyManager.GetSearchResultPropertyValue(res, "nETBIOSName");
 
            return result;
        } 
 
        private static string GetErrorMessage(int errorCode)
        { 
            uint temp = (uint) errorCode;
            temp = ( (((temp) & 0x0000FFFF) | (7 << 16) | 0x80000000));

            string errorMsg = String.Empty; 
            StringBuilder sb = new StringBuilder(256);
            int result = NativeMethods.FormatMessageW(NativeMethods.FORMAT_MESSAGE_IGNORE_INSERTS | 
                                       NativeMethods.FORMAT_MESSAGE_FROM_SYSTEM | 
                                       NativeMethods.FORMAT_MESSAGE_ARGUMENT_ARRAY,
                                       0, (int)temp, 0, sb, sb.Capacity + 1, 0); 
            if (result != 0) {
                errorMsg = sb.ToString(0, result);
            }
            else { 
                errorMsg = SR.GetString(SR.ADMembership_Unknown_Error, string.Format(CultureInfo.InvariantCulture, "{0}", errorCode));
            } 
 
            return errorMsg;
        } 

    }

    internal static class PropertyManager 
    {
        public static object GetPropertyValue(DirectoryEntry directoryEntry, string propertyName) 
        { 

            Debug.Assert(directoryEntry != null, "PropertyManager::GetPropertyValue - directoryEntry is null"); 
            Debug.Assert(propertyName != null, "PropertyManager::GetPropertyValue - propertyName is null");

            if (directoryEntry.Properties[propertyName].Count == 0)
            { 
                if (directoryEntry.Properties["distinguishedName"].Count != 0)
                    throw new ProviderException(SR.GetString(SR.ADMembership_Property_not_found_on_object, propertyName, (string) directoryEntry.Properties["distinguishedName"].Value )); 
                else 
                    throw new ProviderException(SR.GetString(SR.ADMembership_Property_not_found, propertyName));
            } 

            return directoryEntry.Properties[propertyName].Value;
        }
 
        public static object GetSearchResultPropertyValue(SearchResult res, string propertyName)
        { 
 
            Debug.Assert(res != null, "PropertyManager::GetSearchResultPropertyValue - res is null");
            Debug.Assert(propertyName != null, "PropertyManager::GetSearchResultPropertyValue - propertyName is null"); 

            ResultPropertyValueCollection propertyValues = null;

            propertyValues = res.Properties[propertyName]; 
            if ((propertyValues == null) || (propertyValues.Count < 1))
                throw new ProviderException(SR.GetString(SR.ADMembership_Property_not_found,  propertyName)); 
 
            return propertyValues[0];
        } 
    }

    /*typedef struct _DOMAIN_CONTROLLER_INFO {
 		LPTSTR DomainControllerName; 
		LPTSTR DomainControllerAddress;
		ULONG DomainControllerAddressType; 
		GUID DomainGuid; 
 		LPTSTR DomainName;
		LPTSTR DnsForestName; 
 		ULONG Flags;
 		LPTSTR DcSiteName;
		LPTSTR ClientSiteName;
 	} DOMAIN_CONTROLLER_INFO, *PDOMAIN_CONTROLLER_INFO; */ 
	[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
	internal sealed class DomainControllerInfo { 
	#pragma warning disable 0649 
 		public string DomainControllerName;
		public string DomainControllerAddress; 
 		public int DomainControllerAddressType;
 		public Guid DomainGuid;
		public string DomainName;
 		public string DnsForestName; 
		public int Flags;
		public string DcSiteName; 
		public string ClientSiteName; 
       #pragma warning restore 0649
 
              public DomainControllerInfo() {}
 	}

    [SuppressUnmanagedCodeSecurityAttribute()] 
    internal static class NativeMethods
    { 
        internal const int ERROR_NO_SUCH_DOMAIN = 1355; 
        internal const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
        internal const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; 
        internal const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;

        /*DWORD DsGetDcName(
                        LPCTSTR ComputerName, 
                        LPCTSTR DomainName,
                        GUID* DomainGuid, 
                        LPCTSTR SiteName, 
                        ULONG Flags,
                        PDOMAIN_CONTROLLER_INFO* DomainControllerInfo 
                        );*/
        [DllImport("Netapi32.dll", CallingConvention=CallingConvention.StdCall, EntryPoint="DsGetDcNameW", CharSet=CharSet.Unicode)]
        internal static extern int DsGetDcName(
            [In] string computerName, 
            [In] string domainName,
            [In] IntPtr domainGuid, 
            [In] string siteName, 
            [In] uint flags,
            [Out] out IntPtr domainControllerInfo); 

        /*NET_API_STATUS NetApiBufferFree(
                        LPVOID Buffer
                        );*/ 
        [DllImport("Netapi32.dll")]
        internal static extern int NetApiBufferFree( 
            [In] IntPtr buffer); 

        [DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Unicode)] 
        public static extern int FormatMessageW(
            [In] int dwFlags,
            [In] int lpSource,
            [In] int dwMessageId, 
            [In] int dwLanguageId,
            [Out] StringBuilder lpBuffer, 
            [In] int nSize, 
            [In] int arguments);
    } 

    [
        ComVisible(false),
        SuppressUnmanagedCodeSecurityAttribute() 
    ]
    internal static class NativeComInterfaces 
    { 

        /*typedef enum { 
           ADS_SETTYPE_FULL=1,
           ADS_SETTYPE_PROVIDER=2,
           ADS_SETTYPE_SERVER=3,
           ADS_SETTYPE_DN=4 
        } ADS_SETTYPE_ENUM;
 
        typedef enum { 
           ADS_FORMAT_WINDOWS=1,
           ADS_FORMAT_WINDOWS_NO_SERVER=2, 
           ADS_FORMAT_WINDOWS_DN=3,
           ADS_FORMAT_WINDOWS_PARENT=4,
           ADS_FORMAT_X500=5,
           ADS_FORMAT_X500_NO_SERVER=6, 
           ADS_FORMAT_X500_DN=7,
           ADS_FORMAT_X500_PARENT=8, 
           ADS_FORMAT_SERVER=9, 
           ADS_FORMAT_PROVIDER=10,
           ADS_FORMAT_LEAF=11 
        } ADS_FORMAT_ENUM;

        typedef enum {
           ADS_ESCAPEDMODE_DEFAULT=1, 
           ADS_ESCAPEDMODE_ON=2,
           ADS_ESCAPEDMODE_OFF=3, 
           ADS_ESCAPEDMODE_OFF_EX=4 
        } ADS_ESCAPE_MODE_ENUM;*/
 
        internal const int ADS_SETTYPE_FULL = 1;
        internal const int ADS_SETTYPE_DN = 4;
        internal const int ADS_FORMAT_PROVIDER = 10;
        internal const int ADS_FORMAT_SERVER = 9; 
        internal const int ADS_FORMAT_X500_DN = 7;
        internal const int ADS_ESCAPEDMODE_ON = 2; 
        internal const int ADS_ESCAPEDMODE_OFF = 3; 

        // 
        // Pathname as a co-class that implements the IAdsPathname interface
        //
        [ComImport, Guid("080d0d78-f421-11d0-a36e-00c04fb950dc")]
        internal class Pathname 
        {
        } 
 

        [ComImport, Guid("D592AED4-F420-11D0-A36E-00C04FB950DC"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] 
        internal interface IAdsPathname
        {

            // HRESULT Set([in] BSTR bstrADsPath,  [in] long lnSetType); 
            [SuppressUnmanagedCodeSecurityAttribute()]
            int Set([In, MarshalAs(UnmanagedType.BStr)] string bstrADsPath, [In, MarshalAs(UnmanagedType.U4)] int lnSetType); 
 
            // HRESULT SetDisplayType([in] long lnDisplayType);
            int SetDisplayType([In, MarshalAs(UnmanagedType.U4)] int lnDisplayType); 

            // HRESULT Retrieve([in] long lnFormatType,  [out, retval] BSTR* pbstrADsPath);
            [return: MarshalAs(UnmanagedType.BStr)][SuppressUnmanagedCodeSecurityAttribute()]
            string Retrieve([In, MarshalAs(UnmanagedType.U4)] int lnFormatType); 

            // HRESULT GetNumElements([out, retval] long* plnNumPathElements); 
            [return: MarshalAs(UnmanagedType.U4)] 
            int GetNumElements();
 
            // HRESULT GetElement([in]  long lnElementIndex,  [out, retval] BSTR* pbstrElement);
            [return: MarshalAs(UnmanagedType.BStr)]
            string GetElement([In, MarshalAs(UnmanagedType.U4)] int lnElementIndex);
 
            // HRESULT AddLeafElement([in] BSTR bstrLeafElement);
            void AddLeafElement([In, MarshalAs(UnmanagedType.BStr)] string bstrLeafElement); 
 
            // HRESULT RemoveLeafElement();
            void RemoveLeafElement(); 

            // HRESULT CopyPath([out, retval] IDispatch** ppAdsPath);
            [return: MarshalAs(UnmanagedType.Interface)]
            object CopyPath(); 

            // HRESULT GetEscapedElement([in] long lnReserved, [in] BSTR bstrInStr, [out, retval] BSTR*  pbstrOutStr ); 
            [return: MarshalAs(UnmanagedType.BStr)][SuppressUnmanagedCodeSecurityAttribute()] 
            string GetEscapedElement([In, MarshalAs(UnmanagedType.U4)] int lnReserved, [In, MarshalAs(UnmanagedType.BStr)] string bstrInStr);
 
            int EscapedMode {
                get;
                [SuppressUnmanagedCodeSecurityAttribute()]
                set; 
            }
 
        } 

        // 
        // LargeInteger as a co-class that implements the IAdsLargeInteger  interface
        //
        [ComImport, Guid("927971f5-0939-11d1-8be1-00c04fd8d503")]
        internal class LargeInteger 
        {
        } 
 
        [ComImport, Guid("9068270b-0939-11d1-8be1-00c04fd8d503"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
        internal interface IAdsLargeInteger 
        {
            long HighPart {
                [SuppressUnmanagedCodeSecurityAttribute()]
                get; 
                [SuppressUnmanagedCodeSecurityAttribute()]
                set; 
            } 

            long LowPart { 
                [SuppressUnmanagedCodeSecurityAttribute()]
                get;
                [SuppressUnmanagedCodeSecurityAttribute()]
                set; 
            }
        } 
 
    }
 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.Security 
{ 
    using  System.Net;
    using  System.Web; 
    using  System.Text;
    using  System.Text.RegularExpressions;
    using  System.Security;
    using  System.Collections; 
    using  System.Globalization;
    using  System.Configuration; 
    using  System.DirectoryServices; 
    using  System.DirectoryServices.ActiveDirectory;
    using  System.DirectoryServices.Protocols; 
    using  System.Web.Hosting;
    using  System.Security.Cryptography;
    using  System.Web.Configuration;
    using  System.Security.Permissions; 
    using  System.Collections.Specialized;
    using  System.Runtime.InteropServices; 
    using  System.Security.Principal; 
    using  System.Web.DataAccess;
    using  System.Web.Util; 
    using  System.Reflection;
    using  System.Configuration.Provider;
    using  System.Web.Management;
 
    public enum ActiveDirectoryConnectionProtection
    { 
        None		= 0, 
        Ssl			= 1,
        SignAndSeal	= 2 
    }

    internal enum DirectoryType
    { 
        AD = 0,
        ADAM = 1, 
        Unknown = 2 
    }
 
    internal enum CredentialsType
    {
        Windows = 0,
        NonWindows = 1 
    }
 
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)] 
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [DirectoryServicesPermission(SecurityAction.LinkDemand, Unrestricted=true)] 
    [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
    public class ActiveDirectoryMembershipProvider : MembershipProvider
    {
 
        //
        // keeps track of whether the provider has already been initialized 
        // 
        private bool initialized = false;
 
        //
        // configuration parameters common to all membership providers
        //
 
        private string  adConnectionString;
        private bool enablePasswordRetrieval = false; 
        private bool enablePasswordReset; 
        private bool enableSearchMethods;
        private bool requiresQuestionAndAnswer; 
        private string appName;
        private bool requiresUniqueEmail;
        private int maxInvalidPasswordAttempts;
        private int passwordAttemptWindow; 
        private int passwordAnswerAttemptLockoutDuration;
        private int minRequiredPasswordLength; 
        private int minRequiredNonalphanumericCharacters; 
        private string passwordStrengthRegularExpression;
 
        //
        // configuration parameters specific to the AD membership provider
        // and related to the directory connection are stored within the DirectoryInformation class
        // 
        DirectoryInformation directoryInfo = null;
 
        // 
        // custom schema mappings (and their default values)
        // 
        private string attributeMapUsername = "userPrincipalName";
        private string attributeMapEmail = "mail";
        private string attributeMapPasswordQuestion = null;
        private string attributeMapPasswordAnswer = null; 
        private string attributeMapFailedPasswordAnswerCount = null;
        private string attributeMapFailedPasswordAnswerTime= null; 
        private string attributeMapFailedPasswordAnswerLockoutTime = null; 

        // 
        // maximum lengths for the different string properties
        //
        private int maxUsernameLength = 256;
        private int maxUsernameLengthForCreation = 64; 
        private int maxPasswordLength = 128;
        private int maxCommentLength = 1024; 
        private int maxEmailLength = 256; 
        private int maxPasswordQuestionLength = 256;
        private int maxPasswordAnswerLength = 128; 

        //
        // user account flags
        // 
        private const int UF_ACCOUNT_DISABLED =0x2;
        private const int UF_LOCKOUT=0x10; 
        private readonly DateTime DefaultLastLockoutDate = new DateTime(1754, 1, 1, 0, 0, 0, DateTimeKind.Utc); 
        private const int AD_SALT_SIZE_IN_BYTES = 16;
 
        //
        // table containing the valid syntaxes for various attribute mappings
        //
        Hashtable syntaxes = new Hashtable(); 
        Hashtable attributesInUse = new Hashtable(StringComparer.OrdinalIgnoreCase);
        Hashtable userObjectAttributes = null; 
 
        //
        // auth type to be used for validation 
        //
        AuthType authTypeForValidation;
        LdapConnection connection;
        bool usernameIsSAMAccountName = false; 
        bool usernameIsUPN = true;
 
        // 
        // password size for autogenerating password
        // 
        private const int PASSWORD_SIZE      = 14;

        public override string ApplicationName
        { 
            get
            { 
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return appName;
            }
            set
            { 
                throw new NotSupportedException(SR.GetString(SR.ADMembership_Setting_ApplicationName_not_supported));
            } 
        } 

        public ActiveDirectoryConnectionProtection CurrentConnectionProtection 
        {
            get
            {
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return directoryInfo.ConnectionProtection; 
            }
        } 

        public override MembershipPasswordFormat PasswordFormat
        {
            get 
            {
                // 
                // AD membership provider does not support password retrieval 
                // (regardless of the settings). As a result the provider operates as
                // if the password was effectively hashed. 
                //
                return MembershipPasswordFormat.Hashed;
            }
        } 

        public override bool  EnablePasswordRetrieval 
        { 
            get
            { 
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

                return enablePasswordRetrieval; 
             }
        } 
 
        public override bool  EnablePasswordReset
        { 
            get
            {
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

                return enablePasswordReset; 
            } 
        }
 
        public bool  EnableSearchMethods
        {
            get
            { 
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 
 
                return enableSearchMethods;
            } 
        }

        public override bool  RequiresQuestionAndAnswer
        { 
            get
            { 
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return requiresQuestionAndAnswer;
            }
        }
 
        public override bool  RequiresUniqueEmail
        { 
            get 
            {
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

                return requiresUniqueEmail;
            } 
        }
 
        public override int MaxInvalidPasswordAttempts 
        {
            get 
            {
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return maxInvalidPasswordAttempts;
            } 
        } 

        public override int PasswordAttemptWindow 
        {
            get
            {
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
                return passwordAttemptWindow; 
            }
        } 

        public int PasswordAnswerAttemptLockoutDuration
        {
            get 
            {
                if (!initialized) 
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

                return passwordAnswerAttemptLockoutDuration; 
            }
        }

        public override int MinRequiredPasswordLength 
        {
            get 
            { 
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

                return minRequiredPasswordLength;
            }
        } 

        public override int MinRequiredNonAlphanumericCharacters 
        { 
            get
            { 
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

                return minRequiredNonalphanumericCharacters; 
            }
        } 
 
        public override string PasswordStrengthRegularExpression
        { 
            get
            {
                if (!initialized)
                    throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

                return passwordStrengthRegularExpression; 
            } 
        }
 
        //
        // NOTE: In every method of the provider we need to demand DirectoryServicesPermission (irrespective of
        //           whether the underlying calls to S.DS/S.DS.Protocols result in full demand or link demand for that permission.
        //           Moreover, once we demand the permission, we should also assert it so that S.DS/S.DS.Protocols does not make the 
        //           same demand (if we do not assert then in the case of S.DS/S.DS.Protocols making a full demand we would have two stack walks)
        // 
        #pragma warning disable 0688 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override void Initialize(string name, NameValueCollection config)
        {
            if (System.Web.Hosting.HostingEnvironment.IsHosted) 
                HttpRuntime.CheckAspNetHostingPermission (AspNetHostingPermissionLevel.Low, SR.Feature_not_supported_at_this_level);
 
            if (initialized) 
                return;
 
            if (config == null)
                throw new ArgumentNullException("config");

            if (String.IsNullOrEmpty(name)) 
                name = "AspNetActiveDirectoryMembershipProvider";
 
            if (string.IsNullOrEmpty(config["description"])) 
            {
                config.Remove("description"); 
                config.Add("description", SR.GetString(SR.ADMembership_Description));
            }

            base.Initialize(name, config); 

            appName = config["applicationName"]; 
 
            if (string.IsNullOrEmpty(appName))
                appName = SecUtility.GetDefaultAppName(); 

            if( appName.Length > 256 )
                throw new ProviderException(SR.GetString(SR.Provider_application_name_too_long));
 
            string temp = config["connectionStringName"];
            if (String.IsNullOrEmpty(temp)) 
                throw new ProviderException(SR.GetString(SR.Connection_name_not_specified)); 

            adConnectionString = GetConnectionString(temp, true); 
            if (String.IsNullOrEmpty(adConnectionString))
                throw new ProviderException(SR.GetString(SR.Connection_string_not_found, temp));

            // 
            // Get the provider specific configuration settings
            // 
 
            // connectionProtection
            string connProtection = config["connectionProtection"]; 
            if (connProtection == null)
                connProtection = "Secure";
            else
            { 
                if ((String.Compare(connProtection, "Secure", StringComparison.Ordinal) != 0) &&
                    (String.Compare(connProtection, "None", StringComparison.Ordinal) != 0)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_InvalidConnectionProtection, connProtection)); 
            }
 
            //
            // credentials
            // username and password if specified must not be empty, moreover if one is specified the other must
            // be specified as well 
            //
            string username = config["connectionUsername"]; 
            if (username != null && username.Length == 0) 
                throw new ProviderException(SR.GetString(SR.ADMembership_Connection_username_must_not_be_empty));
 
            string password = config["connectionPassword"];
            if (password != null && password.Length == 0)
                throw new ProviderException(SR.GetString(SR.ADMembership_Connection_password_must_not_be_empty));
 
            if ((username != null && password == null) || (password != null && username == null))
                throw new ProviderException(SR.GetString(SR.ADMembership_Username_and_password_reqd)); 
 
            NetworkCredential credential = new NetworkCredential(username, password);
 
            int clientSearchTimeout = SecUtility.GetIntValue(config, "clientSearchTimeout", -1, false, 0);
            int serverSearchTimeout = SecUtility.GetIntValue(config, "serverSearchTimeout", -1, false, 0);

            enableSearchMethods = SecUtility.GetBooleanValue(config, "enableSearchMethods", false); 
            requiresUniqueEmail = SecUtility.GetBooleanValue(config, "requiresUniqueEmail", false);
            enablePasswordReset = SecUtility.GetBooleanValue(config, "enablePasswordReset", false); 
            requiresQuestionAndAnswer = SecUtility.GetBooleanValue(config, "requiresQuestionAndAnswer", false); 
            minRequiredPasswordLength = SecUtility.GetIntValue( config, "minRequiredPasswordLength", 7, false, 128 );
            minRequiredNonalphanumericCharacters = SecUtility.GetIntValue( config, "minRequiredNonalphanumericCharacters", 1, true, 128 ); 

            passwordStrengthRegularExpression = config["passwordStrengthRegularExpression"];
            if( passwordStrengthRegularExpression != null )
            { 
                passwordStrengthRegularExpression = passwordStrengthRegularExpression.Trim();
                if( passwordStrengthRegularExpression.Length != 0 ) 
                { 
                    try
                    { 
                        Regex regex = new Regex( passwordStrengthRegularExpression );
                    }
                    catch( ArgumentException e )
                    { 
                        throw new ProviderException( e.Message, e );
                    } 
                } 
            }
            else 
            {
                passwordStrengthRegularExpression = string.Empty;
            }
            if (minRequiredNonalphanumericCharacters > minRequiredPasswordLength) 
                throw new HttpException(SR.GetString(SR.MinRequiredNonalphanumericCharacters_can_not_be_more_than_MinRequiredPasswordLength));
 
 
            using (new ApplicationImpersonationContext())
            { 
                //
                //  This will make some checks regarding whether the connectionProtection is valid (choose the right
                //  connectionprotection if necessary, make sure credentials are valid, container exists and the directory is
                //  either AD or ADAM) 
                //
                directoryInfo = new DirectoryInformation(adConnectionString, credential, connProtection, clientSearchTimeout, serverSearchTimeout, enablePasswordReset); 
 
                //
                // initialize the syntaxes table 
                //
                syntaxes.Add("attributeMapUsername", "DirectoryString");
                syntaxes.Add("attributeMapEmail", "DirectoryString");
                syntaxes.Add("attributeMapPasswordQuestion", "DirectoryString"); 
                syntaxes.Add("attributeMapPasswordAnswer", "DirectoryString");
                syntaxes.Add("attributeMapFailedPasswordAnswerCount", "Integer"); 
                syntaxes.Add("attributeMapFailedPasswordAnswerTime", "Integer8"); 
                syntaxes.Add("attributeMapFailedPasswordAnswerLockoutTime", "Integer8");
 
                //
                // initialize the in use attributes list
                //
                attributesInUse.Add("objectclass", null); 
                attributesInUse.Add("objectsid", null);
                attributesInUse.Add("comment", null); 
                attributesInUse.Add("whencreated", null); 
                attributesInUse.Add("pwdlastset", null);
                attributesInUse.Add("msds-user-account-control-computed", null); 
                attributesInUse.Add("lockouttime", null);
                if (directoryInfo.DirectoryType == DirectoryType.AD)
                    attributesInUse.Add("useraccountcontrol", null);
                else 
                    attributesInUse.Add("msds-useraccountdisabled", null);
 
                // 
                // initialize the user attributes list
                // 
                userObjectAttributes = GetUserObjectAttributes();

                //
                // get the username/email schema mappings 
                //
                int maxLength; 
                string attrMapping = GetAttributeMapping(config, "attributeMapUsername", out maxLength); 
                if (attrMapping != null)
                { 
                    attributeMapUsername = attrMapping;
                    if (maxLength != -1)
                    {
                        if (maxLength < maxUsernameLength) 
                            maxUsernameLength = maxLength;
                        if (maxLength < maxUsernameLengthForCreation) 
                            maxUsernameLengthForCreation = maxLength; 
                    }
                } 
                attributesInUse.Add(attributeMapUsername, null);
                if (StringUtil.EqualsIgnoreCase(attributeMapUsername, "sAMAccountName"))
                {
                    usernameIsSAMAccountName = true; 
                    usernameIsUPN = false;
                } 
 
                attrMapping = GetAttributeMapping(config, "attributeMapEmail", out maxLength);
                if (attrMapping != null) 
                {
                    attributeMapEmail = attrMapping;
                    if (maxLength != -1 && maxLength < maxEmailLength)
                        maxEmailLength = maxLength; 
                }
                attributesInUse.Add(attributeMapEmail, null); 
 
                //
                // get max length of "comment" attribute 
                //
                maxLength = GetRangeUpperForSchemaAttribute("comment");
                if (maxLength != -1 && maxLength < maxCommentLength)
                    maxCommentLength = maxLength; 

                // 
                // enablePasswordReset and requiresQuestionAndAnswer should match 
                //
                if (enablePasswordReset) 
                {
                    //
                    // AD membership provider does not support password reset without question and answer
                    // 
                    if (!requiresQuestionAndAnswer)
                        throw new ProviderException(SR.GetString(SR.ADMembership_PasswordReset_without_question_not_supported)); 
 
                    //
                    // Other password reset related attributes 
                    //
                    maxInvalidPasswordAttempts = SecUtility.GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0);
                    passwordAttemptWindow = SecUtility.GetIntValue(config, "passwordAttemptWindow", 10, false, 0);
                    passwordAnswerAttemptLockoutDuration = SecUtility.GetIntValue(config, "passwordAnswerAttemptLockoutDuration", 30, false, 0); 

                    // 
                    // some more schema mappings that must be specified for Password Reset 
                    //
                    attributeMapFailedPasswordAnswerCount = GetAttributeMapping(config, "attributeMapFailedPasswordAnswerCount", out maxLength /* ignored */); 
                    if (attributeMapFailedPasswordAnswerCount != null)
                        attributesInUse.Add(attributeMapFailedPasswordAnswerCount, null);

                    attributeMapFailedPasswordAnswerTime = GetAttributeMapping(config, "attributeMapFailedPasswordAnswerTime", out maxLength /* ignored */); 
                    if (attributeMapFailedPasswordAnswerTime != null)
                        attributesInUse.Add(attributeMapFailedPasswordAnswerTime, null); 
 
                    attributeMapFailedPasswordAnswerLockoutTime = GetAttributeMapping(config, "attributeMapFailedPasswordAnswerLockoutTime", out maxLength /* ignored */);
                    if (attributeMapFailedPasswordAnswerLockoutTime != null) 
                        attributesInUse.Add(attributeMapFailedPasswordAnswerLockoutTime, null);

                    if (attributeMapFailedPasswordAnswerCount == null || attributeMapFailedPasswordAnswerTime == null ||
                            attributeMapFailedPasswordAnswerLockoutTime == null) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_BadPasswordAnswerMappings_not_specified));
                } 
 
                //
                // Password Q&A mappings 
                //
                attributeMapPasswordQuestion = GetAttributeMapping(config, "attributeMapPasswordQuestion", out maxLength);
                if (attributeMapPasswordQuestion != null)
                { 
                    if (maxLength != -1 && maxLength < maxPasswordQuestionLength)
                        maxPasswordQuestionLength = maxLength; 
 
                    attributesInUse.Add(attributeMapPasswordQuestion, null);
                } 

                attributeMapPasswordAnswer = GetAttributeMapping(config, "attributeMapPasswordAnswer", out maxLength);
                if (attributeMapPasswordAnswer != null)
                { 
                    if (maxLength != -1 && maxLength < maxPasswordAnswerLength)
                        maxPasswordAnswerLength = maxLength; 
 
                    attributesInUse.Add(attributeMapPasswordAnswer, null);
                } 

                if (requiresQuestionAndAnswer)
                {
                    // 
                    // We also need to check that the password question and answer attributes are mapped
                    // 
                    if (attributeMapPasswordQuestion == null || attributeMapPasswordAnswer == null) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_PasswordQuestionAnswerMapping_not_specified));
                } 

                //
                // the auth type to be used for validation is determined as follows:
                // if directory is ADAM: authType = AuthType.Basic 
                // if directory is AD: authType is based on connectionProtection (None, SSL: AuthType.Basic; SignAndSeal: AuthType.Negotiate)
                // 
                if (directoryInfo.DirectoryType == DirectoryType.ADAM) 
                    authTypeForValidation = AuthType.Basic;
                else 
                    authTypeForValidation = directoryInfo.GetLdapAuthenticationTypes(directoryInfo.ConnectionProtection, CredentialsType.NonWindows);

                if (directoryInfo.DirectoryType == DirectoryType.AD)
                { 
                    //
                    // if password reset is enabled we should perform all operations on a single server 
                    // 
                    if (enablePasswordReset)
                        directoryInfo.SelectServer(); 

                    //
                    // if the username is mapped to upn we need to do  forest wide search to check the uniqueness of the upn.
                    // and if the username is mapped to samAccountName then we need to append the domain name in the username for reliable validation 
                    //
                    directoryInfo.InitializeDomainAndForestName(); 
 
                }
            } 

            //
            // Create a new common ldap connection for validation
            // 
            connection = directoryInfo.CreateNewLdapConnection(authTypeForValidation);
 
            config.Remove("name"); 
            config.Remove("applicationName");
            config.Remove("connectionStringName"); 
            config.Remove("requiresUniqueEmail");
            config.Remove("enablePasswordReset");
            config.Remove("requiresQuestionAndAnswer");
            config.Remove("attributeMapPasswordQuestion"); 
            config.Remove("attributeMapPasswordAnswer");
            config.Remove("attributeMapUsername"); 
            config.Remove("attributeMapEmail"); 
            config.Remove("connectionProtection");
            config.Remove("connectionUsername"); 
            config.Remove("connectionPassword");
            config.Remove("clientSearchTimeout");
            config.Remove("serverSearchTimeout");
            config.Remove("enableSearchMethods"); 
            config.Remove("maxInvalidPasswordAttempts");
            config.Remove("passwordAttemptWindow"); 
            config.Remove("passwordAnswerAttemptLockoutDuration"); 
            config.Remove("attributeMapFailedPasswordAnswerCount");
            config.Remove("attributeMapFailedPasswordAnswerTime"); 
            config.Remove("attributeMapFailedPasswordAnswerLockoutTime");
            config.Remove("minRequiredPasswordLength");
            config.Remove("minRequiredNonalphanumericCharacters");
            config.Remove("passwordStrengthRegularExpression"); 

            if (config.Count > 0) 
            { 
                string attribUnrecognized = config.GetKey(0);
                if (!String.IsNullOrEmpty(attribUnrecognized)) 
                    throw new ProviderException(SR.GetString(SR.Provider_unrecognized_attribute, attribUnrecognized));
            }

            initialized = true; 
        }
        #pragma warning restore 0688 
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override MembershipUser CreateUser(string username,
                                                        string password,
                                                        string email, 
                                                        string passwordQuestion,
                                                        string passwordAnswer, 
                                                        bool   isApproved, 
                                                        object providerUserKey,
                                                        out    MembershipCreateStatus status) 
        {
            status = (MembershipCreateStatus) 0;
            MembershipUser user = null;
 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 
 
            if (providerUserKey != null)
                throw new NotSupportedException(SR.GetString(SR.ADMembership_Setting_UserId_not_supported)); 

            if ((passwordQuestion != null) && (attributeMapPasswordQuestion == null))
                throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordQ_not_supported));
 
            if ((passwordAnswer != null) && (attributeMapPasswordAnswer == null))
                throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordA_not_supported)); 
 
            if(!SecUtility.ValidateParameter(ref username, true, true, true, maxUsernameLengthForCreation))
            { 
                status = MembershipCreateStatus.InvalidUserName;
                return null;
            }
 
            //
            // if username is mapped to UPN, it should not contain '\' 
            // 
            if (usernameIsUPN && (username.IndexOf('\\') != -1))
            { 
                status = MembershipCreateStatus.InvalidUserName;
                return null;
            }
 
            if(!ValidatePassword(password, maxPasswordLength))
            { 
                status = MembershipCreateStatus.InvalidPassword; 
                return null;
            } 

            if(!SecUtility.ValidateParameter(ref email, RequiresUniqueEmail, true, false, maxEmailLength))
            {
                status = MembershipCreateStatus.InvalidEmail; 
                return null;
            } 
 
            if(!SecUtility.ValidateParameter(ref passwordQuestion, RequiresQuestionAndAnswer, true, false, maxPasswordQuestionLength))
            { 
                status = MembershipCreateStatus.InvalidQuestion;
                return null;
            }
 
            // validate the parameter before encoding the password answer
            if(!SecUtility.ValidateParameter(ref passwordAnswer, RequiresQuestionAndAnswer, true, false, maxPasswordAnswerLength)) 
            { 
                status = MembershipCreateStatus.InvalidAnswer;
                return null; 
            }

            string encodedPasswordAnswer;
            if (!string.IsNullOrEmpty(passwordAnswer)) 
            {
                encodedPasswordAnswer = Encrypt(passwordAnswer); 
 
                // check length of encoded password answer
                if (maxPasswordAnswerLength > 0 && encodedPasswordAnswer.Length > maxPasswordAnswerLength) 
                {
                    status = MembershipCreateStatus.InvalidAnswer;
                    return null;
                } 
            }
            else 
                encodedPasswordAnswer = passwordAnswer; 

            if( password.Length < MinRequiredPasswordLength ) 
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            } 

            int count = 0; 
 
            for( int i = 0; i < password.Length; i++ )
            { 
                if( !char.IsLetterOrDigit( password, i ) )
                {
                    count++;
                } 
            }
 
            if( count < MinRequiredNonAlphanumericCharacters ) 
            {
                status = MembershipCreateStatus.InvalidPassword; 
                return null;
            }

            if( PasswordStrengthRegularExpression.Length > 0 ) 
            {
                if( !Regex.IsMatch( password, PasswordStrengthRegularExpression ) ) 
                { 
                    status = MembershipCreateStatus.InvalidPassword;
                    return null; 
                }
            }

            ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, password, true); 
            OnValidatingPassword(e);
 
            if(e.Cancel) 
            {
                status = MembershipCreateStatus.InvalidPassword; 
                return null;
            }

            try 
            {
                // 
                // Get the directory entry for the container and create a user object under it 
                //
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.CreationContainerDN, true /* revertImpersonation */); 

                DirectoryEntry containerEntry = null;
                DirectoryEntry userEntry = null;
 
                try
                { 
                    containerEntry = connection.DirectoryEntry; 
                    // to avoid unnecessary searches (for better performance)
                    containerEntry.AuthenticationType |= AuthenticationTypes.FastBind; 

                    //
                    // we set the username as the cn
                    // 
                    userEntry = containerEntry.Children.Add(GetEscapedRdn("CN=" + username), "user");
 
                    // 
                    // if we are talking to Active Directory
                    // set the sAMAccountName (if username is not mapped to this attribute, we need to autogenerate it) 
                    // (NOTE: We do not need to do this if the domain controller functionality is Windows 2003 (dcLevel = 2))
                    //
                    if (directoryInfo.DirectoryType == DirectoryType.AD)
                    { 
                        string sAMAccountName= null;
                        bool setSAMAccountName = false; 
 
                        if (usernameIsSAMAccountName)
                        { 
                            sAMAccountName = username;
                            setSAMAccountName = true;
                        }
                        else 
                        {
                            int dcLevel = GetDomainControllerLevel(containerEntry.Options.GetCurrentServerName()); 
 
                            if (dcLevel != 2)
                            { 
                                sAMAccountName = GenerateAccountName();
                                setSAMAccountName = true;
                            }
                        } 

                        if (setSAMAccountName) 
                            userEntry.Properties["sAMAccountName"].Value = sAMAccountName; 
                    }
 
                    //
                    // if username is mapped to userPrincipalName and we are talking to AD, we need to do
                    // a GC search to find if the same upn already exists or not
                    // On ADAM, uniqueness of userPrincipalName is enforced on the server itself across all partitions 
                    //
                    if (usernameIsUPN) 
                    { 
                        if (directoryInfo.DirectoryType == DirectoryType.AD && !IsUpnUnique(username))
                        { 
                            status = MembershipCreateStatus.DuplicateUserName;
                            return null;
                        }
 
                        userEntry.Properties["userPrincipalName"].Value = username;
                    } 
 
                    //
                    // set other attributes 
                    //
                    if (email != null)
                    {
                        if (RequiresUniqueEmail && !IsEmailUnique(containerEntry, username, email, false /* existing */)) 
                        {
                            status = MembershipCreateStatus.DuplicateEmail; 
                            return null; 
                        }
                        userEntry.Properties[attributeMapEmail].Value = email; 
                    }

                    if (passwordQuestion != null)
                        userEntry.Properties[attributeMapPasswordQuestion].Value = passwordQuestion; 

                    if (passwordAnswer != null) 
                        userEntry.Properties[attributeMapPasswordAnswer].Value = encodedPasswordAnswer; 

                    // 
                    // commit the user object
                    //
                    try
                    { 
                        userEntry.CommitChanges();
                    } 
                    catch (COMException e1) 
                    {
                        if ((e1.ErrorCode == unchecked((int) 0x80071392)) || (e1.ErrorCode == unchecked((int) 0x8007200d))) 
                        {
                            status = MembershipCreateStatus.DuplicateUserName;
                            return null;
                        } 
                        else if ((e1.ErrorCode == unchecked((int) 0x8007001f)) && (e1 is DirectoryServicesCOMException))
                        { 
                            // 
                            // this error corresponds to LDAP_OTHER
                            // if username was mapped to sAMAccountName and the name is too long 
                            // then the extended error should be 1315 (ERROR_INVALID_ACCOUNT_NAME)
                            //
                            DirectoryServicesCOMException dsce = e1 as DirectoryServicesCOMException;
                            if (dsce.ExtendedError == 1315) 
                            {
                                status = MembershipCreateStatus.InvalidUserName; 
                                return null; 
                            }
                            else 
                                throw;
                        }
                        else
                            throw; 
                    }
 
                    // 
                    // set the password
                    // 
                    try
                    {
                        SetPasswordPortIfApplicable(userEntry);
 
                        //
                        // Set the password 
                        // 
                        userEntry.Invoke("SetPassword", new object[]{ password });
 
                        //
                        // if the user is approved then we need to enable the account (disabled dy default)
                        //
                        if (isApproved) 
                        {
                            if (directoryInfo.DirectoryType ==  DirectoryType.AD) 
                            { 
                                const int UF_ACCOUNT_DISABLED =0x2;
                                const int UF_PASSWD_NOTREQD = 0x20; 

                                int val = (int)PropertyManager.GetPropertyValue(userEntry, "userAccountControl");
                                val &= ~(UF_ACCOUNT_DISABLED | UF_PASSWD_NOTREQD);
                                userEntry.Properties["userAccountControl"].Value = val; 
                            }
                            else 
                            { 
                                // ADAM case
                                userEntry.Properties["msDS-UserAccountDisabled"].Value = false; 
                            }
                            userEntry.CommitChanges();
                        }
                        else 
                        {
                            // 
                            // For ADAM the user may be created as enabled in some cases 
                            // so we need to explicitly disable it
                            // 
                            if (directoryInfo.DirectoryType ==  DirectoryType.ADAM)
                            {
                                userEntry.Properties["msDS-UserAccountDisabled"].Value = true;
                                userEntry.CommitChanges(); 
                            }
                        } 
 
                        //
                        // For ADAM users, we need to add the user to the Readers group in that 
                        // partition
                        //
                        if (directoryInfo.DirectoryType == DirectoryType.ADAM)
                        { 
                            DirectoryEntry readersEntry = new DirectoryEntry(directoryInfo.GetADsPath("CN=Readers,CN=Roles," + directoryInfo.ADAMPartitionDN), directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes);
                            readersEntry.Properties["member"].Add(PropertyManager.GetPropertyValue(userEntry, "distinguishedName")); 
                            readersEntry.CommitChanges(); 
                        }
                    } 
                    //
                    // At this point we have already created the user object in AD/ADAM but we
                    // have failed in either SetPassword or while enabling/disabling the user, so we try to delete the user object
                    // 
                    catch (COMException)
                    { 
                        containerEntry.Children.Remove(userEntry); 
                        throw;
                    } 
                    catch (ProviderException)
                    {
                        containerEntry.Children.Remove(userEntry);
                        throw; 
                    }
                    catch (TargetInvocationException tie) 
                    { 
                        containerEntry.Children.Remove(userEntry);
 
                        if (tie.InnerException is COMException)
                        {
                            COMException ce = (COMException) tie.InnerException;
                            int errorCode = ce.ErrorCode; 

                            // 
                            // if the exception is due to password not meeting complexity requirements, then return 
                            // status as InvalidPassword
                            // 
                            if ((errorCode == unchecked((int) 0x800708c5)) || (errorCode == unchecked((int) 0x8007202f)) || (errorCode == unchecked((int) 0x8007052d)) || (errorCode == unchecked((int) 0x8007052f)))
                            {
                                status = MembershipCreateStatus.InvalidPassword;
                                return null; 
                            }
                            // if the target is ADAM and the exception is due to property not found, this indicates that a secure 
                            // connection could not be setup for changing the password and ADSI is falling back to kerberos which does not work for ADAM 
                            // so we will provide a clearer exception
                            // 
                            else if ((errorCode == unchecked((int) 0x8000500d) && (directoryInfo.DirectoryType == DirectoryType.ADAM)))
                                throw new ProviderException(SR.GetString(SR.ADMembership_No_secure_conn_for_password));
                            else
                                throw; 
                        }
                        else 
                            throw; 
                    }
 
                    //
                    // Create a user object
                    //
                    DirectoryEntry dummyEntry = null; 
                    bool dummyFlag = false;
                    string dummyString; 
                    user = FindUser(userEntry, "(objectClass=*)", System.DirectoryServices.SearchScope.Base, false /*retrieveSAMAccountName */, out dummyEntry, out dummyFlag, out dummyString); 
                }
                finally 
                {
                    if (userEntry != null)
                        userEntry.Dispose();
 
                    connection.Close();
                } 
            } 
            catch
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                //
                throw; 
            }
 
            return user; 
        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override bool ChangePasswordQuestionAndAnswer(string username, 
                                                        string password,
                                                        string newPasswordQuestion, 
                                                        string newPasswordAnswer) 
        {
            if (!initialized) 
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            //
            // if there are no mappings for password question and answer, we should throw a NotSupportedException 
            //
            if ((newPasswordQuestion != null) && (attributeMapPasswordQuestion == null)) 
                throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordQ_not_supported)); 

            if ((newPasswordAnswer != null) && (attributeMapPasswordAnswer == null)) 
                throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordA_not_supported));


            CheckUserName( ref username, maxUsernameLength, "username" ); 
            CheckPassword(password, maxPasswordLength, "password");
 
            SecUtility.CheckParameter( 
                            ref newPasswordQuestion,
                            RequiresQuestionAndAnswer, 
                            true,
                            false,
                            maxPasswordQuestionLength,
                            "newPasswordQuestion" ); 

            // validate the parameter before encoding the password answer 
            CheckPasswordAnswer(ref newPasswordAnswer, RequiresQuestionAndAnswer, maxPasswordAnswerLength, "newPasswordAnswer"); 

            string encodedPasswordAnswer; 
            if (!string.IsNullOrEmpty(newPasswordAnswer))
            {
                encodedPasswordAnswer = Encrypt(newPasswordAnswer);
 
                // check length of encoded password answer
                if (maxPasswordAnswerLength > 0 && encodedPasswordAnswer.Length > maxPasswordAnswerLength) 
                    throw new ArgumentException(SR.GetString(SR.ADMembership_Parameter_too_long, "newPasswordAnswer"), "newPasswordAnswer"); 
            }
            else 
                encodedPasswordAnswer = newPasswordAnswer;

            try
            { 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry; 
                DirectoryEntry userEntry = null; 
                bool resetBadPasswordAnswerAttributes = false;
                string usernameForAuthentication = null; 

                try
                {
                    if (EnablePasswordReset) 
                    {
                        // 
                        // get the user's directory entry 
                        // NOTE: If the username is mapped to userPrincipalName and the username does not contain '@' in it, then simple bind will fail as it needs domain information.
                        //           To workaround this whenever we are talking to AD, username is mapped to userPrincipalName and does not contain '@', we will get the sAMAccountName 
                        //           while getting the user object and use that for authenticating the user.
                        //
                        MembershipUser user = null;
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1)) 
                        {
                            string sAMAccountName = null; 
                            user = FindUserAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes, out sAMAccountName); 
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        } 
                        else
                        {
                            user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes);
                            usernameForAuthentication = username; 
                        }
 
                        // 
                        // user does not exist, return false
                        // 
                        if (user == null)
                            return false;

                        // 
                        // here we want to check if the user is already unlocked due to bad password answer (or bad password)
                        // 
                        if (user.IsLockedOut) 
                            return false;
 
                    }
                    else
                    {
                        // 
                        // get the user's directory entry
                        // 
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1)) 
                        {
                            string sAMAccountName = null; 
                            userEntry = FindUserEntryAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out sAMAccountName);
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        }
                        else 
                        {
                            userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")"); 
                            usernameForAuthentication = username; 
                        }
 
                        //
                        // user does not exist, return false
                        //
                        if (userEntry == null) 
                            return false;
                    } 
 
                    //
                    // validate the user's credentials 
                    //
                    if (!ValidateCredentials(usernameForAuthentication, password))
                        return false;
 
                    if (EnablePasswordReset && resetBadPasswordAnswerAttributes)
                    { 
                        // 
                        // user supplied correct password, so we need to reset the password answer tracking info
                        // (NOTE: The reason we do not call the Reset method here is so that we can make all the modifications in one transaction) 
                        //
                        userEntry.Properties[attributeMapFailedPasswordAnswerCount].Value = 0;
                        userEntry.Properties[attributeMapFailedPasswordAnswerTime].Value = 0;
                        userEntry.Properties[attributeMapFailedPasswordAnswerLockoutTime].Value = 0; 
                    }
 
                    if (newPasswordQuestion == null) 
                    {
                        // set it to null only if it already exists 
                        if ((attributeMapPasswordQuestion != null) && (userEntry.Properties.Contains(attributeMapPasswordQuestion)))
                            userEntry.Properties[attributeMapPasswordQuestion].Clear();
                    }
                    else 
                        userEntry.Properties[attributeMapPasswordQuestion].Value = newPasswordQuestion;
 
                    if (newPasswordAnswer == null) 
                    {
                        // set it to null only if it already exists 
                        if ((attributeMapPasswordAnswer != null) && (userEntry.Properties.Contains(attributeMapPasswordAnswer)))
                            userEntry.Properties[attributeMapPasswordAnswer].Clear();
                    }
                    else 
                        userEntry.Properties[attributeMapPasswordAnswer].Value = encodedPasswordAnswer;
 
                    userEntry.CommitChanges(); 

                } 
                finally
                {
                    if (userEntry != null)
                        userEntry.Dispose(); 
                    connection.Close();
                } 
            } 
            catch
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                //
                throw; 
            }
 
            // 
            // Password question and answer changed successfully
            // 
            return true;
        }

        public override string GetPassword(string username, string passwordAnswer) 
        {
            // 
            // ADMembership Provider does not support password retrieval 
            //
            throw new NotSupportedException(SR.GetString(SR.ADMembership_PasswordRetrieval_not_supported_AD)); 
        }

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override bool ChangePassword(string username, 
                                                        string oldPassword, 
                                                        string newPassword)
        { 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            CheckUserName(ref username, maxUsernameLength, "username" ); 

            CheckPassword(oldPassword, maxPasswordLength, "oldPassword"); 
 
            CheckPassword(newPassword, maxPasswordLength, "newPassword");
 
            if( newPassword.Length < MinRequiredPasswordLength )
            {
                throw new ArgumentException(SR.GetString(
                              SR.Password_too_short, 
                              "newPassword",
                              MinRequiredPasswordLength.ToString(CultureInfo.InvariantCulture))); 
            } 

            int count = 0; 

            for( int i = 0; i < newPassword.Length; i++ )
            {
                if( !char.IsLetterOrDigit( newPassword, i ) ) 
                {
                    count++; 
                } 
            }
 
            if( count < MinRequiredNonAlphanumericCharacters )
            {
                throw new ArgumentException(SR.GetString(
                              SR.Password_need_more_non_alpha_numeric_chars, 
                              "newPassword",
                              MinRequiredNonAlphanumericCharacters.ToString(CultureInfo.InvariantCulture))); 
            } 

            if( PasswordStrengthRegularExpression.Length > 0 ) 
            {
                if( !Regex.IsMatch( newPassword, PasswordStrengthRegularExpression ) )
                {
                    throw new ArgumentException(SR.GetString(SR.Password_does_not_match_regular_expression, 
                                                             "newPassword"));
                } 
            } 

            ValidatePasswordEventArgs e = new ValidatePasswordEventArgs( username, newPassword, false ); 
            OnValidatingPassword(e);

            if(e.Cancel)
            { 
                if(e.FailureInformation != null)
                    throw e.FailureInformation; 
                else 
                    throw new ArgumentException(SR.GetString( SR.Membership_Custom_Password_Validation_Failure), "newPassword");
            } 

            try
            {
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                DirectoryEntry userEntry = null; 
                bool resetBadPasswordAnswerAttributes = false; 
                string usernameForAuthentication = null;
 
                try
                {
                    if (EnablePasswordReset)
                    { 
                        //
                        // get the user's directory entry 
                        // NOTE: If the username is mapped to userPrincipalName and the username does not contain '@' in it, the S.DS(adsi) will pass NULL 
                        //           domain name to the underlying wldap32 layer. This results in authentication failure even for valid credentials. To workaround this
                        //           whenever we are talking to AD, username is mapped to userPrincipalName and does not contain '@', we will get the sAMAccountName 
                        //           while getting the user object and use that for changing the password.
                        //
                        MembershipUser user = null;
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1)) 
                        {
                            string sAMAccountName = null; 
                            user = FindUserAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes, out sAMAccountName); 
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        } 
                        else
                        {
                            user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes);
                            usernameForAuthentication = username; 
                        }
 
                        // 
                        // user does not exist, return false
                        // 
                        if (user == null)
                            return false;

                        // 
                        // here we want to check if the user is already unlocked due to bad password answer (or bad password)
                        // 
                        if (user.IsLockedOut) 
                            return false;
                    } 
                    else
                    {
                        //
                        // get the user's directory entry (Also get sAMAccountName if needed) 
                        //
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1)) 
                        { 
                            string sAMAccountName = null;
                            userEntry = FindUserEntryAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out sAMAccountName); 
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        }
                        else
                        { 
                            userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")");
                            usernameForAuthentication = username; 
                        } 

                        // 
                        // user does not exist, return false
                        //
                        if (userEntry == null)
                            return false; 
                    }
 
                    // 
                    // associate the user's context with the directory entry
                    // 
                    userEntry.Username = (usernameIsSAMAccountName) ? directoryInfo.DomainName + "\\" + usernameForAuthentication : usernameForAuthentication;
                    userEntry.Password = oldPassword;
                    userEntry.AuthenticationType = directoryInfo.GetAuthenticationTypes(directoryInfo.ConnectionProtection, (directoryInfo.DirectoryType == DirectoryType.AD) ? CredentialsType.Windows : CredentialsType.NonWindows);
 
                    try
                    { 
                        SetPasswordPortIfApplicable(userEntry); 

                        // 
                        // Change the password
                        //
                        userEntry.Invoke("ChangePassword", new object[]{ oldPassword, newPassword });
                    } 
                    catch (COMException e2)
                    { 
                        if (e2.ErrorCode == unchecked((int) 0x8007052e)) 
                            return false;
                        else 
                            throw;
                    }
                    catch (TargetInvocationException tie)
                    { 
                        if (tie.InnerException is COMException)
                        { 
                            COMException ce = (COMException) tie.InnerException; 
                            int errorCode = ce.ErrorCode;
 
                            //
                            // if the exception is due to password not meeting complexity requirements, then return
                            // MembershipPasswordException
                            // 
                            if ((errorCode == unchecked((int) 0x800708c5)) || (errorCode == unchecked((int) 0x8007202f))  || (errorCode == unchecked((int) 0x8007052d)) || (errorCode == unchecked((int) 0x8007052f)))
                                throw new MembershipPasswordException(SR.GetString(SR.Membership_InvalidPassword), ce); 
                            // 
                            // if the target is ADAM and the exception is due to property not found, this indicates that a secure
                            // connection could not be setup for changing the password and ADSI is falling back to kerberos which does not work for ADAM 
                            // so we will provide a clearer exception
                            //
                            else if ((errorCode == unchecked((int) 0x8000500d) && (directoryInfo.DirectoryType == DirectoryType.ADAM)))
                                throw new ProviderException(SR.GetString(SR.ADMembership_No_secure_conn_for_password)); 
                            else
                                throw; 
                        } 
                        else
                            throw; 
                    }

                    if (EnablePasswordReset && resetBadPasswordAnswerAttributes)
                    { 
                        //
                        // associate the process context with the directory entry 
                        // 
                        userEntry.Username = directoryInfo.GetUsername();
                        userEntry.Password = directoryInfo.GetPassword(); 
                        userEntry.AuthenticationType = directoryInfo.AuthenticationTypes;

                        //
                        // user supplied correct password, so we need to reset the password answer tracking info 
                        //
                        ResetBadPasswordAnswerAttributes(userEntry); 
                    } 
                }
                finally 
                {
                    if (userEntry != null)
                        userEntry.Dispose();
                    connection.Close(); 
                }
            } 
            catch 
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                //
                throw;
            } 

            // 
            // Password changed successfully 
            //
            return true; 
        }

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override string ResetPassword(string username, string passwordAnswer) 
        { 
            string newPassword = null;
 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            if (!EnablePasswordReset) 
                throw new NotSupportedException(SR.GetString(SR.Not_configured_to_support_password_resets));
 
            CheckUserName(ref username, maxUsernameLength, "username"); 

            CheckPasswordAnswer(ref passwordAnswer, RequiresQuestionAndAnswer, maxPasswordAnswerLength, "passwordAnswer"); 

            try
            {
                // 
                // validate the password answer
                // 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                DirectoryEntry userEntry = null; 
                bool resetBadPasswordAnswerAttributes = false;

                try
                { 
                    //
                    // get the user's directory entry 
                    // 
                    MembershipUser user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes);
 
                    //
                    // user does not exist, throw exception
                    //
                    if (user == null) 
                        throw new ProviderException(SR.GetString(SR.Membership_UserNotFound));
 
                    // 
                    // if user is locked, throw an exception
                    // 
                    if (user.IsLockedOut)
                        throw new MembershipPasswordException(SR.GetString(SR.Membership_AccountLockOut));

                    string storedPasswordAnswer = Decrypt((string) PropertyManager.GetPropertyValue(userEntry, attributeMapPasswordAnswer)); 
                    if (!StringUtil.EqualsIgnoreCase(passwordAnswer, storedPasswordAnswer))
                    { 
                        UpdateBadPasswordAnswerAttributes(userEntry); 
                        throw new MembershipPasswordException(SR.GetString(SR.Membership_WrongAnswer));
                    } 
                    else
                    {
                        if (resetBadPasswordAnswerAttributes)
                            ResetBadPasswordAnswerAttributes(userEntry); 
                    }
 
                    SetPasswordPortIfApplicable(userEntry); 

                    // 
                    // Reset  the password (generating a random new password)
                    //
                    newPassword = GeneratePassword();
 
                    ValidatePasswordEventArgs e = new ValidatePasswordEventArgs( username, newPassword, false );
                    OnValidatingPassword(e); 
 
                    if(e.Cancel)
                    { 
                        if(e.FailureInformation != null)
                            throw e.FailureInformation;
                        else
                            throw new ProviderException(SR.GetString( SR.Membership_Custom_Password_Validation_Failure)); 
                    }
 
                    userEntry.Invoke("SetPassword", new object[]{ newPassword }); 

                } 
                catch (TargetInvocationException tie)
                {
                    if (tie.InnerException is COMException)
                    { 
                        COMException ce = (COMException) tie.InnerException;
                        int errorCode = ce.ErrorCode; 
 
                        //
                        // if the exception is due to password not meeting complexity requirements, then return 
                        // ProviderException
                        //
                        if ((errorCode == unchecked((int) 0x800708c5)) || (errorCode == unchecked((int) 0x8007202f))  || (errorCode == unchecked((int) 0x8007052d)) || (errorCode == unchecked((int) 0x8007052f)))
                            throw new ProviderException(SR.GetString(SR.ADMembership_Generated_password_not_complex), ce); 
                        //
                        // if the target is ADAM and the exception is due to property not found, this indicates that a secure 
                        // connection could not be setup for changing the password and ADSI is falling back to kerberos which does not work for ADAM 
                        // so we will provide a clearer exception
                        // 
                        if ((errorCode == unchecked((int) 0x8000500d) && (directoryInfo.DirectoryType == DirectoryType.ADAM)))
                            throw new ProviderException(SR.GetString(SR.ADMembership_No_secure_conn_for_password));
                        else
                            throw; 
                    }
                    else 
                        throw; 
                }
                finally 
                {
                    if (userEntry != null)
                        userEntry.Dispose();
                    connection.Close(); 
                }
            } 
            catch 
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                //
                throw;
            } 

            // 
            // Password was reset successfully, return the generated password 
            //
            return newPassword; 
        }

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override bool UnlockUser(string username) 
        { 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

            CheckUserName( ref username, maxUsernameLength, "username" );

            try 
            {
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry; 
                DirectoryEntry userEntry = null;
 
                try
                {
                    //
                    // get the user's directory entry 
                    //
                    userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")"); 
 
                    //
                    // user does not exist, return false 
                    //
                    if (userEntry == null)
                        return false;
 
                    userEntry.Properties["lockoutTime"].Value = 0;
 
                    if (EnablePasswordReset) 
                    {
                        userEntry.Properties[attributeMapFailedPasswordAnswerCount].Value = 0; 
                        userEntry.Properties[attributeMapFailedPasswordAnswerTime].Value = 0;
                        userEntry.Properties[attributeMapFailedPasswordAnswerLockoutTime].Value = 0;
                    }
 
                    userEntry.CommitChanges();
                } 
                finally 
                {
                    if (userEntry != null) 
                        userEntry.Dispose();
                    connection.Close();
                }
            } 
            catch
            { 
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                // 
                throw;
            }

            // 
            // user unlocked successfully, return true
            // 
            return true; 
        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override void UpdateUser(MembershipUser user) 
        {
            bool emailModified = true; 
            bool commentModified = true; 
            bool isApprovedModified = true;
 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            if( user == null ) 
            {
                throw new ArgumentNullException("user" ); 
            } 

            ActiveDirectoryMembershipUser adUser = user as ActiveDirectoryMembershipUser; 

            if (adUser != null)
            {
                // 
                // check which fields have really been modified
                // 
                emailModified = adUser.emailModified; 
                commentModified = adUser.commentModified;
                isApprovedModified = adUser.isApprovedModified; 
            }

            string temp = user.UserName;
            CheckUserName( ref temp, maxUsernameLength, "UserName" ); 

            string email = user.Email; 
            if (emailModified) 
                SecUtility.CheckParameter( ref email, RequiresUniqueEmail, true, false, maxEmailLength, "Email");
 
            if (commentModified && user.Comment != null)
            {
                if (user.Comment.Length == 0)
                    throw new ArgumentException(SR.GetString(SR.Parameter_can_not_be_empty, "Comment"), "Comment"); 

                if (maxCommentLength > 0 && user.Comment.Length > maxCommentLength) 
                    throw new ArgumentException(SR.GetString(SR.Parameter_too_long, "Comment", maxCommentLength.ToString(CultureInfo.InvariantCulture)), "Comment"); 
            }
 
            try
            {

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                DirectoryEntry userEntry = null; 
 
                try
                { 
                    //
                    // get the user's directory entry
                    //
                    userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(user.UserName) + ")"); 

                    if (userEntry == null) 
                        throw new ProviderException(SR.GetString(SR.Membership_UserNotFound)); 

                    if (!((emailModified) || (commentModified) || (isApprovedModified))) 
                        // nothing has been modified
                        return;

                    // 
                    // update the email
                    // if enableUniqueEmail is specified, we need to ensure that the email is unique 
                    // 
                    if (emailModified)
                    { 
                        if (email == null)
                        {
                            // set the email to null only if email already exists
                            if (userEntry.Properties.Contains(attributeMapEmail)) 
                                userEntry.Properties[attributeMapEmail].Clear();
                        } 
                        else 
                        {
                            if (RequiresUniqueEmail && !IsEmailUnique(null, user.UserName, email, true /* existing */)) 
                                throw new ProviderException(SR.GetString(SR.Membership_DuplicateEmail));

                            userEntry.Properties[attributeMapEmail].Value = email;
                        } 
                    }
 
                    // 
                    // update the comment
                    // 
                    if (commentModified)
                    {
                        if (user.Comment == null)
                        { 
                            // set the comment to null only if comment already exists
                            if (userEntry.Properties.Contains("comment")) 
                                userEntry.Properties["comment"].Clear(); 
                        }
                        else 
                        {
                            //
                            // we use the original value ("user.Comment") to preserve all white space
                            // (including leading and trailing white space) 
                            userEntry.Properties["comment"].Value = user.Comment;
                        } 
                    } 

                    // 
                    // update the IsApproved field
                    //
                    if (isApprovedModified)
                    { 
                        if (directoryInfo.DirectoryType == DirectoryType.AD)
                        { 
                            // userAccountControl attribute 
                            const int UF_ACCOUNT_DISABLED =0x2;
 
                            int val = (int)PropertyManager.GetPropertyValue(userEntry, "userAccountControl");

                            if (user.IsApproved)
                                val &= ~UF_ACCOUNT_DISABLED; 
                            else
                                val |= UF_ACCOUNT_DISABLED; 
                            userEntry.Properties["userAccountControl"].Value = val; 
                        }
                        else 
                        {
                            // different attribute for ADAM
                            userEntry.Properties["msDS-UserAccountDisabled"].Value = !(user.IsApproved);
                        } 
                    }
 
                    userEntry.CommitChanges(); 

                    if (adUser != null) 
                    {
                        adUser.emailModified = false;
                        adUser.commentModified = false;
                        adUser.isApprovedModified = false; 
                    }
 
                } 
                finally
                { 
                    if (userEntry != null)
                        userEntry.Dispose();
                    connection.Close();
                } 
            }
            catch 
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                //
                throw;
            }
 
            return;
        } 
 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.LinkDemand, Unrestricted=true)] 
        public override bool ValidateUser(string username, string password)
        {
            if( ValidateUserCore(username, password))
            { 
                PerfCounters.IncrementCounter(AppPerfCounter.MEMBER_SUCCESS);
                WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditMembershipAuthenticationSuccess, username); 
                return true; 
            } else {
                PerfCounters.IncrementCounter(AppPerfCounter.MEMBER_FAIL); 
                WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditMembershipAuthenticationFailure, username);
                return false;
            }
        } 

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        private bool ValidateUserCore(string username, string password) 
        {
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
            if(!SecUtility.ValidateParameter(ref username, true, true, true, maxUsernameLength))
            { 
                return false; 
            }
 
            //
            // if username is mapped to UPN, it should not contain '\'
            //
            if (usernameIsUPN && (username.IndexOf('\\') != -1)) 
            {
                return false; 
            } 

            if( !ValidatePassword(password, maxPasswordLength)) 
            {
                return false;
            }
 
            bool result = false;
            try 
            { 

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                DirectoryEntry userEntry = null;
                bool resetBadPasswordAnswerAttributes = false;
                string usernameForAuthentication = null; 

                try 
                { 
                    if (EnablePasswordReset)
                    { 
                        //
                        // get the user's directory entry
                        // NOTE: If the username is mapped to userPrincipalName and the username does not contain '@' in it, then simple bind will fail as it needs domain information.
                        //           To workaround this whenever we are talking to AD, username is mapped to userPrincipalName and does not contain '@', we will get the sAMAccountName 
                        //           while getting the user object and use that for authenticating the user.
                        // 
                        MembershipUser user = null; 
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1))
                        { 
                            string sAMAccountName = null;
                            user = FindUserAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes, out sAMAccountName);
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        } 
                        else
                        { 
                            user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out userEntry, out resetBadPasswordAnswerAttributes); 
                            usernameForAuthentication = username;
                        } 

                        //
                        // user does not exist, return false
                        // 
                        if (user == null)
                            return false; 
 
                        //
                        // here we want to check if the user is already unlocked due to bad password answer (or bad password) 
                        //
                        if (user.IsLockedOut)
                            return false;
                    } 
                    else
                    { 
                        // 
                        // get the user's directory entry
                        // 
                        if ((directoryInfo.DirectoryType == DirectoryType.AD) && (usernameIsUPN) && (username.IndexOf('@') == -1))
                        {
                            string sAMAccountName = null;
                            userEntry = FindUserEntryAndSAMAccountName(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out sAMAccountName); 
                            usernameForAuthentication = directoryInfo.DomainName + "\\" + sAMAccountName;
                        } 
                        else 
                        {
                            userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")"); 
                            usernameForAuthentication = username;
                        }

                        // 
                        // user does not exist, return false
                        // 
                        if (userEntry == null) 
                            return false;
                    } 

                    result = ValidateCredentials(usernameForAuthentication, password);

                    if (EnablePasswordReset && result && resetBadPasswordAnswerAttributes) 
                    {
                        // 
                        // user supplied correct password, so we need to reset the password answer tracking info 
                        //
                        ResetBadPasswordAnswerAttributes(userEntry); 
                    }

                }
                finally 
                {
                    if (userEntry != null) 
                        userEntry.Dispose(); 
                    connection.Close();
                } 
            }
            catch
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                // 
                throw; 
            }
 
            return result;

        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] 
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        { 
            MembershipUser user = null;

            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

            if( providerUserKey == null ) 
            { 
                throw new ArgumentNullException( "providerUserKey" );
            } 

            if ( !( providerUserKey is SecurityIdentifier) )
            {
                throw new ArgumentException( SR.GetString( SR.ADMembership_InvalidProviderUserKey ), "providerUserKey" ); 
            }
 
            try 
            {
 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry;

                try 
                {
                    // 
                    // Search for the user and return a MembershipUser object 
                    //
                    SecurityIdentifier sid = providerUserKey as SecurityIdentifier; 
                    StringBuilder sidHexValueStr = new StringBuilder();
                    int binaryLength = sid.BinaryLength;
                    byte[] sidBinaryForm = new byte[binaryLength];
                    sid.GetBinaryForm(sidBinaryForm, 0); 

                    for (int i = 0; i < binaryLength; i++) 
                    { 
                        sidHexValueStr.Append("\\");
                        sidHexValueStr.Append(sidBinaryForm[i].ToString("x2", NumberFormatInfo.InvariantInfo)); 
                    }

                    DirectoryEntry dummyEntry;
                    bool resetBadPasswordAnswerAttributes = false; 
                    user = FindUser(containerEntry, "(" + attributeMapUsername + "=*)(objectSid=" + sidHexValueStr.ToString() + ")", out dummyEntry /* ignored */, out resetBadPasswordAnswerAttributes /* ignored */);
                } 
                finally 
                {
                    connection.Close(); 
                }
            }
            catch
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                // 
                throw;
            } 

            return user;
        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] 
        public override MembershipUser GetUser(string username, bool userIsOnline)
        { 
            MembershipUser user = null;

            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

            CheckUserName(ref username, maxUsernameLength, "username" ); 
 
            try
            { 

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry;
 
                try
                { 
                    // 
                    // Search for the user and return a MembershipUser object
                    // 
                    DirectoryEntry dummyEntry;
                    bool resetBadPasswordAnswerAttributes = false;
                    user = FindUser(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", out dummyEntry /*ignored */, out resetBadPasswordAnswerAttributes /* ignored */);
                } 
                finally
                { 
                    connection.Close(); 
                }
            } 
            catch
            {
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                //
                throw; 
            } 

            return user; 
        }

        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override string GetUserNameByEmail(string email) 
        { 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized)); 

            SecUtility.CheckParameter(ref email, false, true, false, maxEmailLength,  "email");

            string username = null; 
            try
            { 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;
                SearchResultCollection resCol = null; 

                try
                {
                    DirectorySearcher searcher = new DirectorySearcher(containerEntry); 
                    if (email != null)
                        searcher.Filter = "(&(objectCategory=person)(objectClass=user)(" + attributeMapUsername + "=*)(" + attributeMapEmail + "=" + GetEscapedFilterValue(email) +"))"; 
                    else 
                        searcher.Filter = "(&(objectCategory=person)(objectClass=user)(" + attributeMapUsername + "=*)(!(" + attributeMapEmail + "=" +"*)))";
                    searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree; 
                    searcher.PropertiesToLoad.Add(attributeMapUsername);

                    if (directoryInfo.ClientSearchTimeout != -1)
                        searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0); 
                    if (directoryInfo.ServerSearchTimeout != -1)
                        searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0); 
 
                    resCol = searcher.FindAll();
                    bool userFound = false; 

                    foreach (SearchResult res in resCol)
                    {
                        if (!userFound) 
                        {
                            username = (string) PropertyManager.GetSearchResultPropertyValue(res, attributeMapUsername); 
                            userFound = true; 

                            if (!RequiresUniqueEmail) 
                                break;
                        }
                        else
                        { 
                            if (RequiresUniqueEmail)
                            { 
                                // there is a duplicate entry, so we need to throw an ProviderException 
                                throw new ProviderException(SR.GetString(SR.Membership_more_than_one_user_with_email));
                            } 
                            else
                                // we should never get here
                                break;
                        } 
                    }
                } 
                finally 
                {
                    if (resCol != null) 
                        resCol.Dispose();
                    connection.Close();
                }
            } 
            catch
            { 
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                // 
                throw;
            }

            return username; 
        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] 
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {

            if (!initialized) 
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
            CheckUserName(ref username, maxUsernameLength, "username"); 

            try 
            {
                //
                // Get the Directory Entry for the container
                // 
                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.CreationContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry; 
                // to avoid unnecessary searches (for better performance) 
                containerEntry.AuthenticationType |= AuthenticationTypes.FastBind;
                DirectoryEntry userEntry = null; 

                try
                {
                    // 
                    // Get the directory entry for the user
                    // 
                    string dummyString; 
                    userEntry = FindUserEntry(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(username) + ")", System.DirectoryServices.SearchScope.OneLevel, false /* retrieveSAMAccountName */, out dummyString);
 
                    if (userEntry == null)
                        return false;

                    // 
                    // Remove the entry from the container
                    // 
                    containerEntry.Children.Remove(userEntry); 

                } 
                catch (COMException e)
                {
                    if (e.ErrorCode == unchecked((int) 0x80072030))
                    { 
                        //
                        // incase some one else deleted the object just before this 
                        // 
                        return false;
                    } 
                    else
                        throw;
                }
                finally 
                {
                    if (userEntry != null) 
                        userEntry.Dispose(); 
                    connection.Close();
                } 
            }
            catch
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation)
                // 
                throw; 
            }
 
            return true;
        }

        public virtual string GeneratePassword() 
        {
            // 
            // 

 


            return Membership.GeneratePassword(
                      MinRequiredPasswordLength < PASSWORD_SIZE ? PASSWORD_SIZE : MinRequiredPasswordLength, 
                      MinRequiredNonAlphanumericCharacters);
        } 
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override MembershipUserCollection GetAllUsers(int pageIndex,
                                                        int pageSize,
                                                        out int totalRecords) 
        {
            return FindUsersByName("*", pageIndex, pageSize, out totalRecords); 
        } 

        public override int GetNumberOfUsersOnline() 
        {
            //
            // ADMembershipProvider does not support the notion of online users
            // 
            throw new NotSupportedException(SR.GetString(SR.ADMembership_OnlineUsers_not_supported));
        } 
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)]
        public override MembershipUserCollection FindUsersByName(string usernameToMatch,
                                                        int pageIndex,
                                                        int pageSize, 
                                                        out int totalRecords)
        { 
            if (!initialized) 
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));
 
            if (!EnableSearchMethods)
                throw new NotSupportedException(SR.GetString(SR.ADMembership_Provider_SearchMethods_not_supported));

            SecUtility.CheckParameter( ref usernameToMatch, true, true, true, maxUsernameLength, "usernameToMatch" ); 

            if ( pageIndex < 0 ) 
                throw new ArgumentException(SR.GetString(SR.PageIndex_bad), "pageIndex"); 
            if ( pageSize < 1 )
                throw new ArgumentException(SR.GetString(SR.PageSize_bad), "pageSize"); 

            long upperBound = (long)pageIndex * pageSize + pageSize - 1;
            if ( upperBound > Int32.MaxValue )
                throw new ArgumentException(SR.GetString(SR.PageIndex_PageSize_bad), "pageIndex and pageSize"); 

            try 
            { 

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */); 
                DirectoryEntry containerEntry = connection.DirectoryEntry;

                try
                { 
                    totalRecords = 0;
                    return FindUsers(containerEntry, "(" + attributeMapUsername + "=" + GetEscapedFilterValue(usernameToMatch, false) + ")", attributeMapUsername, pageIndex, pageSize, out totalRecords); 
                } 
                finally
                { 
                    connection.Close();
                }
            }
            catch 
            {
                // 
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                //
                throw; 
            }

        }
 
        [DirectoryServicesPermission(SecurityAction.Assert, Unrestricted=true)]
        [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted=true)] 
        [DirectoryServicesPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] 
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        { 
            if (!initialized)
                throw new InvalidOperationException(SR.GetString(SR.ADMembership_Provider_not_initialized));

            if (!EnableSearchMethods) 
                throw new NotSupportedException(SR.GetString(SR.ADMembership_Provider_SearchMethods_not_supported));
 
            SecUtility.CheckParameter(ref emailToMatch, false, true, false, maxEmailLength, "emailToMatch"); 

            if ( pageIndex < 0 ) 
                throw new ArgumentException(SR.GetString(SR.PageIndex_bad), "pageIndex");
            if ( pageSize < 1 )
                throw new ArgumentException(SR.GetString(SR.PageSize_bad), "pageSize");
 
            long upperBound = (long)pageIndex * pageSize + pageSize - 1;
            if ( upperBound > Int32.MaxValue ) 
                throw new ArgumentException(SR.GetString(SR.PageIndex_PageSize_bad), "pageIndex and pageSize"); 

            try 
            {

                DirectoryEntryHolder connection = ActiveDirectoryConnectionHelper.GetDirectoryEntry(directoryInfo, directoryInfo.ContainerDN, true /* revertImpersonation */);
                DirectoryEntry containerEntry = connection.DirectoryEntry; 

                try 
                { 
                    totalRecords = 0;
                    string filter = null; 
                    if (emailToMatch != null)
                        filter = "(" + attributeMapUsername + "=*)(" + attributeMapEmail + "=" + GetEscapedFilterValue(emailToMatch, false) +")";
                    else
                        filter = "(" + attributeMapUsername + "=*)(!(" + attributeMapEmail + "=" +"*))"; 
                    return FindUsers(containerEntry, filter, attributeMapEmail, pageIndex, pageSize, out totalRecords);
                } 
                finally 
                {
                    connection.Close(); 
                }
            }
            catch
            { 
                //
                // this outer try-catch is to mitigate the exception filter attack (since we maybe suspending impersonation) 
                // 
                throw;
            } 

        }

        private bool ValidateCredentials(string username, string password) 
        {
            bool result = false; 
            NetworkCredential credentialForValidation = (usernameIsSAMAccountName) ? new NetworkCredential(username, password, directoryInfo.DomainName) 
                                                                                                                                    : DirectoryInformation.GetCredentialsWithDomain(new NetworkCredential(username, password));
 
            //
            // NOTE: we do not need to revert context here since this method is always
            //           called with explicit credentials
            // 

            // 
            // if this is concurrent bind (use the common connection) 
            //
 
            if (directoryInfo.ConcurrentBindSupported)
            {
                try
                { 
                    connection.Bind(credentialForValidation);
                    result = true; 
                } 
                catch (LdapException e)
                { 
                    if (e.ErrorCode == 0x31)
                    {
                        //
                        // authentication failure, invalid user 
                        //
                        result = false; 
                    } 
                    else
                    { 
                        //
                        // some other failure
                        //
                        throw; 
                    }
                } 
            } 
            else
            { 
                //
                // create a new ldap connection
                //
                LdapConnection newConnection = directoryInfo.CreateNewLdapConnection(authTypeForValidation); 

                try 
                { 
                    newConnection.Bind(credentialForValidation);
                    result = true; 
                }
                catch (LdapException e2)
                {
                    if (e2.ErrorCode == 0x31) 
                    {
                        // 
                        // authentication failure, invalid user 
                        //
                        result = false; 
                    }
                    else
                    {
                        // 
                        // some other failure
                        // 
                        throw; 
                    }
                } 
                finally
                {
                    newConnection.Dispose();
                } 
            }
 
            return result; 
        }
 
        private DirectoryEntry FindUserEntryAndSAMAccountName(DirectoryEntry containerEntry, string filter, out string sAMAccountName)
        {
            return FindUserEntry(containerEntry, filter, System.DirectoryServices.SearchScope.Subtree, true /*retrieveSAMAccountName */, out sAMAccountName);
        } 

        private DirectoryEntry FindUserEntry(DirectoryEntry containerEntry, string filter) 
        { 
            string dummyString;
            return FindUserEntry(containerEntry, filter, System.DirectoryServices.SearchScope.Subtree, false /*retrieveSAMAccountName */, out dummyString); 
        }

        private DirectoryEntry FindUserEntry(DirectoryEntry containerEntry, string filter, System.DirectoryServices.SearchScope searchScope, bool retrieveSAMAccountName, out string sAMAccountName)
        { 
            Debug.Assert(containerEntry != null);
            DirectorySearcher searcher = new DirectorySearcher(containerEntry); 
 
            searcher.SearchScope = searchScope;
            searcher.Filter = "(&(objectCategory=person)(objectClass=user)" + filter + ")"; 

            if (directoryInfo.ClientSearchTimeout != -1)
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0);
            if (directoryInfo.ServerSearchTimeout != -1) 
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0);
 
            if (retrieveSAMAccountName) 
                searcher.PropertiesToLoad.Add("sAMAccountName");
 
            SearchResult res = searcher.FindOne();

            sAMAccountName = null;
            if (res != null) 
            {
                if (retrieveSAMAccountName) 
                    sAMAccountName = (string) PropertyManager.GetSearchResultPropertyValue(res, "sAMAccountName"); 
                return res.GetDirectoryEntry();
            } 
            else
                return null;

        } 

        private MembershipUser FindUserAndSAMAccountName(DirectoryEntry containerEntry, string filter, out DirectoryEntry userEntry, out bool resetBadPasswordAnswerAttributes, out string sAMAccountName) 
        { 
            return FindUser(containerEntry, filter, System.DirectoryServices.SearchScope.Subtree, true /* retrieveSAMAccountName */, out userEntry, out resetBadPasswordAnswerAttributes, out sAMAccountName);
        } 

        private MembershipUser FindUser(DirectoryEntry containerEntry, string filter, out DirectoryEntry userEntry, out bool resetBadPasswordAnswerAttributes)
        {
            string dummyString; 
            return FindUser(containerEntry, filter, System.DirectoryServices.SearchScope.Subtree, false /* retrieveSAMAccountName */, out userEntry, out resetBadPasswordAnswerAttributes, out dummyString);
        } 
 
        private MembershipUser FindUser(DirectoryEntry containerEntry, string filter, System.DirectoryServices.SearchScope searchScope,  bool retrieveSAMAccountName, out DirectoryEntry userEntry, out bool resetBadPasswordAnswerAttributes, out string sAMAccountName)
        { 
            Debug.Assert(containerEntry != null);
            MembershipUser user = null;
            DirectorySearcher searcher = new DirectorySearcher(containerEntry);
 
            searcher.SearchScope = searchScope;
            searcher.Filter = "(&(objectCategory=person)(objectClass=user)" + filter + ")"; 
 
            if (directoryInfo.ClientSearchTimeout != -1)
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0); 
            if (directoryInfo.ServerSearchTimeout != -1)
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0);

            // 
            // load all the attributes needed to create a MembershipUser object
            // 
            searcher.PropertiesToLoad.Add(attributeMapUsername); 
            searcher.PropertiesToLoad.Add("objectSid");
            searcher.PropertiesToLoad.Add(attributeMapEmail); 
            searcher.PropertiesToLoad.Add("comment");
            searcher.PropertiesToLoad.Add("whenCreated");
            searcher.PropertiesToLoad.Add("pwdLastSet");
            searcher.PropertiesToLoad.Add("msDS-User-Account-Control-Computed"); 
            searcher.PropertiesToLoad.Add("lockoutTime");
 
            if (retrieveSAMAccountName) 
                searcher.PropertiesToLoad.Add("sAMAccountName");
 
            if (attributeMapPasswordQuestion != null)
                searcher.PropertiesToLoad.Add(attributeMapPasswordQuestion);

            if (directoryInfo.DirectoryType == DirectoryType.AD) 
                searcher.PropertiesToLoad.Add("userAccountControl");
            else 
                searcher.PropertiesToLoad.Add("msDS-UserAccountDisabled"); 

            if (EnablePasswordReset) 
            {
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerCount);
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerTime);
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerLockoutTime); 
            }
 
 
            SearchResult res = searcher.FindOne();
            resetBadPasswordAnswerAttributes = false; 
            sAMAccountName = null;
            if (res != null)
            {
                user = GetMembershipUserFromSearchResult(res); 
                userEntry = res.GetDirectoryEntry();
 
                if (retrieveSAMAccountName) 
                    sAMAccountName = (string) PropertyManager.GetSearchResultPropertyValue(res, "sAMAccountName");
 
                if ((EnablePasswordReset) && res.Properties.Contains(attributeMapFailedPasswordAnswerCount))
                    resetBadPasswordAnswerAttributes = ((int) PropertyManager.GetSearchResultPropertyValue(res, attributeMapFailedPasswordAnswerCount) > 0);
            }
            else 
            {
                userEntry = null; 
            } 

            return user; 

        }

        private MembershipUserCollection FindUsers(DirectoryEntry containerEntry, string filter, string sortKey, int pageIndex, int pageSize, out int totalRecords) 
        {
            Debug.Assert(containerEntry != null); 
            MembershipUserCollection col = new MembershipUserCollection(); 
            int lastOffset = (pageIndex + 1) * pageSize;
            int startOffset = lastOffset -pageSize + 1; 

            DirectorySearcher searcher = new DirectorySearcher(containerEntry);
            searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
            searcher.Filter = "(&(objectCategory=person)(objectClass=user)" + filter + ")"; 

            if (directoryInfo.ClientSearchTimeout != -1) 
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0); 
            if (directoryInfo.ServerSearchTimeout != -1)
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0); 

            //
            // load all the attributes needed to create a MembershipUser object
            // 
            searcher.PropertiesToLoad.Add(attributeMapUsername);
            searcher.PropertiesToLoad.Add("objectSid"); 
            searcher.PropertiesToLoad.Add(attributeMapEmail); 
            searcher.PropertiesToLoad.Add("comment");
            searcher.PropertiesToLoad.Add("whenCreated"); 
            searcher.PropertiesToLoad.Add("pwdLastSet");
            searcher.PropertiesToLoad.Add("msDS-User-Account-Control-Computed");
            searcher.PropertiesToLoad.Add("lockoutTime");
 
            if (attributeMapPasswordQuestion != null)
                searcher.PropertiesToLoad.Add(attributeMapPasswordQuestion); 
 
            if (directoryInfo.DirectoryType == DirectoryType.AD)
                searcher.PropertiesToLoad.Add("userAccountControl"); 
            else
                searcher.PropertiesToLoad.Add("msDS-UserAccountDisabled");

            if (EnablePasswordReset) 
            {
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerCount); 
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerTime); 
                searcher.PropertiesToLoad.Add(attributeMapFailedPasswordAnswerLockoutTime);
            } 

            //
            // turn on paging
            // 
            searcher.PageSize = 512;
 
            // 
            // need to sort the users based on the attribute that is mapped to the username
            // 
            searcher.Sort = new SortOption(sortKey, SortDirection.Ascending);

            SearchResultCollection resCol = searcher.FindAll();
 
            try
            { 
                int count = 0; 
                totalRecords = 0;
 
                foreach(SearchResult res in resCol)
                {
                    count++;
 
                    //
                    // add only the requested window of the result set 
                    // 
                    if (count >= startOffset && count <= lastOffset)
                    { 
                        col.Add(GetMembershipUserFromSearchResult(res));
                    }
                }
                totalRecords = count; 
            }
            finally 
            { 
                resCol.Dispose();
            } 

            return col;

        } 

        private void CheckPasswordAnswer(ref string passwordAnswer, bool checkForNull, int maxSize,string paramName) 
        { 
            if (passwordAnswer == null)
            { 
                if (checkForNull)
                    throw new ArgumentNullException(paramName);
                return;
            } 

            passwordAnswer = passwordAnswer.Trim(); 
 
            if (passwordAnswer.Length < 1)
                throw new ArgumentException(SR.GetString(SR.Parameter_can_not_be_empty, paramName), paramName); 

            if (maxSize > 0 && passwordAnswer.Length > maxSize)
                throw new ArgumentException(SR.GetString(SR.ADMembership_Parameter_too_long, paramName), paramName);
        } 

        private bool ValidatePassword(string password, int maxSize) 
        { 
            if (password == null)
                return false; 

            if (password.Trim().Length < 1)
                return false;
 
            if (maxSize > 0 && password.Length > maxSize)
                return false; 
 
            return true;
        } 

        private void CheckPassword(string password, int maxSize, string paramName)
        {
            if (password == null) 
                throw new ArgumentNullException(paramName);
 
            if (password.Trim().Length < 1) 
                throw new ArgumentException(SR.GetString(SR.Parameter_can_not_be_empty, paramName), paramName);
 
            if (maxSize > 0 && password.Length > maxSize)
                throw new ArgumentException(SR.GetString(SR.Parameter_too_long, paramName, maxSize.ToString(CultureInfo.InvariantCulture)), paramName);
        }
 
        private void CheckUserName(ref string username, int maxSize, string paramName)
        { 
            SecUtility.CheckParameter( ref username, true, true, true, maxSize, paramName ); 

            // 
            // if username is mapped to UPN, it should not contain '\'
            //
            if (usernameIsUPN && (username.IndexOf('\\') != -1))
                throw new ArgumentException(SR.GetString(SR.ADMembership_UPN_contains_backslash, paramName), paramName); 
        }
 
        private int GetDomainControllerLevel(string serverName) 
        {
            int dcLevel = 0; 

            DirectoryEntry rootdse = new DirectoryEntry("LDAP://" + serverName + "/RootDSE", directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes);
            string dcLevelString = (string) rootdse.Properties["domainControllerFunctionality"].Value;
            if (dcLevelString != null) 
                dcLevel = Int32.Parse(dcLevelString, NumberFormatInfo.InvariantInfo);
 
            return dcLevel; 
        }
 
        private void UpdateBadPasswordAnswerAttributes(DirectoryEntry userEntry)
        {

            // 
            // get the password answer tracking related attributes to determine if we are still in an
            // active window for bad password answer attempts 
            // 
            int badPasswordAttemptCount = 0;
            bool inActiveWindow = false; 


            DateTime currentTime = DateTime.UtcNow;
            if (userEntry.Properties.Contains(attributeMapFailedPasswordAnswerTime)) 
            {
                DateTime lastBadPasswordAnswerTime = GetDateTimeFromLargeInteger((NativeComInterfaces.IAdsLargeInteger) PropertyManager.GetPropertyValue(userEntry, attributeMapFailedPasswordAnswerTime)); 
                TimeSpan diffTime = currentTime.Subtract(lastBadPasswordAnswerTime); 
                inActiveWindow = (diffTime <= new TimeSpan(0, PasswordAttemptWindow, 0));
            } 

            // get the current bad password count
            int currentBadPasswordAttemptCount = 0;
            if (userEntry.Properties.Contains(attributeMapFailedPasswordAnswerCount)) 
                currentBadPasswordAttemptCount = (int) PropertyManager.GetPropertyValue(userEntry, attributeMapFailedPasswordAnswerCount);
 
            if (inActiveWindow && (currentBadPasswordAttemptCount > 0)) 
            {
                // within an active window for bad password answer attempts (increment count, if greater than 0) 
                badPasswordAttemptCount =  currentBadPasswordAttemptCount + 1;
            }
            else
            { 
                // start a new active window (set count = 1)
                badPasswordAttemptCount = 1; 
            } 

            // set the bad password attempt count and time 
            userEntry.Properties[attributeMapFailedPasswordAnswerCount].Value = badPasswordAttemptCount;
            userEntry.Properties[attributeMapFailedPasswordAnswerTime].Value = GetLargeIntegerFromDateTime(currentTime);

            if (badPasswordAttemptCount >= maxInvalidPasswordAttempts) 
            {
                // 
                // user needs to be locked out due to too many bad password answer attempts 
                //
                userEntry.Properties[attributeMapFailedPasswordAnswerLockoutTime].Value = GetLargeIntegerFromDateTime(currentTime); 
            }

            userEntry.CommitChanges();
        } 

 
        private void ResetBadPasswordAnswerAttributes(DirectoryEntry userEntry) 
        {
            // 
            // clear the password answer tracking related attributes (reset the window)
            //
            userEntry.Properties[attributeMapFailedPasswordAnswerCount].Value = 0;
            userEntry.Properties[attributeMapFailedPasswordAnswerTime].Value = 0; 
            userEntry.Properties[attributeMapFailedPasswordAnswerLockoutTime].Value = 0;
 
            userEntry.CommitChanges(); 
        }
 
        private MembershipUser GetMembershipUserFromSearchResult(SearchResult res)
        {
            // username
            string username = (string) PropertyManager.GetSearchResultPropertyValue(res, attributeMapUsername); 

            // providerUserKey is the SID of the user 
            byte[] sidBinaryForm = (byte[]) PropertyManager.GetSearchResultPropertyValue(res, "objectSid"); 
            object providerUserKey = new SecurityIdentifier(sidBinaryForm, 0);
 
            // email (optional)
            string email = (res.Properties.Contains(attributeMapEmail)) ? (string) res.Properties[attributeMapEmail][0] : null;

            // passwordQuestion 
            string passwordQuestion = null;
            if ((attributeMapPasswordQuestion != null) && (res.Properties.Contains(attributeMapPasswordQuestion))) 
                passwordQuestion = (string) PropertyManager.GetSearchResultPropertyValue(res, attributeMapPasswordQuestion); 

            //comment (optional) 
            string comment = (res.Properties.Contains("comment")) ? (string) res.Properties["comment"][0] : null;

            //isApproved and isLockedOut
            bool isApproved; 
            bool isLockedOut = false;
            if (directoryInfo.DirectoryType == DirectoryType.AD) 
            { 
                int val = (int) PropertyManager.GetSearchResultPropertyValue(res, "userAccountControl");
                if ((val & UF_ACCOUNT_DISABLED) == 0) 
                    isApproved = true;
                else
                    isApproved = false;
 
                //
                // the "msDS-User-Account-Control-Computed" is the correct attribute to determine if  the 
                // user is locked out or not. This attribute does not exist in W2K schema, so if we do not see this attribute in the result set 
                // we will use the "lockoutTime". Note, if the user is not locked out and the schema is W2K3, this attribute will exist in the result
                // and have value 0 (since it's constructed), therefore absence of the attribute signifies that schema is W2K. 
                //
                if (res.Properties.Contains("msDS-User-Account-Control-Computed"))
                {
                    int val2 = (int) PropertyManager.GetSearchResultPropertyValue(res, "msDS-User-Account-Control-Computed"); 
                    if ((val2 & UF_LOCKOUT) != 0)
                        isLockedOut = true; 
                } 
                else if (res.Properties.Contains("lockoutTime"))
                { 
                    // NOTE: all date-time computation is done in UTC time though the values returned are in local time
                    DateTime lockoutTime = DateTime.FromFileTimeUtc((Int64) PropertyManager.GetSearchResultPropertyValue(res, "lockoutTime"));
                    DateTime currentTime = DateTime.UtcNow;
                    TimeSpan diffTime = currentTime.Subtract(lockoutTime); 
                    isLockedOut = (diffTime <= directoryInfo.ADLockoutDuration);
                } 
 
            }
            else 
            {
                isApproved = true; // if the msDS-UserAccountDisabled attribute if not present then the user is enabled

                if (res.Properties.Contains("msDS-UserAccountDisabled")) 
                    isApproved = !((bool) PropertyManager.GetSearchResultPropertyValue(res, "msDS-UserAccountDisabled"));
 
                // 
                // ADAM schema contains the "msDS-User-Account-Control-Computed" attribute, therefore it is used to determine the
                // lockout status of the user 
                //
                int val2 = (int) PropertyManager.GetSearchResultPropertyValue(res, "msDS-User-Account-Control-Computed");
                if ((val2 & UF_LOCKOUT) != 0)
                    isLockedOut = true; 
            }
 
            // lastLockoutDate (DateTime.FromFileTime cnoverts to Local time) 
            DateTime lastLockoutDate = DefaultLastLockoutDate;
            if (isLockedOut) 
                lastLockoutDate = DateTime.FromFileTime((Int64) PropertyManager.GetSearchResultPropertyValue(res, "lockoutTime"));

            //
            // if password reset is enabled, we need to check if user is locked out due to bad password answer (and set/change the last lockout date) 
            //
            if ((EnablePasswordReset) && (res.Properties.Contains(attributeMapFailedPasswordAnswerLockoutTime))) 
            { 
                // NOTE: all date-time computation is done in UTC time though the values returned are in local time
                DateTime badPasswordAnswerLockoutTime = DateTime.FromFileTimeUtc((Int64) PropertyManager.GetSearchResultPropertyValue(res, attributeMapFailedPasswordAnswerLockoutTime)); 
                DateTime currentTime = DateTime.UtcNow;
                TimeSpan diffTime = currentTime.Subtract(badPasswordAnswerLockoutTime);
                bool isLockedOutByBadPasswordAnswer = (diffTime <= new TimeSpan(0, PasswordAnswerAttemptLockoutDuration, 0));
 
                if (isLockedOutByBadPasswordAnswer)
                { 
                    if (isLockedOut) 
                    {
                        // 
                        // The account is locked both due to bad password and bad password answer, so we have two lockout dates
                        // Taking the later one.
                        //
                        if (DateTime.Compare(badPasswordAnswerLockoutTime, DateTime.FromFileTimeUtc((Int64) PropertyManager.GetSearchResultPropertyValue(res, "lockoutTime"))) > 0) 
                            lastLockoutDate = DateTime.FromFileTime((Int64) PropertyManager.GetSearchResultPropertyValue(res, attributeMapFailedPasswordAnswerLockoutTime));
                    } 
                    else 
                    {
                        // 
                        // Account is locked out only due to bad password answer
                        //
                        isLockedOut = true;
                        lastLockoutDate = DateTime.FromFileTime((Int64) PropertyManager.GetSearchResultPropertyValue(res, attributeMapFailedPasswordAnswerLockoutTime)); 
                    }
                } 
            } 

            //createTimeStamp 
            DateTime whenCreated =  ((DateTime) PropertyManager.GetSearchResultPropertyValue(res, "whenCreated")).ToLocalTime();

            //lastLogon (not supported)
            DateTime lastLogon = DateTime.MinValue; 

            //lastActivity (not supported) 
            DateTime lastActivity = DateTime.MinValue; 

            //lastpwdchange (DateTime.FromFileTime cnoverts to Local time) 
            DateTime lastPasswordChange = DateTime.FromFileTime((Int64) PropertyManager.GetSearchResultPropertyValue(res, "pwdLastSet"));

            return new ActiveDirectoryMembershipUser(Name, username, sidBinaryForm, providerUserKey, email, passwordQuestion, comment, isApproved, isLockedOut, whenCreated, lastLogon, lastActivity, lastPasswordChange, lastLockoutDate, true /* valuesAreUpdated */);
        } 

        private string GetEscapedRdn(string rdn) 
        { 
            NativeComInterfaces.IAdsPathname pathCracker = (NativeComInterfaces.IAdsPathname) new NativeComInterfaces.Pathname();
            return pathCracker.GetEscapedElement(0, rdn); 
        }

        //
        // Generates an escaped name that may be used in an LDAP query. The characters 
        // ( ) * \ must be escaped when used in an LDAP query per RFC 2254.
        // 
 
        internal string GetEscapedFilterValue(string filterValue)
        { 
            return GetEscapedFilterValue(filterValue, true /* escapeWildChar */);
        }

        internal string GetEscapedFilterValue(string filterValue, bool escapeWildChar) 
        {
            int index = -1; 
            char[] specialCharacters = new char[] { '(', ')', '*', '\\' }; 
            char[] specialCharactersWithoutWildChar = new char[] { '(', ')', '\\' };
 
            index = escapeWildChar ? filterValue.IndexOfAny(specialCharacters) : filterValue.IndexOfAny(specialCharactersWithoutWildChar);
            if (index != -1)
            {
 
                //
                // if it contains any of the special characters then we 
                // need to escape those 
                //
 
                StringBuilder str = new StringBuilder(2 * filterValue.Length);
                str.Append(filterValue.Substring(0, index));

                for (int i = index; i < filterValue.Length; i++) { 

                switch (filterValue[i]) { 
                    case ('(') : { 
                        str.Append("\\28");
                        break; 
                    }

                    case (')') : {
                        str.Append("\\29"); 
                        break;
                    } 
 
                    case ('*') : {
                        if (escapeWildChar) 
                            str.Append("\\2A");
                        else
                            str.Append("*");
                        break; 
                    }
 
                    case ('\\') : { 
                        // this may be the escaped version of '*', i.e. "\2A" or "\2a"
                        if ((escapeWildChar) || (!(((filterValue.Length - i) >= 3) && (filterValue[i + 1] == '2') && ((filterValue[i + 2] == 'A') || (filterValue[i + 2] == 'a'))))) 
                            str.Append("\\5C");
                        else
                            str.Append("\\");
                        break; 
                    }
 
                    default : { 
                        str.Append(filterValue[i]);
                        break; 
                    }
                }
            }
 
            return str.ToString();
            } 
            else 
            {
                // 
                // just return the original string
                //

                return filterValue; 
            }
        } 
 
        //
        // 



        private string GenerateAccountName() 
        {
            char[] accountNameEncodingTable = new char[] {'0','1','2','3','4','5','6','7', 
                                                                                '8','9','A','B','C','D','E','F', 
                                                                                'G','H','I','J','K','L','M','N',
                                                                                'O','P','Q','R','S','T','U','V' }; 
            //
            // account name will be 20 characters long;
            //
            char[] accountName = new char[20]; 

            // 
            // Generate a 64 bit random quantity 
            //
            byte[] random = new byte[12]; 

            //RNGCryptoServiceProvider is an implementation of a random number generator.
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            rng.GetBytes(random); // The array is now filled with cryptographically strong random bytes. 

            // create a 32 bit random numbers from this 
            uint random32a = 0; 
            uint random32b = 0;
            uint random32c = 0; 

            for (int i = 0; i < 4; i++)
            {
                random32a = random32a | unchecked((uint)(random[i] << (8 * i))); 
            }
            for (int i = 0; i < 4; i++) 
            { 
                random32b = random32b | unchecked((uint)(random[4 + i] << (8 * i)));
            } 
            for (int i = 0; i < 4; i++)
            {
                random32c = random32c | unchecked((uint)(random[8 + i] << (8 * i)));
            } 

            // 
            // The first character in the account name is a $ sign 
            //
 
            accountName[0] = '$';

            //
            // The next 6 chars are the least 30 bits of random32a (base 32 encoded) 
            //
            for (int i=1;i<=6;i++) 
            { 

                 // 
                 // Lookup the char corresponding to the last 5 bits of
                 // random32a
                 //
 
                 accountName[i] = accountNameEncodingTable[(random32a & 0x1F)];
 
                 // 
                 // Shift random32a right by 5 places
                 // 

                 random32a = random32a >> 5;
            }
 
            //
            // The next char is a "-" to make the name more readable 
            // 

            accountName[7] = '-'; 

            //
            // The next 12 chars are formed by base 32 encoding the last 30
            // bits of random32b and random32c. 
            //
 
            for (int i=8;i<=13;i++) 
            {
                 // 
                 // Lookup the char corresponding to the last 5 bits
                 //

                 accountName[i] = accountNameEncodingTable[(random32b & 0x1F)]; 

                 // 
                 // Shift  right by 5 places 
                 //
                 random32b = random32b >> 5; 
            }

            for (int i=13;i<=19;i++)
            { 
                 //
                 // Lookup the char corresponding to the last 5 bits 
                 // 

                 accountName[i] = accountNameEncodingTable[(random32c & 0x1F)]; 

                 //
                 // Shift  right by 5 places
                 // 
                 random32c = random32c >> 5;
            } 
 
            return new String(accountName);
        } 

        private void SetPasswordPortIfApplicable(DirectoryEntry userEntry)
        {
            // 
            // For ADAM, if the port is specified and we are using Ssl for connection protection,
            // we should set the password port. 
            // 
            if (directoryInfo.DirectoryType == DirectoryType.ADAM)
            { 

                try
                {
                    if ((directoryInfo.ConnectionProtection == ActiveDirectoryConnectionProtection.Ssl) && (directoryInfo.PortSpecified)) 
                    {
                        userEntry.Options.PasswordPort = directoryInfo.Port; 
                        userEntry.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingSsl; 
                    }
                    else if ((directoryInfo.ConnectionProtection == ActiveDirectoryConnectionProtection.SignAndSeal) || (directoryInfo.ConnectionProtection == ActiveDirectoryConnectionProtection.None)) 
                    {
                        userEntry.Options.PasswordPort = directoryInfo.Port;
                        userEntry.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingClear;
                    } 
                }
                catch (COMException e) 
                { 

                    if (e.ErrorCode == unchecked((int) 0x80005008)) 
                    {
                        //
                        // If ADSI returns E_ADS_BAD_PARAMETER, it means we are running
                        // on a platform where ADSI does not support setting of the password port 
                        // Since ADSI will set the password port to 636 and password method to Ssl, we can
                        // ignore this error only if that is what we are trying to set 
                        // 
                        if (!((directoryInfo.Port == DirectoryInformation.SSL_PORT) &&
                            (directoryInfo.ConnectionProtection == ActiveDirectoryConnectionProtection.Ssl))) 
                            throw new ProviderException(SR.GetString(SR.ADMembership_unable_to_set_password_port));
                    }
                    else
                        throw; 

                } 
            } 
        }
 
        private bool IsUpnUnique(string username)
        {

            // 
            // NOTE: we do not need to revert context here since this method is always
            //           called after reverting any impersonated context 
            // 

            DirectoryEntry rootEntry = new DirectoryEntry("GC://" + directoryInfo.ForestName, directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes); 

            DirectorySearcher searcher = new DirectorySearcher(rootEntry);
            searcher.Filter = "(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + GetEscapedFilterValue(username) + "))";
            searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree; 

            if (directoryInfo.ClientSearchTimeout != -1) 
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0); 
            if (directoryInfo.ServerSearchTimeout != -1)
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0); 

            bool result;
            try
            { 
                result = (searcher.FindOne() == null);
            } 
            finally 
            {
                rootEntry.Dispose(); 
            }

            return result;
 
        }
 
        private bool IsEmailUnique(DirectoryEntry containerEntry, string username, string email, bool existing) 
        {
            bool disposeContainerEntry = false; 

            if (containerEntry == null)
            {
                // 
                // NOTE: we do not need to revert context here since this method is always
                //           called after reverting any impersonated context 
                // 
                containerEntry = new DirectoryEntry(directoryInfo.GetADsPath(directoryInfo.ContainerDN), directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes);
                disposeContainerEntry = true; 
            }

            DirectorySearcher searcher = new DirectorySearcher(containerEntry);
            if (existing) 
                searcher.Filter = "(&(objectCategory=person)(objectClass=user)(" + attributeMapUsername + "=*)(" + attributeMapEmail + "=" + GetEscapedFilterValue(email) + ")(!(" + GetEscapedRdn("cn=" + GetEscapedFilterValue(username)) + ")))";
            else 
                searcher.Filter = "(&(objectCategory=person)(objectClass=user)(" + attributeMapUsername + "=*)(" + attributeMapEmail + "=" + GetEscapedFilterValue(email) + "))"; 
            searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
 
            if (directoryInfo.ClientSearchTimeout != -1)
                searcher.ClientTimeout = new TimeSpan(0, directoryInfo.ClientSearchTimeout, 0);
            if (directoryInfo.ServerSearchTimeout != -1)
                searcher.ServerPageTimeLimit = new TimeSpan(0, directoryInfo.ServerSearchTimeout, 0); 

            bool result; 
            try 
            {
                result = (searcher.FindOne() == null); 
            }
            finally
            {
                if (disposeContainerEntry) 
                {
                    containerEntry.Dispose(); 
                    containerEntry = null; 
                }
            } 

            return result;
        }
 
        private string GetConnectionString(string connectionStringName, bool appLevel)
        { 
            if (String.IsNullOrEmpty(connectionStringName)) 
                return null;
 
            RuntimeConfig config = (appLevel) ? RuntimeConfig.GetAppConfig() : RuntimeConfig.GetConfig();
            ConnectionStringSettings connObj = config.ConnectionStrings.ConnectionStrings[connectionStringName];

            if (connObj == null) 
            {
                // 
                // No connection string by the specified name 
                //
                throw new ProviderException(SR.GetString(SR.Connection_string_not_found, connectionStringName)); 
            }

            return connObj.ConnectionString;
        } 

        private string GetAttributeMapping(NameValueCollection config, string valueName, out int maxLength) 
        { 
            string sValue = config[valueName];
            maxLength = -1; 

            if (sValue == null)
                return null;
 
            sValue = sValue.Trim();
 
            if (sValue.Length == 0) 
                throw new ProviderException(SR.GetString(SR.ADMembership_Schema_mappings_must_not_be_empty, valueName));
 
            return GetValidatedSchemaMapping(valueName, sValue, out maxLength);
        }

        private string GetValidatedSchemaMapping(string valueName, string attributeName, out int maxLength) 
        {
            if (String.Compare(valueName, "attributeMapUsername", StringComparison.Ordinal) == 0) 
            { 
                if (directoryInfo.DirectoryType == DirectoryType.AD)
                { 
                    //
                    // username can only be mapped to "sAMAccountName", "userPrincipalName"
                    //
 
                    if ((!StringUtil.EqualsIgnoreCase(attributeName, "sAMAccountName"))
                        && (!StringUtil.EqualsIgnoreCase(attributeName, "userPrincipalName"))) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_Username_mapping_invalid)); 
                }
                else 
                {
                    //
                    // for ADAM, username can only be mapped to "userPrincipalName"
                    // 
                    if (!StringUtil.EqualsIgnoreCase(attributeName, "userPrincipalName"))
                        throw new ProviderException(SR.GetString(SR.ADMembership_Username_mapping_invalid_ADAM)); 
 
                }
            } 
            else
            {
                //
                // ensure that we are not already using this attribute 
                //
                if (attributesInUse.Contains(attributeName)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_mapping_not_unique, valueName, attributeName)); 

                // 
                // ensure that the attribute exists on the user object
                //
                if (!userObjectAttributes.Contains(attributeName))
                    throw new ProviderException(SR.GetString(SR.ADMembership_MappedAttribute_does_not_exist_on_user, attributeName, valueName)); 
            }
 
            try 
            {
                // 
                // verify that this is an existing property and it's syntax is correct
                //
                DirectoryEntry propertyEntry = new DirectoryEntry(directoryInfo.GetADsPath("schema") + "/"  + attributeName, directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes);
 
                //
                // to get the syntax we need to invoke the "syntax" property 
                // 
                string syntax = (string) propertyEntry.InvokeGet("Syntax");
 
                //
                // check that the syntax is as per the syntaxes table
                //
                if (!StringUtil.EqualsIgnoreCase(syntax, (string) syntaxes[valueName])) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_Wrong_syntax, valueName, (string) syntaxes[valueName]));
 
                // 
                // if the type is "DirectoryString", then set the maxLength value if any
                // 
                maxLength = -1;
                if (StringUtil.EqualsIgnoreCase(syntax, "DirectoryString"))
                {
                    try 
                    {
                        maxLength = (int) propertyEntry.InvokeGet("MaxRange"); 
                    } 
                    catch (TargetInvocationException e)
                    { 
                        //
                        // if the inner exception is a comexception with error code 0x8007500d, then the max range is not set
                        // so we ignore that exception
                        // 
                        if (!((e.InnerException is COMException) && (((COMException)e.InnerException).ErrorCode == unchecked((int) 0x8000500d))))
                            throw; 
                    } 
                }
 
                //
                // unless this is the username (which we already know is mapped
                // to a single valued attribute), the attribute should be single valued
                // 
                if (String.Compare(valueName, "attributeMapUsername", StringComparison.Ordinal) != 0)
                { 
                    bool isMultiValued = (bool) propertyEntry.InvokeGet("MultiValued"); 

                    if (isMultiValued) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_attribute_not_single_valued, valueName));
                }

            } 
            catch (COMException e)
            { 
                if (e.ErrorCode == unchecked((int) 0x80005000)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_MappedAttribute_does_not_exist, attributeName, valueName), e);
                else 
                    throw;
            }

            // 
            // add the attribute name (lower cased) to the in use attributes list
            // 
            return attributeName; 
        }
 
        private int GetRangeUpperForSchemaAttribute(string attributeName)
        {
            int rangeUpper = -1;
            DirectoryEntry propertyEntry = new DirectoryEntry(directoryInfo.GetADsPath("schema") + "/"  + attributeName, directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes); 

            try 
            { 
                rangeUpper = (int) propertyEntry.InvokeGet("MaxRange");
            } 
            catch (TargetInvocationException e)
            {
                //
                // if the inner exception is a comexception with error code 0x8007500d, then the max range is not set 
                // so we ignore that exception
                // 
                if (!((e.InnerException is COMException) && (((COMException)e.InnerException).ErrorCode == unchecked((int) 0x8000500d)))) 
                    throw;
            } 

            return rangeUpper;
        }
 
        private Hashtable GetUserObjectAttributes()
        { 
            DirectoryEntry de = new DirectoryEntry(directoryInfo.GetADsPath("schema") + "/user", directoryInfo.GetUsername(), directoryInfo.GetPassword(), directoryInfo.AuthenticationTypes); 
            object value = null;
            bool listEmpty = false; 
            Hashtable attributes = new Hashtable(StringComparer.OrdinalIgnoreCase);

            try
            { 
                value = de.InvokeGet("MandatoryProperties");
            } 
            catch (COMException e) 
            {
                if (e.ErrorCode == unchecked((int) 0x8000500D)) 
                {
                    listEmpty = true;
                }
                else 
                    throw;
            } 
 
            if (!listEmpty)
            { 
                if (value is ICollection)
                {
                    foreach (string attribute in (ICollection) value)
                    { 
                        if (!attributes.Contains(attribute))
                            attributes.Add(attribute, null); 
                     } 
                }
                else 
                {
                    // single value

                    if (!attributes.Contains(value)) 
                        attributes.Add(value, null);
                } 
            } 

            listEmpty = false; 
            try
            {
                value = de.InvokeGet("OptionalProperties");
            } 
            catch (COMException e)
            { 
                if (e.ErrorCode == unchecked((int) 0x8000500D)) 
                {
                    listEmpty = true; 
                }
                else
                    throw;
            } 

            if (!listEmpty) 
            { 
                if (value is ICollection)
                { 
                    foreach (string attribute in (ICollection) value)
                    {
                        if (!attributes.Contains(attribute))
                            attributes.Add(attribute, null); 
                     }
                } 
                else 
                {
                    // single value 
                    if (!attributes.Contains(value))
                        attributes.Add(value, null);
                }
            } 

            return attributes; 
 
        }
 
        private DateTime GetDateTimeFromLargeInteger(NativeComInterfaces.IAdsLargeInteger largeIntValue)
        {
            //
            // Convert large integer to int64 value 
            //
            Int64 int64Value = largeIntValue.HighPart * 0x100000000 + (uint) largeIntValue.LowPart; 
 
            //
            // Return the DateTime in utc 
            //
            return DateTime.FromFileTimeUtc(int64Value);

        } 

        private NativeComInterfaces.IAdsLargeInteger GetLargeIntegerFromDateTime(DateTime dateTimeValue) 
        { 
            //
            // Convert DateTime value to utc file time 
            //
            Int64 int64Value = dateTimeValue.ToFileTimeUtc();

            // 
            // convert to large integer
            // 
            NativeComInterfaces.IAdsLargeInteger largeIntValue = (NativeComInterfaces.IAdsLargeInteger) new NativeComInterfaces.LargeInteger(); 
            largeIntValue.HighPart = (int) (int64Value >> 32);
            largeIntValue.LowPart = (int) (int64Value & 0xFFFFFFFF); 

            return largeIntValue;
        }
 
        private string Encrypt(string clearTextString)
        { 
            // we should never be getting null input here 
            Debug.Assert(clearTextString != null);
 
            byte[] bIn = Encoding.Unicode.GetBytes(clearTextString);

            byte[] bSalt = new byte[AD_SALT_SIZE_IN_BYTES];
            (new RNGCryptoServiceProvider()).GetBytes(bSalt); 

            byte[] bAll = new byte[bSalt.Length + bIn.Length]; 
            Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length); 
            Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
 
            return Convert.ToBase64String(EncryptPassword(bAll));
        }

        private string Decrypt(string encryptedString) 
        {
            // we should never be getting null input here 
            Debug.Assert(encryptedString != null); 

            byte[] bEncryptedData = Convert.FromBase64String(encryptedString); 

            byte[] bAll = DecryptPassword(bEncryptedData);

            return Encoding.Unicode.GetString(bAll, AD_SALT_SIZE_IN_BYTES, bAll.Length - AD_SALT_SIZE_IN_BYTES); 
        }
 
    } 

    internal sealed class DirectoryInformation 
    {
        private string serverName = null;
        private string containerDN = null;
        private string creationContainerDN = null; 
        private string adspath = null;
        private int port = 389; 
        private bool portSpecified = false; 
        private DirectoryType directoryType = DirectoryType.Unknown;
        private ActiveDirectoryConnectionProtection connectionProtection = ActiveDirectoryConnectionProtection.None; 
        private bool concurrentBindSupported = false;
        private int clientSearchTimeout = -1;
        private int serverSearchTimeout = -1;
        private DirectoryEntry rootdse = null; 
        private NetworkCredential credentials = null;
        private AuthenticationTypes authenticationType = AuthenticationTypes.None; 
        private AuthType ldapAuthType = AuthType.Basic; 
        private string adamPartitionDN = null;
        private TimeSpan adLockoutDuration; 
        private string forestName = null;
        private string domainName = null;
        private bool isServer = false;
 
        private const string LDAP_CAP_ACTIVE_DIRECTORY_ADAM_OID ="1.2.840.113556.1.4.1851";
        private const string LDAP_CAP_ACTIVE_DIRECTORY_OID ="1.2.840.113556.1.4.800"; 
        private const string LDAP_SERVER_FAST_BIND_OID = "1.2.840.113556.1.4.1781"; 
        internal const int SSL_PORT = 636;
        private const int GC_PORT = 3268; 
        private const int GC_SSL_PORT = 3269;
        private const string GUID_USERS_CONTAINER_W = "a9d1ca15768811d1aded00c04fd8d5cd";

        // 
        // authentication types for S.DS and S.DS.Protocols (rows are indexed by connection protection
        // columns are indexed by type of credentials (see CredentialType enum) 
        // 
        AuthenticationTypes[,] authTypes = new AuthenticationTypes[,]
                    {{AuthenticationTypes.None, AuthenticationTypes.None}, 
                      {AuthenticationTypes.Secure | AuthenticationTypes.SecureSocketsLayer , AuthenticationTypes.SecureSocketsLayer },
                      {AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing, AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing}};

        AuthType[,] ldapAuthTypes = new AuthType[,] 
                     {{AuthType.Negotiate, AuthType.Basic},
                      {AuthType.Negotiate, AuthType.Basic}, 
                      {AuthType.Negotiate, AuthType.Negotiate}}; 

        internal DirectoryInformation(string adspath, 
                                                            NetworkCredential credentials,
                                                            string connProtection,
                                                            int clientSearchTimeout,
                                                            int serverSearchTimeout, 
                                                            bool enablePasswordReset)
        { 
 
           //
           // all parameters have already been validated at this point 
           //

            this.adspath = adspath;
            this.credentials = credentials; 
            this.clientSearchTimeout = clientSearchTimeout;
            this.serverSearchTimeout = serverSearchTimeout; 
 
            Debug.Assert(adspath != null);
            Debug.Assert(adspath.Length > 0); 

            //
            // Provider must be LDAP
            // 
            if (!(adspath.StartsWith("LDAP", StringComparison.Ordinal)))
                throw new ProviderException(SR.GetString(SR.ADMembership_OnlyLdap_supported)); 
 
            //
            // Parse out the server/domain information 
            //
            NativeComInterfaces.IAdsPathname pathCracker = (NativeComInterfaces.IAdsPathname) new NativeComInterfaces.Pathname();

            try { 
                pathCracker.Set(adspath, NativeComInterfaces.ADS_SETTYPE_FULL);
            } 
            catch (COMException e) 
            {
                if (e.ErrorCode == unchecked((int) 0x80005000)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_invalid_path));
                else
                    throw;
            } 

            // Get the server and container names 
            try 
            {
                serverName = pathCracker.Retrieve(NativeComInterfaces.ADS_FORMAT_SERVER); 
            }
            catch (COMException e)
            {
                if (e.ErrorCode == unchecked((int) 0x80005000)) 
                    throw new ProviderException(SR.GetString(SR.ADMembership_ServerlessADsPath_not_supported));
                else 
                    throw; 
            }
            Debug.Assert(serverName != null); 

            creationContainerDN = containerDN = pathCracker.Retrieve(NativeComInterfaces.ADS_FORMAT_X500_DN);

            // 
            // Parse out the port number if specified
            // 
            int index = serverName.IndexOf(':'); 
            if (index != -1)
            { 
                string tempStr = serverName;

                serverName = tempStr.Substring(0, index);
 
                Debug.Assert(tempStr.Length > index);
                port = Int32.Parse(tempStr.Substring(index + 1), NumberFormatInfo.InvariantInfo); 
                portSpecified = true; 
            }
 
            if (String.Compare(connProtection, "Secure", StringComparison.Ordinal) == 0)
            {
                //
                // The logic is as follows: 
                // 1. Try Ssl first and check if concurrent binds are possible for validating users
                // 2. If Ssl is not supported, try signing and sealing 
                // 3. If both the above are not supported, then we will fail 
                //
 
                bool trySignAndSeal = false;
                bool trySslWithSecureAuth = false;

                // first try with simple bind 
                if (!IsDefaultCredential())
                { 
 
                    authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.NonWindows);
                    ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.NonWindows); 

                    try
                    {
                        rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); 
                        // this will force a bind
                        rootdse.RefreshCache(); 
                        this.connectionProtection = ActiveDirectoryConnectionProtection.Ssl; 
                        if (!portSpecified)
                        { 
                            port = SSL_PORT;
                            portSpecified = true;
                        }
                    } 
                    catch (COMException ce)
                    { 
 
                        if (ce.ErrorCode == unchecked((int) 0x8007052e))
                        { 
                            //
                            // this could be an ADAM target with windows user (in that case simple bind will not work)
                            //
                            trySslWithSecureAuth = true; 
                        }
                        else if (ce.ErrorCode == unchecked((int) 0x8007203a)) 
                        { 
                            // server is not operational error, do nothing, we need to fall back to SignAndSeal
                            trySignAndSeal = true; 
                        }
                        else
                            throw;
                     } 
                }
                else 
                { 
                    // default credentials, so we have to do secure bind
                    trySslWithSecureAuth = true; 
                }

                if (trySslWithSecureAuth)
                { 

                    authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.Windows); 
                    ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.Ssl, CredentialsType.Windows); 

                    try 
                    {
                        rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType);
                        // this will force a bind
                        rootdse.RefreshCache(); 
                        this.connectionProtection = ActiveDirectoryConnectionProtection.Ssl;
                        if (!portSpecified) 
                        { 
                            port = SSL_PORT;
                            portSpecified = true; 
                        }

                    }
                    catch (COMException ce) 
                    {
                        if (ce.ErrorCode == unchecked((int) 0x8007203a)) 
                        { 
                            // server is not operational error, do nothing, we need to fall back to SignAndSeal
                            trySignAndSeal = true; 
                        }
                        else
                            throw;
                     } 

                } 
 
                if (trySignAndSeal)
                { 
                    authenticationType = GetAuthenticationTypes(ActiveDirectoryConnectionProtection.SignAndSeal, CredentialsType.Windows);
                    ldapAuthType = GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection.SignAndSeal, CredentialsType.Windows);

                    try 
                    {
                        rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); 
                        rootdse.RefreshCache(); 
                        this.connectionProtection = ActiveDirectoryConnectionProtection.SignAndSeal;
                    } 
                    catch (COMException e)
                    {
                        throw new ProviderException(SR.GetString(SR.ADMembership_Secure_connection_not_established, e.Message), e);
                    } 
                }
            } 
            else 
            {
                // 
                // No connection protection
                //

                // 
                // we will do a simple bind but we must ensure that the credentials are explicitly specified
                // since in the case of default credentials we cannot honor it (default credentials become anonymous in the case of 
                // simple bind) 
                //
                if (IsDefaultCredential()) 
                    throw new NotSupportedException(SR.GetString(SR.ADMembership_Default_Creds_not_supported));

                // simple bind
                authenticationType = GetAuthenticationTypes(connectionProtection, CredentialsType.NonWindows); 
                ldapAuthType = GetLdapAuthenticationTypes(connectionProtection, CredentialsType.NonWindows);
 
                rootdse = new DirectoryEntry(GetADsPath("rootdse"), GetUsername(), GetPassword(), authenticationType); 

            } 

            //
            // Determine whether this is AD or ADAM by binding to the rootdse and
            // checking the supported capabilities 
            //
            if (rootdse == null) 
                rootdse = new DirectoryEntry(GetADsPath("RootDSE"), GetUsername(), GetPassword(), authenticationType); 
            directoryType = GetDirectoryType();
 
            //
            // if the directory type is ADAM and the conntectionProtection was selected
            // as sign and seal, then we should throw an ProviderException. This is becuase validate user will always fail for ADAM
            // because ADAM does not support secure authentication for ADAM users. 
            //
            if ((directoryType == DirectoryType.ADAM) && (this.connectionProtection == ActiveDirectoryConnectionProtection.SignAndSeal)) 
                throw new ProviderException(SR.GetString(SR.ADMembership_Ssl_connection_not_established)); 

            // 
            // for AD, we need to block the GC ports
            //
            if ((directoryType == DirectoryType.AD) && ((port == GC_PORT) || (port == GC_SSL_PORT)))
                throw new ProviderException(SR.GetString(SR.ADMembership_GCPortsNotSupported)); 

            // 
            // if container dn is null, we need to get the default naming context 
            // (containerDN cannot be null for ADAM)
            // 
            if (String.IsNullOrEmpty(containerDN))
            {
                if (directoryType == DirectoryType.AD)
                { 
                    containerDN = (string)rootdse.Properties["defaultNamingContext"].Value;
                    if (containerDN == null) 
                        throw new ProviderException(SR.GetString(SR.ADMembership_DefContainer_not_specified)); 

                    // 
                    // we will create users in the default users container, check that it exists
                    //
                    string wkUsersContainerPath = GetADsPath("");
                    DirectoryEntry containerEntry = new DirectoryEntry(wkUsersContainerPath, GetUsername(), GetPassword(), authenticationType); 

                    try 
                    { 
                        creationContainerDN = (string) PropertyManager.GetPropertyValue(containerEntry, "distinguishedName");
                    } 
                    catch (COMException ce)
                    {
                        if (ce.ErrorCode == unchecked((int) 0x80072030))
                            throw new ProviderException(SR.GetString(SR.ADMembership_DefContainer_does_not_exist)); 
                        else
                            throw; 
                    } 
                }
                else 
                {
                    // container must be specified for ADAM
                    throw new ProviderException(SR.GetString(SR.ADMembership_Container_must_be_specified));
                } 
            }
            else 
            { 
                //
                // Normalize the container name (incase it was specified as GUID or WKGUID) 
                //
                DirectoryEntry containerEntry = new DirectoryEntry(GetADsPath(containerDN), GetUsername(), GetPassword(), authenticationType);

                try 
                {
                    creationContainerDN = containerDN = (string) PropertyManager.GetPropertyValue(containerEntry, "distinguishedName"); 
                } 
                catch (COMException ce)
                { 
                    if (ce.ErrorCode == unchecked((int) 0x80072030))
                        throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist));
                    else
                        throw; 
                }
            } 
 
            //
            // Check if the specified path(container) exists on the specified server/domain 
            // (NOTE: We need to do this using S.DS.Protocols rather than S.DS because we need to
            //            bypass the referral chasing which is automatic in S.DS)
            //
 
            LdapConnection tempConnection = new LdapConnection(new LdapDirectoryIdentifier(serverName + ":" + port), GetCredentialsWithDomain(credentials), ldapAuthType);
            tempConnection.SessionOptions.ProtocolVersion = 3; 
 
            try
            { 
                tempConnection.SessionOptions.ReferralChasing = System.DirectoryServices.Protocols.ReferralChasingOptions.None;
                SetSessionOptionsForSecureConnection(tempConnection, false /*useConcurrentBind */);
                tempConnection.Bind();
 

                SearchRequest request = new SearchRequest(); 
                request.DistinguishedName = containerDN; 
                request.Filter = "(objectClass=*)";
                request.Scope = System.DirectoryServices.Protocols.SearchScope.Base; 
                request.Attributes.Add("distinguishedName");
                request.Attributes.Add("objectClass");

                if (ServerSearchTimeout != -1) 
                    request.TimeLimit = new TimeSpan(0, ServerSearchTimeout, 0);
 
                SearchResponse response; 
                try
                { 
                    response = (SearchResponse) tempConnection.SendRequest(request);
                    if (response.ResultCode == ResultCode.Referral || response.ResultCode ==  ResultCode.NoSuchObject)
                        throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist));
                    else if (response.ResultCode != ResultCode.Success) 
                        throw new ProviderException(response.ErrorMessage);
                } 
                catch (DirectoryOperationException oe) 
                {
                    SearchResponse errorResponse = (SearchResponse) oe.Response; 
                    if (errorResponse.ResultCode == ResultCode.NoSuchObject)
                        throw new ProviderException(SR.GetString(SR.ADMembership_Container_does_not_exist));
                    else throw;
                } 

                // 
                // check that the container is of an object type that can be a superior of a user object 
                //
                DirectoryAttribute objectClass = response.Entries[0].Attributes["objectClass"]; 
                if (!ContainerIsSuperiorOfUser(objectClass))
                    throw new ProviderException(SR.GetString(SR.ADMembership_Container_not_superior));

                // 
                // Determine whether concurrent bind is supported
                // 
                if ((connectionProtection == ActiveDirectoryConnectionProtection.None) || (connectionProtection == ActiveDirectoryConnectionProtection.Ssl)) 
                {
                    this.concurrentBindSupported = IsConcurrentBindSupported(tempConnection); 
                }

            }
            finally 
            {
                tempConnection.Dispose(); 
            } 

            // 
            // if this is ADAM, get the partition DN
            //
            if (directoryType == DirectoryType.ADAM)
            { 
                adamPartitionDN = GetADAMPartitionFromContainer();
            } 
            else 
            {
                if (enablePasswordReset) 
                {
                    // for AD, get the lockout duration for user account auto unlock
                    DirectoryEntry de = new DirectoryEntry(GetADsPath((string) PropertyManager.GetPropertyValue(rootdse, "defaultNamingContext")), GetUsername(), GetPassword(), AuthenticationTypes);
                    NativeComInterfaces.IAdsLargeInteger largeIntValue = (NativeComInterfaces.IAdsLargeInteger) PropertyManager.GetPropertyValue(de, "lockoutDuration"); 
                    Int64 int64Value = largeIntValue.HighPart * 0x100000000 + (uint) largeIntValue.LowPart;
 
                    // int64Value is the negative of the number of 100 nanoseconds interval that makes up the lockout duration 
                    adLockoutDuration = new TimeSpan(-int64Value);
                } 
            }
        }

        internal bool ConcurrentBindSupported 
        {
            get { return concurrentBindSupported; } 
        } 

        internal string ContainerDN 
        {
            get { return containerDN; }
        }
 
        internal string CreationContainerDN
        { 
            get { return creationContainerDN; } 
        }
 
        internal int Port
        {
            get { return port; }
        } 

        internal bool PortSpecified 
        { 
            get { return portSpecified; }
        } 

#if UNUSED_CODE
        internal NetworkCredential Credential
        { 
            get { return credentials; }
        } 
#endif 

        internal DirectoryType DirectoryType 
        {
            get { return directoryType; }
        }
 
        internal ActiveDirectoryConnectionProtection ConnectionProtection
        { 
            get { return connectionProtection; } 
        }
 
        internal AuthenticationTypes AuthenticationTypes
        {
            get { return authenticationType; }
        } 

        internal int ClientSearchTimeout 
        { 
            get { return clientSearchTimeout; }
        } 

        internal int ServerSearchTimeout
        {
            get { return serverSearchTimeout; } 
        }
 
        internal string ADAMPartitionDN 
        {
            get { return adamPartitionDN; } 
        }

        internal TimeSpan ADLockoutDuration
        { 
            get { return adLockoutDuration; }
        } 
 
        internal string ForestName
        { 
            get { return forestName; }
        }

        internal string DomainName 
        {
            get { return domainName; } 
        } 

        internal void InitializeDomainAndForestName() 
        {
            if (!isServer)
            {
                DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, serverName, GetUsername(), GetPassword()); 
                try
                { 
                    Domain domain = Domain.GetDomain(context); 
                    domainName = GetNetbiosDomainNameIfAvailable(domain.Name);
                    forestName = domain.Forest.Name; 
                }
                catch (ActiveDirectoryObjectNotFoundException)
                {
                    // the serverName may be the name of the server rather than domain 
                    isServer = true;
                } 
            } 

            if (isServer) 
            {
                DirectoryContext context = new DirectoryContext(DirectoryContextType.DirectoryServer, serverName, GetUsername(), GetPassword());
                try
                { 
                    Domain domain = Domain.GetDomain(context);
                    domainName = GetNetbiosDomainNameIfAvailable(domain.Name); 
                    forestName = domain.Forest.Name; 
                }
                catch (ActiveDirectoryObjectNotFoundException) 
                {
                    // we were unable to contact the domain or server
                    throw new ProviderException(SR.GetString(SR.ADMembership_unable_to_contact_domain));
                } 
            }
        } 
 
        internal void SelectServer()
        { 
            //
            // if the name specified in the target is a domain name, then we should
            // perform all operations on the PDC. If the name is not a domain name
            // then it would be the name of a server. In that case we perform all 
            // operations on that server
            // 
            serverName = GetPdcIfDomain(serverName); 
            isServer = true;
        } 

        //
        // Creates a new ldap connection with the specified auth types
        // (the session options are set based on the connection protection that was 
        // determined during the initialize method)
        // 
        internal LdapConnection CreateNewLdapConnection(AuthType authType) 
        {
            LdapConnection newConnection = null; 

            newConnection = new LdapConnection(new LdapDirectoryIdentifier(serverName + ":" + port));
            newConnection.AuthType = authType;
            newConnection.SessionOptions.ProtocolVersion = 3; 
            SetSessionOptionsForSecureConnection(newConnection, true /* useConcurrentBind */);
 
            return newConnection; 
        }
 
        //
        // this method returns the ADsPath for the given DN
        //
        internal string GetADsPath(string dn) 
        {
            string path = null; 
 
            //
            // provider and server information 
            //
            Debug.Assert(serverName != null);
            path = "LDAP://" + serverName;
 
            //
            // port info if specified 
            // 
            if (portSpecified)
                path = path + ":" + port; 

            //
            // DN of the object
            // 
            Debug.Assert(dn != null);
            NativeComInterfaces.IAdsPathname pathCracker = (NativeComInterfaces.IAdsPathname) new NativeComInterfaces.Pathname(); 
            pathCracker.Set(dn, NativeComInterfaces.ADS_SETTYPE_DN); 
            pathCracker.EscapedMode = NativeComInterfaces.ADS_ESCAPEDMODE_ON;
            path = path + "/" + pathCracker.Retrieve(NativeComInterfaces.ADS_FORMAT_X500_DN); 

            return path;

        } 

        internal void SetSessionOptionsForSecureConnection(LdapConnection connection, bool useConcurrentBind) 
        { 

            if (connectionProtection == ActiveDirectoryConnectionProtection.Ssl) { 
                connection.SessionOptions.SecureSocketLayer = true;
            }
            else if (connectionProtection == ActiveDirectoryConnectionProtection.SignAndSeal)
            { 
                connection.SessionOptions.Signing = true;
                connection.SessionOptions.Sealing = true; 
            } 

            if (useConcurrentBind && this.concurrentBindSupported) 
            {
                try
                {
                    connection.SessionOptions.FastConcurrentBind(); 
                }
                catch (PlatformNotSupportedException) 
                { 
                    //
                    // concurrent bind is not supported by the client, (continue without it and don't try to set it next time) 
                    //
                    this.concurrentBindSupported = false;
                }
                catch (DirectoryOperationException) 
                {
                    // Dev10 Bug# 623663: 
                    // concurrent bind is not supported when a client certificate is specified, (continue without it and don't try to set it next time) 

                    this.concurrentBindSupported = false; 
                }
            }
        }
 
        [EnvironmentPermission(SecurityAction.Assert, Read="USERNAME")]
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] 
        internal string GetUsername() 
        {
            if (credentials == null) 
                return null;

            if (credentials.UserName == null)
                return null; 

            if (credentials.UserName.Length == 0 && (credentials.Password == null || credentials.Password.Length == 0)) 
                return null; 

            return this.credentials.UserName; 
        }

        [EnvironmentPermission(SecurityAction.Assert, Read="USERNAME")]
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] 
        internal string GetPassword()
        { 
            if (credentials == null) 
                return null;
 
            if (credentials.Password == null)
                return null;

            if (credentials.Password.Length == 0 && (credentials.UserName == null || credentials.UserName.Length == 0)) 
                return null;
 
            return this.credentials.Password; 
        }
 
        internal AuthenticationTypes GetAuthenticationTypes(ActiveDirectoryConnectionProtection connectionProtection, CredentialsType type)
        {
            return authTypes[(int) connectionProtection, (int) type];
        } 

        internal AuthType GetLdapAuthenticationTypes(ActiveDirectoryConnectionProtection connectionProtection, CredentialsType type) 
        { 
            return ldapAuthTypes[(int) connectionProtection, (int) type];
        } 

        [EnvironmentPermission(SecurityAction.Assert, Read="USERNAME")]
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)]
        internal bool IsDefaultCredential() 
        {
            if ((credentials.UserName == null || credentials.UserName.Length == 0) && (credentials.Password == null || credentials.Password.Length == 0)) 
                return true; 

            return false; 
        }

        [EnvironmentPermission(SecurityAction.Assert, Read="USERNAME")]
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] 
        internal static NetworkCredential GetCredentialsWithDomain(NetworkCredential credentials)
        { 
            NetworkCredential credentialsWithDomain; 

            if (credentials == null) 
                credentialsWithDomain = new NetworkCredential(null, null);
            else
            {
                string tempUsername = credentials.UserName; 
                string username = null;
                string password = null; 
                string domainName = null; 

                if (!String.IsNullOrEmpty(tempUsername)) 
                {
                    int index = tempUsername.IndexOf('\\');
                    if (index != -1)
                    { 
                        domainName = tempUsername.Substring(0, index);
                        username = tempUsername.Substring(index + 1); 
                    } 
                    else
                        username = tempUsername; 

                    password = credentials.Password;
                }
                credentialsWithDomain = new NetworkCredential(username, password, domainName); 
            }
 
            return credentialsWithDomain; 
        }
 
        private bool IsConcurrentBindSupported(LdapConnection ldapConnection)
        {
            bool result = false;
 
            Debug.Assert(ldapConnection != null);
 
            // 
            // supportedExtension is a constructed attribute so we need to search and load that attribute explicitly
            // 
            SearchRequest request = new SearchRequest();
            request.Scope = System.DirectoryServices.Protocols.SearchScope.Base;
            request.Attributes.Add("supportedExtension");
 
            if (ServerSearchTimeout != -1)
                request.TimeLimit = new TimeSpan(0, ServerSearchTimeout, 0); 
 
            SearchResponse response = (SearchResponse) ldapConnection.SendRequest(request);
            if (response.ResultCode != ResultCode.Success) 
                throw new ProviderException(response.ErrorMessage);

            foreach (string supportedExtension in response.Entries[0].Attributes["supportedExtension"].GetValues(typeof(string)))
            { 
                if (StringUtil.EqualsIgnoreCase(supportedExtension, LDAP_SERVER_FAST_BIND_OID))
                { 
                    result = true; 
                    break;
                } 
            }

            return result;
        } 

        // 
        // This function goes through each of the naming contexts on the server 
        // and determines which one is the longest postfix of the container DN.
        // That will give the DN of partition that the container lives in. 
        //
        //
        private string GetADAMPartitionFromContainer()
        { 
            string partitionName = null;
            int startsAt = Int32.MaxValue; 
 
            foreach(string namingContext in rootdse.Properties["namingContexts"])
            { 
                bool endsWith = containerDN.EndsWith(namingContext, StringComparison.Ordinal);
                int lastIndexOf = containerDN.LastIndexOf(namingContext, StringComparison.Ordinal);

                if (endsWith && (lastIndexOf != -1) && (lastIndexOf < startsAt)) 
                {
                    partitionName = namingContext; 
                    startsAt = lastIndexOf; 
                }
            } 

            if (partitionName == null)
                throw new ProviderException(SR.GetString(SR.ADMembership_No_ADAM_Partition));
 
            return partitionName;
        } 
 
        //
        // This function goes through each of the object class values for the container to determine 
        // whether the object class is one of the possible superiors of the user object
        //
        private bool ContainerIsSuperiorOfUser(DirectoryAttribute objectClass)
        { 
            ArrayList possibleSuperiorsList = new ArrayList();
 
            // 
            // first get a list of all the classes from which the user class is derived
            // 
            DirectoryEntry de = new DirectoryEntry(GetADsPath("schema") + "/user", GetUsername(), GetPassword(), AuthenticationTypes);
            ArrayList classesList = new ArrayList();
            bool derivedFromlistEmpty = false;
            object value = null; 

            try 
            { 
                value = de.InvokeGet("DerivedFrom");
            } 
            catch (COMException e)
            {
                if (e.ErrorCode == unchecked((int) 0x8000500D))
                { 
                    derivedFromlistEmpty = true;
                } 
                else 
                    throw;
            } 

            if (!derivedFromlistEmpty)
            {
                if (value is ICollection) 
                {
                    classesList.AddRange((ICollection) value); 
                } 
                else
                { 
                    // single value
                    classesList.Add((string) value);
                }
            } 

            // 
            // we will use this list to create a filter of all the classSchema objects that we need to determine the recursive list 
            // of "possibleSecuperiors". We need to add the user class also.
            // 
            classesList.Add("user");

            //
            // Now search under the schema naming context for all these classes and get the "possSuperiors" and "systemPossSuperiors" attributes 
            //
            DirectoryEntry schemaNC = new DirectoryEntry(GetADsPath((string) rootdse.Properties["schemaNamingContext"].Value), GetUsername(), GetPassword(), AuthenticationTypes); 
            DirectorySearcher searcher = new DirectorySearcher(schemaNC); 

            searcher.Filter = "(&(objectClass=classSchema)(|"; 
            foreach(string supClass in classesList)
                searcher.Filter += "(ldapDisplayName=" + supClass + ")";
            searcher.Filter += "))";
 
            searcher.SearchScope = System.DirectoryServices.SearchScope.OneLevel;
            searcher.PropertiesToLoad.Add("possSuperiors"); 
            searcher.PropertiesToLoad.Add("systemPossSuperiors"); 

            SearchResultCollection resCol = searcher.FindAll(); 

            try
            {
                foreach (SearchResult res in resCol) 
                {
                    possibleSuperiorsList.AddRange(res.Properties["possSuperiors"]); 
                    possibleSuperiorsList.AddRange(res.Properties["systemPossSuperiors"]); 
                }
            } 
            finally
            {
                resCol.Dispose();
            } 

            // 
            // Now we have the list of all the possible superiors, check if the objectClass that was specified as a parameter 
            // to this function is one of these values, if so, return true else false
            // 
            foreach (string objectClassValue in objectClass.GetValues(typeof(string)))
            {
                if (possibleSuperiorsList.Contains(objectClassValue))
                    return true; 
            }
 
            return false; 
        }
 
        //
        // This method determines whether the server we are talking to
        // is an AD domain controller or an ADAM instance
        // 
        private DirectoryType GetDirectoryType()
        { 
            DirectoryType directoryType = DirectoryType.Unknown; 

            foreach (string supportedCapability in rootdse.Properties["supportedCapabilities"]) 
            {
                if (StringUtil.EqualsIgnoreCase(supportedCapability, LDAP_CAP_ACTIVE_DIRECTORY_ADAM_OID))
                {
                    directoryType = DirectoryType.ADAM; 
                    break;
                } 
                else if (StringUtil.EqualsIgnoreCase(supportedCapability, LDAP_CAP_ACTIVE_DIRECTORY_OID)) 
                {
                    directoryType = DirectoryType.AD; 
                    break;
                }
            }
 
            if (directoryType == DirectoryType.Unknown)
                throw new ProviderException(SR.GetString(SR.ADMembership_Valid_Targets)); 
 
            return directoryType;
        } 

        //
        // This method returns the dns name of the primary domain controller if the specified name is a domain,
        // else is just returns the name as is 
        //
        internal string GetPdcIfDomain(string name) 
        { 
            IntPtr pDomainControllerInfo = IntPtr.Zero;
 
            /* DS_DIRECTORY_SERVICE_REQUIRED   0x00000010
                 DS_RETURN_DNS_NAME              0x40000000
                 DS_PDC_REQUIRED                 0x00000080 */
            uint flags = 0x00000010 | 0x40000000 | 0x00000080; 
            string pdc = null;
 
            int ERROR_NO_SUCH_DOMAIN = 1355; 

            int result = NativeMethods.DsGetDcName(null, name, IntPtr.Zero, null,  flags, out pDomainControllerInfo); 

            try {
                if (result == 0)
                { 
                    // success case
                    DomainControllerInfo domainControllerInfo = new DomainControllerInfo(); 
                    Marshal.PtrToStructure(pDomainControllerInfo, domainControllerInfo); 

                    Debug.Assert(domainControllerInfo != null); 
                    Debug.Assert(domainControllerInfo.DomainControllerName != null);
                    Debug.Assert(domainControllerInfo.DomainControllerName.Length > 2);

                    // domain controller name is in the format "\\server", so we need to strip the back slashes 
                    pdc = domainControllerInfo.DomainControllerName.Substring(2);
                } 
                else if (result == ERROR_NO_SUCH_DOMAIN) 
                    pdc = name;
                else 
                    throw new ProviderException(GetErrorMessage(result));
            }
            finally
            { 
                // free the buffer
                if (pDomainControllerInfo != IntPtr.Zero) { 
                    NativeMethods.NetApiBufferFree(pDomainControllerInfo); 
                }
            } 

            return pdc;
        }
 
        internal string GetNetbiosDomainNameIfAvailable(string dnsDomainName)
        { 
            string result = null; 

            // 
            // Get the netbios name from the "nETBIOSName" attribute on the crossRef object for this domain
            //
            DirectoryEntry partitionsEntry = new DirectoryEntry(GetADsPath("CN=Partitions," + (string) PropertyManager.GetPropertyValue(rootdse, "configurationNamingContext")), GetUsername(), GetPassword());
            DirectorySearcher searcher = new DirectorySearcher(partitionsEntry); 
            searcher.SearchScope = System.DirectoryServices.SearchScope.OneLevel;
 
            StringBuilder str = new StringBuilder(15); 
            str.Append("(&(objectCategory=crossRef)(dnsRoot=");
            str.Append(dnsDomainName); 
            str.Append(")(systemFlags:1.2.840.113556.1.4.804:=1)(systemFlags:1.2.840.113556.1.4.804:=2))");

            searcher.Filter = str.ToString();
            searcher.PropertiesToLoad.Add("nETBIOSName"); 

            SearchResult res = searcher.FindOne(); 
            if ((res == null) || (!(res.Properties.Contains("nETBIOSName")))) 
                // return the dns name
                result = dnsDomainName; 
            else
                // return the netbios name
                result = (string) PropertyManager.GetSearchResultPropertyValue(res, "nETBIOSName");
 
            return result;
        } 
 
        private static string GetErrorMessage(int errorCode)
        { 
            uint temp = (uint) errorCode;
            temp = ( (((temp) & 0x0000FFFF) | (7 << 16) | 0x80000000));

            string errorMsg = String.Empty; 
            StringBuilder sb = new StringBuilder(256);
            int result = NativeMethods.FormatMessageW(NativeMethods.FORMAT_MESSAGE_IGNORE_INSERTS | 
                                       NativeMethods.FORMAT_MESSAGE_FROM_SYSTEM | 
                                       NativeMethods.FORMAT_MESSAGE_ARGUMENT_ARRAY,
                                       0, (int)temp, 0, sb, sb.Capacity + 1, 0); 
            if (result != 0) {
                errorMsg = sb.ToString(0, result);
            }
            else { 
                errorMsg = SR.GetString(SR.ADMembership_Unknown_Error, string.Format(CultureInfo.InvariantCulture, "{0}", errorCode));
            } 
 
            return errorMsg;
        } 

    }

    internal static class PropertyManager 
    {
        public static object GetPropertyValue(DirectoryEntry directoryEntry, string propertyName) 
        { 

            Debug.Assert(directoryEntry != null, "PropertyManager::GetPropertyValue - directoryEntry is null"); 
            Debug.Assert(propertyName != null, "PropertyManager::GetPropertyValue - propertyName is null");

            if (directoryEntry.Properties[propertyName].Count == 0)
            { 
                if (directoryEntry.Properties["distinguishedName"].Count != 0)
                    throw new ProviderException(SR.GetString(SR.ADMembership_Property_not_found_on_object, propertyName, (string) directoryEntry.Properties["distinguishedName"].Value )); 
                else 
                    throw new ProviderException(SR.GetString(SR.ADMembership_Property_not_found, propertyName));
            } 

            return directoryEntry.Properties[propertyName].Value;
        }
 
        public static object GetSearchResultPropertyValue(SearchResult res, string propertyName)
        { 
 
            Debug.Assert(res != null, "PropertyManager::GetSearchResultPropertyValue - res is null");
            Debug.Assert(propertyName != null, "PropertyManager::GetSearchResultPropertyValue - propertyName is null"); 

            ResultPropertyValueCollection propertyValues = null;

            propertyValues = res.Properties[propertyName]; 
            if ((propertyValues == null) || (propertyValues.Count < 1))
                throw new ProviderException(SR.GetString(SR.ADMembership_Property_not_found,  propertyName)); 
 
            return propertyValues[0];
        } 
    }

    /*typedef struct _DOMAIN_CONTROLLER_INFO {
 		LPTSTR DomainControllerName; 
		LPTSTR DomainControllerAddress;
		ULONG DomainControllerAddressType; 
		GUID DomainGuid; 
 		LPTSTR DomainName;
		LPTSTR DnsForestName; 
 		ULONG Flags;
 		LPTSTR DcSiteName;
		LPTSTR ClientSiteName;
 	} DOMAIN_CONTROLLER_INFO, *PDOMAIN_CONTROLLER_INFO; */ 
	[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
	internal sealed class DomainControllerInfo { 
	#pragma warning disable 0649 
 		public string DomainControllerName;
		public string DomainControllerAddress; 
 		public int DomainControllerAddressType;
 		public Guid DomainGuid;
		public string DomainName;
 		public string DnsForestName; 
		public int Flags;
		public string DcSiteName; 
		public string ClientSiteName; 
       #pragma warning restore 0649
 
              public DomainControllerInfo() {}
 	}

    [SuppressUnmanagedCodeSecurityAttribute()] 
    internal static class NativeMethods
    { 
        internal const int ERROR_NO_SUCH_DOMAIN = 1355; 
        internal const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
        internal const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; 
        internal const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;

        /*DWORD DsGetDcName(
                        LPCTSTR ComputerName, 
                        LPCTSTR DomainName,
                        GUID* DomainGuid, 
                        LPCTSTR SiteName, 
                        ULONG Flags,
                        PDOMAIN_CONTROLLER_INFO* DomainControllerInfo 
                        );*/
        [DllImport("Netapi32.dll", CallingConvention=CallingConvention.StdCall, EntryPoint="DsGetDcNameW", CharSet=CharSet.Unicode)]
        internal static extern int DsGetDcName(
            [In] string computerName, 
            [In] string domainName,
            [In] IntPtr domainGuid, 
            [In] string siteName, 
            [In] uint flags,
            [Out] out IntPtr domainControllerInfo); 

        /*NET_API_STATUS NetApiBufferFree(
                        LPVOID Buffer
                        );*/ 
        [DllImport("Netapi32.dll")]
        internal static extern int NetApiBufferFree( 
            [In] IntPtr buffer); 

        [DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Unicode)] 
        public static extern int FormatMessageW(
            [In] int dwFlags,
            [In] int lpSource,
            [In] int dwMessageId, 
            [In] int dwLanguageId,
            [Out] StringBuilder lpBuffer, 
            [In] int nSize, 
            [In] int arguments);
    } 

    [
        ComVisible(false),
        SuppressUnmanagedCodeSecurityAttribute() 
    ]
    internal static class NativeComInterfaces 
    { 

        /*typedef enum { 
           ADS_SETTYPE_FULL=1,
           ADS_SETTYPE_PROVIDER=2,
           ADS_SETTYPE_SERVER=3,
           ADS_SETTYPE_DN=4 
        } ADS_SETTYPE_ENUM;
 
        typedef enum { 
           ADS_FORMAT_WINDOWS=1,
           ADS_FORMAT_WINDOWS_NO_SERVER=2, 
           ADS_FORMAT_WINDOWS_DN=3,
           ADS_FORMAT_WINDOWS_PARENT=4,
           ADS_FORMAT_X500=5,
           ADS_FORMAT_X500_NO_SERVER=6, 
           ADS_FORMAT_X500_DN=7,
           ADS_FORMAT_X500_PARENT=8, 
           ADS_FORMAT_SERVER=9, 
           ADS_FORMAT_PROVIDER=10,
           ADS_FORMAT_LEAF=11 
        } ADS_FORMAT_ENUM;

        typedef enum {
           ADS_ESCAPEDMODE_DEFAULT=1, 
           ADS_ESCAPEDMODE_ON=2,
           ADS_ESCAPEDMODE_OFF=3, 
           ADS_ESCAPEDMODE_OFF_EX=4 
        } ADS_ESCAPE_MODE_ENUM;*/
 
        internal const int ADS_SETTYPE_FULL = 1;
        internal const int ADS_SETTYPE_DN = 4;
        internal const int ADS_FORMAT_PROVIDER = 10;
        internal const int ADS_FORMAT_SERVER = 9; 
        internal const int ADS_FORMAT_X500_DN = 7;
        internal const int ADS_ESCAPEDMODE_ON = 2; 
        internal const int ADS_ESCAPEDMODE_OFF = 3; 

        // 
        // Pathname as a co-class that implements the IAdsPathname interface
        //
        [ComImport, Guid("080d0d78-f421-11d0-a36e-00c04fb950dc")]
        internal class Pathname 
        {
        } 
 

        [ComImport, Guid("D592AED4-F420-11D0-A36E-00C04FB950DC"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] 
        internal interface IAdsPathname
        {

            // HRESULT Set([in] BSTR bstrADsPath,  [in] long lnSetType); 
            [SuppressUnmanagedCodeSecurityAttribute()]
            int Set([In, MarshalAs(UnmanagedType.BStr)] string bstrADsPath, [In, MarshalAs(UnmanagedType.U4)] int lnSetType); 
 
            // HRESULT SetDisplayType([in] long lnDisplayType);
            int SetDisplayType([In, MarshalAs(UnmanagedType.U4)] int lnDisplayType); 

            // HRESULT Retrieve([in] long lnFormatType,  [out, retval] BSTR* pbstrADsPath);
            [return: MarshalAs(UnmanagedType.BStr)][SuppressUnmanagedCodeSecurityAttribute()]
            string Retrieve([In, MarshalAs(UnmanagedType.U4)] int lnFormatType); 

            // HRESULT GetNumElements([out, retval] long* plnNumPathElements); 
            [return: MarshalAs(UnmanagedType.U4)] 
            int GetNumElements();
 
            // HRESULT GetElement([in]  long lnElementIndex,  [out, retval] BSTR* pbstrElement);
            [return: MarshalAs(UnmanagedType.BStr)]
            string GetElement([In, MarshalAs(UnmanagedType.U4)] int lnElementIndex);
 
            // HRESULT AddLeafElement([in] BSTR bstrLeafElement);
            void AddLeafElement([In, MarshalAs(UnmanagedType.BStr)] string bstrLeafElement); 
 
            // HRESULT RemoveLeafElement();
            void RemoveLeafElement(); 

            // HRESULT CopyPath([out, retval] IDispatch** ppAdsPath);
            [return: MarshalAs(UnmanagedType.Interface)]
            object CopyPath(); 

            // HRESULT GetEscapedElement([in] long lnReserved, [in] BSTR bstrInStr, [out, retval] BSTR*  pbstrOutStr ); 
            [return: MarshalAs(UnmanagedType.BStr)][SuppressUnmanagedCodeSecurityAttribute()] 
            string GetEscapedElement([In, MarshalAs(UnmanagedType.U4)] int lnReserved, [In, MarshalAs(UnmanagedType.BStr)] string bstrInStr);
 
            int EscapedMode {
                get;
                [SuppressUnmanagedCodeSecurityAttribute()]
                set; 
            }
 
        } 

        // 
        // LargeInteger as a co-class that implements the IAdsLargeInteger  interface
        //
        [ComImport, Guid("927971f5-0939-11d1-8be1-00c04fd8d503")]
        internal class LargeInteger 
        {
        } 
 
        [ComImport, Guid("9068270b-0939-11d1-8be1-00c04fd8d503"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
        internal interface IAdsLargeInteger 
        {
            long HighPart {
                [SuppressUnmanagedCodeSecurityAttribute()]
                get; 
                [SuppressUnmanagedCodeSecurityAttribute()]
                set; 
            } 

            long LowPart { 
                [SuppressUnmanagedCodeSecurityAttribute()]
                get;
                [SuppressUnmanagedCodeSecurityAttribute()]
                set; 
            }
        } 
 
    }
 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK