keyutils 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/keyutils.gemspec +28 -0
- data/lib/keyutils.rb +11 -0
- data/lib/keyutils/key.rb +572 -0
- data/lib/keyutils/key_perm.rb +49 -0
- data/lib/keyutils/key_types.rb +24 -0
- data/lib/keyutils/keyring.rb +527 -0
- data/lib/keyutils/lib.rb +433 -0
- data/lib/keyutils/version.rb +3 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6213f48145ec2d4753b4aee0d9548ba589695058
|
4
|
+
data.tar.gz: 58ab32a041df3267482516366d46dacfbc982d06
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b64b8ead7edc387485ba92318ec6c278e692c0189254a6f00fd4d0981471c21e13a8a0eceebceba553a07c0ae9726361510e327a69766eb159348c03ff93ca7d
|
7
|
+
data.tar.gz: 495c16a4226d589208cbd1e285330ee27efb4d5ae35833575489c77eb9493de59afff10730def7984701f681cca3697866e7cc8775edba12674910e50829165d
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Rafał Rzepecki
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Keyutils
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/keyutils`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'keyutils'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install keyutils
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/keyutils.
|
36
|
+
|
37
|
+
|
38
|
+
## License
|
39
|
+
|
40
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
41
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "keyutils"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/keyutils.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'keyutils/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "keyutils"
|
8
|
+
spec.version = Keyutils::VERSION
|
9
|
+
spec.authors = ["Rafał Rzepecki"]
|
10
|
+
spec.email = ["divided.mind@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Wrapper for Linux keyutils library}
|
13
|
+
spec.description = %q{FFI-based wrapper for Linux keyutils library, providing idiomatic Ruby access to the kernel keyring.}
|
14
|
+
spec.homepage = "https://github.com/dividedmind/ruby-keyutils"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "ffi", "~> 1.9"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "pry"
|
28
|
+
end
|
data/lib/keyutils.rb
ADDED
data/lib/keyutils/key.rb
ADDED
@@ -0,0 +1,572 @@
|
|
1
|
+
require 'keyutils/key_perm'
|
2
|
+
|
3
|
+
module Keyutils
|
4
|
+
class Key
|
5
|
+
# Numeric identifier of the key this object points to.
|
6
|
+
#
|
7
|
+
# @return [Fixnum] key serial number or one of {Lib::KEY_SPEC}
|
8
|
+
# @see #serial
|
9
|
+
attr_accessor :id
|
10
|
+
alias to_i id
|
11
|
+
alias hash id
|
12
|
+
|
13
|
+
# Get the serial number of this key.
|
14
|
+
#
|
15
|
+
# For ordinary keys, {#serial} == {#id}, and this method always succeeds.
|
16
|
+
#
|
17
|
+
# For special key handles (such as {Keyring::Session}), this method
|
18
|
+
# will resolve the actual serial number of the key it points to.
|
19
|
+
#
|
20
|
+
# Note if this is a special key handle and the key(ring) is not already
|
21
|
+
# instantiated, calling this method will attempt to create it. For this
|
22
|
+
# reason it can fail if memory or quota is exhausted.
|
23
|
+
#
|
24
|
+
# @return [Fixnum] serial number of this key
|
25
|
+
# @raise [Errno::ENOKEY] no matching key was found
|
26
|
+
# @raise [Errno::ENOMEM] insufficient memory to create a key
|
27
|
+
# @raise [Errno::EDQUOT] the key quota for this user would be exceeded by
|
28
|
+
# creating this key or linking it to the keyring
|
29
|
+
# @see #exists?
|
30
|
+
# @see #id
|
31
|
+
def serial
|
32
|
+
return id unless id < 0
|
33
|
+
Lib.keyctl_get_keyring_ID id, true
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check if this key exists in the kernel.
|
37
|
+
#
|
38
|
+
# The key may not exist eg. if it has been removed by another process, or if
|
39
|
+
# this is a special keyring handle (such as {Keyring::Thread}) and the
|
40
|
+
# keyring has not been instantiated yet.
|
41
|
+
#
|
42
|
+
# @return [Boolean] true if the key exists
|
43
|
+
def exists?
|
44
|
+
Lib.keyctl_get_keyring_ID(id, false) && true
|
45
|
+
rescue Errno::EACCES
|
46
|
+
true
|
47
|
+
rescue Errno::ENOKEY
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
# Update the payload of the key if the key type permits it.
|
52
|
+
#
|
53
|
+
# The caller must have write permission on the key to be able to update it.
|
54
|
+
#
|
55
|
+
# +payload+ specifies the data for the new payload; it may be nil
|
56
|
+
# if the key type permits that. The key type may reject the data if it's
|
57
|
+
# in the wrong format or in some other way invalid.
|
58
|
+
#
|
59
|
+
# @param payload [#to_s, nil] data for the new key payload
|
60
|
+
# @return [Key] self
|
61
|
+
# @raise [Errno::ENOKEY] the key is invalid
|
62
|
+
# @raise [Errno::EKEYEXPIRED] the key has expired
|
63
|
+
# @raise [Errno::EKEYREVOKED] the key had been revoked
|
64
|
+
# @raise [Errno::EINVAL] the payload data was invalid
|
65
|
+
# @raise [Errno::ENOMEM] insufficient memory to store the new payload
|
66
|
+
# @raise [Errno::EDQUOT] the key quota for this user would be exceeded by
|
67
|
+
# increasing the size of the key to accommodate the new payload
|
68
|
+
# @raise [Errno::EACCES] the key exists, but is not writable by the
|
69
|
+
# calling process
|
70
|
+
# @raise [Errno::EOPNOTSUPP] the key type does not support the update
|
71
|
+
# operation on its keys
|
72
|
+
def update payload
|
73
|
+
Lib.keyctl_update \
|
74
|
+
id,
|
75
|
+
payload && payload.to_s,
|
76
|
+
payload && payload.to_s.length || 0
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
# Mark the key as being revoked.
|
81
|
+
#
|
82
|
+
# After this operation has been performed on a key, attempts to access it
|
83
|
+
# will meet with error EKEYREVOKED.
|
84
|
+
#
|
85
|
+
# The caller must have write permission on a key to be able revoke it.
|
86
|
+
#
|
87
|
+
# @return [Key] self
|
88
|
+
# @raise [Errno::ENOKEY] the key does not exist
|
89
|
+
# @raise [Errno::EKEYREVOKED] the key has already been revoked
|
90
|
+
# @raise [Errno::EACCES] the key exists, but is not writable by the
|
91
|
+
# calling process
|
92
|
+
# @see #invalidate
|
93
|
+
def revoke
|
94
|
+
Lib.keyctl_revoke id
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
# Change the user and group ownership details of the key.
|
99
|
+
#
|
100
|
+
# A setting of -1 or nil on either +uid+ or +gid+ will cause that setting
|
101
|
+
# to be ignored.
|
102
|
+
#
|
103
|
+
# A process that does not have the _SysAdmin_ capability may not change a
|
104
|
+
# key's UID or set the key's GID to a value that does not match the
|
105
|
+
# process's GID or one of its group list.
|
106
|
+
#
|
107
|
+
# The caller must have _setattr_ permission on a key to be able change its
|
108
|
+
# ownership.
|
109
|
+
#
|
110
|
+
# @param uid [Fixnum, nil] numeric UID of the new owner
|
111
|
+
# @param gid [Fixnum, nil] numeric GID of the new owning group
|
112
|
+
# @return [Key] self
|
113
|
+
# @raise [Errno::ENOKEY] the key does not exist
|
114
|
+
# @raise [Errno::EKEYEXPIRED] the key has expired
|
115
|
+
# @raise [Errno::EKEYREVOKED] the key has been revoked
|
116
|
+
# @raise [Errno::EDQUOT] changing the UID to the one specified would run
|
117
|
+
# that UID out of quota
|
118
|
+
# @raise [Errno::EACCES] the key exists, but does not grant setattr
|
119
|
+
# permission to the calling process; or insufficient process permissions
|
120
|
+
# @see #setperm
|
121
|
+
# @see #uid
|
122
|
+
# @see #gid
|
123
|
+
def chown uid = nil, gid = nil
|
124
|
+
Lib.keyctl_chown id, uid || -1, gid || -1
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
# Change the permissions mask on the key.
|
129
|
+
#
|
130
|
+
# A process that does not have the _SysAdmin_ capability may not change the
|
131
|
+
# permissions mask on a key that doesn't have the same UID as the caller.
|
132
|
+
#
|
133
|
+
# The caller must have _setattr_ permission on a key to be able change its
|
134
|
+
# permissions mask.
|
135
|
+
#
|
136
|
+
# The permissions mask is a bitwise-OR of the following flags:
|
137
|
+
# - +KEY_xxx_VIEW+
|
138
|
+
# Grant permission to view the attributes of a key.
|
139
|
+
#
|
140
|
+
# - +KEY_xxx_READ+
|
141
|
+
# Grant permission to read the payload of a key or to list a keyring.
|
142
|
+
# - +KEY_xxx_WRITE+
|
143
|
+
# Grant permission to modify the payload of a key or to add or remove
|
144
|
+
# links to/from a keyring.
|
145
|
+
# - +KEY_xxx_SEARCH+
|
146
|
+
# Grant permission to find a key or to search a keyring.
|
147
|
+
# - +KEY_xxx_LINK+
|
148
|
+
# Grant permission to make links to a key.
|
149
|
+
# - +KEY_xxx_SETATTR+
|
150
|
+
# Grant permission to change the ownership and permissions attributes of
|
151
|
+
# a key.
|
152
|
+
# - +KEY_xxx_ALL+
|
153
|
+
# Grant all the above.
|
154
|
+
#
|
155
|
+
# The 'xxx' in the above should be replaced by one of:
|
156
|
+
# - +POS+ Grant the permission to a process that possesses the key (has it
|
157
|
+
# attached searchably to one of the process's keyrings).
|
158
|
+
# - +USR+ Grant the permission to a process with the same UID as the key.
|
159
|
+
# - +GRP+ Grant the permission to a process with the same GID as the key,
|
160
|
+
# or with a match for the key's GID amongst that process's Groups list.
|
161
|
+
# - +OTH+ Grant the permission to any other process.
|
162
|
+
#
|
163
|
+
# Examples include: {KEY_POS_VIEW}, {KEY_USR_READ}, {KEY_GRP_SEARCH} and
|
164
|
+
# {KEY_OTH_ALL}.
|
165
|
+
#
|
166
|
+
# User, group and other grants are exclusive: if a process qualifies in
|
167
|
+
# the 'user' category, it will not qualify in the 'groups' category; and
|
168
|
+
# if a process qualifies in either 'user' or 'groups' then it will not
|
169
|
+
# qualify in the 'other' category.
|
170
|
+
#
|
171
|
+
# Possessor grants are cumulative with the grants from the 'user',
|
172
|
+
# 'groups' and 'other' categories.
|
173
|
+
#
|
174
|
+
# @param permissions [Fixnum] permission mask; bitwise OR-ed constants from
|
175
|
+
# {KeyPerm}
|
176
|
+
# @return [Key] self
|
177
|
+
# @raise [Errno::ENOKEY] the key does not exist
|
178
|
+
# @raise [Errno::EKEYEXPIRED] the key has expired
|
179
|
+
# @raise [Errno::EKEYREVOKED] the key has been revoked
|
180
|
+
# @raise [Errno::EACCES] the key exists, but does not grant setattr
|
181
|
+
# permission to the calling process
|
182
|
+
# @see #perm
|
183
|
+
def setperm permissions
|
184
|
+
Lib.keyctl_setperm id, permissions
|
185
|
+
self
|
186
|
+
end
|
187
|
+
|
188
|
+
# @return [Symbol] the key type name
|
189
|
+
def type
|
190
|
+
@type ||= describe[:type]
|
191
|
+
end
|
192
|
+
|
193
|
+
# @return [String] the key description
|
194
|
+
def description
|
195
|
+
@description ||= describe[:desc]
|
196
|
+
end
|
197
|
+
|
198
|
+
# @return [Fixnum] the key UID
|
199
|
+
# @see #gid
|
200
|
+
# @see #describe
|
201
|
+
# @see #chown
|
202
|
+
def uid
|
203
|
+
describe[:uid]
|
204
|
+
end
|
205
|
+
|
206
|
+
# @return [Fixnum] the key GID
|
207
|
+
# @see #uid
|
208
|
+
# @see #describe
|
209
|
+
# @see #chown
|
210
|
+
def gid
|
211
|
+
describe[:gid]
|
212
|
+
end
|
213
|
+
|
214
|
+
# @return [Fixnum] the key permission mask
|
215
|
+
# @see #setperm
|
216
|
+
# @see #describe
|
217
|
+
def perm
|
218
|
+
describe[:perm]
|
219
|
+
end
|
220
|
+
|
221
|
+
# Describe the attributes of the key.
|
222
|
+
#
|
223
|
+
# The caller must have view permission on a key to be able to get
|
224
|
+
# attributes of it.
|
225
|
+
#
|
226
|
+
# Attributes are returned as a hash of the following keys:
|
227
|
+
# - +:type+ [Symbol],
|
228
|
+
# - +:uid+ [Fixnum],
|
229
|
+
# - +:gid+ [Fixnum],
|
230
|
+
# - +:perm+ [Fixnum],
|
231
|
+
# - +:desc+ [String].
|
232
|
+
#
|
233
|
+
# @return [Hash] key attributes
|
234
|
+
# @see #type
|
235
|
+
# @see #uid
|
236
|
+
# @see #gid
|
237
|
+
# @see #perm
|
238
|
+
# @see #description
|
239
|
+
# @raise [Errno::ENOKEY] the key is invalid
|
240
|
+
# @raise [Errno::EKEYEXPIRED] the key has expired
|
241
|
+
# @raise [Errno::EKEYREVOKED] the key had been revoked
|
242
|
+
# @raise [Errno::EACCES] the key is not viewable by the calling process
|
243
|
+
def describe
|
244
|
+
buf = FFI::MemoryPointer.new :char, 64
|
245
|
+
len = Lib.keyctl_describe id, buf, buf.size
|
246
|
+
while len > buf.size
|
247
|
+
buf = FFI::MemoryPointer.new :char, len
|
248
|
+
len = Lib.keyctl_describe id, buf, buf.size
|
249
|
+
end
|
250
|
+
Key.send :parse_describe, buf.read_string(len - 1)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Read the key.
|
254
|
+
#
|
255
|
+
# Reads the payload of a key if the key type supports it.
|
256
|
+
#
|
257
|
+
# The caller must have read permission on a key to be able to read it.
|
258
|
+
#
|
259
|
+
# @return [String] the key payload
|
260
|
+
# @raise [Errno::ENOKEY] the key is invalid
|
261
|
+
# @raise [Errno::EKEYEXPIRED] the key has expired
|
262
|
+
# @raise [Errno::EKEYREVOKED] the key had been revoked
|
263
|
+
# @raise [Errno::EACCES] the key exists, but is not readable by the
|
264
|
+
# calling process
|
265
|
+
# @raise [Errno::EOPNOTSUPP] the key type does not support reading of the
|
266
|
+
# payload data
|
267
|
+
def read
|
268
|
+
buf = FFI::MemoryPointer.new :char, 64
|
269
|
+
len = Lib.keyctl_read id, buf, buf.size
|
270
|
+
while len > buf.size
|
271
|
+
buf = FFI::MemoryPointer.new :char, len
|
272
|
+
len = Lib.keyctl_read id, buf, buf.size
|
273
|
+
end
|
274
|
+
buf.read_string len
|
275
|
+
end
|
276
|
+
|
277
|
+
alias to_s read
|
278
|
+
|
279
|
+
# Instantiate a key
|
280
|
+
#
|
281
|
+
# Instantiate the payload of an uninstantiated key from the data specified.
|
282
|
+
# +payload+ specifies the data for the new payload. +payload+ may be nil
|
283
|
+
# if the key type permits that. The key type may reject the data if it's
|
284
|
+
# in the wrong format or in some other way invalid.
|
285
|
+
#
|
286
|
+
# Only a key for which authority has been assumed may be instantiated or
|
287
|
+
# negatively instantiated, and once instantiated, the authorisation key
|
288
|
+
# will be revoked and the requesting process will be able to resume.
|
289
|
+
#
|
290
|
+
# The +destination+ keyring, if given, is assumed to belong to the initial
|
291
|
+
# requester, and not the instantiating process. Therefore, the special
|
292
|
+
# keyring objects (such as {Keyring::Session}) refer to the requesting
|
293
|
+
# process's keyrings, not the caller's, and the requester's UID, etc. will
|
294
|
+
# be used to access them.
|
295
|
+
#
|
296
|
+
# The +destination+ keyring can be nil if no extra link is desired.
|
297
|
+
#
|
298
|
+
# The requester, not the caller, must have write permission on the
|
299
|
+
# +destination+ for a link to be made there.
|
300
|
+
# @param payload [String, nil] the payload to instantiate the key with
|
301
|
+
# @param destination [Keyring, nil] keyring to link the key to
|
302
|
+
# @return [Key] self
|
303
|
+
# @raise [Errno::ENOKEY] the key or specified keyring is invalid
|
304
|
+
# @raise [Errno::EKEYEXPIRED] the keyring specified has expired
|
305
|
+
# @raise [Errno::EKEYREVOKED] the key or keyring specified had been
|
306
|
+
# revoked, or the authorisation has been revoked
|
307
|
+
# @raise [Errno::EINVAL] the payload data was invalid
|
308
|
+
# @raise [Errno::ENOMEM] insufficient memory to store the new payload or
|
309
|
+
# to expand the destination keyring
|
310
|
+
# @raise [Errno::EDQUOT] the key quota for the key's user would be
|
311
|
+
# exceeded by increasing the size of the key to accommodate the new
|
312
|
+
# payload or the key quota for the keyring's user would be exceeded by
|
313
|
+
# expanding the destination keyring
|
314
|
+
# @raise [Errno::EACCES] the key exists, but is not writable by the
|
315
|
+
# requester
|
316
|
+
# @see #reject
|
317
|
+
# @see #assume_authority
|
318
|
+
def instantiate payload, destination = nil
|
319
|
+
Lib.keyctl_instantiate id,
|
320
|
+
payload && payload.to_s,
|
321
|
+
payload && payload.to_s.length || 0,
|
322
|
+
destination.to_i
|
323
|
+
self
|
324
|
+
end
|
325
|
+
|
326
|
+
# Set the expiration timer on a key
|
327
|
+
#
|
328
|
+
# Sets the expiration timer on a key to +timeout_s+ seconds into the
|
329
|
+
# future. Setting timeout to zero cancels the expiration, assuming the key
|
330
|
+
# hasn't already expired.
|
331
|
+
#
|
332
|
+
# When the key expires, further attempts to access it will be met with
|
333
|
+
# error EKEYEXPIRED.
|
334
|
+
#
|
335
|
+
# The caller must have _setattr_ permission on a key to be able change its
|
336
|
+
# timeout.
|
337
|
+
#
|
338
|
+
# @param timeout_s [Fixnum] expiration timer, in seconds
|
339
|
+
# @return [Key] self
|
340
|
+
# @raise [Errno::ENOKEY] the key does not exist.
|
341
|
+
# @raise [Errno::EKEYEXPIRED] the key has already expired.
|
342
|
+
# @raise [Errno::EKEYREVOKED] the key has been revoked.
|
343
|
+
# @raise [Errno::EACCES] the key does not grant _setattr_ permission to
|
344
|
+
# the calling process.
|
345
|
+
def set_timeout timeout_s
|
346
|
+
Lib.keyctl_set_timeout id, timeout_s
|
347
|
+
self
|
348
|
+
end
|
349
|
+
|
350
|
+
# Assume the authority to instantiate the key.
|
351
|
+
#
|
352
|
+
# Assumes the authority for the calling thread to deal with and
|
353
|
+
# instantiate this uninstantiated key.
|
354
|
+
#
|
355
|
+
# The calling thread must have the appropriate authorisation key resident
|
356
|
+
# in one of its keyrings for this to succeed, and that authority must not
|
357
|
+
# have been revoked.
|
358
|
+
#
|
359
|
+
# The authorising key is allocated by
|
360
|
+
# {http://man7.org/linux/man-pages/man2/request_key.2.html request_key(2)}
|
361
|
+
# when it needs to invoke userspace to generate a key for the requesting
|
362
|
+
# process. This is then attached to one of the keyrings of the userspace
|
363
|
+
# process to which the task of instantiating the key is given:
|
364
|
+
#
|
365
|
+
# requester ⟶ request_key() ⟶ instantiator
|
366
|
+
#
|
367
|
+
# Calling this function modifies the way {.request} works when called
|
368
|
+
# thereafter by the calling (instantiator) thread; once the authority is
|
369
|
+
# assumed, the keyrings of the initial process are added to the search
|
370
|
+
# path, using the initial process's UID, GID, groups and security context.
|
371
|
+
#
|
372
|
+
# If a thread has multiple instantiations to deal with, it may call this
|
373
|
+
# function to change the authorisation key currently in effect.
|
374
|
+
#
|
375
|
+
# @note This is a per-thread setting and not a per-process setting so that
|
376
|
+
# a multithreaded process can be used to instantiate several keys at
|
377
|
+
# once.
|
378
|
+
#
|
379
|
+
# @return (Key) self
|
380
|
+
# @see #instantiate
|
381
|
+
# @see .request
|
382
|
+
# @raise [Errno::ENOKEY] the key is invalid.
|
383
|
+
# @raise [Errno::EKEYREVOKED] the key had been revoked, or the
|
384
|
+
# authorisation has been revoked.
|
385
|
+
# @see .renounce_authority
|
386
|
+
def assume_authority
|
387
|
+
Lib.keyctl_assume_authority id
|
388
|
+
self
|
389
|
+
end
|
390
|
+
|
391
|
+
# Retrieve the key's security context.
|
392
|
+
#
|
393
|
+
# This will be rendered in a form appropriate to the LSM in force---for
|
394
|
+
# instance, with SELinux, it may look like
|
395
|
+
#
|
396
|
+
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
|
397
|
+
#
|
398
|
+
# The caller must have view permission on a key to be able to get its
|
399
|
+
# security context.
|
400
|
+
#
|
401
|
+
# @return [String] key security context
|
402
|
+
# @raise [Errno::ENOKEY] the key is invalid.
|
403
|
+
# @raise [Errno::EKEYEXPIRED] the key has expired.
|
404
|
+
# @raise [Errno::EKEYREVOKED] the key had been revoked.
|
405
|
+
# @raise [Errno::EACCES] the key is not viewable by the calling process.
|
406
|
+
def security
|
407
|
+
return @security if @security
|
408
|
+
|
409
|
+
buf = FFI::MemoryPointer.new :char, 64
|
410
|
+
len = Lib.keyctl_get_security id, buf, buf.size
|
411
|
+
while len > buf.size
|
412
|
+
buf = FFI::MemoryPointer.new :char, len
|
413
|
+
len = Lib.keyctl_get_security id, buf, buf.size
|
414
|
+
end
|
415
|
+
@security = buf.read_string (len - 1)
|
416
|
+
end
|
417
|
+
|
418
|
+
# Negatively instantiate a key
|
419
|
+
#
|
420
|
+
# Marks a key as negatively instantiated and sets the expiration timer on
|
421
|
+
# it. Attempts to access the key will raise the given +error+.
|
422
|
+
#
|
423
|
+
# @note On some kernel versions +error+ setting is not supported. In this
|
424
|
+
# case it will fall back to always raising {Errno::ENOKEY}.
|
425
|
+
#
|
426
|
+
# Only a key for which authority has been assumed may be negatively
|
427
|
+
# instantiated, and once instantiated, the authorisation key
|
428
|
+
# will be revoked and the requesting process will be able to resume.
|
429
|
+
#
|
430
|
+
# The +destination+ keyring, if given, is assumed to belong to the initial
|
431
|
+
# requester, and not the instantiating process. Therefore, the special
|
432
|
+
# keyring objects (such as {Keyring::Session}) refer to the requesting
|
433
|
+
# process's keyrings, not the caller's, and the requester's UID, etc. will
|
434
|
+
# be used to access them.
|
435
|
+
#
|
436
|
+
# The +destination+ keyring can be nil if no extra link is desired.
|
437
|
+
#
|
438
|
+
# The requester, not the caller, must have write permission on the
|
439
|
+
# +destination+ for a link to be made there.
|
440
|
+
#
|
441
|
+
# @param timeout_s [Fixnum] the lifetime of the key in seconds
|
442
|
+
# @param error [::Errno] error to be raised when attempting to
|
443
|
+
# access the key, typically one of {Errno::ENOKEY},
|
444
|
+
# {Errno::EKEYREJECTED}, {Errno::EKEYREVOKED} or {Errno::EKEYEXPIRED}
|
445
|
+
# @param destination [Keyring, nil] keyring to link the key to
|
446
|
+
# @return [Key] self
|
447
|
+
# @raise [Errno::ENOKEY] the key or specified keyring is invalid
|
448
|
+
# @raise [Errno::EKEYEXPIRED] the keyring specified has expired
|
449
|
+
# @raise [Errno::EKEYREVOKED] the key or keyring specified had been
|
450
|
+
# revoked, or the authorisation has been revoked
|
451
|
+
# @raise [Errno::ENOMEM] insufficient memory to expand the destination
|
452
|
+
# keyring
|
453
|
+
# @raise [Errno::EDQUOT] the key quota for the keyring's user would be
|
454
|
+
# exceeded by expanding the destination keyring
|
455
|
+
# @raise [Errno::EACCES] the keyring exists, but is not writable by the
|
456
|
+
# requester
|
457
|
+
# @see #instantiate
|
458
|
+
def reject timeout_s, error = Errno::ENOKEY, destination = nil
|
459
|
+
Lib.keyctl_reject id, timeout_s, error::Errno, keyring.to_i
|
460
|
+
self
|
461
|
+
end
|
462
|
+
|
463
|
+
# Invalidate the key.
|
464
|
+
#
|
465
|
+
# The key is scheduled for immediate removal from all the keyrings that
|
466
|
+
# point to it, after which it will be deleted. The key will be ignored by
|
467
|
+
# all searches once this function is called even if it is not yet fully
|
468
|
+
# dealt with.
|
469
|
+
#
|
470
|
+
# The caller must have _search_ permission on a key to be able to
|
471
|
+
# invalidate it.
|
472
|
+
# @raise [Errno::ENOKEY] the key is invalid.
|
473
|
+
# @raise [Errno::EKEYEXPIRED] the key specified has expired.
|
474
|
+
# @raise [Errno::EKEYREVOKED] the key specified had been revoked.
|
475
|
+
# @raise [Errno::EACCES] the key is not searchable by the calling process.
|
476
|
+
# @return [Key] self
|
477
|
+
# @see #revoke
|
478
|
+
def invalidate
|
479
|
+
Lib.keyctl_invalidate id
|
480
|
+
self
|
481
|
+
end
|
482
|
+
|
483
|
+
# Key equality
|
484
|
+
#
|
485
|
+
# @return [Boolean] whether the objects point to the same key
|
486
|
+
# @see #eql?
|
487
|
+
def == other
|
488
|
+
serial == other.serial
|
489
|
+
end
|
490
|
+
|
491
|
+
# Key handle equality
|
492
|
+
#
|
493
|
+
# Same as {#==}, except it doesn't dereference the special handles such
|
494
|
+
# as {Keyring::Session}. This means {#eql?} can be false even if the
|
495
|
+
# argument points to the same keyring, as long as only one of them is a
|
496
|
+
# special handle.
|
497
|
+
# @see #==
|
498
|
+
# @see #serial
|
499
|
+
# @return [Boolean] whether the key handles are equal
|
500
|
+
def eql? other
|
501
|
+
to_i == other.to_i
|
502
|
+
end
|
503
|
+
|
504
|
+
class << self
|
505
|
+
# Find a key by type and description
|
506
|
+
#
|
507
|
+
# Searches for a key with the given type and exact description, firstly
|
508
|
+
# in the thread, process and session keyrings to which a process is
|
509
|
+
# subscribed and secondly in +/proc/keys+.
|
510
|
+
#
|
511
|
+
# If a key is found, and +destination+ is not nil and specifies a
|
512
|
+
# keyring, then the found key will be linked into it.
|
513
|
+
#
|
514
|
+
# @param type [Symbol] key type
|
515
|
+
# @param description [String] key description
|
516
|
+
# @param destination [Keyring, nil] destination keyring
|
517
|
+
# @return [Key, nil] the key, if found
|
518
|
+
#
|
519
|
+
# @raise [Errno::EKEYEXPIRED] key or keyring have expired.
|
520
|
+
# @raise [Errno::EKEYREVOKED] the key or keyring have been revoked.
|
521
|
+
# @raise [Errno::EACCES] the key is not accessible or keyring exists,
|
522
|
+
# but is not writable by the calling process.
|
523
|
+
# @see Keyring#request
|
524
|
+
# @see Keyring#search
|
525
|
+
def find type, description, destination = nil
|
526
|
+
serial = Lib.find_key_by_type_and_desc \
|
527
|
+
type.to_s,
|
528
|
+
description,
|
529
|
+
destination.to_i
|
530
|
+
new_dispatch serial, type.intern, description
|
531
|
+
rescue Errno::ENOKEY
|
532
|
+
nil
|
533
|
+
end
|
534
|
+
|
535
|
+
# De-assume the currently assumed authority.
|
536
|
+
# @see #assume_authority
|
537
|
+
# @return [void]
|
538
|
+
def renounce_authority
|
539
|
+
Lib.keyctl_assume_authority 0
|
540
|
+
end
|
541
|
+
|
542
|
+
protected
|
543
|
+
protected :new
|
544
|
+
|
545
|
+
def new_dispatch id, type, description
|
546
|
+
if klass = KeyTypes[type]
|
547
|
+
klass.send :new, id, description
|
548
|
+
else
|
549
|
+
new id, type, description
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
def parse_describe description
|
554
|
+
type, uid, gid, perm, desc = description.split ';', 5
|
555
|
+
{
|
556
|
+
type: @type = type.intern,
|
557
|
+
uid: uid.to_i,
|
558
|
+
gid: gid.to_i,
|
559
|
+
perm: perm.to_i(16),
|
560
|
+
desc: @description = desc
|
561
|
+
}
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
private
|
566
|
+
def initialize id, type, description
|
567
|
+
@id = id
|
568
|
+
@type = type
|
569
|
+
@description = description
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|