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.
@@ -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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.7
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in keyutils.gemspec
4
+ gemspec
@@ -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.
@@ -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
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -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
@@ -0,0 +1,11 @@
1
+ require "keyutils/version"
2
+ require "keyutils/lib"
3
+ require "keyutils/key"
4
+ require "keyutils/keyring"
5
+ require "keyutils/key_perm"
6
+
7
+ module Keyutils
8
+ LIBRARY_VERSION = Lib.keyutils_version_string[/[\d.]+/].freeze
9
+
10
+ include KeyPerm
11
+ end
@@ -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