Проверьте MaxPasswordAge в Windows с локальным приложением C#

У меня самое плохое время, когда я пытаюсь найти документацию об этом в Интернете. По сути, я хочу знать, что для Secpol MaXPWAge установлено значение 90 или меньше, и он отображается в текстовом поле (для простоты назовем его textbox1). Я искал решение WMI, реестр, GPEDIT в аудиторе и ничего не нашел. Я нашел это, но, честно говоря, я понятия не имею, как использовать один и тот же код для проверки максимального срока действия пароля вместо требований сложности. ПОЖАЛУЙСТА, кто-нибудь может показать мне, что я должен делать здесь? C# не мой основной язык.

https://gist.github.com/jkingry/421802

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        Console.Write(PasswordComplexityPolicy());
    }

    static bool PasswordComplexityPolicy()
    {
        var tempFile = Path.GetTempFileName();

        Process p = new Process();
        p.StartInfo.FileName = Environment.ExpandEnvironmentVariables(@"%SystemRoot%\system32\secedit.exe");
        p.StartInfo.Arguments = String.Format(@"/export /cfg ""{0}"" /quiet", tempFile);
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.UseShellExecute = false;
        p.Start();
        p.WaitForExit();

        var file = IniFile.Load(tempFile);

        IniSection systemAccess = null;
        var passwordComplexityString = "";
        var passwordComplexity = 0;

        return file.Sections.TryGetValue("System Access", out systemAccess)
            && systemAccess.TryGetValue("PasswordComplexity", out passwordComplexityString)
            && Int32.TryParse(passwordComplexityString, out passwordComplexity)
            && passwordComplexity == 1;
    }

    class IniFile
    {
        public static IniFile Load(string filename)
        {
            var result = new IniFile();
            result.Sections = new Dictionary<string, IniSection>();
            var section = new IniSection(String.Empty);
            result.Sections.Add(section.Name, section);

            foreach (var line in File.ReadAllLines(filename))
            {
                var trimedLine = line.Trim();
                switch (line[0])
                {
                    case ';':
                        continue;
                    case '[':
                        section = new IniSection(trimedLine.Substring(1, trimedLine.Length - 2));
                        result.Sections.Add(section.Name, section);
                        break;
                    default:
                        var parts = trimedLine.Split('=');
                        if(parts.Length > 1)
                        {
                            section.Add(parts[0].Trim(), parts[1].Trim());
                        }
                        break;
                }                    
            }

            return result;
        }

        public IDictionary<string, IniSection> Sections { get; private set; }
    }

    class IniSection : Dictionary<string, string>
    {
        public IniSection(string name) : base(StringComparer.OrdinalIgnoreCase)
        {
            this.Name = name;
        }

        public string Name { get; private set; }
    }
}

2 ответа

Решение

Это своего рода обман, но он работает, если вы ищете только одну вещь. По сути, он запускает новый процесс и работает net accounts, затем отпугивает Maximum password age поле из вывода. Попробуйте, но вам, возможно, придется запустить его от имени администратора:

var process = new Process
{
    StartInfo = new ProcessStartInfo()
    {
        FileName = "net",
        Arguments = "accounts",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

process.Start();
string text = "";
while (!process.StandardOutput.EndOfStream)
{
    text = process.StandardOutput.ReadLine();
    if (text != null && text.StartsWith("Maximum password age (days):"))
        break;
}
if (text == null || !text.StartsWith("Maximum password age (days):"))
    return;
text = text.Replace("Maximum password age (days):", "").Trim();

textBox1.Text = text;

Я бы написал IniFile класс как это:

class IniFile : Dictionary<string,Dictionary<string,string>> {
    public IniFile(string filename) {
        var currentSection = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        Add("", currentSection);
        foreach (var line in File.ReadAllLines(filename)) {
            var trimedLine = line.Trim();
            switch (line[0]) {
                case ';':
                    continue;
                case '[':
                    currentSection = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                    Add(trimedLine.Substring(1, trimedLine.Length - 2), currentSection);
                    break;
                default:
                    var parts = trimedLine.Split('=');
                    if (parts.Length > 1) {
                        currentSection.Add(parts[0].Trim(), parts[1].Trim());
                    }
                    break;
            }
        }
    }
    public string this[string sectionName, string key] {
        get {
            Dictionary<string, string> section;
            if (!TryGetValue(sectionName, out section)) { return null; }
            string value;
            if (!section.TryGetValue(key, out value)) { return null; }
            return value;
        }
    }
    public int? GetInt(string sectionName, string key) {
        string stringValue = this[sectionName, key];
        int result;
        if (!int.TryParse(stringValue, out result)) { return null; }
        return result;
    }
}

и поместите генерацию INI-файла в отдельный метод:

class Program {
    static void GenerateSecEditOutput(out string tempFile) {
        tempFile = Path.GetTempFileName();
        var p = new Process {
            StartInfo = new ProcessStartInfo {
                FileName = Environment.ExpandEnvironmentVariables(@"%SystemRoot%\system32\secedit.exe"),
                Arguments = String.Format(@"/export /cfg ""{0}"" /quiet", tempFile),
                CreateNoWindow = true,
                UseShellExecute = false
            }
        };
        p.Start();
        p.WaitForExit();
    }

    //... Main goes here
}

Затем Main Метод выглядит так:

static void Main(string[] args) {
    //This will be the path of the temporary file which contains the output of secedit.exe
    string tempFile;

    //Write the output of secedit.exe to the temporary file
    GenerateSecEditOutput(out tempFile);

    //Parse the temporary file
    var iniFile = new IniFile(tempFile);

    //Read the maximum password age from the "System Access" section
    var maxPasswordAge = iniFile.GetInt("System Access", "MaximumPasswordAge");
    if (maxPasswordAge.HasValue) {
        Console.WriteLine("MaxPasswordAge = {0}", maxPasswordAge);
    } else {
        Console.WriteLine("Unable to find MaximumPasswordAge");
    }
    Console.ReadKey(true);
}

Если у вас есть текстовое поле, в которое вы хотите поместить значение, шаги более или менее одинаковы. Мы можем избежать целочисленного анализа и использовать индексатор IniFile:

string tempFile;
GenerateSecEditOutput(out tempFile);
var iniFile = new IniFile(tempFile);
//assuming tb is a variable referring to a textbox
tb.Text = iniFile["System Access", "MaximumPasswordAge"];

Имейте в виду, что secedit.exe требует прав администратора для запуска. Без прав администратора код не будет работать; временный файл будет просто пустым. Смотрите здесь для некоторых предложений о том, как это сделать.

Другие вопросы по тегам