Private/Security.psm1


class Security {

  static [object] ClosePort([uint32[]]$Port, [string[]]$Protocol) {
    $result = @()
    $firewall        = New-Object -ComObject HNetCfg.FwMgr
    $firewallProfile = $firewall.LocalPolicy.CurrentProfile
    foreach ($p in $Port) {
      $toClose = $firewallProfile.GloballyOpenPorts | Where-Object { $_.Port -eq $p }
      foreach ($proto in $Protocol) {
        if ($proto -eq 'TCP')      { $firewallProfile.GloballyOpenPorts.Remove($toClose, 6) }
        elseif ($proto -eq 'UDP') { $firewallProfile.GloballyOpenPorts.Remove($toClose, 17) }
      }
      $result += [PSCustomObject]@{ Port = $p; Closed = $true }
    }
    return $result
  }

  static [object] GetNetworkCredential([System.Management.Automation.PSCredential]$Credential) {
    return $Credential.GetNetworkCredential()
  }

  static [object] GetSID([string]$Domain, [string]$Username, [string]$Email, [bool]$IncludeInput) {
    $result = $null
    if (![string]::IsNullOrEmpty($Domain) -and ![string]::IsNullOrEmpty($Username)) {
      $ADObj      = [Security.Principal.NTAccount]::new($Domain, $Username)
      $SID        = $ADObj.Translate([Security.Principal.SecurityIdentifier])
      $ReturnVal  = $SID.Value
      $result     = if ($IncludeInput) {
        [PSCustomObject]@{ Domain = $Domain.ToLower(); UserName = $Username.ToLower(); SID = $ReturnVal }
      } else { $ReturnVal }
    } elseif (![string]::IsNullOrEmpty($Email)) {
      $ADObj      = [Security.Principal.NTAccount]::new($Email)
      $SID        = $ADObj.Translate([Security.Principal.SecurityIdentifier])
      $ReturnVal  = $SID.Value
      $result     = if ($IncludeInput) { [PSCustomObject]@{ Email = $Email.ToLower(); SID = $ReturnVal } } else { $ReturnVal }
    }
    return $result
  }

  static [UInt32] ConvertToDecimalIP([System.Net.IPAddress]$IPAddress) {
    $i = 3; $DecimalIP = [UInt32]0
    $IPAddress.GetAddressBytes() | ForEach-Object { $DecimalIP += [UInt32]($_ * [Math]::Pow(256, $i)); $i-- }
    return $DecimalIP
  }

  static [string] ConvertToDottedDecimalIP([string]$IPAddress) {
    $result = $null
    if ($IPAddress -match "^([01]{8}\.){3}[01]{8}$") {
      $result = [String]::Join('.', ($IPAddress.Split('.') | ForEach-Object { [Convert]::ToUInt32($_, 2) }))
    } elseif ($IPAddress -match "^\d+$") {
      $val = [UInt32]$IPAddress
      $dotted = for ($i = 3; $i -gt -1; $i--) {
        $rem = $val % [Math]::Pow(256, $i)
        ($val - $rem) / [Math]::Pow(256, $i)
        $val = [UInt32]$rem
      }
      $result = [String]::Join('.', $dotted)
    } else {
      Write-Error "Cannot convert '$IPAddress' to dotted decimal IP"
    }
    return $result
  }

  static [PSCustomObject] GetNetworkAddress([System.Net.IPAddress]$IPAddress, [System.Net.IPAddress]$SubnetMask) {
    $decIP   = [Security]::ConvertToDecimalIP($IPAddress)
    $decMask = [Security]::ConvertToDecimalIP($SubnetMask)
    $netAddr = [Security]::ConvertToDottedDecimalIP(($decIP -band $decMask).ToString())
    return [PSCustomObject]@{ NetworkAddress = $netAddr }
  }

  static [string] ConvertToMask([int]$MaskLength) {
    [ValidateRange(0,32)][int]$MaskLength = $MaskLength
    return [Security]::ConvertToDottedDecimalIP([Convert]::ToUInt32(('1' * $MaskLength).PadRight(32, '0'), 2).ToString())
  }

  static [string[]] GetNetworkRange([string]$IP, [string]$Mask) {
    if ($IP.Contains('/')) {
      $parts = $IP.Split('/')
      $IP    = $parts[0]
      $Mask  = $parts[1]
    }
    if (!$Mask.Contains('.')) { $Mask = [Security]::ConvertToMask([int]$Mask) }
    $DecimalIP   = [Security]::ConvertToDecimalIP([System.Net.IPAddress]$IP)
    $DecimalMask = [Security]::ConvertToDecimalIP([System.Net.IPAddress]$Mask)
    $Network     = $DecimalIP -band $DecimalMask
    $Broadcast   = $DecimalIP -bor ((-bnot $DecimalMask) -band [UInt32]::MaxValue)
    $range = [System.Collections.Generic.List[string]]::new()
    for ($i = $Network + 1; $i -lt $Broadcast; $i++) {
      $range.Add([Security]::ConvertToDottedDecimalIP($i.ToString()))
    }
    return $range.ToArray()
  }

  static [bool] TestPing([string]$ComputerName) {
    try {
      $ping = [System.Net.NetworkInformation.Ping]::new()
      return ($ping.Send($ComputerName, 200).Status -ne 'TimedOut')
    } catch {
      return $false
    }
  }

