加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Windows > 正文

Windows – 如何编程检查“密码必须满足复杂性要求”组策略设置

发布时间:2020-12-14 04:34:01 所属栏目:Windows 来源:网络整理
导读:窗口有五个与密码安全相关的策略设置: 强制密码历史记录 最大密码年龄 最小密码年龄 最小密码长度 密码必须满足复杂性要求 使用可逆加密存储密码 我知道如何使用 NetUserModalsGet 读取most of these items.但它不支持检查密码复杂性要求是否启用: 强制密
窗口有五个与密码安全相关的策略设置:

>强制密码历史记录
>最大密码年龄
>最小密码年龄
>最小密码长度
>密码必须满足复杂性要求
>使用可逆加密存储密码

我知道如何使用NetUserModalsGet读取most of these items.但它不支持检查密码复杂性要求是否启用:

>强制密码历史记录:usrmod0_password_hist_len
>最大密码年龄:usrmod0_max_passwd_age
>最小密码年龄:usrmod0_min_passwd_age
>最小密码长度:usrmod0_min_passwd_len
密码必须符合复杂性要求:?
>使用可逆加密存储密码:

我也知道,WMI的RSOP(“结果集策略”)不合适,as it only works on a domain.我肯定不会去crawling through an undocumented binary blob(即我想要支持的方式).

注意:我不在乎“使用可逆加密存储密码”组策略设置.

奖金

您也可以使用NetUserModalsGet API来检索帐户锁定策略设置:

>帐号锁定持续时间:usrmod3_lockout_duration
>帐号锁定阈值:usrmod3_lockout_threshold
>重设帐号锁定计数器:usrmod3_lockout_observation_window

从而整理所有与密码相关的组策略选项;除了“必须满足复杂性要求”.

为了完整性,假设非域连接的机器(即,没有AD服务器进行查询,没有RSOP查询等).

这是可以使用SAM( Security Account Manager)API.

这个API(由SAMLIB.DLL提供)没有直接记录(没有标题,没有SDK),但是使用它的“协议”在这里记录:[MS-SAMR]: Security Account Manager (SAM) Remote Protocol (Client-to-Server),你只需要删除描述的SamrXXXX方法中的r.

这里有一个是SamQueryInformationDomain(和相关的SamSetInformationDomain),它会给你一个DOMAIN_PASSWORD_INFORMATION的结构

typedef struct _DOMAIN_PASSWORD_INFORMATION {
   unsigned short MinPasswordLength;
   unsigned short PasswordHistoryLength;
   unsigned long PasswordProperties;
   OLD_LARGE_INTEGER MaxPasswordAge;
   OLD_LARGE_INTEGER MinPasswordAge;
 } DOMAIN_PASSWORD_INFORMATION,

PasswordProperties会员可以包含DOMAIN_PASSWORD_COMPLEX标志:

DOMAIN_PASSWORD_COMPLEX
0x00000001
The server enforces password complexity policy. See section 3.1.1.7.2 for details of the password policy.

我提供了一些C#示例来检查这个.

首先,转储当前机器的SAM服务器所服务的所有域的策略:

using (SamServer server = new SamServer(null,SamServer.SERVER_ACCESS_MASK.SAM_SERVER_ENUMERATE_DOMAINS | SamServer.SERVER_ACCESS_MASK.SAM_SERVER_LOOKUP_DOMAIN))
        {
            foreach (string domain in server.EnumerateDomains())
            {
                Console.WriteLine("domain: " + domain);

                var sid = server.GetDomainSid(domain);
                Console.WriteLine(" sid: " + sid);

                var pi = server.GetDomainPasswordInformation(sid);
                Console.WriteLine(" MaxPasswordAge: " + pi.MaxPasswordAge);
                Console.WriteLine(" MinPasswordAge: " + pi.MinPasswordAge);
                Console.WriteLine(" MinPasswordLength: " + pi.MinPasswordLength);
                Console.WriteLine(" PasswordHistoryLength: " + pi.PasswordHistoryLength);
                Console.WriteLine(" PasswordProperties: " + pi.PasswordProperties);
            }
        }

第二个读取并更新当前机器域的策略:

using (SamServer server = new SamServer(null,SamServer.SERVER_ACCESS_MASK.SAM_SERVER_ALL_ACCESS))
        {
            var sid = server.GetDomainSid(Environment.MachineName);
            var pi = server.GetDomainPasswordInformation(sid);

            // remove password complexity
            pi.PasswordProperties &= ~SamServer.PASSWORD_PROPERTIES.DOMAIN_PASSWORD_COMPLEX;
            server.SetDomainPasswordInformation(sid,pi);
        }

这是SamServer实用程序:

public sealed class SamServer : IDisposable
{
    private IntPtr _handle;

