ADMembershipProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / Security / ADMembershipProvider.cs / 1305376 / 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 
    }
 
    [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; 
        private MembershipPasswordCompatibilityMode _LegacyPasswordCompatibilityMode = MembershipPasswordCompatibilityMode.Framework20; 

        // 
        // 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) 
        //
        [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);
 
            temp = config["passwordCompatMode"];
            if (!string.IsNullOrEmpty(temp)) 
                _LegacyPasswordCompatibilityMode = (MembershipPasswordCompatibilityMode) Enum.Parse(typeof(MembershipPasswordCompatibilityMode), temp); 

            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"); 
            config.Remove("passwordCompatMode"); 

            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; 
        }
 
        [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, _LegacyPasswordCompatibilityMode));
        } 

        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; }
        }

        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, "");
            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