using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
namespace SAM_API_Test
{
public 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; }
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(nameof(domainSid));
var sid = new byte[domainSid.BinaryLength];
domainSid.GetBinaryForm(sid, 0);
Check(SamOpenDomain(_handle, DOMAIN_ACCESS_MASK.DOMAIN_WRITE_PASSWORD_PARAMS, sid, out IntPtr 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(nameof(domainSid));
var sid = new byte[domainSid.BinaryLength];
domainSid.GetBinaryForm(sid, 0);
Check(SamOpenDomain(_handle, DOMAIN_ACCESS_MASK.DOMAIN_READ_PASSWORD_PARAMETERS, sid, out IntPtr domain));
var info = IntPtr.Zero;
try
{
Check(SamQueryInformationDomain(domain, DOMAIN_INFORMATION_CLASS.DomainPasswordInformation, 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(nameof(domain));
Check(SamLookupDomainInSamServer(_handle, new UNICODE_STRING(domain), out IntPtr sid));
return new SecurityIdentifier(sid);
}
public IEnumerable<string> EnumerateDomains()
{
int cookie = 0;
while (true)
{
var status = SamEnumerateDomainsInSamServer(_handle, ref cookie, out IntPtr info, 1, out int count);
if (status != NTSTATUS.STATUS_SUCCESS && status != NTSTATUS.STATUS_MORE_ENTRIES)
Check(status);
if (count == 0)
break;
var us = (UNICODE_STRING)Marshal.PtrToStructure(info + IntPtr.Size, typeof(UNICODE_STRING));
SamFreeMemory(info);
yield return us.ToString();
us.Buffer = IntPtr.Zero; // we don't own this one
}
}
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 class UNICODE_STRING : IDisposable
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
public UNICODE_STRING()
: this(null)
{
}
public UNICODE_STRING(string s)
{
if (s != null)
{
Length = (ushort)(s.Length * 2);
MaximumLength = (ushort)(Length + 2);
Buffer = Marshal.StringToHGlobalUni(s);
}
}
public override string ToString() => Buffer != IntPtr.Zero ? Marshal.PtrToStringUni(Buffer) : null;
protected virtual void Dispose(bool disposing)
{
if (Buffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(Buffer);
Buffer = IntPtr.Zero;
}
}
~UNICODE_STRING() => Dispose(false);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
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, DOMAIN_INFORMATION_CLASS DomainInformationClass, 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);
}
}
메인 Form에서 불러들이는 방법
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SAM_API_Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string match_account = "";
string match_account_info = "";
private void Form1_Load(object sender, EventArgs e)
{
match_account = Environment.UserName;
Console.WriteLine("로그인 계정 : {0}", match_account);
// Class1 class1 = new Class1();
// class1.New();
dmp_Sam();
setPasswordPolicy();
dmp_Sam();
}
private void dmp_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())
{
//신규 조건문 추가
if (match_account != "")
{
//domain으로 불러들인 계정은 대문자임으로 UserName으로 불러들인 계정 치환
if (match_account.ToUpper() == domain)
{
Console.WriteLine("domain: " + domain);
var sid = server.GetDomainSid(domain);
Console.WriteLine(" sid: " + sid);
var pi = server.GetDomainPasswordInformation(sid);
Console.WriteLine(" PasswordProperties: " + pi.PasswordProperties);
Console.WriteLine(" PasswordHistoryLength: " + pi.PasswordHistoryLength);
Console.WriteLine(" MaxPasswordAge: " + pi.MaxPasswordAge);
Console.WriteLine(" MinPasswordLength: " + pi.MinPasswordLength);
Console.WriteLine(" MinPasswordAge: " + pi.MinPasswordAge);
match_account_info = " domain: " + domain + "\n";
match_account_info += " sid: " + sid + "\n";
match_account_info += " PasswordProperties: " + pi.PasswordProperties + "\n";
match_account_info += " PasswordHistoryLength: " + pi.PasswordHistoryLength + "\n";
match_account_info += " MaxPasswordAge: " + pi.MaxPasswordAge + "\n";
match_account_info += " MinPasswordLength: " + pi.MinPasswordLength + "\n";
match_account_info += " MinPasswordAge: " + pi.MinPasswordAge;
richTextBox1.Clear();
richTextBox1.Text = match_account_info;
}
}
//Console.WriteLine("domain: " + domain);
//var sid = server.GetDomainSid(domain);
//Console.WriteLine(" sid: " + sid);
//var pi = server.GetDomainPasswordInformation(sid);
//Console.WriteLine(" PasswordProperties: " + pi.PasswordProperties);
//Console.WriteLine(" PasswordHistoryLength: " + pi.PasswordHistoryLength);
//Console.WriteLine(" MaxPasswordAge: " + pi.MaxPasswordAge);
//Console.WriteLine(" MinPasswordLength: " + pi.MinPasswordLength);
//Console.WriteLine(" MinPasswordAge: " + pi.MinPasswordAge);
}
}
}
private void setPasswordPolicy()
{
using (SamServer server = new SamServer(null, SamServer.SERVER_ACCESS_MASK.SAM_SERVER_ALL_ACCESS))
foreach (string domain in server.EnumerateDomains())
{
//신규 조건문 추가
if (match_account != "")
{
//domain으로 불러들인 계정은 대문자임으로 UserName으로 불러들인 계정 치환
if (match_account.ToUpper() == domain)
{
var sid = server.GetDomainSid(domain);
var pi = server.GetDomainPasswordInformation(sid);
/*
* [변경값 지정]
* MaxPasswordAge 값과, MinPasswordAge 값은 SAM에서 상대 시간을 음수 값으로 저장하고 절대 시간을 양수로 저장하기때문에
* 시스템 상에서 값을 지정해줄 때 음수 값으로 지정해줘야함
* PasswordHistoryLength과 MinPasswordLength Short Type인 Int16으로
*/
//암호 복잡성 사용여부 = 사용 ( 미사용 시 ~SamServer.PASSWORD_PROPERTIES.DOMAIN_PASSWORD_COMPLEX;)
//pi.PasswordProperties = SamServer.PASSWORD_PROPERTIES.DOMAIN_PASSWORD_COMPLEX;
//pi.PasswordHistoryLength = 1;
//pi.MaxPasswordAge = TimeSpan.Parse("-30.00:00:00");
//pi.MinPasswordLength = 8;
//pi.MinPasswordAge = TimeSpan.Parse("-0.00:00:00");
// 임의 지정 테스트
pi.PasswordProperties = ~SamServer.PASSWORD_PROPERTIES.DOMAIN_PASSWORD_COMPLEX;
pi.PasswordHistoryLength = 5;
pi.MaxPasswordAge = TimeSpan.Parse("-40.00:00:00");
pi.MinPasswordLength = 10;
pi.MinPasswordAge = TimeSpan.Parse("-4.00:00:00");
//변경값 적용
server.SetDomainPasswordInformation(sid, pi);
}
}
}
}
}
}
'C#.Net' 카테고리의 다른 글
외부 프로그램 실행 및 위치, 사이즈 조정 (0) | 2024.03.05 |
---|---|
Windows 작업 스케줄 API (0) | 2020.12.08 |
GoTo 키워드 사용하지 않고 재시도(Retry) 하는 로직 (0) | 2020.09.24 |
한글 형태소 분석 (0) | 2020.09.21 |
IE를 윈도우 탐색기 또는 InternetExplorer창에 띄우는 방법 컨트롤 (0) | 2020.02.27 |