  static [bool] TestWmi([string]$IpAddress) {
    try {
      $result = ([WMICLASS]"\\$IpAddress\Root\CIMV2:Win32_Process").Create("hostname")
      return ($result.ReturnValue -eq 0)
    } catch {
      return $false
    }
  }

  static [object] SendWolProxyRequest([string]$Computername, [string]$ConfigMgrSite, [string]$ConfigMgrSiteServer, [string]$WolCmdFilePath, [bool]$UsePsRemoting, [string]$KnownGoodWolProxyHostsFilePath) {
    if (Test-Connection -ComputerName $Computername -Quiet -Count 1) {
      Write-Verbose "The computer $Computername is already online"
      return $null
    }
    $LocalIPAddressNetworks = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled = 'True'" |
      Where-Object { $_.IPAddress -and $_.IPSubnet } |
      ForEach-Object { [PSCustomObject]@{ LocalIPNetwork = ([Security]::GetNetworkAddress([Net.IPAddress]$_.IPAddress[0], [Net.IPAddress]$_.IPSubnet[0])).NetworkAddress } }

    $WolUdpPort = 9
    $WmiQuery = "SELECT DISTINCT * FROM SMS_R_System AS sys JOIN SMS_G_System_NETWORK_ADAPTER_CONFIGURATION AS net ON net.ResourceID = sys.ResourceID WHERE sys.Name = '$Computername' AND net.IPAddress IS NOT NULL"
    $WmiParams = @{ 'ComputerName' = $ConfigMgrSiteServer; 'Namespace' = "root\sms\site_$ConfigMgrSite"; 'Query' = $WmiQuery }
    try {
      $NetworkInfo = Get-WmiObject @WmiParams
      if (!$NetworkInfo) { throw "Computer '$Computername' could not be found in the SCCM database" }
      $OfflineComputerNetwork = $NetworkInfo | ForEach-Object {
        [PSCustomObject]@{
          IPAddress  = [string]([regex]'\b(?:\d{1,3}\.){3}\d{1,3}\b').Matches($_.net.IPAddress)
          SubnetMask = [string]([regex]'\b(?:\d{1,3}\.){3}\d{1,3}\b').Matches($_.net.IPSubnet)
          MACAddress = [string]($_.net.MACAddress -replace '[:\-\.]', '')
        }
      }
    } catch {
      Write-Error $_.Exception.Message
      return $null
    }
    foreach ($Network in $OfflineComputerNetwork) {
      $RemoteIpNetwork = [Security]::GetNetworkAddress([Net.IPAddress]$Network.IPAddress, [Net.IPAddress]$Network.SubnetMask)
      if ($LocalIPAddressNetworks.LocalIPNetwork -contains $RemoteIpNetwork.NetworkAddress) {
        & $WolCmdFilePath $Network.MacAddress $RemoteIpNetwork.NetworkAddress $Network.SubnetMask $WolUdpPort 2>&1 | Out-Null
      } else {
        $HostIps = [Security]::GetNetworkRange($Network.IPAddress, $Network.SubnetMask)
        $WolProxy = $null
        foreach ($Ip in $HostIps) {
          if ([Security]::TestPing($Ip) -and [Security]::TestWmi($Ip)) { $WolProxy = $Ip; break }
        }
        if (!$WolProxy) {
          Write-Warning "Unable to find a WOL proxy for '$Computername'"
        } else {
          Copy-Item $WolCmdFilePath "\\$WolProxy\c$" -Force
          $WolCmdString = "C:\$($WolCmdFilePath | Split-Path -Leaf) $($Network.MACAddress) $($RemoteIpNetwork.NetworkAddress) $($Network.SubnetMask) $WolUdpPort"
          $res = ([WMICLASS]"\\$WolProxy\Root\CIMV2:Win32_Process").Create($WolCmdString)
          if ($res) {
            while (Get-Process -Id $res.ProcessID -ComputerName $WolProxy -ErrorAction SilentlyContinue) { Start-Sleep 1 }
          }
          Remove-Item "\\$WolProxy\c$\$($WolCmdFilePath | Split-Path -Leaf)" -Force -ErrorAction SilentlyContinue
        }
      }
    }
    return $null
  }


  static [void] SetDynamicPort([string]$StartPort, [string]$EndPort) {
    $hc = Get-Command netsh -Type Application -ErrorAction Ignore
    if (!$hc) { Write-Warning 'netsh not found'; return }
    netsh int ipv4 set dynamicport tcp start=49152 num=16384 | Out-Null
    netsh int ipv4 set dynamicport udp start=49152 num=16384 | Out-Null
    netsh int ipv6 set dynamicport tcp start=49152 num=16384 | Out-Null
    netsh int ipv6 set dynamicport udp start=49152 num=16384 | Out-Null
    Write-Verbose "Dynamic port range set: start=$StartPort end=$EndPort"
  }

  static [void] SetTlsLevel([bool]$Tls12, [bool]$Revert) {
    if ($Tls12 -and !$Revert) {
      [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
      Write-Verbose "TLS 1.2 enabled"
    } elseif ($Revert) {
      [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::SystemDefault
      Write-Verbose "TLS settings reverted to system default"
    }
  }
}