From 40e7bdf16b503b3b8895c03edd1e33c6177d4503 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Tue, 20 Nov 2018 09:21:28 +1000 Subject: [stable-2.3] win_security_policy - add warning when using this module ot edit rights (#48850). (cherry picked from commit 9b0dd5224be225be1981065e209ea6221ee4948f) Co-authored-by: Jordan Borean --- .../fragments/win_security_policy-rights.yaml | 2 + .../modules/windows/win_security_policy.ps1 | 197 +++++++++++++++++++++ lib/ansible/modules/windows/win_security_policy.py | 121 +++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 changelogs/fragments/win_security_policy-rights.yaml create mode 100644 lib/ansible/modules/windows/win_security_policy.ps1 create mode 100644 lib/ansible/modules/windows/win_security_policy.py diff --git a/changelogs/fragments/win_security_policy-rights.yaml b/changelogs/fragments/win_security_policy-rights.yaml new file mode 100644 index 0000000000..986004c3e4 --- /dev/null +++ b/changelogs/fragments/win_security_policy-rights.yaml @@ -0,0 +1,2 @@ +minor_changes: +- win_security_policy - warn users to use win_user_right instead when editing ``Privilege Rights`` diff --git a/lib/ansible/modules/windows/win_security_policy.ps1 b/lib/ansible/modules/windows/win_security_policy.ps1 new file mode 100644 index 0000000000..45734c15b7 --- /dev/null +++ b/lib/ansible/modules/windows/win_security_policy.ps1 @@ -0,0 +1,197 @@ +#!powershell + +# Copyright: (c) 2017, Jordan Borean +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#Requires -Module Ansible.ModuleUtils.Legacy + +$ErrorActionPreference = 'Stop' + +$params = Parse-Args $args -supports_check_mode $true +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false +$diff_mode = Get-AnsibleParam -obj $Params -name "_ansible_diff" -type "bool" -default $false + +$section = Get-AnsibleParam -obj $params -name "section" -type "str" -failifempty $true +$key = Get-AnsibleParam -obj $params -name "key" -type "str" -failifempty $true +$value = Get-AnsibleParam -obj $params -name "value" -failifempty $true + +$result = @{ + changed = $false + section = $section + key = $key + value = $value +} + +if ($diff_mode) { + $result.diff = @{} +} + +Function Run-SecEdit($arguments) { + $rc = $null + $stdout = $null + $stderr = $null + $log_path = [IO.Path]::GetTempFileName() + $arguments = $arguments + @("/log", $log_path) + + try { + $stdout = &SecEdit.exe $arguments | Out-String + } catch { + $stderr = $_.Exception.Message + } + $log = Get-Content -Path $log_path + Remove-Item -Path $log_path -Force + + $return = @{ + log = ($log -join "`n").Trim() + stdout = $stdout + stderr = $stderr + rc = $LASTEXITCODE + } + + return $return +} + +Function Export-SecEdit() { + $secedit_ini_path = [IO.Path]::GetTempFileName() + # while this will technically make a change to the system in check mode by + # creating a new file, we need these values to be able to do anything + # substantial in check mode + $export_result = Run-SecEdit -arguments @("/export", "/cfg", $secedit_ini_path, "/quiet") + + # check the return code and if the file has been populated, otherwise error out + if (($export_result.rc -ne 0) -or ((Get-Item -Path $secedit_ini_path).Length -eq 0)) { + Remove-Item -Path $secedit_ini_path -Force + $result.rc = $export_result.rc + $result.stdout = $export_result.stdout + $result.stderr = $export_result.stderr + Fail-Json $result "Failed to export secedit.ini file to $($secedit_ini_path)" + } + $secedit_ini = ConvertFrom-Ini -file_path $secedit_ini_path + + return $secedit_ini +} + +Function Import-SecEdit($ini) { + $secedit_ini_path = [IO.Path]::GetTempFileName() + $secedit_db_path = [IO.Path]::GetTempFileName() + Remove-Item -Path $secedit_db_path -Force # needs to be deleted for SecEdit.exe /import to work + + $ini_contents = ConvertTo-Ini -ini $ini + Set-Content -Path $secedit_ini_path -Value $ini_contents + $result.changed = $true + + $import_result = Run-SecEdit -arguments @("/configure", "/db", $secedit_db_path, "/cfg", $secedit_ini_path, "/quiet") + $result.import_log = $import_result.log + Remove-Item -Path $secedit_ini_path -Force + if ($import_result.rc -ne 0) { + $result.rc = $import_result.rc + $result.stdout = $import_result.stdout + $result.stderr = $import_result.stderr + Fail-Json $result "Failed to import secedit.ini file from $($secedit_ini_path)" + } +} + +Function ConvertTo-Ini($ini) { + $content = @() + foreach ($key in $ini.GetEnumerator()) { + $section = $key.Name + $values = $key.Value + + $content += "[$section]" + foreach ($value in $values.GetEnumerator()) { + $value_key = $value.Name + $value_value = $value.Value + + if ($value_value -ne $null) { + $content += "$value_key = $value_value" + } + } + } + + return $content -join "`r`n" +} + +Function ConvertFrom-Ini($file_path) { + $ini = @{} + switch -Regex -File $file_path { + "^\[(.+)\]" { + $section = $matches[1] + $ini.$section = @{} + } + "(.+?)\s*=(.*)" { + $name = $matches[1].Trim() + $value = $matches[2].Trim() + if ($value -match "^\d+$") { + $value = [int]$value + } elseif ($value.StartsWith('"') -and $value.EndsWith('"')) { + $value = $value.Substring(1, $value.Length - 2) + } + + $ini.$section.$name = $value + } + } + + return $ini +} + +if ($section -eq "Privilege Rights") { + Add-Warning -obj $result -message "Using this module to edit rights and privileges is error-prone, use the win_user_right module instead" +} + +$will_change = $false +$secedit_ini = Export-SecEdit +if (-not ($secedit_ini.ContainsKey($section))) { + Fail-Json $result "The section '$section' does not exist in SecEdit.exe output ini" +} + +if ($secedit_ini.$section.ContainsKey($key)) { + $current_value = $secedit_ini.$section.$key + + if ($current_value -cne $value) { + if ($diff_mode) { + $result.diff.prepared = @" +[$section] +-$key = $current_value ++$key = $value +"@ + } + + $secedit_ini.$section.$key = $value + $will_change = $true + } +} elseif ([string]$value -eq "") { + # Value is requested to be removed, and has already been removed, do nothing +} else { + if ($diff_mode) { + $result.diff.prepared = @" +[$section] ++$key = $value +"@ + } + $secedit_ini.$section.$key = $value + $will_change = $true +} + +if ($will_change -eq $true) { + $result.changed = $true + if (-not $check_mode) { + Import-SecEdit -ini $secedit_ini + + # secedit doesn't error out on improper entries, re-export and verify + # the changes occurred + $verification_ini = Export-SecEdit + $new_section_values = $verification_ini.$section + if ($new_section_values.ContainsKey($key)) { + $new_value = $new_section_values.$key + if ($new_value -cne $value) { + Fail-Json $result "Failed to change the value for key '$key' in section '$section', the value is still $new_value" + } + } elseif ([string]$value -eq "") { + # Value was empty, so OK if no longer in the result + } else { + Fail-Json $result "The key '$key' in section '$section' is not a valid key, cannot set this value" + } + } +} + +Exit-Json $result diff --git a/lib/ansible/modules/windows/win_security_policy.py b/lib/ansible/modules/windows/win_security_policy.py new file mode 100644 index 0000000000..6282ac54e2 --- /dev/null +++ b/lib/ansible/modules/windows/win_security_policy.py @@ -0,0 +1,121 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# this is a windows documentation stub, actual code lives in the .ps1 +# file of the same name + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: win_security_policy +version_added: '2.4' +short_description: Change local security policy settings +description: +- Allows you to set the local security policies that are configured by + SecEdit.exe. +notes: +- This module uses the SecEdit.exe tool to configure the values, more details + of the areas and keys that can be configured can be found here + U(https://msdn.microsoft.com/en-us/library/bb742512.aspx). +- If you are in a domain environment these policies may be set by a GPO policy, + this module can temporarily change these values but the GPO will override + it if the value differs. +- You can also run C(SecEdit.exe /export /cfg C:\temp\output.ini) to view the + current policies set on your system. +- When assigning user rights, use the M(win_user_right) module instead. +options: + section: + description: + - The ini section the key exists in. + - If the section does not exist then the module will return an error. + - Example sections to use are 'Account Policies', 'Local Policies', + 'Event Log', 'Restricted Groups', 'System Services', 'Registry' and + 'File System' + - If wanting to edit the C(Privilege Rights) section, use the + M(win_user_right) module instead. + required: yes + key: + description: + - The ini key of the section or policy name to modify. + - The module will return an error if this key is invalid. + required: yes + value: + description: + - The value for the ini key or policy name. + - If the key takes in a boolean value then 0 = False and 1 = True. + required: yes +author: +- Jordan Borean (@jborean93) +''' + +EXAMPLES = r''' +- name: change the guest account name + win_security_policy: + section: System Access + key: NewGuestName + value: Guest Account + +- name: set the maximum password age + win_security_policy: + section: System Access + key: MaximumPasswordAge + value: 15 + +- name: do not store passwords using reversible encryption + win_security_policy: + section: System Access + key: ClearTextPassword + value: 0 + +- name: enable system events + win_security_policy: + section: Event Audit + key: AuditSystemEvents + value: 1 +''' + +RETURN = r''' +rc: + description: The return code after a failure when running SecEdit.exe. + returned: failure with secedit calls + type: int + sample: -1 +stdout: + description: The output of the STDOUT buffer after a failure when running + SecEdit.exe. + returned: failure with secedit calls + type: string + sample: check log for error details +stderr: + description: The output of the STDERR buffer after a failure when running + SecEdit.exe. + returned: failure with secedit calls + type: string + sample: failed to import security policy +import_log: + description: The log of the SecEdit.exe /configure job that configured the + local policies. This is used for debugging purposes on failures. + returned: secedit.exe /import run and change occurred + type: string + sample: Completed 6 percent (0/15) \tProcess Privilege Rights area. +key: + description: The key in the section passed to the module to modify. + returned: success + type: string + sample: NewGuestName +section: + description: The section passed to the module to modify. + returned: success + type: string + sample: System Access +value: + description: The value passed to the module to modify to. + returned: success + type: string + sample: Guest Account +''' -- cgit v1.2.1