Class: Seccomp::Notify::Request

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id:, tid:, flags:, nr:, arch:, instruction_pointer:, args:, listener:, sizes:, memory_cache:, memory_cache_mutex:, features:) ⇒ Request

Returns a new instance of Request.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/seccomp/notify/request.rb', line 19

def initialize(id:, tid:, flags:, nr:, arch:, instruction_pointer:, args:, listener:, sizes:, memory_cache:, memory_cache_mutex:, features:)
  @id = id
  @tid = tid
  @flags = flags
  @nr = nr
  @arch = arch
  @instruction_pointer = instruction_pointer
  @args = args.freeze
  @listener = listener
  @sizes = sizes
  @features = features
  @memory = TargetMemory.new(listener, self, memory_cache, memory_cache_mutex)
  @responded = false
  @pointer_read = false
end

Instance Attribute Details

#archObject (readonly)

Returns the value of attribute arch.



35
36
37
# File 'lib/seccomp/notify/request.rb', line 35

def arch
  @arch
end

#argsObject (readonly)

Returns the value of attribute args.



6
7
8
# File 'lib/seccomp/notify/request.rb', line 6

def args
  @args
end

#flagsObject (readonly)

Returns the value of attribute flags.



6
7
8
# File 'lib/seccomp/notify/request.rb', line 6

def flags
  @flags
end

#idObject (readonly)

Returns the value of attribute id.



6
7
8
# File 'lib/seccomp/notify/request.rb', line 6

def id
  @id
end

#instruction_pointerObject (readonly)

Returns the value of attribute instruction_pointer.



6
7
8
# File 'lib/seccomp/notify/request.rb', line 6

def instruction_pointer
  @instruction_pointer
end

#nrObject (readonly)

Returns the value of attribute nr.



6
7
8
# File 'lib/seccomp/notify/request.rb', line 6

def nr
  @nr
end

#tidObject (readonly) Also known as: pid

Returns the value of attribute tid.



6
7
8
# File 'lib/seccomp/notify/request.rb', line 6

def tid
  @tid
end

Class Method Details

.from_binary(binary, listener:, sizes:, memory_cache:, memory_cache_mutex:, features:) ⇒ Object



9
10
11
12
13
14
15
16
17
# File 'lib/seccomp/notify/request.rb', line 9

def self.from_binary(binary, listener:, sizes:, memory_cache:, memory_cache_mutex:, features:)
  id, tid, flags, nr, audit_arch, ip, *args = binary.unpack(Structs::NOTIF_FORMAT)
  arch = case audit_arch
  when Constants::AUDIT_ARCH_X86_64 then :x86_64
  when Constants::AUDIT_ARCH_AARCH64 then :aarch64
  else raise NotSupportedError, "unsupported audit architecture 0x#{audit_arch.to_s(16)}"
  end
  new(id:, tid:, flags:, nr:, arch:, instruction_pointer: ip, args:, listener:, sizes:, memory_cache:, memory_cache_mutex:, features:)
end

Instance Method Details

#add_fd!(io, flags: 0, newfd: 0, newfd_flags: 0) ⇒ Integer

Injects an open file descriptor into the target.

Returns:

  • (Integer)

    the target file descriptor number

Raises:



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/seccomp/notify/request.rb', line 103

def add_fd!(io, flags: 0, newfd: 0, newfd_flags: 0)
  raise NotSupportedError, "SECCOMP_IOCTL_NOTIF_ADDFD is unavailable" unless @features[:addfd]
  ensure_unresponded!
  unless [flags, newfd, newfd_flags].all? { |value| value.is_a?(Integer) } && (0..0xffff_ffff).cover?(newfd)
    raise ArgumentError, "flags and fd values must be unsigned integers"
  end
  unless (flags & ~Constants::SECCOMP_ADDFD_FLAG_SETFD).zero?
    raise ArgumentError, "flags may only contain SECCOMP_ADDFD_FLAG_SETFD"
  end
  unless (newfd_flags & ~Constants::O_CLOEXEC).zero?
    raise ArgumentError, "newfd_flags may only contain O_CLOEXEC"
  end
  if (flags & Constants::SECCOMP_ADDFD_FLAG_SETFD).zero? && !newfd.zero?
    raise ArgumentError, "newfd requires SECCOMP_ADDFD_FLAG_SETFD"
  end

  addfd_flags = flags
  addfd_flags |= Constants::SECCOMP_ADDFD_FLAG_SEND if @features[:addfd_send]
  buffer = [@id, addfd_flags, io.fileno, newfd, newfd_flags].pack(Structs::ADDFD_FORMAT)
  injected = Ioctl.call(@listener, Ioctl::NOTIF_ADDFD, buffer)
  if @features[:addfd_send]
    @responded = true
  else
    allow!(injected)
  end
  injected
