diff --git a/README.md b/README.md index e69de29..e92f033 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,191 @@ +# RDP - IntuneWin Pakke + +> Denne pakke installerer og signerer RDP-genveje på skrivebordet via et selvsigneret certifikat, så brugerne ikke får en sikkerhedsadvarsel ved åbning. +> Som standard arbejder alle scripts mod det **offentlige skrivebord** (`C:\Users\Public\Desktop`). +> Brug `-AllUsers` switchen for at køre mod alle brugeres individuelle skriveborde i stedet. + +--- + +## Indhold + +- [Pakkens indhold](#pakkens-indhold) +- [Intune App Konfiguration](#intune-app-konfiguration) +- [Hvad Install.ps1 gør](#hvad-installps1-gør) +- [AllUsers Switch](#allusers-switch) +- [Registry-ændringer](#registry-ændringer) +- [Logning og fejlfinding](#logning-og-fejlfinding) +- [SignOnly Intune App Konfiguration](#signonly-intune-app-konfiguration) +- [Øvrige scripts i pakken](#øvrige-scripts-i-pakken) + +--- + +## Pakkens indhold + +| Fil | Formål | +|-----|--------| +| `Install.ps1` | Kopierer RDP-filer til skrivebord(e), opretter selvsigneret certifikat og signerer alle RDP-filer. Understøtter `-AllUsers` switch. | +| `Detect.ps1` | Registrerer om alle RDP-filer fra pakken er til stede på skrivebord(e). Understøtter `-AllUsers` switch. | +| `Uninstall.ps1` | Fjerner RDP-filer fra skrivebord(e). Understøtter `-AllUsers` switch. | +| `SignOnly.ps1` | Signerer eksisterende RDP-filer på skrivebord(e) — ingen filkopiering. Understøtter `-AllUsers` switch. | +| `SignOnly_detect.ps1` | Registrerer om certifikat er til stede og alle RDP-filer på skrivebord(e) er signerede. Understøtter `-AllUsers` switch. | +| `Clearsigning.ps1` | Fjerner signaturer fra RDP-filer og rydder op i certifikat og registry (til oprydning/debug). Understøtter `-AllUsers` switch. | +| `sandbox.ps1` | Kombineret install + detect script til lokal sandkasse-test. Understøtter `-AllUsers` switch. | +| `rdp-files\*.rdp` | Kilde-RDP-filer der installeres på skrivebord(e) | + +[↑ Tilbage til toppen](#rdp---intunewin-pakke) + +--- + +## Intune App Konfiguration + +### Program +- **Installationskommando:** + `powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Install.ps1` +- **Installationskommando (alle brugeres skriveborde):** + `powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Install.ps1 -AllUsers` +- **Afinstallationskommando:** + `powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Uninstall.ps1` +- **Afinstallationskommando (alle brugeres skriveborde):** + `powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Uninstall.ps1 -AllUsers` +- **Installationsadfærd:** System + +### Registreringsregel +Brug et brugerdefineret registreringsscript: +- **Scriptfil:** `Detect.ps1` +- **Kør script som 32-bit proces på 64-bit klienter:** Nej +- **Gennemtving scriptsignaturkontrol:** Nej + +Registreringslogik: +- Uden `-AllUsers`: tjekker at alle `.rdp`-filer er til stede på det offentlige skrivebord (`C:\Users\Public\Desktop`) +- Med `-AllUsers`: tjekker alle brugeres skriveborde under `C:\Users\` +- Returnerer `exit 0` = installeret +- Returnerer `exit 1` = ikke installeret + +[↑ Tilbage til toppen](#rdp---intunewin-pakke) + +--- + +## Hvad Install.ps1 gør + +1. Finder målskrivebord(e) — offentligt skrivebord som standard, alle brugeres skriveborde med `-AllUsers` +2. Kopierer alle `.rdp`-filer fra `.\rdp-files\` til de fundne skrivebord(e) +3. Søger efter eksisterende selvsigneret certifikat (`CN=RDPSign`) i `Cert:\LocalMachine\My` +4. Opretter et nyt selvsigneret kodesigneringscertifikat hvis intet findes (gyldigt i 25 år, ikke-eksporterbart) +5. Tilføjer certifikatet til `Trusted Root` og `Trusted Publishers` i den lokale maskines certifikatstore +6. Tilføjer certifikatets thumbprint til registry-nøglen `TrustedCertThumbprints` +7. Signerer alle `.rdp`-filer på skrivebord(e) med `rdpsign.exe /sha256` +8. Sætter `RedirectionWarningDialogVersion = 1` i registry for at undertrykke RDP-advarselsdialoger + +[↑ Tilbage til toppen](#rdp---intunewin-pakke) + +--- + +## AllUsers Switch + +Alle scripts understøtter `-AllUsers` switchen. Som standard — uden `-AllUsers` — køres alle scripts kun mod det **offentlige skrivebord** (`C:\Users\Public\Desktop`). Når `-AllUsers` angives, itererer scriptet i stedet over alle brugerprofiler under `C:\Users\` og behandler hver brugers `Desktop`-mappe. + +```powershell +# Kun offentligt skrivebord (standard) +.\Install.ps1 + +# Alle brugeres skriveborde +.\Install.ps1 -AllUsers +``` + +[↑ Tilbage til toppen](#rdp---intunewin-pakke) + +--- + +## Registry-ændringer + +| Sti | Værdi | Type | Data | +|-----|-------|------|------| +| `HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services` | `TrustedCertThumbprints` | String | Certifikatets thumbprint | +| `HKLM\Software\Policies\Microsoft\Windows NT\Terminal Services\Client` | `RedirectionWarningDialogVersion` | DWORD | `1` | + +[↑ Tilbage til toppen](#rdp---intunewin-pakke) + +--- + +## Logning og fejlfinding + +| Script | Logfil | +|--------|--------| +| `Install.ps1` | `C:\Intune\DesktopShortcuts.log` | +| `Detect.ps1` | `C:\Intune\DesktopShortcuts.log` | +| `Uninstall.ps1` | `C:\Intune\DesktopShortcuts.log` | +| `SignOnly.ps1` | `C:\Intune\RDPSign.log` | + +[↑ Tilbage til toppen](#rdp---intunewin-pakke) + +--- + +## SignOnly Intune App Konfiguration + +### Program +- **Installationskommando:** + `powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\SignOnly.ps1` +- **Installationskommando (alle brugeres skriveborde):** + `powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\SignOnly.ps1 -AllUsers` +- **Afinstallationskommando:** + `powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Clearsigning.ps1` +- **Afinstallationskommando (alle brugeres skriveborde):** + `powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Clearsigning.ps1 -AllUsers` +- **Installationsadfærd:** System + +### Registreringsregel +Brug et brugerdefineret registreringsscript: +- **Scriptfil:** `SignOnly_detect.ps1` +- **Kør script som 32-bit proces på 64-bit klienter:** Nej +- **Gennemtving scriptsignaturkontrol:** Nej + +Registreringslogik: +- Tjekker at `CN=RDPSign`-certifikatet findes i `My`, `Root` og `TrustedPublisher` stores +- Tjekker at thumbprint er til stede i registry +- Uden `-AllUsers`: tjekker at alle `.rdp`-filer på det offentlige skrivebord er signerede +- Med `-AllUsers`: tjekker alle `.rdp`-filer på tværs af alle brugeres skriveborde +- Returnerer `exit 0` + `"Detected"` = installeret +- Returnerer `exit 1` = ikke installeret + +[↑ Tilbage til toppen](#rdp---intunewin-pakke) + +--- + +## Øvrige scripts i pakken + +> **Bemærk:** Alle PowerShell-scripts er pakket ind i samme IntuneWin-pakke. Det betyder at man altid kan kalde på et vilkårligt script direkte fra Intune eller manuelt, f.eks.: +> +> ```powershell +> powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Install.ps1 +> powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Install.ps1 -AllUsers +> powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\SignOnly.ps1 +> powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\SignOnly.ps1 -AllUsers +> powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Clearsigning.ps1 +> powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File .\Clearsigning.ps1 -AllUsers +> ``` +> osv. for alle scripts i pakken. + +### `SignOnly.ps1` +Signerer alle eksisterende `.rdp`-filer på skrivebord(e) uden at kopiere filer. Nyttigt til gensignering efter en opdatering eller hvis filer allerede er installeret. Kør som administrator. Understøtter `-AllUsers` switch. + +### `SignOnly_detect.ps1` +Registreringsscript til brug med `SignOnly.ps1` som Intune-pakke. Tjekker: +- Certifikat findes i `My`, `Root` og `TrustedPublisher` stores +- Thumbprint er til stede i registry +- Alle `.rdp`-filer på skrivebord(e) indeholder en gyldig signatur + +Understøtter `-AllUsers` switch. + +### `Clearsigning.ps1` +Fjerner alle signeringsartefakter. Nyttigt til test eller oprydning. Kør som administrator. Understøtter `-AllUsers` switch. +- Fjerner `signscope`- og `signature`-linjer fra alle `.rdp`-filer på skrivebord(e) +- Fjerner `CN=RDPSign`-certifikatet fra `My`, `Root` og `TrustedPublisher` stores +- Fjerner `TrustedCertThumbprints` fra registry +- Fjerner `RedirectionWarningDialogVersion` fra registry + +### `sandbox.ps1` +Kombinerer install og detect i ét script til lokal sandkasse-test. Understøtter `-AllUsers` switch. +Kør fra mappen der indeholder `rdp-files\` som administrator. +Output skrives til både konsollen og `C:\Intune\DesktopShortcuts.log`. + +[↑ Tilbage til toppen](#rdp---intunewin-pakke) diff --git a/app/Clearsigning.ps1 b/app/Clearsigning.ps1 new file mode 100644 index 0000000..1b57cf0 --- /dev/null +++ b/app/Clearsigning.ps1 @@ -0,0 +1,58 @@ +# ============================================================ +# CHANGELOG +# v1.1 - Added -AllUsers switch to remove signatures from RDP +# files on all user desktops instead of only the public +# desktop. +# Added Get-DesktopPaths helper function. +# v1.0 - Initial version, clears signing on public desktop only. +# ============================================================ + +param( + [switch]$AllUsers +) + +function Get-DesktopPaths { + if ($AllUsers) { + Get-ChildItem "$env:SystemDrive\Users" -Directory | ForEach-Object { + $path = Join-Path $_.FullName "Desktop" + if (Test-Path $path) { $path } + } + } else { + [Environment]::GetFolderPath("CommonDesktopDirectory") + } +} + +$certSubject = "CN=RDPSign" + +# 1. Unsign RDP files +foreach ($desktopPath in (Get-DesktopPaths)) { + $rdpFiles = Get-ChildItem -Path $desktopPath -Filter "*.rdp" + foreach ($file in $rdpFiles) { + $content = Get-Content $file.FullName + $cleaned = $content | Where-Object { $_ -notmatch "^signscope:s:" -and $_ -notmatch "^signature:s:" } + $cleaned | Set-Content $file.FullName -Encoding Unicode + Write-Host "Unsigned: $($file.Name) on $desktopPath" + } +} + +# 2. Remove certificate from all stores +foreach ($storeName in @("My", "Root", "TrustedPublisher")) { + $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storeName, "LocalMachine") + $store.Open("ReadWrite") + $certs = $store.Certificates | Where-Object { $_.Subject -eq $certSubject } + foreach ($cert in $certs) { + $store.Remove($cert) + Write-Host "Removed certificate from store: $storeName" + } + $store.Close() +} + +# 3. Remove thumbprint from registry +$gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" +Remove-ItemProperty -Path $gpoPath -Name "TrustedCertThumbprints" -ErrorAction SilentlyContinue +Write-Host "Removed TrustedCertThumbprints" + +# 4. Remove RedirectionWarningDialogVersion +$clientPath = "HKLM:\Software\Policies\Microsoft\Windows NT\Terminal Services\Client" +Remove-ItemProperty -Path $clientPath -Name "RedirectionWarningDialogVersion" -ErrorAction SilentlyContinue +Write-Host "Removed RedirectionWarningDialogVersion" diff --git a/app/Detect.ps1 b/app/Detect.ps1 new file mode 100644 index 0000000..04774f1 --- /dev/null +++ b/app/Detect.ps1 @@ -0,0 +1,69 @@ +# ============================================================ +# CHANGELOG +# v1.1 - Added -AllUsers switch to check all user desktops +# instead of only the public desktop. +# Added Get-DesktopPaths helper function. +# v1.0 - Initial version, checks public desktop only. +# ============================================================ + +param( + [switch]$AllUsers +) + +# Setup logging +$baseFolder = Join-Path $env:SystemDrive "Intune" +$logFile = Join-Path $baseFolder "DesktopShortcuts.log" + +if (-not (Test-Path $baseFolder)) { + New-Item -Path $baseFolder -ItemType Directory -Force | Out-Null +} + +function Write-Log { + param($Message) + $logMessage = "$(Get-Date -Format 'dd-MM-yyyy HH:mm'): $Message" + Add-Content -Path $logFile -Value $logMessage + Write-Host $logMessage +} + +function Get-DesktopPaths { + if ($AllUsers) { + Get-ChildItem "$env:SystemDrive\Users" -Directory | ForEach-Object { + $path = Join-Path $_.FullName "Desktop" + if (Test-Path $path) { $path } + } + } else { + [Environment]::GetFolderPath("CommonDesktopDirectory") + } +} + +try { + $desktopPaths = Get-DesktopPaths + $allFound = $true + + foreach ($desktopPath in $desktopPaths) { + $rdpFiles = Get-ChildItem -Path "$desktopPath\*.rdp" -ErrorAction SilentlyContinue + + if ($rdpFiles.Count -eq 0) { + Write-Log "DETECT [FAIL]: No .rdp files found on desktop: $desktopPath" + $allFound = $false + continue + } + + foreach ($file in $rdpFiles) { + if (-not (Test-Path (Join-Path $desktopPath $file.Name))) { + Write-Log "DETECT [FAIL]: Missing: $($file.Name) on $desktopPath" + $allFound = $false + } + } + } + + if ($allFound) { + Write-Log "DETECT [OK]: All .rdp files found on all desktop(s)" + exit 0 + } else { + exit 1 + } +} catch { + Write-Log "Detect error: $_" + exit 1 +} diff --git a/app/Install.ps1 b/app/Install.ps1 new file mode 100644 index 0000000..4abeee9 --- /dev/null +++ b/app/Install.ps1 @@ -0,0 +1,172 @@ +# ============================================================ +# CHANGELOG +# v1.1 - Added -AllUsers switch to deploy to all user desktops +# instead of only the public desktop. +# Added Get-DesktopPaths helper function. +# v1.0 - Initial version, deploys to public desktop only. +# ============================================================ + +param( + [switch]$AllUsers +) + +# Setup logging +$baseFolder = Join-Path $env:SystemDrive "Intune" +$logFile = Join-Path $baseFolder "DesktopShortcuts.log" + +# Check if Sysnative exists (means script is running in 32-bit mode on x64 OS) +if (Test-Path "$($env:windir)\Sysnative") { + $rdpSignPath = "$($env:windir)\Sysnative\rdpsign.exe" +} else { + $rdpSignPath = "$($env:windir)\System32\rdpsign.exe" +} + +if (-not (Test-Path $baseFolder)) { + New-Item -Path $baseFolder -ItemType Directory -Force | Out-Null +} + +function Write-Log { + param($Message) + $logMessage = "$(Get-Date -Format 'dd-MM-yyyy HH:mm'): $Message" + Add-Content -Path $logFile -Value $logMessage + Write-Host $logMessage +} + +function Get-DesktopPaths { + if ($AllUsers) { + # Return desktop path for each user profile found + Get-ChildItem "$env:SystemDrive\Users" -Directory | ForEach-Object { + $path = Join-Path $_.FullName "Desktop" + if (Test-Path $path) { $path } + } + } else { + [Environment]::GetFolderPath("CommonDesktopDirectory") + } +} + +try { + $desktopPaths = Get-DesktopPaths + Write-Log "Running in mode: $(if ($AllUsers) { 'All Users' } else { 'Public Desktop' })" + + foreach ($desktopPath in $desktopPaths) { + Write-Log "Processing desktop: $desktopPath" + + # Copy RDP files to desktop + Get-ChildItem -Path ".\rdp-files\*.rdp" | ForEach-Object { + try { + Copy-Item -Path $_.FullName -Destination $desktopPath -Force + } catch { + Write-Log "Error copying file: $_" + exit 1 + } + } + } + + Start-Sleep -Seconds 2 # Wait for files to be fully copied before signing + Write-Log "Copied .rdp files to desktop(s)" + + # Certificate setup + $certSubjectName = "RDPSign" + $certSubject = "CN=$certSubjectName" + + Write-Log "Searching for existing certificate: $certSubjectName..." + + $existingCert = Get-ChildItem Cert:\LocalMachine\My | + Where-Object { $_.Subject -eq $certSubject } | + Select-Object -First 1 + + if ($existingCert) { + Write-Log "Found existing certificate with Thumbprint: $($existingCert.Thumbprint)" + $thumbprint = $existingCert.Thumbprint + } else { + Write-Log "No existing certificate found. Creating new one..." + + $cert = New-SelfSignedCertificate ` + -Subject $certSubject ` + -CertStoreLocation "Cert:\LocalMachine\My" ` + -Type CodeSigningCert ` + -KeyExportPolicy NonExportable ` + -NotAfter (Get-Date).AddYears(25) + + $thumbprint = $cert.Thumbprint + + # Add to Trusted Root + $rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine") + $rootStore.Open("ReadWrite") + $rootStore.Add($cert) + $rootStore.Close() + + # Add to Trusted Publishers + $pubStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher", "LocalMachine") + $pubStore.Open("ReadWrite") + $pubStore.Add($cert) + $pubStore.Close() + + # Add thumbprint to RDP trusted certs in registry + $gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" + $keyName = "TrustedCertThumbprints" + + if (-not (Test-Path $gpoPath)) { + New-Item -Path $gpoPath -Force | Out-Null + } + + $currentValue = (Get-ItemProperty -Path $gpoPath -Name $keyName -ErrorAction SilentlyContinue).$keyName + + if ($currentValue -notmatch [regex]::Escape($thumbprint)) { + $newValue = if ([string]::IsNullOrWhiteSpace($currentValue)) { $thumbprint } else { "$currentValue,$thumbprint" } + + if ($null -eq $currentValue) { + New-ItemProperty -Path $gpoPath -Name $keyName -Value $newValue -PropertyType String -Force | Out-Null + } else { + Set-ItemProperty -Path $gpoPath -Name $keyName -Value $newValue + } + Write-Log "Updated TrustedCertThumbprints registry value." + } else { + Write-Log "Thumbprint already exists in registry. Skipping update." + } + + Write-Log "Certificate created and trusted successfully." + } + + # Sign RDP files on all desktop paths + $successCount = 0 + $failCount = 0 + + foreach ($desktopPath in $desktopPaths) { + $rdpFiles = Get-ChildItem -Path $desktopPath -Filter "*.rdp" -Recurse -ErrorAction SilentlyContinue + + if ($rdpFiles.Count -eq 0) { + Write-Log "No .rdp files found on desktop: $desktopPath" + continue + } + + Write-Log "Found $($rdpFiles.Count) file(s) to sign on: $desktopPath" + + foreach ($rdpFile in $rdpFiles) { + Write-Log "Signing: $($rdpFile.Name)" + & $rdpSignPath /sha256 $thumbprint $rdpFile.FullName + + if ($LASTEXITCODE -eq 0) { + Write-Log " [OK] $($rdpFile.Name)" + $successCount++ + } else { + Write-Log " [FAILED] $($rdpFile.Name) - rdpsign exit code: $LASTEXITCODE" + $failCount++ + } + } + } + + # Set registry key to suppress RDP warning dialogs + reg add "HKLM\Software\Policies\Microsoft\Windows NT\Terminal Services\Client" /v RedirectionWarningDialogVersion /t REG_DWORD /d 1 /f + + if ($failCount -eq 0) { + Write-Log "All $successCount file(s) signed successfully." + } else { + Write-Log "$successCount file(s) signed successfully, $failCount file(s) failed." + exit 1 + } + exit 0 +} catch { + Write-Log "Error: $_" + exit 1 +} diff --git a/app/SignOnly.ps1 b/app/SignOnly.ps1 new file mode 100644 index 0000000..3d48797 --- /dev/null +++ b/app/SignOnly.ps1 @@ -0,0 +1,150 @@ +# ============================================================ +# CHANGELOG +# v1.1 - Added -AllUsers switch to sign RDP files on all user +# desktops instead of only the public desktop. +# Added Get-DesktopPaths helper function. +# v1.0 - Initial version, signs on public desktop only. +# ============================================================ + +param( + [switch]$AllUsers +) + +# Setup logging +$baseFolder = Join-Path $env:SystemDrive "Intune" +$logFile = Join-Path $baseFolder "RDPSign.log" + +# Check if Sysnative exists (means script is running in 32-bit mode on x64 OS) +if (Test-Path "$($env:windir)\Sysnative") { + $rdpSignPath = "$($env:windir)\Sysnative\rdpsign.exe" +} else { + $rdpSignPath = "$($env:windir)\System32\rdpsign.exe" +} + +if (-not (Test-Path $baseFolder)) { + New-Item -Path $baseFolder -ItemType Directory -Force | Out-Null +} + +function Write-Log { + param($Message) + $logMessage = "$(Get-Date -Format 'dd-MM-yyyy HH:mm'): $Message" + Add-Content -Path $logFile -Value $logMessage + Write-Host $logMessage +} + +function Get-DesktopPaths { + if ($AllUsers) { + Get-ChildItem "$env:SystemDrive\Users" -Directory | ForEach-Object { + $path = Join-Path $_.FullName "Desktop" + if (Test-Path $path) { $path } + } + } else { + [Environment]::GetFolderPath("CommonDesktopDirectory") + } +} + +try { + $certSubjectName = "RDPSign" + $certSubject = "CN=$certSubjectName" + + Write-Log "Running in mode: $(if ($AllUsers) { 'All Users' } else { 'Public Desktop' })" + Write-Log "Searching for existing certificate: $certSubjectName..." + + $existingCert = Get-ChildItem Cert:\LocalMachine\My | + Where-Object { $_.Subject -eq $certSubject } | + Select-Object -First 1 + + if ($existingCert) { + Write-Log "Found existing certificate with Thumbprint: $($existingCert.Thumbprint)" + $thumbprint = $existingCert.Thumbprint + } else { + Write-Log "No existing certificate found. Creating new one..." + + $cert = New-SelfSignedCertificate ` + -Subject $certSubject ` + -CertStoreLocation "Cert:\LocalMachine\My" ` + -Type CodeSigningCert ` + -KeyExportPolicy NonExportable ` + -NotAfter (Get-Date).AddYears(25) + + $thumbprint = $cert.Thumbprint + + # Add to Trusted Root + $rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine") + $rootStore.Open("ReadWrite") + $rootStore.Add($cert) + $rootStore.Close() + + # Add to Trusted Publishers + $pubStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher", "LocalMachine") + $pubStore.Open("ReadWrite") + $pubStore.Add($cert) + $pubStore.Close() + + # Add thumbprint to RDP trusted certs in registry + $gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" + $keyName = "TrustedCertThumbprints" + + if (-not (Test-Path $gpoPath)) { + New-Item -Path $gpoPath -Force | Out-Null + } + + $currentValue = (Get-ItemProperty -Path $gpoPath -Name $keyName -ErrorAction SilentlyContinue).$keyName + + if ($currentValue -notmatch [regex]::Escape($thumbprint)) { + $newValue = if ([string]::IsNullOrWhiteSpace($currentValue)) { $thumbprint } else { "$currentValue,$thumbprint" } + + if ($null -eq $currentValue) { + New-ItemProperty -Path $gpoPath -Name $keyName -Value $newValue -PropertyType String -Force | Out-Null + } else { + Set-ItemProperty -Path $gpoPath -Name $keyName -Value $newValue + } + Write-Log "Updated TrustedCertThumbprints registry value." + } else { + Write-Log "Thumbprint already exists in registry. Skipping update." + } + + Write-Log "Certificate created and trusted successfully." + } + + $successCount = 0 + $failCount = 0 + + foreach ($desktopPath in (Get-DesktopPaths)) { + $rdpFiles = Get-ChildItem -Path $desktopPath -Filter "*.rdp" -Recurse -ErrorAction SilentlyContinue + + if ($rdpFiles.Count -eq 0) { + Write-Log "No .rdp files found on desktop: $desktopPath" + continue + } + + Write-Log "Found $($rdpFiles.Count) file(s) to sign on: $desktopPath" + + foreach ($rdpFile in $rdpFiles) { + Write-Log "Signing: $($rdpFile.Name)" + & $rdpSignPath /sha256 $thumbprint $rdpFile.FullName + + if ($LASTEXITCODE -eq 0) { + Write-Log " [OK] $($rdpFile.Name)" + $successCount++ + } else { + Write-Log " [FAILED] $($rdpFile.Name) - rdpsign exit code: $LASTEXITCODE" + $failCount++ + } + } + } + + # Set registry key to suppress RDP warning dialogs + reg add "HKLM\Software\Policies\Microsoft\Windows NT\Terminal Services\Client" /v RedirectionWarningDialogVersion /t REG_DWORD /d 1 /f + + if ($failCount -eq 0) { + Write-Log "All $successCount file(s) signed successfully." + } else { + Write-Log "$successCount file(s) signed successfully, $failCount file(s) failed." + exit 1 + } + exit 0 +} catch { + Write-Log "Error: $_" + exit 1 +} diff --git a/app/SignOnly_detect.ps1 b/app/SignOnly_detect.ps1 new file mode 100644 index 0000000..71d2cc6 --- /dev/null +++ b/app/SignOnly_detect.ps1 @@ -0,0 +1,61 @@ +# ============================================================ +# CHANGELOG +# v1.1 - Added -AllUsers switch to check certificate and signed +# RDP files across all user desktops instead of only the +# public desktop. +# Added Get-DesktopPaths helper function. +# v1.0 - Initial version, checks public desktop only. +# ============================================================ + +param( + [switch]$AllUsers +) + +function Get-DesktopPaths { + if ($AllUsers) { + Get-ChildItem "$env:SystemDrive\Users" -Directory | ForEach-Object { + $path = Join-Path $_.FullName "Desktop" + if (Test-Path $path) { $path } + } + } else { + [Environment]::GetFolderPath("CommonDesktopDirectory") + } +} + +$certSubject = "CN=RDPSign" + +# Check certificate exists in all three stores +$certInMy = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -eq $certSubject } +$certInRoot = Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -eq $certSubject } +$certInPub = Get-ChildItem Cert:\LocalMachine\TrustedPublisher | Where-Object { $_.Subject -eq $certSubject } + +if (-not $certInMy -or -not $certInRoot -or -not $certInPub) { + exit 1 +} + +# Check registry thumbprint +$gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" +$thumbprintValue = (Get-ItemProperty -Path $gpoPath -Name "TrustedCertThumbprints" -ErrorAction SilentlyContinue).TrustedCertThumbprints +if ([string]::IsNullOrWhiteSpace($thumbprintValue)) { + exit 1 +} + +# Check that all RDP files on desktop(s) are signed +foreach ($desktopPath in (Get-DesktopPaths)) { + $rdpFiles = Get-ChildItem -Path $desktopPath -Filter "*.rdp" -ErrorAction SilentlyContinue + + if ($rdpFiles.Count -eq 0) { + exit 1 + } + + foreach ($file in $rdpFiles) { + $content = Get-Content $file.FullName -ErrorAction SilentlyContinue + $hasSig = $content | Where-Object { $_ -match "^signature:s:" } + if (-not $hasSig) { + exit 1 + } + } +} + +Write-Host "Detected" +exit 0 diff --git a/app/Uninstall.ps1 b/app/Uninstall.ps1 new file mode 100644 index 0000000..6776a24 --- /dev/null +++ b/app/Uninstall.ps1 @@ -0,0 +1,60 @@ +# ============================================================ +# CHANGELOG +# v1.1 - Added -AllUsers switch to remove files from all user +# desktops instead of only the public desktop. +# Added Get-DesktopPaths helper function. +# v1.0 - Initial version, removes from public desktop only. +# ============================================================ + +param( + [switch]$AllUsers +) + +# Setup logging +$baseFolder = Join-Path $env:SystemDrive "Intune" +$logFile = Join-Path $baseFolder "DesktopShortcuts.log" + +if (-not (Test-Path $baseFolder)) { + New-Item -Path $baseFolder -ItemType Directory -Force | Out-Null +} + +function Write-Log { + param($Message) + $logMessage = "$(Get-Date -Format 'dd-MM-yyyy HH:mm'): $Message" + Add-Content -Path $logFile -Value $logMessage + Write-Host $logMessage +} + +function Get-DesktopPaths { + if ($AllUsers) { + Get-ChildItem "$env:SystemDrive\Users" -Directory | ForEach-Object { + $path = Join-Path $_.FullName "Desktop" + if (Test-Path $path) { $path } + } + } else { + [Environment]::GetFolderPath("CommonDesktopDirectory") + } +} + +try { + $desktopPaths = Get-DesktopPaths + Write-Log "Running in mode: $(if ($AllUsers) { 'All Users' } else { 'Public Desktop' })" + + foreach ($desktopPath in $desktopPaths) { + Get-ChildItem -Path ".\rdp-files\*.rdp" | ForEach-Object { + $targetFile = Join-Path $desktopPath $_.Name + if (Test-Path $targetFile) { + Remove-Item -Path $targetFile -Force + Write-Log "Removed: $($_.Name) from $desktopPath" + } else { + Write-Log "Not found, skipping: $($_.Name) on $desktopPath" + } + } + } + + Write-Log "Uninstall completed." + exit 0 +} catch { + Write-Log "An error occurred: $_" + exit 1 +} diff --git a/app/sandbox.ps1 b/app/sandbox.ps1 new file mode 100644 index 0000000..5244f00 --- /dev/null +++ b/app/sandbox.ps1 @@ -0,0 +1,208 @@ +# ============================================================ +# INSTALL + DETECT - Sandbox test script +# ============================================================ +# CHANGELOG +# v1.1 - Added -AllUsers switch to deploy and detect across all +# user desktops instead of only the public desktop. +# Added Get-DesktopPaths helper function. +# v1.0 - Initial version, public desktop only. +# ============================================================ + +param( + [switch]$AllUsers +) + +# Setup logging +$baseFolder = Join-Path $env:SystemDrive "Intune" +$logFile = Join-Path $baseFolder "DesktopShortcuts.log" + +# Check if Sysnative exists (means script is running in 32-bit mode on x64 OS) +if (Test-Path "$($env:windir)\Sysnative") { + $rdpSignPath = "$($env:windir)\Sysnative\rdpsign.exe" +} else { + $rdpSignPath = "$($env:windir)\System32\rdpsign.exe" +} + +if (-not (Test-Path $baseFolder)) { + New-Item -Path $baseFolder -ItemType Directory -Force | Out-Null +} + +function Write-Log { + param($Message) + $logMessage = "$(Get-Date -Format 'dd-MM-yyyy HH:mm'): $Message" + Add-Content -Path $logFile -Value $logMessage + Write-Host $logMessage +} + +function Get-DesktopPaths { + if ($AllUsers) { + Get-ChildItem "$env:SystemDrive\Users" -Directory | ForEach-Object { + $path = Join-Path $_.FullName "Desktop" + if (Test-Path $path) { $path } + } + } else { + [Environment]::GetFolderPath("CommonDesktopDirectory") + } +} + +# ============================================================ +# INSTALL +# ============================================================ +Write-Log "=== START INSTALL ===" +Write-Log "Running in mode: $(if ($AllUsers) { 'All Users' } else { 'Public Desktop' })" + +try { + $desktopPaths = Get-DesktopPaths + + foreach ($desktopPath in $desktopPaths) { + Write-Log "Copying files to: $desktopPath" + Get-ChildItem -Path ".\rdp-files\*.rdp" | ForEach-Object { + try { + Copy-Item -Path $_.FullName -Destination $desktopPath -Force + } catch { + Write-Log "Error copying file: $_" + } + } + } + + Start-Sleep -Seconds 2 # Wait for files to be fully copied before signing + Write-Log "Copied .rdp files to desktop(s)" + + # Certificate setup + $certSubjectName = "RDPSign" + $certSubject = "CN=$certSubjectName" + + Write-Log "Searching for existing certificate: $certSubjectName..." + + $existingCert = Get-ChildItem Cert:\LocalMachine\My | + Where-Object { $_.Subject -eq $certSubject } | + Select-Object -First 1 + + if ($existingCert) { + Write-Log "Found existing certificate with Thumbprint: $($existingCert.Thumbprint)" + $thumbprint = $existingCert.Thumbprint + } else { + Write-Log "No existing certificate found. Creating new one..." + + $cert = New-SelfSignedCertificate ` + -Subject $certSubject ` + -CertStoreLocation "Cert:\LocalMachine\My" ` + -Type CodeSigningCert ` + -KeyExportPolicy NonExportable ` + -NotAfter (Get-Date).AddYears(25) + + $thumbprint = $cert.Thumbprint + + # Add to Trusted Root + $rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine") + $rootStore.Open("ReadWrite") + $rootStore.Add($cert) + $rootStore.Close() + + # Add to Trusted Publishers + $pubStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher", "LocalMachine") + $pubStore.Open("ReadWrite") + $pubStore.Add($cert) + $pubStore.Close() + + # Add thumbprint to RDP trusted certs in registry + $gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" + $keyName = "TrustedCertThumbprints" + + if (-not (Test-Path $gpoPath)) { + New-Item -Path $gpoPath -Force | Out-Null + } + + $currentValue = (Get-ItemProperty -Path $gpoPath -Name $keyName -ErrorAction SilentlyContinue).$keyName + + if ($currentValue -notmatch [regex]::Escape($thumbprint)) { + $newValue = if ([string]::IsNullOrWhiteSpace($currentValue)) { $thumbprint } else { "$currentValue,$thumbprint" } + + if ($null -eq $currentValue) { + New-ItemProperty -Path $gpoPath -Name $keyName -Value $newValue -PropertyType String -Force | Out-Null + } else { + Set-ItemProperty -Path $gpoPath -Name $keyName -Value $newValue + } + Write-Log "Updated TrustedCertThumbprints registry value." + } else { + Write-Log "Thumbprint already exists in registry. Skipping update." + } + + Write-Log "Certificate created and trusted successfully." + } + + $successCount = 0 + $failCount = 0 + + foreach ($desktopPath in $desktopPaths) { + $rdpFiles = Get-ChildItem -Path $desktopPath -Filter "*.rdp" -Recurse -ErrorAction SilentlyContinue + + if ($rdpFiles.Count -eq 0) { + Write-Log "No .rdp files found on desktop: $desktopPath" + continue + } + + Write-Log "Found $($rdpFiles.Count) file(s) to sign on: $desktopPath" + + foreach ($rdpFile in $rdpFiles) { + Write-Log "Signing: $($rdpFile.Name)" + & $rdpSignPath /sha256 $thumbprint $rdpFile.FullName + + if ($LASTEXITCODE -eq 0) { + Write-Log " [OK] $($rdpFile.Name)" + $successCount++ + } else { + Write-Log " [FAILED] $($rdpFile.Name) - rdpsign exit code: $LASTEXITCODE" + $failCount++ + } + } + } + + # Set registry key to suppress RDP warning dialogs + reg add "HKLM\Software\Policies\Microsoft\Windows NT\Terminal Services\Client" /v RedirectionWarningDialogVersion /t REG_DWORD /d 1 /f + + if ($failCount -eq 0) { + Write-Log "All $successCount file(s) signed successfully." + } else { + Write-Log "$successCount file(s) signed successfully, $failCount file(s) failed." + } + +} catch { + Write-Log "Install error: $_" +} + +# ============================================================ +# DETECT +# ============================================================ +Write-Log "=== START DETECT ===" + +try { + $allFound = $true + + foreach ($desktopPath in (Get-DesktopPaths)) { + $rdpFilesOnDesktop = Get-ChildItem -Path "$desktopPath\*.rdp" -ErrorAction SilentlyContinue + + if ($rdpFilesOnDesktop.Count -eq 0) { + Write-Log "DETECT [FAIL]: No .rdp files found on desktop: $desktopPath" + $allFound = $false + continue + } + + foreach ($file in $rdpFilesOnDesktop) { + if (-not (Test-Path (Join-Path $desktopPath $file.Name))) { + Write-Log "DETECT [FAIL]: Missing: $($file.Name) on $desktopPath" + $allFound = $false + } + } + } + + if ($allFound) { + Write-Log "DETECT [OK]: All .rdp files found on all desktop(s)" + exit 0 + } else { + exit 1 + } +} catch { + Write-Log "Detect error: $_" + exit 1 +}