From 5bea3a3759eb9d49bb3ad9e968be96026bb45839 Mon Sep 17 00:00:00 2001 From: Yanbing Date: Sun, 11 Jun 2017 09:57:38 -0700 Subject: [PATCH] change to shouldprocess, workaround set-acl issue on win7, use approved verb (#164) PowerShell/Win32-OpenSSH#758 PowerShell/Win32-OpenSSH#749 PowerShell/Win32-OpenSSH#745 --- .../win32/openssh/FixHostFilePermissions.ps1 | 30 +- .../win32/openssh/FixUserFilePermissions.ps1 | 10 +- contrib/win32/openssh/OpenSSHUtils.psd1 | 44 ++ contrib/win32/openssh/OpenSSHUtils.psm1 | 434 ++++++++++-------- 4 files changed, 302 insertions(+), 216 deletions(-) create mode 100644 contrib/win32/openssh/OpenSSHUtils.psd1 diff --git a/contrib/win32/openssh/FixHostFilePermissions.ps1 b/contrib/win32/openssh/FixHostFilePermissions.ps1 index 4601bc07e..e15e16f21 100644 --- a/contrib/win32/openssh/FixHostFilePermissions.ps1 +++ b/contrib/win32/openssh/FixHostFilePermissions.ps1 @@ -1,12 +1,14 @@ -param ([switch]$Quiet) +[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] +param () +Set-StrictMode -Version 2.0 If (!(Test-Path variable:PSScriptRoot)) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Definition} -Import-Module $PSScriptRoot\OpenSSHUtils.psm1 -Force -DisableNameChecking +Import-Module $PSScriptRoot\OpenSSHUtils -Force #check sshd config file $sshdConfigPath = join-path $PSScriptRoot "sshd_config" if(Test-Path $sshdConfigPath -PathType Leaf) { - Fix-HostSSHDConfigPermissions -FilePath $sshdConfigPath @psBoundParameters + Repair-SshdConfigPermission -FilePath $sshdConfigPath @psBoundParameters } else { @@ -14,16 +16,16 @@ else } #check host keys -<#$result = 'n' -if (-not $Quiet) { - Do - { - $input = Read-Host -Prompt "Did you register host private keys with ssh-agent? [Yes] Y; [No] N" - } until ($input -match "^(y(es)?|N(o)?)$") - $result = $Matches[0] -} +<# +$warning = @" +To keep the host private keys secure, it is recommended to register them with ssh-agent following +steps in link https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH. +If you choose not to register the keys with ssh-agent, please grant sshd read access to the private host keys after run this script. +"@ +$prompt = "Did you register host private keys with ssh-agent?" +$description = "Grant sshd read access to the private host keys" -if($result.ToLower().Startswith('n')) +if($pscmdlet.ShouldProcess($description, $prompt, $warning)) { $warning = @" To keep the host private keys secure, it is recommended to register them with ssh-agent following @@ -35,7 +37,7 @@ If you choose not to register the keys with ssh-agent, please grant sshd read ac }#> Get-ChildItem $PSScriptRoot\ssh_host_*_key -ErrorAction SilentlyContinue | % { - Fix-HostKeyPermissions -FilePath $_.FullName @psBoundParameters + Repair-SshdHostKeyPermission -FilePath $_.FullName @psBoundParameters } @@ -50,7 +52,7 @@ Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $filePath = Join-Path $userProfilePath .ssh\authorized_keys if(Test-Path $filePath -PathType Leaf) { - Fix-AuthorizedKeyPermissions -FilePath $filePath @psBoundParameters + Repair-AuthorizedKeyPermission -FilePath $filePath @psBoundParameters } } diff --git a/contrib/win32/openssh/FixUserFilePermissions.ps1 b/contrib/win32/openssh/FixUserFilePermissions.ps1 index c21c00533..93e646807 100644 --- a/contrib/win32/openssh/FixUserFilePermissions.ps1 +++ b/contrib/win32/openssh/FixUserFilePermissions.ps1 @@ -1,15 +1,17 @@ -param ([switch]$Quiet) +[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] +param () +Set-StrictMode -Version 2.0 If (!(Test-Path variable:PSScriptRoot)) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Definition} -Import-Module $PSScriptRoot\OpenSSHUtils.psm1 -Force -DisableNameChecking +Import-Module $PSScriptRoot\OpenSSHUtils -Force if(Test-Path ~\.ssh\config -PathType Leaf) { - Fix-UserSSHConfigPermissions -FilePath ~\.ssh\config @psBoundParameters + Repair-UserSshConfigPermission -FilePath ~\.ssh\config @psBoundParameters } Get-ChildItem ~\.ssh\* -Include "id_rsa","id_dsa" -ErrorAction SilentlyContinue | % { - Fix-UserKeyPermissions -FilePath $_.FullName @psBoundParameters + Repair-UserKeyPermission -FilePath $_.FullName @psBoundParameters } Write-Host " Done." diff --git a/contrib/win32/openssh/OpenSSHUtils.psd1 b/contrib/win32/openssh/OpenSSHUtils.psd1 new file mode 100644 index 000000000..bc96eb18e --- /dev/null +++ b/contrib/win32/openssh/OpenSSHUtils.psd1 @@ -0,0 +1,44 @@ +# +# Module manifest for module 'OpenSSHUtils' +# +# Generated on: 6/9/2017 +# + +@{ + +# Script module or binary module file associated with this manifest +ModuleToProcess = 'OpenSSHUtils.psm1' + +# Version number of this module. +ModuleVersion = '1.0.0.1' + +# ID used to uniquely identify this module +GUID = '08285dee-3d08-476b-8948-1a7e2562c079' + +# Author of this module +Author = 'Yanbing Wang' + +# Company or vendor of this module +CompanyName = '' + +# Copyright statement for this module +Copyright = '' + +# Description of the functionality provided by this module +Description = 'Configure OpenSSH for Windows related security settings like file owner and permissions.' + +# Functions to export from this module +FunctionsToExport = '*' + +# Cmdlets to export from this module +CmdletsToExport = '*' + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module +AliasesToExport = '*' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '2.0' +} diff --git a/contrib/win32/openssh/OpenSSHUtils.psm1 b/contrib/win32/openssh/OpenSSHUtils.psm1 index 6ab9aead6..297413137 100644 --- a/contrib/win32/openssh/OpenSSHUtils.psm1 +++ b/contrib/win32/openssh/OpenSSHUtils.psm1 @@ -1,4 +1,5 @@ -$systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") +Set-StrictMode -Version 2.0 +$systemAccount = New-Object System.Security.Principal.NTAccount("NT AUTHORITY", "SYSTEM") $adminsAccount = New-Object System.Security.Principal.NTAccount("BUILTIN","Administrators") $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) $everyone = New-Object System.Security.Principal.NTAccount("EveryOne") @@ -6,78 +7,57 @@ $sshdAccount = New-Object System.Security.Principal.NTAccount("NT SERVICE","sshd <# .Synopsis - Fix-HostSSHDConfigPermissions - fix the file owner and permissions of sshd_config + Repair-SshdConfigPermission + Repair the file owner and Permission of sshd_config #> -function Fix-HostSSHDConfigPermissions +function Repair-SshdConfigPermission { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] - [string]$FilePath, - [switch] $Quiet) + [string]$FilePath) - if ($PSVersionTable.CLRVersion.Major -gt 2) - { - Fix-FilePermissions -Owners $systemAccount,$adminsAccount -ReadAccessNeeded $sshdAccount @psBoundParameters - } - else - { - Fix-FilePermissions -Owners $adminsAccount, $systemAccount -ReadAccessNeeded $sshdAccount @psBoundParameters - } + Repair-FilePermission -Owners $systemAccount,$adminsAccount -ReadAccessNeeded $sshdAccount @psBoundParameters } <# .Synopsis - Fix-HostKeyPermissions - fix the file owner and permissions of host private and public key + Repair-SshdHostKeyPermission + Repair the file owner and Permission of host private and public key -FilePath: The path of the private host key #> -function Fix-HostKeyPermissions +function Repair-SshdHostKeyPermission { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] - [string]$FilePath, - [switch] $Quiet) - $parameters = $PSBoundParameters - if($parameters["FilePath"].EndsWith(".pub")) - { - $parameters["FilePath"] = $parameters["FilePath"].Replace(".pub", "") - } - if ($PSVersionTable.CLRVersion.Major -gt 2) - { - Fix-FilePermissions -Owners $systemAccount,$adminsAccount -ReadAccessNeeded $sshdAccount @psBoundParameters - } - else - { - # issue in ps 2.0: system account is not allowed to set to a owner of the file - Fix-FilePermissions -Owners $adminsAccount, $systemAccount -ReadAccessNeeded $sshdAccount @psBoundParameters - } + [string]$FilePath) - $parameters["FilePath"] += ".pub" - if ($PSVersionTable.CLRVersion.Major -gt 2) + if($PSBoundParameters["FilePath"].EndsWith(".pub")) { - Fix-FilePermissions -Owners $systemAccount,$adminsAccount -ReadAccessOK $everyone -ReadAccessNeeded $sshdAccount @parameters - } - else - { - Fix-FilePermissions -Owners $adminsAccount,$systemAccount -ReadAccessOK $everyone -ReadAccessNeeded $sshdAccount @parameters + $PSBoundParameters["FilePath"] = $PSBoundParameters["FilePath"].Replace(".pub", "") } + + Repair-FilePermission -Owners $systemAccount,$adminsAccount -ReadAccessNeeded $sshdAccount @psBoundParameters + + $PSBoundParameters["FilePath"] += ".pub" + Repair-FilePermission -Owners $systemAccount,$adminsAccount -ReadAccessOK $everyone -ReadAccessNeeded $sshdAccount @psBoundParameters } <# .Synopsis - Fix-AuthorizedKeyPermissions - fix the file owner and permissions of authorized_keys + Repair-AuthorizedKeyPermission + Repair the file owner and Permission of authorized_keys #> -function Fix-AuthorizedKeyPermissions +function Repair-AuthorizedKeyPermission { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] - [string]$FilePath, - [switch] $Quiet) + [string]$FilePath) if(-not (Test-Path $FilePath -PathType Leaf)) { @@ -101,7 +81,7 @@ function Fix-AuthorizedKeyPermissions $account = Get-UserAccount -UserSid $userSid if($account) { - Fix-FilePermissions -Owners $account,$adminsAccount,$systemAccount -AnyAccessOK $account -ReadAccessNeeded $sshdAccount @psBoundParameters + Repair-FilePermission -Owners $account,$adminsAccount,$systemAccount -AnyAccessOK $account -ReadAccessNeeded $sshdAccount @psBoundParameters } else { @@ -116,51 +96,53 @@ function Fix-AuthorizedKeyPermissions <# .Synopsis - Fix-UserKeyPermissions - fix the file owner and permissions of user config + Repair-UserKeyPermission + Repair the file owner and Permission of user config -FilePath: The path of the private user key + -User: The user associated with this ssh config #> -function Fix-UserKeyPermissions +function Repair-UserKeyPermission { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$FilePath, - [switch] $Quiet) + [System.Security.Principal.NTAccount] $User = $currentUser) - $parameters = $PSBoundParameters - if($parameters["FilePath"].EndsWith(".pub")) + if($PSBoundParameters["FilePath"].EndsWith(".pub")) { - $parameters["FilePath"] = $parameters["FilePath"].Replace(".pub", "") + $PSBoundParameters["FilePath"] = $PSBoundParameters["FilePath"].Replace(".pub", "") } - Fix-FilePermissions -Owners $currentUser, $adminsAccount,$systemAccount -AnyAccessOK $currentUser @psBoundParameters + Repair-FilePermission -Owners $User, $adminsAccount,$systemAccount -AnyAccessOK $User @psBoundParameters - $parameters["FilePath"] += ".pub" - Fix-FilePermissions -Owners $currentUser, $adminsAccount,$systemAccount -AnyAccessOK $currentUser -ReadAccessOK $everyone @parameters + $PSBoundParameters["FilePath"] += ".pub" + Repair-FilePermission -Owners $User, $adminsAccount,$systemAccount -AnyAccessOK $User -ReadAccessOK $everyone @psBoundParameters } <# .Synopsis - Fix-UserSSHConfigPermissions - fix the file owner and permissions of user config + Repair-UserSSHConfigPermission + Repair the file owner and Permission of user config #> -function Fix-UserSSHConfigPermissions +function Repair-UserSshConfigPermission { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] - [string]$FilePath, - [switch] $Quiet) - Fix-FilePermissions -Owners $currentUser,$adminsAccount,$systemAccount -AnyAccessOK $currentUser @psBoundParameters + [string]$FilePath) + Repair-FilePermission -Owners $currentUser,$adminsAccount,$systemAccount -AnyAccessOK $currentUser @psBoundParameters } <# .Synopsis - Fix-FilePermissionInternal + Repair-FilePermissionInternal Only validate owner and ACEs of the file #> -function Fix-FilePermissions +function Repair-FilePermission { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] @@ -169,8 +151,7 @@ function Fix-FilePermissions [System.Security.Principal.NTAccount[]] $Owners = $currentUser, [System.Security.Principal.NTAccount[]] $AnyAccessOK, [System.Security.Principal.NTAccount[]] $ReadAccessOK, - [System.Security.Principal.NTAccount[]] $ReadAccessNeeded, - [switch] $Quiet + [System.Security.Principal.NTAccount[]] $ReadAccessNeeded ) if(-not (Test-Path $FilePath -PathType Leaf)) @@ -180,20 +161,21 @@ function Fix-FilePermissions } Write-host " [*] $FilePath" - $return = Fix-FilePermissionInternal @PSBoundParameters + $return = Repair-FilePermissionInternal @PSBoundParameters if($return -contains $true) { #Write-host "Re-check the health of file $FilePath" - Fix-FilePermissionInternal @PSBoundParameters + Repair-FilePermissionInternal @PSBoundParameters } } <# .Synopsis - Fix-FilePermissionInternal + Repair-FilePermissionInternal #> -function Fix-FilePermissionInternal { +function Repair-FilePermissionInternal { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] @@ -202,45 +184,42 @@ function Fix-FilePermissionInternal { [System.Security.Principal.NTAccount[]] $Owners = $currentUser, [System.Security.Principal.NTAccount[]] $AnyAccessOK, [System.Security.Principal.NTAccount[]] $ReadAccessOK, - [System.Security.Principal.NTAccount[]] $ReadAccessNeeded, - [switch] $Quiet + [System.Security.Principal.NTAccount[]] $ReadAccessNeeded ) $acl = Get-Acl $FilePath $needChange = $false $health = $true - if ($Quiet) - { - $result = 'Y' - } + $paras = @{} + $PSBoundParameters.GetEnumerator() | % { if((-not $_.key.Contains("Owners")) -and (-not $_.key.Contains("Access"))) { $paras.Add($_.key,$_.Value) } } $validOwner = $owners | ? { $_.equals([System.Security.Principal.NTAccount]$acl.owner)} - if($validOwner -eq $null) - { - if (-not $Quiet) { - $warning = "Current owner: '$($acl.Owner)'. '$($Owners[0])' should own $FilePath." - Do { - Write-Warning $warning - $input = Read-Host -Prompt "Shall I set the file owner? [Yes] Y; [No] N (default is `"Y`")" - if([string]::IsNullOrEmpty($input)) - { - $input = 'Y' - } - } until ($input -match "^(y(es)?|N(o)?)$") - $result = $Matches[0] - } - - if($result.ToLower().Startswith('y')) - { - $needChange = $true + { + $caption = "Current owner: '$($acl.Owner)'. '$($Owners[0])' should own '$FilePath'." + $prompt = "Shall I set the file owner?" + $description = "Set '$($Owners[0])' as owner of '$FilePath'." + if($pscmdlet.ShouldProcess($description, $prompt, $caption)) + { + Enable-Privilege SeRestorePrivilege | out-null $acl.SetOwner($Owners[0]) - Write-Host "'$($Owners[0])' now owns $FilePath. " -ForegroundColor Green + Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false + if($e) + { + Write-Warning "Set owner failed with error: $($e[0].ToString())." + } + else + { + Write-Host "'$($Owners[0])' now owns '$FilePath'. " -ForegroundColor Green + } } else { $health = $false - Write-Host "The owner is still set to '$($acl.Owner)'." -ForegroundColor Yellow + if(-not $PSBoundParameters.ContainsKey("WhatIf")) + { + Write-Host "The owner is still set to '$($acl.Owner)'." -ForegroundColor Yellow + } } } @@ -261,7 +240,7 @@ function Fix-FilePermissionInternal { $specialIdRefs = "ALL APPLICATION PACKAGES","ALL RESTRICTED APPLICATION PACKAGES" foreach($a in $acl.Access) - { + { if($realAnyAccessOKList -and (($realAnyAccessOKList | ? { $_.equals($a.IdentityReference)}) -ne $null)) { #ignore those accounts listed in the AnyAccessOK list. @@ -284,39 +263,28 @@ function Fix-FilePermissionInternal { (-not (([System.UInt32]$a.FileSystemRights.value__) -band (-bnot $ReadAccessPerm)))) { continue; - } - - $warning = "'$($a.IdentityReference)' has the following access to $($FilePath): '$($a.FileSystemRights)'." + } + if($a.IsInherited) { if($needChange) { - Set-Acl -Path $FilePath -AclObject $acl + Enable-Privilege SeRestorePrivilege | out-null + Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false + if($e) + { + Write-Warning "Repair permission failed with error: $($e[0].ToString())." + } } - - $message = @" -$warning -Need to remove inheritance to fix it. -"@ - - return Remove-RuleProtection -FilePath $FilePath -Message $message -Quiet:$Quiet - } - - if (-not $Quiet) { - Do { - Write-Warning $warning - $input = Read-Host -Prompt "Shall I make it Read only? [Yes] Y; [No] N (default is `"Y`")" - if([string]::IsNullOrEmpty($input)) - { - $input = 'Y' - } - - } until ($input -match "^(y(es)?|N(o)?)$") - $result = $Matches[0] + + return Remove-RuleProtection @paras } + $caption = "'$($a.IdentityReference)' has the following access to '$FilePath': '$($a.FileSystemRights)'." + $prompt = "Shall I make it Read only?" + $description = "Set'$($a.IdentityReference)' Read access only to '$FilePath'. " - if($result.ToLower().Startswith('y')) - { + if($pscmdlet.ShouldProcess($description, $prompt, $caption)) + { $needChange = $true $idRefShortValue = ($a.IdentityReference.Value).split('\')[-1] if ($specialIdRefs -icontains $idRefShortValue ) @@ -329,7 +297,7 @@ Need to remove inheritance to fix it. } else { - Write-Warning "can't translate '$idRefShortValue'. " + Write-Warning "Can't translate '$idRefShortValue'. " continue } } @@ -339,45 +307,40 @@ Need to remove inheritance to fix it. ($a.IdentityReference, "Read", "None", "None", "Allow") } $acl.SetAccessRule($ace) - Write-Host "'$($a.IdentityReference)' now has Read access to $FilePath. " -ForegroundColor Green + Write-Host "'$($a.IdentityReference)' now has Read access to '$FilePath'. " -ForegroundColor Green } else { $health = $false - Write-Host "'$($a.IdentityReference)' still has these access to $($FilePath): '$($a.FileSystemRights)'." -ForegroundColor Yellow + if(-not $PSBoundParameters.ContainsKey("WhatIf")) + { + Write-Host "'$($a.IdentityReference)' still has these access to '$FilePath': '$($a.FileSystemRights)'." -ForegroundColor Yellow + } } - } + } #other than AnyAccessOK and ReadAccessOK list, if any other account is allowed, they should be removed from the dacl elseif($a.AccessControlType.Equals([System.Security.AccessControl.AccessControlType]::Allow)) - { - - $warning = "'$($a.IdentityReference)' should not have access to '$FilePath'. " + { + $caption = "'$($a.IdentityReference)' should not have access to '$FilePath'." if($a.IsInherited) { if($needChange) { - Set-Acl -Path $FilePath -AclObject $acl - } - $message = @" -$warning -Need to remove inheritance to fix it. -"@ - return Remove-RuleProtection -FilePath $FilePath -Message $message -Quiet:$Quiet - } - if (-not $Quiet) { - Do { - Write-Warning $warning - $input = Read-Host -Prompt "Shall I remove this access? [Yes] Y; [No] N (default is `"Y`")" - if([string]::IsNullOrEmpty($input)) + Enable-Privilege SeRestorePrivilege | out-null + Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false + if($e) { - $input = 'Y' - } - } until ($input -match "^(y(es)?|N(o)?)$") - $result = $Matches[0] + Write-Warning "Repair permission failed with error: $($e[0].ToString())." + } + } + return Remove-RuleProtection @paras } - - if($result.ToLower().Startswith('y')) - { + + $prompt = "Shall I remove this access?" + $description = "Remove access rule of '$($a.IdentityReference)' from '$FilePath'." + + if($pscmdlet.ShouldProcess($description, $prompt, "$caption.")) + { $needChange = $true $ace = $a $idRefShortValue = ($a.IdentityReference.Value).split('\')[-1] @@ -398,19 +361,22 @@ Need to remove inheritance to fix it. if(-not ($acl.RemoveAccessRule($ace))) { - Write-Warning "failed to remove access of $($a.IdentityReference) rule to file $FilePath" + Write-Warning "Failed to remove access of '$($a.IdentityReference)' from '$FilePath'." } else { - Write-Host "'$($a.IdentityReference)' has no more access to $FilePath." -ForegroundColor Green + Write-Host "'$($a.IdentityReference)' has no more access to '$FilePath'." -ForegroundColor Green } } else { $health = $false - Write-Host "'$($a.IdentityReference)' still has access to $FilePath." -ForegroundColor Yellow + if(-not $PSBoundParameters.ContainsKey("WhatIf")) + { + Write-Host "'$($a.IdentityReference)' still has access to '$FilePath'." -ForegroundColor Yellow + } } - } + } } #This is the real account list we need to add read access to the file @@ -419,35 +385,29 @@ Need to remove inheritance to fix it. $realReadAccessNeeded | % { if((Get-UserSID -User $_) -eq $null) { - Write-Warning "'$_' needs Read access to $FilePath', but it can't be translated on the machine." + Write-Warning "'$_' needs Read access to '$FilePath', but it can't be translated on the machine." } else { - if (-not $Quiet) { - $warning = "'$_' needs Read access to $FilePath'." - Do { - Write-Warning $warning - $input = Read-Host -Prompt "Shall I make the above change? [Yes] Y; [No] N (default is `"Y`")" - if([string]::IsNullOrEmpty($input)) - { - $input = 'Y' - } - } until ($input -match "^(y(es)?|N(o)?)$") - $result = $Matches[0] - } - - if($result.ToLower().Startswith('y')) - { + $caption = "'$_' needs Read access to '$FilePath'." + $prompt = "Shall I make the above change?" + $description = "Set '$_' Read only access to '$FilePath'. " + + if($pscmdlet.ShouldProcess($description, $prompt, $caption)) + { $needChange = $true $ace = New-Object System.Security.AccessControl.FileSystemAccessRule ` ($_, "Read", "None", "None", "Allow") $acl.AddAccessRule($ace) - Write-Host "'$_' now has Read access to $FilePath. " -ForegroundColor Green + Write-Host "'$_' now has Read access to '$FilePath'." -ForegroundColor Green } else { $health = $false - Write-Host "'$_' does not have Read access to $FilePath." -ForegroundColor Yellow + if(-not $PSBoundParameters.ContainsKey("WhatIf")) + { + Write-Host "'$_' does not have Read access to '$FilePath'." -ForegroundColor Yellow + } } } } @@ -455,15 +415,20 @@ Need to remove inheritance to fix it. if($needChange) { - Set-Acl -Path $FilePath -AclObject $acl + Enable-Privilege SeRestorePrivilege | out-null + Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false + if($e) + { + Write-Warning "Repair permission failed with error: $($e[0].ToString())." + } } if($health) { if ($needChange) { - Write-Host " fixed permissions" -ForegroundColor Yellow + Write-Host " Repaired permissions" -ForegroundColor Yellow } - else + else { Write-Host " looks good" -ForegroundColor Green } @@ -477,36 +442,32 @@ Need to remove inheritance to fix it. #> function Remove-RuleProtection { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param ( [parameter(Mandatory=$true)] - [string]$FilePath, - [string]$Message, - [switch] $Quiet + [string]$FilePath ) - if (-not $Quiet) { - Do - { - Write-Warning $Message - $input = Read-Host -Prompt "Shall I remove the inheritace? [Yes] Y; [No] N (default is `"Y`")" - if([string]::IsNullOrEmpty($input)) - { - $input = 'Y' - } - } until ($input -match "^(y(es)?|N(o)?)$") - $result = $Matches[0] - } + $message = "Need to remove the inheritance before repair the rules." + $prompt = "Shall I remove the inheritace?" + $description = "Remove inheritance of '$FilePath'." - if($result.ToLower().Startswith('y')) - { - $acl = Get-ACL $FilePath + if($pscmdlet.ShouldProcess($description, $prompt, $message)) + { + $acl = Get-acl -Path $FilePath $acl.SetAccessRuleProtection($True, $True) - Set-Acl -Path $FilePath -AclObject $acl - Write-Host "inheritance is removed from $FilePath. " -ForegroundColor Green + Enable-Privilege SeRestorePrivilege | out-null + Set-Acl -Path $FilePath -AclObject $acl -ErrorVariable e -Confirm:$false + if($e) + { + Write-Warning "Remove-RuleProtection failed with error: $($e[0].ToString())." + } + + Write-Host "Inheritance is removed from '$FilePath'." -ForegroundColor Green return $true } - else + elseif(-not $PSBoundParameters.ContainsKey("WhatIf")) { - Write-Host "inheritance is not removed from $FilePath. Skip Checking FilePath." -ForegroundColor Yellow + Write-Host "inheritance is not removed from '$FilePath'. Skip Checking FilePath." -ForegroundColor Yellow return $false } } @@ -545,5 +506,82 @@ function Get-UserSID } } +function Enable-Privilege { + param( + ## The privilege to adjust. This set is taken from + ## http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx + [ValidateSet( + "SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege", + "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", "SeCreatePagefilePrivilege", + "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege", + "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege", + "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege", + "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", "SeManageVolumePrivilege", + "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege", + "SeRestorePrivilege", "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege", + "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", "SeSystemtimePrivilege", + "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege", + "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")] + $Privilege, + ## Switch to disable the privilege, rather than enable it. + [Switch] $Disable + ) -Export-ModuleMember -Function Fix-FilePermissions, Fix-HostSSHDConfigPermissions, Fix-HostKeyPermissions, Fix-AuthorizedKeyPermissions, Fix-UserKeyPermissions, Fix-UserSSHConfigPermissions + ## Taken from P/Invoke.NET with minor adjustments. + $definition = @' + using System; + using System.Runtime.InteropServices; + + public class AdjPriv + { + [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] + internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, + ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + internal static extern IntPtr GetCurrentProcess(); + [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] + internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok); + [DllImport("advapi32.dll", SetLastError = true)] + internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct TokPriv1Luid + { + public int Count; + public long Luid; + public int Attr; + } + + internal const int SE_PRIVILEGE_ENABLED = 0x00000002; + internal const int SE_PRIVILEGE_DISABLED = 0x00000000; + internal const int TOKEN_QUERY = 0x00000008; + internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; + public static bool EnablePrivilege(string privilege, bool disable) + { + bool retVal; + TokPriv1Luid tp; + IntPtr hproc = GetCurrentProcess(); + IntPtr htok = IntPtr.Zero; + retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); + tp.Count = 1; + tp.Luid = 0; + if(disable) + { + tp.Attr = SE_PRIVILEGE_DISABLED; + } + else + { + tp.Attr = SE_PRIVILEGE_ENABLED; + } + retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); + retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); + return retVal; + } + } +'@ + + + $type = Add-Type $definition -PassThru + $type[0]::EnablePrivilege($Privilege, $Disable) +} + +Export-ModuleMember -Function Repair-FilePermission, Repair-SshdConfigPermission, Repair-SshdHostKeyPermission, Repair-AuthorizedKeyPermission, Repair-UserKeyPermission, Repair-UserSshConfigPermission