mirror of
https://github.com/PowerShell/Win32-OpenSSH.git
synced 2025-11-17 04:09:56 +01:00
286 lines
9.8 KiB
PowerShell
286 lines
9.8 KiB
PowerShell
# @manojampalam - authored initial script
|
|
# @friism - Fixed issue with invalid SDDL on Set-Acl
|
|
# @manojampalam - removed ntrights.exe dependency
|
|
# @bingbing8 - removed secedit.exe dependency
|
|
|
|
$scriptpath = $MyInvocation.MyCommand.Path
|
|
$scriptdir = Split-Path $scriptpath
|
|
|
|
$sshdpath = Join-Path $scriptdir "sshd.exe"
|
|
$sshagentpath = Join-Path $scriptdir "ssh-agent.exe"
|
|
$logsdir = Join-Path $scriptdir "logs"
|
|
|
|
$sshdAccount = "NT SERVICE\SSHD"
|
|
$sshdSid = "S-1-5-80-3847866527-469524349-687026318-516638107-1125189541"
|
|
|
|
#Idea borrowed from https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Query-user-26e259b0
|
|
$definition = @'
|
|
using System;
|
|
|
|
namespace MyLsaWrapper
|
|
{
|
|
using System.Runtime.InteropServices;
|
|
using System.Security;
|
|
using System.ComponentModel;
|
|
using System.Security.Principal;
|
|
|
|
using LSA_HANDLE = IntPtr;
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
struct LSA_OBJECT_ATTRIBUTES
|
|
{
|
|
internal int Length;
|
|
internal IntPtr RootDirectory;
|
|
internal IntPtr ObjectName;
|
|
internal int Attributes;
|
|
internal IntPtr SecurityDescriptor;
|
|
internal IntPtr SecurityQualityOfService;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
struct LSA_UNICODE_STRING
|
|
{
|
|
internal ushort Length;
|
|
internal ushort MaximumLength;
|
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|
internal string Buffer;
|
|
}
|
|
sealed class Win32Sec
|
|
{
|
|
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern uint LsaOpenPolicy(
|
|
LSA_UNICODE_STRING[] SystemName,
|
|
ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
|
|
int AccessMask,
|
|
out IntPtr PolicyHandle
|
|
);
|
|
|
|
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern uint LsaAddAccountRights(
|
|
LSA_HANDLE PolicyHandle,
|
|
IntPtr pSID,
|
|
LSA_UNICODE_STRING[] UserRights,
|
|
int CountOfRights
|
|
);
|
|
|
|
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern uint LsaRemoveAccountRights(
|
|
LSA_HANDLE PolicyHandle,
|
|
IntPtr pSID,
|
|
bool AllRights,
|
|
LSA_UNICODE_STRING[] UserRights,
|
|
int CountOfRights
|
|
);
|
|
|
|
[DllImport("advapi32")]
|
|
internal static extern int LsaNtStatusToWinError(int NTSTATUS);
|
|
|
|
[DllImport("advapi32")]
|
|
internal static extern int LsaClose(IntPtr PolicyHandle);
|
|
}
|
|
|
|
internal sealed class Sid : IDisposable
|
|
{
|
|
public IntPtr pSid = IntPtr.Zero;
|
|
public System.Security.Principal.SecurityIdentifier sid = null;
|
|
|
|
public Sid(string account)
|
|
{
|
|
try { sid = new SecurityIdentifier(account); }
|
|
catch { sid = (SecurityIdentifier)(new NTAccount(account)).Translate(typeof(SecurityIdentifier)); }
|
|
Byte[] buffer = new Byte[sid.BinaryLength];
|
|
sid.GetBinaryForm(buffer, 0);
|
|
|
|
pSid = Marshal.AllocHGlobal(sid.BinaryLength);
|
|
Marshal.Copy(buffer, 0, pSid, sid.BinaryLength);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (pSid != IntPtr.Zero)
|
|
{
|
|
Marshal.FreeHGlobal(pSid);
|
|
pSid = IntPtr.Zero;
|
|
}
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
~Sid() { Dispose(); }
|
|
}
|
|
|
|
public sealed class LsaWrapper : IDisposable
|
|
{
|
|
enum Access : int
|
|
{
|
|
POLICY_READ = 0x20006,
|
|
POLICY_ALL_ACCESS = 0x00F0FFF,
|
|
POLICY_EXECUTE = 0X20801,
|
|
POLICY_WRITE = 0X207F8
|
|
}
|
|
const uint STATUS_ACCESS_DENIED = 0xc0000022;
|
|
const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a;
|
|
const uint STATUS_NO_MEMORY = 0xc0000017;
|
|
const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034;
|
|
const uint STATUS_NO_MORE_ENTRIES = 0x8000001a;
|
|
|
|
IntPtr lsaHandle;
|
|
|
|
public LsaWrapper() : this(null) { } // local system if systemName is null
|
|
public LsaWrapper(string systemName)
|
|
{
|
|
LSA_OBJECT_ATTRIBUTES lsaAttr;
|
|
lsaAttr.RootDirectory = IntPtr.Zero;
|
|
lsaAttr.ObjectName = IntPtr.Zero;
|
|
lsaAttr.Attributes = 0;
|
|
lsaAttr.SecurityDescriptor = IntPtr.Zero;
|
|
lsaAttr.SecurityQualityOfService = IntPtr.Zero;
|
|
lsaAttr.Length = Marshal.SizeOf(typeof(LSA_OBJECT_ATTRIBUTES));
|
|
lsaHandle = IntPtr.Zero;
|
|
LSA_UNICODE_STRING[] system = null;
|
|
if (systemName != null)
|
|
{
|
|
system = new LSA_UNICODE_STRING[1];
|
|
system[0] = InitLsaString(systemName);
|
|
}
|
|
|
|
uint ret = Win32Sec.LsaOpenPolicy(system, ref lsaAttr, (int)Access.POLICY_ALL_ACCESS, out lsaHandle);
|
|
if (ret == 0) return;
|
|
if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException();
|
|
if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException();
|
|
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret));
|
|
}
|
|
|
|
public void AddPrivilege(string account, string privilege)
|
|
{
|
|
uint ret = 0;
|
|
using (Sid sid = new Sid(account))
|
|
{
|
|
LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1];
|
|
privileges[0] = InitLsaString(privilege);
|
|
ret = Win32Sec.LsaAddAccountRights(lsaHandle, sid.pSid, privileges, 1);
|
|
}
|
|
if (ret == 0) return;
|
|
if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException();
|
|
if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException();
|
|
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret));
|
|
}
|
|
|
|
public void RemovePrivilege(string account, string privilege)
|
|
{
|
|
uint ret = 0;
|
|
using (Sid sid = new Sid(account))
|
|
{
|
|
LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1];
|
|
privileges[0] = InitLsaString(privilege);
|
|
ret = Win32Sec.LsaRemoveAccountRights(lsaHandle, sid.pSid, false, privileges, 1);
|
|
}
|
|
if (ret == 0) return;
|
|
if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException();
|
|
if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException();
|
|
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret));
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (lsaHandle != IntPtr.Zero)
|
|
{
|
|
Win32Sec.LsaClose(lsaHandle);
|
|
lsaHandle = IntPtr.Zero;
|
|
}
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
~LsaWrapper() { Dispose(); }
|
|
|
|
// helper functions:
|
|
static LSA_UNICODE_STRING InitLsaString(string s)
|
|
{
|
|
// Unicode strings max. 32KB
|
|
if (s.Length > 0x7ffe) throw new ArgumentException("String too long");
|
|
LSA_UNICODE_STRING lus = new LSA_UNICODE_STRING();
|
|
lus.Buffer = s;
|
|
lus.Length = (ushort)(s.Length * sizeof(char));
|
|
lus.MaximumLength = (ushort)(lus.Length + sizeof(char));
|
|
return lus;
|
|
}
|
|
}
|
|
public class LsaWrapperCaller
|
|
{
|
|
public static void AddPrivilege(string account, string privilege)
|
|
{
|
|
using (LsaWrapper lsaWrapper = new LsaWrapper())
|
|
{
|
|
lsaWrapper.AddPrivilege(account, privilege);
|
|
}
|
|
}
|
|
public static void RemovePrivilege(string account, string privilege)
|
|
{
|
|
using (LsaWrapper lsaWrapper = new LsaWrapper())
|
|
{
|
|
lsaWrapper.RemovePrivilege(account, privilege);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
'@
|
|
$references = @()
|
|
if(($psversiontable.Containskey("psedition")) -and ($psversiontable.PSEdition -ieq "core"))
|
|
{
|
|
$references = "System.Security.Principal.Windows", "Microsoft.Win32.Primitives"
|
|
}
|
|
|
|
try {
|
|
$null = [MyLsaWrapper.LsaWrapperCaller]
|
|
}
|
|
catch {
|
|
$types = Add-Type $definition -ref $references -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
|
|
function Add-Privilege
|
|
{
|
|
param(
|
|
[ValidateNotNullOrEmpty()]
|
|
[string] $Account,
|
|
|
|
[ValidateSet("SeAssignPrimaryTokenPrivilege", "SeServiceLogonRight")]
|
|
[string] $Privilege
|
|
)
|
|
|
|
[MyLsaWrapper.LsaWrapperCaller]::AddPrivilege($Account, $Privilege)
|
|
}
|
|
|
|
|
|
if (-not (Test-Path $sshdpath)) {
|
|
throw "sshd.exe is not present in script path"
|
|
}
|
|
|
|
if (Get-Service sshd -ErrorAction SilentlyContinue)
|
|
{
|
|
Stop-Service sshd
|
|
sc.exe delete sshd 1> null
|
|
}
|
|
|
|
if (Get-Service ssh-agent -ErrorAction SilentlyContinue)
|
|
{
|
|
Stop-Service ssh-agent
|
|
sc.exe delete ssh-agent 1> null
|
|
}
|
|
|
|
New-Service -Name ssh-agent -BinaryPathName $sshagentpath -Description "SSH Agent" -StartupType Manual | Out-Null
|
|
cmd.exe /c 'sc.exe sdset ssh-agent D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RP;;;AU)'
|
|
|
|
New-Service -Name sshd -BinaryPathName $sshdpath -Description "SSH Daemon" -StartupType Manual -DependsOn ssh-agent | Out-Null
|
|
sc.exe config sshd obj= $sshdAccount
|
|
sc.exe privs sshd SeAssignPrimaryTokenPrivilege
|
|
|
|
Add-Privilege -Account $sshdSid -Privilege SeAssignPrimaryTokenPrivilege
|
|
|
|
if(-not (test-path $logsdir -PathType Container))
|
|
{
|
|
$null = New-Item $logsdir -ItemType Directory -Force -ErrorAction Stop
|
|
}
|
|
$rights = [System.Security.AccessControl.FileSystemRights]"Read, Write"
|
|
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($sshdAccount, $rights, "ContainerInherit,ObjectInherit", "None", "Allow")
|
|
$acl = Get-Acl -Path $logsdir
|
|
$Acl.SetAccessRule($accessRule)
|
|
Set-Acl -Path $logsdir -AclObject $acl
|
|
Write-Host -ForegroundColor Green "sshd and ssh-agent services successfully installed"
|