Class: Seccomp::Notify::Policy

Inherits:
Object
  • Object
show all
Defined in:
lib/seccomp/notify/policy.rb

Constant Summary collapse

BOOTSTRAP_SYSCALLS =
%i[fcntl].freeze
ALLOWED_FLAGS =
Constants::SECCOMP_FILTER_FLAG_LOG |
Constants::SECCOMP_FILTER_FLAG_SPEC_ALLOW |
Constants::SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV
DEFAULTS =
{
  allow: Constants::SECCOMP_RET_ALLOW,
  errno: Constants::SECCOMP_RET_ERRNO | Errno::EPERM::Errno,
  kill: Constants::SECCOMP_RET_KILL_PROCESS
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(default: :allow, deny_io_uring: true, flags: 0, &block) ⇒ Policy

Returns a new instance of Policy.

Raises:

  • (ArgumentError)


18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/seccomp/notify/policy.rb', line 18

def initialize(default: :allow, deny_io_uring: true, flags: 0, &block)
  @default_action = DEFAULTS.fetch(default) { raise InvalidPolicyError, "invalid default action: #{default.inspect}" }
  raise ArgumentError, "flags must be an integer" unless flags.is_a?(Integer)

  @flags = flags
  @notifications = []
  @conditional_notifications = []
  @denials = {}
  deny_io_uring! if deny_io_uring
  instance_eval(&block) if block
  validate!
end

Instance Attribute Details

#default_actionObject (readonly)

Returns the value of attribute default_action.



16
17
18
# File 'lib/seccomp/notify/policy.rb', line 16

def default_action
  @default_action
end

Instance Method Details

#conditional_notifications(arch) ⇒ Object



81
82
83
84
85
# File 'lib/seccomp/notify/policy.rb', line 81

def conditional_notifications(arch)
  @conditional_notifications.filter_map do |name, argument, mask|
    [Syscalls.number(name, arch), argument, mask] unless notified?(name) || @denials.key?(name)
  end
end

#decisions(arch) ⇒ Object



66
67
68
69
70
71
72
73
74
75
# File 'lib/seccomp/notify/policy.rb', line 66

def decisions(arch)
  validate!
  notified = @notifications.to_h { |name| [Syscalls.number(name, arch), Constants::SECCOMP_RET_USER_NOTIF] }
  denied = @denials.to_h { |name, errno| [Syscalls.number(name, arch), Constants::SECCOMP_RET_ERRNO | errno] }
  notified.merge(denied).tap do |decisions|
    BOOTSTRAP_SYSCALLS.each do |name|
      decisions[Syscalls.number(name, arch)] = Constants::SECCOMP_RET_ALLOW unless @default_action == Constants::SECCOMP_RET_ALLOW
    end
  end
end

#deny(*names, errno: Errno::EPERM) ⇒ void

This method returns an undefined value.

Rejects syscalls in the kernel without notifying the supervisor.

Parameters:

  • names (Array<Symbol>)
  • errno (Class, Integer) (defaults to: Errno::EPERM)

    an Errno class or numeric errno



50
51
52
53
# File 'lib/seccomp/notify/policy.rb', line 50

def deny(*names, errno: Errno::EPERM)
  number = Constants.errno_number(errno)
  names.each { |name| @denials[name.to_sym] = number }
end

#deny_io_uring!void

This method returns an undefined value.

Denies io_uring setup so submitted I/O cannot bypass seccomp.



58
59
60
# File 'lib/seccomp/notify/policy.rb', line 58

def deny_io_uring!
  deny(:io_uring_setup, errno: Errno::ENOSYS)
end

#flagsObject



62
63
64
# File 'lib/seccomp/notify/policy.rb', line 62

def flags
  @flags
end

#notified?(name) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/seccomp/notify/policy.rb', line 77

def notified?(name)
  @notifications.include?(name.to_sym)
end

#notify(*names) ⇒ void

This method returns an undefined value.

Marks syscalls for supervisor notification.

Parameters:

  • names (Array<Symbol>)


34
35
36
# File 'lib/seccomp/notify/policy.rb', line 34

def notify(*names)
  @notifications.concat(names.map(&:to_sym))
end

#notify_if(name, argument:, mask:) ⇒ Object

Notifies only when one of the low 32 bits in a syscall argument is set.

Raises:

  • (ArgumentError)


39
40
41
42
43
44
# File 'lib/seccomp/notify/policy.rb', line 39

def notify_if(name, argument:, mask:)
  raise ArgumentError, "argument must be between 0 and 5" unless (0..5).cover?(argument)
  raise ArgumentError, "mask must be between 1 and 0xffffffff" unless mask.is_a?(Integer) && (1..0xffff_ffff).cover?(mask)

  @conditional_notifications << [name.to_sym, argument, mask]
end