mirror of
https://github.com/inspec/inspec
synced 2024-11-27 07:00:39 +00:00
a6582bea9b
* Remove any "All Rights Reserved" references InSpec is licensed and released under the Apache 2.0 license. This change removes all reference to legacy code files that still had any Copyright or License lines referring to "All Rights Reserved". Signed-off-by: Adam Leff <adam@leff.co> * fix functional tests Signed-off-by: Christoph Hartmann <chris@lollyrock.com>
217 lines
6.8 KiB
Ruby
217 lines
6.8 KiB
Ruby
# encoding: utf-8
|
|
# copyright: 2015, Vulcano Security GmbH
|
|
# author: Dominik Richter
|
|
# author: Christoph Hartmann
|
|
|
|
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 LinuxMountParser
|
|
|
|
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
|