diff --git a/contrib/win32/openssh/install-sshd.ps1 b/contrib/win32/openssh/install-sshd.ps1 index 5ecc6bae9..f7b0df18b 100644 --- a/contrib/win32/openssh/install-sshd.ps1 +++ b/contrib/win32/openssh/install-sshd.ps1 @@ -1,6 +1,7 @@ # @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 @@ -10,68 +11,240 @@ $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 +} + -#Idea borrowed from http://sqldbamusings.blogspot.com/2012/03/powershell-adding-accounts-to-local.html function Add-Privilege { param( + [ValidateNotNullOrEmpty()] [string] $Account, [ValidateSet("SeAssignPrimaryTokenPrivilege", "SeServiceLogonRight")] [string] $Privilege ) - #Get $Account SID - $account_sid = $null - try - { - $ntprincipal = new-object System.Security.Principal.NTAccount "$Account" - $sid = $ntprincipal.Translate([System.Security.Principal.SecurityIdentifier]) - $account_sid = $sid.Value.ToString() - } - catch - { - Throw 'Unable to resolve '+ $Account - } - - #Prepare policy settings file to be applied - $settings_to_export = [System.IO.Path]::GetTempFileName() - "[Unicode]" | Set-Content $settings_to_export -Encoding Unicode - "Unicode=yes" | Add-Content $settings_to_export -Force -WhatIf:$false - "[Version]" | Add-Content $settings_to_export -Force -WhatIf:$false - "signature=`"`$CHICAGO`$`"" | Add-Content $settings_to_export -Force -WhatIf:$false - "Revision=1" | Add-Content $settings_to_export -Force -WhatIf:$false - "[Privilege Rights]" | Add-Content $settings_to_export -Force -WhatIf:$false - - #Get Current policy settings - $imported_settings = [System.IO.Path]::GetTempFileName() - secedit.exe /export /areas USER_RIGHTS /cfg "$($imported_settings)" > $null - - if (-not(Test-Path $imported_settings)) { - Throw "Unable to import current security policy settings" - } - - #find current assigned accounts to $Privilege and add it to $settings_to_export - $current_settings = Get-Content $imported_settings -Encoding Unicode - $existing_setting = $null - foreach ($setting in $current_settings) { - if ($setting -like "$Privilege`*") { - $existing_setting = $setting - } - } - - #Add $account_sid to list - if ($existing_setting -eq $null) { - $Privilege + " = *" + $account_sid | Add-Content $settings_to_export -Force -WhatIf:$false - } - else - { - $existing_setting + ",*" + $account_sid | Add-Content $settings_to_export -Force -WhatIf:$false - } - - #export - secedit.exe /configure /db "secedit.sdb" /cfg "$($settings_to_export)" /areas USER_RIGHTS > $null - + [MyLsaWrapper.LsaWrapperCaller]::AddPrivilege($Account, $Privilege) } @@ -98,7 +271,7 @@ New-Service -Name sshd -BinaryPathName $sshdpath -Description "SSH Daemon" -Star sc.exe config sshd obj= $sshdAccount sc.exe privs sshd SeAssignPrimaryTokenPrivilege -Add-Privilege -Account $sshdAccount -Privilege SeAssignPrimaryTokenPrivilege +Add-Privilege -Account $sshdSid -Privilege SeAssignPrimaryTokenPrivilege if(-not (test-path $logsdir -PathType Container)) {