mirror of
https://github.com/inspec/inspec
synced 2024-11-27 07:00:39 +00:00
1ea83f526c
Signed-off-by: Tom Duffield <tom@chef.io>
218 lines
6.9 KiB
Ruby
218 lines
6.9 KiB
Ruby
# encoding: utf-8
|
|
# copyright: 2015, Vulcano Security GmbH
|
|
# author: Dominik Richter
|
|
# author: Christoph Hartmann
|
|
# license: All rights reserved
|
|
|
|
require 'shellwords'
|
|
|
|
module Inspec::Resources
|
|
module FilePermissionsSelector
|
|
def select_file_perms_style(os)
|
|
if os.unix?
|
|
UnixFilePermissions.new(inspec)
|
|
elsif os.windows?
|
|
WindowsFilePermissions.new(inspec)
|
|
end
|
|
end
|
|
end
|
|
|
|
class FileResource < Inspec.resource(1)
|
|
include FilePermissionsSelector
|
|
include MountParser
|
|
|
|
name 'file'
|
|
desc 'Use the file InSpec audit resource to test all system file types, including files, directories, symbolic links, named pipes, sockets, character devices, block devices, and doors.'
|
|
example "
|
|
describe file('path') do
|
|
it { should exist }
|
|
it { should be_file }
|
|
it { should be_readable }
|
|
it { should be_writable }
|
|
it { should be_executable.by_user('root') }
|
|
it { should be_owned_by 'root' }
|
|
its('mode') { should cmp '0644' }
|
|
end
|
|
"
|
|
|
|
attr_reader :file, :mount_options
|
|
def initialize(path)
|
|
# select permissions style
|
|
@perms_provider = select_file_perms_style(inspec.os)
|
|
@file = inspec.backend.file(path)
|
|
end
|
|
|
|
%w{
|
|
type exist? file? block_device? character_device? socket? directory?
|
|
symlink? pipe? mode mode? owner owned_by? group grouped_into?
|
|
link_path linked_to? mtime size selinux_label immutable?
|
|
product_version file_version version? md5sum sha256sum
|
|
path basename source source_path uid gid
|
|
}.each do |m|
|
|
define_method m.to_sym do |*args|
|
|
file.method(m.to_sym).call(*args)
|
|
end
|
|
end
|
|
|
|
def content
|
|
res = file.content
|
|
return nil if res.nil?
|
|
res.force_encoding('utf-8')
|
|
end
|
|
|
|
def contain(*_)
|
|
raise 'Contain is not supported. Please use standard RSpec matchers.'
|
|
end
|
|
|
|
def readable?(by_usergroup, by_specific_user)
|
|
return false unless exist?
|
|
return skip_resource '`readable?` is not supported on your OS yet.' if @perms_provider.nil?
|
|
|
|
file_permission_granted?('read', by_usergroup, by_specific_user)
|
|
end
|
|
|
|
def writable?(by_usergroup, by_specific_user)
|
|
return false unless exist?
|
|
return skip_resource '`writable?` is not supported on your OS yet.' if @perms_provider.nil?
|
|
|
|
file_permission_granted?('write', by_usergroup, by_specific_user)
|
|
end
|
|
|
|
def executable?(by_usergroup, by_specific_user)
|
|
return false unless exist?
|
|
return skip_resource '`executable?` is not supported on your OS yet.' if @perms_provider.nil?
|
|
|
|
file_permission_granted?('execute', by_usergroup, by_specific_user)
|
|
end
|
|
|
|
def mounted?(expected_options = nil, identical = false)
|
|
mounted = file.mounted
|
|
|
|
# return if no additional parameters have been provided
|
|
return file.mounted? if expected_options.nil?
|
|
|
|
# deprecation warning, this functionality will be removed in future version
|
|
warn "[DEPRECATION] `be_mounted.with and be_mounted.only_with` are deprecated. Please use `mount('#{source_path}')` instead."
|
|
|
|
# we cannot read mount data on non-Linux systems
|
|
return nil if !inspec.os.linux?
|
|
|
|
# parse content if we are on linux
|
|
@mount_options ||= parse_mount_options(mounted.stdout, true)
|
|
|
|
if identical
|
|
# check if the options should be identical
|
|
@mount_options == expected_options
|
|
else
|
|
# otherwise compare the selected values
|
|
@mount_options.contains(expected_options)
|
|
end
|
|
end
|
|
|
|
def suid
|
|
(mode & 04000) > 0
|
|
end
|
|
|
|
def sgid
|
|
(mode & 02000) > 0
|
|
end
|
|
|
|
def sticky
|
|
(mode & 01000) > 0
|
|
end
|
|
|
|
def to_s
|
|
"File #{source_path}"
|
|
end
|
|
|
|
private
|
|
|
|
def file_permission_granted?(access_type, by_usergroup, by_specific_user)
|
|
raise '`file_permission_granted?` is not supported on your OS' if @perms_provider.nil?
|
|
if by_specific_user.nil? || by_specific_user.empty?
|
|
@perms_provider.check_file_permission_by_mask(file, access_type, by_usergroup, by_specific_user)
|
|
else
|
|
@perms_provider.check_file_permission_by_user(access_type, by_specific_user, source_path)
|
|
end
|
|
end
|
|
end
|
|
|
|
class FilePermissions
|
|
attr_reader :inspec
|
|
def initialize(inspec)
|
|
@inspec = inspec
|
|
end
|
|
end
|
|
|
|
class UnixFilePermissions < FilePermissions
|
|
def permission_flag(access_type)
|
|
case access_type
|
|
when 'read'
|
|
'r'
|
|
when 'write'
|
|
'w'
|
|
when 'execute'
|
|
'x'
|
|
else
|
|
raise 'Invalid access_type provided'
|
|
end
|
|
end
|
|
|
|
def usergroup_for(usergroup, specific_user)
|
|
if usergroup == 'others'
|
|
'other'
|
|
elsif (usergroup.nil? || usergroup.empty?) && specific_user.nil?
|
|
'all'
|
|
else
|
|
usergroup
|
|
end
|
|
end
|
|
|
|
def check_file_permission_by_mask(file, access_type, usergroup, specific_user)
|
|
usergroup = usergroup_for(usergroup, specific_user)
|
|
flag = permission_flag(access_type)
|
|
mask = file.unix_mode_mask(usergroup, flag)
|
|
raise 'Invalid usergroup/owner provided' if mask.nil?
|
|
(file.mode & mask) != 0
|
|
end
|
|
|
|
def check_file_permission_by_user(access_type, user, path)
|
|
flag = permission_flag(access_type)
|
|
if inspec.os.linux?
|
|
perm_cmd = "su -s /bin/sh -c \"test -#{flag} #{path}\" #{user}"
|
|
elsif inspec.os.bsd? || inspec.os.solaris?
|
|
perm_cmd = "sudo -u #{user} test -#{flag} #{path}"
|
|
elsif inspec.os.aix?
|
|
perm_cmd = "su #{user} -c test -#{flag} #{path}"
|
|
elsif inspec.os.hpux?
|
|
perm_cmd = "su #{user} -c \"test -#{flag} #{path}\""
|
|
else
|
|
return skip_resource 'The `file` resource does not support `by_user` on your OS.'
|
|
end
|
|
|
|
cmd = inspec.command(perm_cmd)
|
|
cmd.exit_status == 0 ? true : false
|
|
end
|
|
end
|
|
|
|
class WindowsFilePermissions < FilePermissions
|
|
def check_file_permission_by_mask(_file, _access_type, _usergroup, _specific_user)
|
|
raise '`check_file_permission_by_mask` is not supported on Windows'
|
|
end
|
|
|
|
def check_file_permission_by_user(access_type, user, path)
|
|
access_rule = case access_type
|
|
when 'read'
|
|
'@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'Read\', \'ListDirectory\')'
|
|
when 'write'
|
|
'@(\'FullControl\', \'Modify\', \'Write\')'
|
|
when 'execute'
|
|
'@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'ExecuteFile\')'
|
|
else
|
|
raise 'Invalid access_type provided'
|
|
end
|
|
cmd = inspec.command("@(@((Get-Acl '#{path}').access | Where-Object {$_.AccessControlType -eq 'Allow' -and $_.IdentityReference -eq '#{user}' }) | Where-Object {($_.FileSystemRights.ToString().Split(',') | % {$_.trim()} | ? {#{access_rule} -contains $_}) -ne $null}) | measure | % { $_.Count }")
|
|
cmd.stdout.chomp == '0' ? false : true
|
|
end
|
|
end
|
|
end
|