    public SamServer(string name,SERVER_ACCESS_MASK access)
    {
        Name = name;
        Check(SamConnect(new UNICODE_STRING(name),out _handle,access,IntPtr.Zero));
    }

    public string Name { get; private set; }

    public void Dispose()
    {
        if (_handle != IntPtr.Zero)
        {
            SamCloseHandle(_handle);
            _handle = IntPtr.Zero;
        }
    }

    public void SetDomainPasswordInformation(SecurityIdentifier domainSid,DOMAIN_PASSWORD_INFORMATION passwordInformation)
    {
        if (domainSid == null)
            throw new ArgumentNullException("domainSid");

        byte[] sid = new byte[domainSid.BinaryLength];
        domainSid.GetBinaryForm(sid,0);

        IntPtr domain;
        Check(SamOpenDomain(_handle,DOMAIN_ACCESS_MASK.DOMAIN_WRITE_PASSWORD_PARAMS,sid,out domain));
        IntPtr info = Marshal.AllocHGlobal(Marshal.SizeOf(passwordInformation));
        Marshal.StructureToPtr(passwordInformation,info,false);
        try
        {
            Check(SamSetInformationDomain(domain,DOMAIN_INFORMATION_CLASS.DomainPasswordInformation,info));
        }
        finally
        {
            Marshal.FreeHGlobal(info);
            SamCloseHandle(domain);
        }
    }