end

#allow!(value = 0) ⇒ void

This method returns an undefined value.

Emulates a successful syscall return.



81
82
83
# File 'lib/seccomp/notify/request.rb', line 81

def allow!(value = 0)
  respond!(value:, error: 0, flags: 0)
end

#closeObject



141
142
143
# File 'lib/seccomp/notify/request.rb', line 141

def close
  @memory.close
end

#continue!(unsafe: false) ⇒ void

This method returns an undefined value.

Lets the kernel execute the original syscall.

Parameters:

  • unsafe (Boolean) (defaults to: false)

    acknowledges pointer-read TOCTOU risk

Raises:



95
96
97
98
99
# File 'lib/seccomp/notify/request.rb', line 95

def continue!(unsafe: false)
  raise NotSupportedError, "SECCOMP_USER_NOTIF_FLAG_CONTINUE is unavailable" unless @features[:continue]
  warn("seccomp-notify: continuing after reading target memory is subject to TOCTOU; pass unsafe: true to acknowledge") if @pointer_read && !unsafe
  respond!(value: 0, error: 0, flags: Constants::SECCOMP_USER_NOTIF_FLAG_CONTINUE)
end

#error!(error = Errno::EPERM) ⇒ void

This method returns an undefined value.

Emulates a failed syscall return.



87
88
89
90
# File 'lib/seccomp/notify/request.rb', line 87

def error!(error = Errno::EPERM)
  errno = Constants.errno_number(error)
  respond!(value: 0, error: -errno, flags: 0)
end

#kill!(signal = "KILL") ⇒ Object



131
132
133
134
135
# File 'lib/seccomp/notify/request.rb', line 131

def kill!(signal = "KILL")
  ensure_unresponded!
  Process.kill(signal, @tid)
  @responded = true
end

#mark_pointer_read!Object



137
138
139
# File 'lib/seccomp/notify/request.rb', line 137

def mark_pointer_read!
  @pointer_read = true
end

#read(address, length) ⇒ String

Reads bytes from target memory and revalidates the notification afterward.

Returns:

  • (String)


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

def read(address, length)
  @memory.read(address, length)
end

#read_sockaddr(address, length) ⇒ Addrinfo

Decodes an AF_INET, AF_INET6, or AF_UNIX socket address.

Returns:

  • (Addrinfo)


75
76
77
# File 'lib/seccomp/notify/request.rb', line 75

def read_sockaddr(address, length)
  @memory.read_sockaddr(address, length)
end

#read_string(address, max: 4096) ⇒ String Also known as: read_cstring

Reads a NUL-terminated string from target memory.

Returns:

  • (String)


68
69
70
# File 'lib/seccomp/notify/request.rb', line 68

def read_string(address, max: 4096)
  @memory.read_string(address, max:)
end

#responded?Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/seccomp/notify/request.rb', line 41

def responded?
  @responded
end

#syscallObject



37
38
39
# File 'lib/seccomp/notify/request.rb', line 37

def syscall
  Syscalls.name(@nr, @arch)
end

#valid?Boolean

Returns:

  • (Boolean)


45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/seccomp/notify/request.rb', line 45

def valid?
  buffer = [@id].pack("Q<")
  validate_id(Ioctl::NOTIF_ID_VALID, buffer)
  true
rescue Errno::EINVAL
  begin
    validate_id(Ioctl::NOTIF_ID_VALID_OLD, buffer)
    true
  rescue Errno::ENOENT
    false
  end
rescue Errno::ENOENT
  false
end