72 lines
4.1 KiB
PowerShell
72 lines
4.1 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[string]$ComputerName,
|
|
[switch]$RevealPassword,
|
|
[string]$InventoryRoot = "$env:ProgramData\SGU\RustDesk\Devices"
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Assert-Administrator {
|
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
throw 'Only a local administrator can read the RustDesk device inventory.'
|
|
}
|
|
}
|
|
|
|
function Initialize-DataProtection {
|
|
if (-not ('SguRustDeskDataProtection' -as [type])) {
|
|
Add-Type -TypeDefinition @'
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
public static class SguRustDeskDataProtection {
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
private struct DataBlob { public int cbData; public IntPtr pbData; }
|
|
[DllImport("crypt32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
private static extern bool CryptProtectData(ref DataBlob input, string description, IntPtr entropy, IntPtr reserved, IntPtr prompt, int flags, out DataBlob output);
|
|
[DllImport("crypt32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
private static extern bool CryptUnprotectData(ref DataBlob input, IntPtr description, IntPtr entropy, IntPtr reserved, IntPtr prompt, int flags, out DataBlob output);
|
|
[DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr LocalFree(IntPtr memory);
|
|
private const int CryptProtectLocalMachine = 0x4;
|
|
private static DataBlob ToBlob(byte[] value) { var blob = new DataBlob { cbData = value.Length, pbData = IntPtr.Zero }; if (value.Length > 0) { blob.pbData = Marshal.AllocHGlobal(value.Length); Marshal.Copy(value, 0, blob.pbData, value.Length); } return blob; }
|
|
private static byte[] FromBlob(DataBlob blob) { var value = new byte[blob.cbData]; if (blob.cbData > 0) Marshal.Copy(blob.pbData, value, 0, blob.cbData); return value; }
|
|
public static byte[] Protect(byte[] value) { var input = ToBlob(value); var output = new DataBlob(); try { if (!CryptProtectData(ref input, null, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, CryptProtectLocalMachine, out output)) throw new Win32Exception(Marshal.GetLastWin32Error()); return FromBlob(output); } finally { if (input.pbData != IntPtr.Zero) Marshal.FreeHGlobal(input.pbData); if (output.pbData != IntPtr.Zero) LocalFree(output.pbData); } }
|
|
public static byte[] Unprotect(byte[] value) { var input = ToBlob(value); var output = new DataBlob(); try { if (!CryptUnprotectData(ref input, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0, out output)) throw new Win32Exception(Marshal.GetLastWin32Error()); return FromBlob(output); } finally { if (input.pbData != IntPtr.Zero) Marshal.FreeHGlobal(input.pbData); if (output.pbData != IntPtr.Zero) LocalFree(output.pbData); } }
|
|
}
|
|
'@ -ErrorAction Stop
|
|
}
|
|
}
|
|
|
|
Assert-Administrator
|
|
Initialize-DataProtection
|
|
if (-not (Test-Path -LiteralPath $InventoryRoot -PathType Container)) {
|
|
return @()
|
|
}
|
|
|
|
$entries = @(Get-ChildItem -LiteralPath $InventoryRoot -Filter '*.json' -File |
|
|
ForEach-Object {
|
|
$metadata = Get-Content -LiteralPath $_.FullName -Raw | ConvertFrom-Json
|
|
if ($ComputerName -and -not $metadata.ComputerName.Equals($ComputerName, [StringComparison]::OrdinalIgnoreCase)) {
|
|
return
|
|
}
|
|
$result = [ordered]@{
|
|
ComputerName = [string]$metadata.ComputerName
|
|
RustDeskId = [string]$metadata.RustDeskId
|
|
RegisteredAt = [datetime]$metadata.RegisteredAt
|
|
}
|
|
if ($RevealPassword) {
|
|
$secretPath = [string]$metadata.SecretPath
|
|
if (-not (Test-Path -LiteralPath $secretPath -PathType Leaf)) {
|
|
throw "The protected RustDesk credential for $($metadata.ComputerName) is missing."
|
|
}
|
|
$result.AccessPassword = [Text.Encoding]::UTF8.GetString(
|
|
[SguRustDeskDataProtection]::Unprotect(
|
|
[IO.File]::ReadAllBytes($secretPath)))
|
|
}
|
|
[pscustomobject]$result
|
|
})
|
|
|
|
$entries | Sort-Object ComputerName
|