    public DOMAIN_PASSWORD_INFORMATION GetDomainPasswordInformation(SecurityIdentifier domainSid)
    {
        if (domainSid == null)
            throw new ArgumentNullException("domainSid");

        byte[] sid = new byte[domainSid.BinaryLength];
        domainSid.GetBinaryForm(sid,DOMAIN_ACCESS_MASK.DOMAIN_READ_PASSWORD_PARAMETERS,out domain));
        IntPtr info = IntPtr.Zero;
        try
        {
            Check(SamQueryInformationDomain(domain,out info));
            return (DOMAIN_PASSWORD_INFORMATION)Marshal.PtrToStructure(info,typeof(DOMAIN_PASSWORD_INFORMATION));
        }
        finally
        {
            SamFreeMemory(info);
            SamCloseHandle(domain);
        }
    }

    public SecurityIdentifier GetDomainSid(string domain)
    {
        if (domain == null)
            throw new ArgumentNullException("domain");

        IntPtr sid;
        Check(SamLookupDomainInSamServer(_handle,new UNICODE_STRING(domain),out sid));
        return new SecurityIdentifier(sid);
    }

    public IEnumerable<string> EnumerateDomains()
    {
        int cookie = 0;
        while (true)
        {
            IntPtr info;
            int count;
            var status = SamEnumerateDomainsInSamServer(_handle,ref cookie,out info,1,out count);
            if (status != NTSTATUS.STATUS_SUCCESS && status != NTSTATUS.STATUS_MORE_ENTRIES)
                Check(status);

            if (count == 0)
                break;

            UNICODE_STRING us = (UNICODE_STRING)Marshal.PtrToStructure(info + 8,typeof(UNICODE_STRING));
            SamFreeMemory(info);
            yield return us.ToString();
        }
    }

    private enum DOMAIN_INFORMATION_CLASS
    {
        DomainPasswordInformation = 1,}

    [Flags]
    public enum PASSWORD_PROPERTIES
    {
        DOMAIN_PASSWORD_COMPLEX = 0x00000001,DOMAIN_PASSWORD_NO_ANON_CHANGE = 0x00000002,DOMAIN_PASSWORD_NO_CLEAR_CHANGE = 0x00000004,DOMAIN_LOCKOUT_ADMINS = 0x00000008,DOMAIN_PASSWORD_STORE_CLEARTEXT = 0x00000010,DOMAIN_REFUSE_PASSWORD_CHANGE = 0x00000020,}

    [Flags]
    private enum DOMAIN_ACCESS_MASK
    {
        DOMAIN_READ_PASSWORD_PARAMETERS = 0x00000001,DOMAIN_WRITE_PASSWORD_PARAMS = 0x00000002,DOMAIN_READ_OTHER_PARAMETERS = 0x00000004,DOMAIN_WRITE_OTHER_PARAMETERS = 0x00000008,DOMAIN_CREATE_USER = 0x00000010,DOMAIN_CREATE_GROUP = 0x00000020,DOMAIN_CREATE_ALIAS = 0x00000040,DOMAIN_GET_ALIAS_MEMBERSHIP = 0x00000080,DOMAIN_LIST_ACCOUNTS = 0x00000100,DOMAIN_LOOKUP = 0x00000200,DOMAIN_ADMINISTER_SERVER = 0x00000400,DOMAIN_ALL_ACCESS = 0x000F07FF,DOMAIN_READ = 0x00020084,DOMAIN_WRITE = 0x0002047A,DOMAIN_EXECUTE = 0x00020301
    }

    [Flags]
    public enum SERVER_ACCESS_MASK
    {
        SAM_SERVER_CONNECT = 0x00000001,SAM_SERVER_SHUTDOWN = 0x00000002,SAM_SERVER_INITIALIZE = 0x00000004,SAM_SERVER_CREATE_DOMAIN = 0x00000008,SAM_SERVER_ENUMERATE_DOMAINS = 0x00000010,SAM_SERVER_LOOKUP_DOMAIN = 0x00000020,SAM_SERVER_ALL_ACCESS = 0x000F003F,SAM_SERVER_READ = 0x00020010,SAM_SERVER_WRITE = 0x0002000E,SAM_SERVER_EXECUTE = 0x00020021
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DOMAIN_PASSWORD_INFORMATION
    {
        public short MinPasswordLength;
        public short PasswordHistoryLength;
        public PASSWORD_PROPERTIES PasswordProperties;
        private long _maxPasswordAge;
        private long _minPasswordAge;

        public TimeSpan MaxPasswordAge
        {
            get
            {
                return -new TimeSpan(_maxPasswordAge);
            }
            set
            {
                _maxPasswordAge = value.Ticks;
            }
        }

        public TimeSpan MinPasswordAge
        {
            get
            {
                return -new TimeSpan(_minPasswordAge);
            }
            set
            {
                _minPasswordAge = value.Ticks;
            }
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct UNICODE_STRING : IDisposable
    {
        public ushort Length;
        public ushort MaximumLength;
        private IntPtr Buffer;

        public UNICODE_STRING(string s)
            : this()
        {
            if (s != null)
            {
                Length = (ushort)(s.Length * 2);
                MaximumLength = (ushort)(Length + 2);
                Buffer = Marshal.StringToHGlobalUni(s);
            }
        }

        public void Dispose()
        {
            if (Buffer != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(Buffer);
                Buffer = IntPtr.Zero;
            }
        }

        public override string ToString()
        {
            return Buffer != IntPtr.Zero ? Marshal.PtrToStringUni(Buffer) : null;
        }
    }

    private static void Check(NTSTATUS err)
    {
        if (err == NTSTATUS.STATUS_SUCCESS)
            return;

        throw new Win32Exception("Error " + err + " (0x" + ((int)err).ToString("X8") + ")");
    }

    private enum NTSTATUS
    {
        STATUS_SUCCESS = 0x0,STATUS_MORE_ENTRIES = 0x105,STATUS_INVALID_HANDLE = unchecked((int)0xC0000008),STATUS_INVALID_PARAMETER = unchecked((int)0xC000000D),STATUS_ACCESS_DENIED = unchecked((int)0xC0000022),STATUS_OBJECT_TYPE_MISMATCH = unchecked((int)0xC0000024),STATUS_NO_SUCH_DOMAIN = unchecked((int)0xC00000DF),}

    [DllImport("samlib.dll",CharSet = CharSet.Unicode)]
    private static extern NTSTATUS SamConnect(UNICODE_STRING ServerName,out IntPtr ServerHandle,SERVER_ACCESS_MASK DesiredAccess,IntPtr ObjectAttributes);

    [DllImport("samlib.dll",CharSet = CharSet.Unicode)]
    private static extern NTSTATUS SamCloseHandle(IntPtr ServerHandle);

    [DllImport("samlib.dll",CharSet = CharSet.Unicode)]
    private static extern NTSTATUS SamFreeMemory(IntPtr Handle);

    [DllImport("samlib.dll",CharSet = CharSet.Unicode)]
    private static extern NTSTATUS SamOpenDomain(IntPtr ServerHandle,DOMAIN_ACCESS_MASK DesiredAccess,byte[] DomainId,out IntPtr DomainHandle);

    [DllImport("samlib.dll",CharSet = CharSet.Unicode)]
    private static extern NTSTATUS SamLookupDomainInSamServer(IntPtr ServerHandle,UNICODE_STRING name,out IntPtr DomainId);

    [DllImport("samlib.dll",CharSet = CharSet.Unicode)]
    private static extern NTSTATUS SamQueryInformationDomain(IntPtr DomainHandle,DOMAIN_INFORMATION_CLASS DomainInformationClass,out IntPtr Buffer);

    [DllImport("samlib.dll",CharSet = CharSet.Unicode)]
    private static extern NTSTATUS SamSetInformationDomain(IntPtr DomainHandle,IntPtr Buffer);

    [DllImport("samlib.dll",CharSet = CharSet.Unicode)]
    private static extern NTSTATUS SamEnumerateDomainsInSamServer(IntPtr ServerHandle,ref int EnumerationContext,out IntPtr EnumerationBuffer,int PreferedMaximumLength,out int CountReturned);
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读