inspec/lib/resources/security_policy.rb
Lynn Frank 2857d07151 Fixes resource examples
The opening and closing mechanic varied between all the various
resources. This changes them all to use a HEREDOC with a tilde
to remove leading whitespace. This removes the need for the
special method to trim the `#print_example` method from shell.

Signed-off-by: Franklin Webber <franklin.webber@gmail.com>
2019-03-19 11:25:41 -05:00

180 lines
5.5 KiB
Ruby

# encoding: utf-8
#
# Security Configuration and Analysis
#
# Export local security policy:
# secedit /export /cfg secpol.cfg
#
# @link http://www.microsoft.com/en-us/download/details.aspx?id=25250
#
# In Windows, some security options are managed differently that the local GPO
# All local GPO parameters can be examined via Registry, but not all security
# parameters. Therefore we need a combination of Registry and secedit output
require 'hashie'
module Inspec::Resources
# known and supported MS privilege rights
# @see https://technet.microsoft.com/en-us/library/dd277311.aspx
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
MS_PRIVILEGES_RIGHTS = [
'SeNetworkLogonRight',
'SeBackupPrivilege',
'SeChangeNotifyPrivilege',
'SeSystemtimePrivilege',
'SeCreatePagefilePrivilege',
'SeDebugPrivilege',
'SeRemoteShutdownPrivilege',
'SeAuditPrivilege',
'SeIncreaseQuotaPrivilege',
'SeIncreaseBasePriorityPrivilege',
'SeLoadDriverPrivilege',
'SeBatchLogonRight',
'SeServiceLogonRight',
'SeInteractiveLogonRight',
'SeSecurityPrivilege',
'SeSystemEnvironmentPrivilege',
'SeProfileSingleProcessPrivilege',
'SeSystemProfilePrivilege',
'SeAssignPrimaryTokenPrivilege',
'SeRestorePrivilege',
'SeShutdownPrivilege',
'SeTakeOwnershipPrivilege',
'SeUndockPrivilege',
'SeManageVolumePrivilege',
'SeRemoteInteractiveLogonRight',
'SeImpersonatePrivilege',
'SeCreateGlobalPrivilege',
'SeIncreaseWorking',
'SeTimeZonePrivilege',
'SeCreateSymbolicLinkPrivilege',
'SeDenyNetworkLogonRight', # Deny access to this computer from the network
'SeDenyInteractiveLogonRight', # Deny logon locally
'SeDenyBatchLogonRight', # Deny logon as a batch job
'SeDenyServiceLogonRight', # Deny logon as a service
'SeTcbPrivilege',
'SeMachineAccountPrivilege',
'SeCreateTokenPrivilege',
'SeCreatePermanentPrivilege',
'SeEnableDelegationPrivilege',
'SeLockMemoryPrivilege',
'SeSyncAgentPrivilege',
'SeUnsolicitedInputPrivilege',
'SeTrustedCredManAccessPrivilege',
'SeRelabelPrivilege', # the privilege to change a Windows integrity label (new to Windows Vista)
'SeDenyRemoteInteractiveLogonRight', # Deny logon through Terminal Services
].freeze
class SecurityPolicy < Inspec.resource(1)
name 'security_policy'
supports platform: 'windows'
desc 'Use the security_policy InSpec audit resource to test security policies on the Microsoft Windows platform.'
example <<~EXAMPLE
describe security_policy do
its('SeNetworkLogonRight') { should include 'S-1-5-11' }
end
describe security_policy(translate_sid: true) do
its('SeNetworkLogonRight') { should include 'NT AUTHORITY\\Authenticated Users' }
end
EXAMPLE
def initialize(opts = {})
@translate_sid = opts[:translate_sid] || false
end
def content
read_content
end
def params(*opts)
opts.inject(read_params) do |res, nxt|
res.respond_to?(:key) ? res[nxt] : nil
end
end
def method_missing(name)
params = read_params
return nil if params.nil?
# deep search for hash key
params.extend Hashie::Extensions::DeepFind
res = params.deep_find(name.to_s)
# return an empty array if configuration does not include rights configuration
return [] if res.nil? && MS_PRIVILEGES_RIGHTS.include?(name.to_s)
res
end
def to_s
'Security Policy'
end
private
def read_content
return @content if defined?(@content)
# using process pid to prevent any race conditions with multiple runners
export_file = "win_secpol-#{Process.pid}.cfg"
# export the security policy
cmd = inspec.command("secedit /export /cfg #{export_file}")
return nil if cmd.exit_status.to_i != 0
# store file content
cmd = inspec.command("Get-Content #{export_file}")
return skip_resource "Can't read security policy" if cmd.exit_status.to_i != 0
@content = cmd.stdout
ensure
# delete temp file
inspec.command("Remove-Item #{export_file}").exit_status.to_i
end
def read_params
return @params if defined?(@params)
return @params = {} if read_content.nil?
conf = SimpleConfig.new(
@content,
assignment_regex: /^\s*(.*)=\s*(\S*)\s*$/,
)
@params = convert_hash(conf.params)
end
# extracts the values, this methods detects:
# numbers and SIDs and optimizes them for further usage
def extract_value(val)
if val =~ /^\d+$/
val.to_i
# special handling for SID array
elsif val =~ /[,]{0,1}\*\S/
if @translate_sid
val.split(',').map { |v|
object_name = inspec.command("(New-Object System.Security.Principal.SecurityIdentifier(\"#{v.sub('*S', 'S')}\")).Translate( [System.Security.Principal.NTAccount]).Value").stdout.to_s.strip
object_name.empty? || object_name.nil? ? v.sub('*S', 'S') : object_name
}
else
val.split(',').map { |v|
v.sub('*S', 'S')
}
end
# special handling for string values with "
elsif !(m = /^\"(.*)\"$/.match(val)).nil?
m[1]
else
val
end
end
def convert_hash(hash)
new_hash = {}
hash.each do |k, v|
v.is_a?(Hash) ? value = convert_hash(v) : value = extract_value(v)
new_hash[k.strip] = value
end
new_hash
end
end
end