Class: Keyutils::Keyring

Inherits:
Key
  • Object
show all
Includes:
Enumerable
Defined in:
lib/keyutils/keyring.rb,
lib/keyutils/keyring.rb

Constant Summary collapse

Thread =

thread-specific keyring

Keyring.new Lib::KEY_SPEC[:THREAD_KEYRING], nil
Process =

process-specific keyring

Keyring.new Lib::KEY_SPEC[:PROCESS_KEYRING], nil
Session =

session-specific keyring

See Also:

Keyring.new(Lib::KEY_SPEC[:SESSION_KEYRING], nil).
extend SessionKeyring
User =

UID-specific keyring

Keyring.new Lib::KEY_SPEC[:USER_KEYRING], nil
UserSession =

UID-session keyring

Keyring.new Lib::KEY_SPEC[:USER_SESSION_KEYRING], nil
Group =

GID-specific keyring

Keyring.new Lib::KEY_SPEC[:GROUP_KEYRING], nil

Instance Attribute Summary

Attributes inherited from Key

#id

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Key

#==, #assume_authority, #chown, #describe, #description, #eql?, #exists?, find, #gid, #instantiate, #invalidate, #perm, #reject, renounce_authority, #revoke, #security, #serial, #set_timeout, #setperm, #type, #uid, #update

Class Method Details

.defaultKeyring?

Get the implicit destination keyring

Gets the default destination for implicit key requests for the current thread.

Keys acquired by implicit key requests, such as might be performed by open() on an AFS or NFS filesystem, will be linked by default to that keyring.

Only one of the special keyrings can be returned:

If nil is returned, the default behaviour is selected, which is to use the thread-specific keyring if there is one, otherwise the process-specific keyring if there is one, otherwise the session keyring if there is one, otherwise the UID-specific session keyring.

Returns:

  • (Keyring, nil)

    the default keyring

See Also:



394
395
396
397
# File 'lib/keyutils/keyring.rb', line 394

def default
  [nil, Thread, Process, Session, User, UserSession, Group]\
      [Lib.keyctl_set_reqkey_keyring -1]
end

.default=(keyring = nil) ⇒ Keyring?

Set the implicit destination keyring

Sets the default destination for implicit key requests for the current thread.

After this operation has been issued, keys acquired by implicit key requests, such as might be performed by open() on an AFS or NFS filesystem, will be linked by default to the specified keyring by this function.

Only one of the special keyrings can be set as default:

If keyring is nil, the default behaviour is selected, which is to use the thread-specific keyring if there is one, otherwise the process-specific keyring if there is one, otherwise the session keyring if there is one, otherwise the UID-specific session keyring.

Parameters:

  • keyring (Keyring, nil) (defaults to: nil)

    the new default keyring

Returns:

Raises:

  • (ArgumentError)

See Also:



363
364
365
366
367
368
# File 'lib/keyutils/keyring.rb', line 363

def default= keyring = nil
  id = keyring.to_i
  raise ArgumentError, 'only special keyrings can be default' \
      if id > 0
  Lib.keyctl_set_reqkey_keyring -id
end

.persistent(uid = nil, destination = nil) ⇒ Keyring

Note:

Not every system supports persistent keyrings.

Get the persistent keyring for a user.

Unlike the session and user keyrings, this keyring will persist once all login sessions have been deleted and can thus be used to carry authentication tokens for processes that run without user interaction, such as programs started by cron.

The persistent keyring will be created by the kernel if it does not yet exist. Each time this function is called, the persistent keyring will have its expiration timeout reset to the value in /proc/sys/kernel/keys/persistent_keyring_expiry (by default three days). Should the timeout be reached, the persistent keyring will be removed and everything it pins can then be garbage collected.

If UID is nil then the calling process’s real user ID will be used. If UID is not nil then Errno::EPERM will be raised if the user ID requested does not match either the caller’s real or effective user IDs or if the calling process does not have SetUid capability.

If successful, a link to the persistent keyring will be added into destination.

Parameters:

  • uid (Fixnum, nil) (defaults to: nil)

    UID of the user for which the persistent keyring is requested

  • destination (Keyring, nil) (defaults to: nil)

    keyring to add the persistent keyring to

Returns:

  • (Keyring)

    the persistent keyring

Raises:

  • (Errno::EPERM)

    not permitted to access the persistent keyring for the requested UID.

  • (Errno::ENOMEM)

    insufficient memory to create the persistent keyring or to extend destination.

  • (Errno::ENOKEY)

    destination does not exist.

  • (Errno::EKEYEXPIRED)

    destination has expired.

  • (Errno::EKEYREVOKED)

    destination has been revoked.

  • (Errno::EDQUOT)

    the user does not have sufficient quota to extend destination.

  • (Errno::EACCES)

    destination exists, but does not grant write permission to the calling process.

  • (Errno::EOPNOTSUPP)

    persistent keyrings are not supported by this system



441
442
443
444
445
446
447
# File 'lib/keyutils/keyring.rb', line 441

def persistent uid = nil, destination = nil
  Keyring.send \
      :new,
      Lib.keyctl_get_persistent(uid || -1, destination.to_i),
      nil,
      nil
end

Instance Method Details

#[](description) ⇒ Key?

Get a member “user” key

Searches the members of the keyring for a :user type key with the given description

Parameters:

  • description (String)

    description of the key to find

Returns:

  • (Key, nil)

    the key, if found



307
308
309
310
311
# File 'lib/keyutils/keyring.rb', line 307

def [] description
  find do |key|
    key.type == :user && key.description == description rescue false
  end
end

#[]=(description, payload) ⇒ String

Set a member “user” key

Updates or creates a member key of type “user” and given description

Parameters:

  • description (String)

    the description of the key to update or create

  • payload (String)

    the new key payload

Returns:

  • (String)

    payload



321
322
323
# File 'lib/keyutils/keyring.rb', line 321

def []= description, payload
  add :user, description, payload
end

#add(type, description, payload) ⇒ Key

Add a key to the kernel’s key management facility.

Asks the kernel to create or update a key of the given type and description, instantiate it with the payload, and to attach it to this keyring.

The key type may reject the data if it’s in the wrong format or in some other way invalid.

Keys of the user-defined key type (“user”) may contain a blob of arbitrary data, and the description may be any valid string, though it is preferred that the description be prefixed with a string representing the service to which the key is of interest and a colon (for instance “afs:mykey”).

If this keyring already contains a key that matches the specified type and description then, if the key type supports it, that key will be updated rather than a new key being created; if not, a new key will be created and it will displace the link to the extant key from the keyring.

Parameters:

  • type (Symbol)

    key type

  • description (String)

    key description

  • payload (#to_s, nil)

    payload

Returns:

  • (Key)

    the key created or updated

Raises:

  • (Errno::ENOKEY)

    the keyring doesn’t exist

  • (Errno::EKEYEXPIRED)

    the keyring has expired

  • (Errno::EKEYREVOKED)

    the keyring has been revoked

  • (Errno::EINVAL)

    the payload data was invalid

  • (Errno::ENODEV)

    the key type was invalid

  • (Errno::ENOMEM)

    insufficient memory to create a key

  • (Errno::EDQUOT)

    the key quota for this user would be exceeded by creating this key or linking it to the keyring

  • (Errno::EACCES)

    the keyring wasn’t available for modification by the user



233
234
235
236
237
238
239
240
241
# File 'lib/keyutils/keyring.rb', line 233

def add type, description, payload
  serial = Lib.add_key \
      type.to_s,
      description,
      payload && payload.to_s,
      payload && payload.to_s.length || 0,
      to_i
  Key.send :new_dispatch, serial, type.intern, description
end

#clearKeyring

Clear the contents of the keyring.

The caller must have write permission on a keyring to be able clear it.

Returns:

Raises:

  • (Errno::ENOKEY)

    the keyring is invalid

  • (Errno::EKEYEXPIRED)

    the keyring has expired

  • (Errno::EKEYREVOKED)

    the keyring had been revoked

  • (Errno::EACCES)

    the keyring is not writable by the calling process



16
17
18
19
# File 'lib/keyutils/keyring.rb', line 16

def clear
  Lib.keyctl_clear id
  self
end

#each {|key| ... } ⇒ Enumerator, Keyring

Iterate over linked keys

Yield Parameters:

  • key (Key)

    member of the keyring

Returns:

  • (Enumerator, Keyring)

    self if block given, else an Enumerator

See Also:



147
148
149
# File 'lib/keyutils/keyring.rb', line 147

def each &b
  read.each &b
end

#each_recursive {|key, parent, attributes, error| ... } ⇒ Enumerator, Keyring

Iterate over keys recursively

Performs a depth-first recursive scan of the keyring tree and yields for every link found in the accessible keyrings in that tree.

Errors are ignored. Inaccessible keyrings are not scanned, but links to them are still yielded. If key attributes (and hence ype) cannot be retrieved, a generic Keyutils::Key object is yielded and an error that prevented it is indicated.

This method yields for each link found in all the keyrings in the tree and so may be called multiple times for a particular key if that key has multiple links to it.

Yield Parameters:

  • key (Key)

    the key to which the link points

  • parent (Keyring, nil)

    the keyring containing the link or nil for the initial key.

  • attributes (Hash)

    key attributes, as returned by Keyutils::Key#describe

  • error (SystemCallError, nil)

    error that prevented retrieving key attributes

Returns:

  • (Enumerator, Keyring)

    self if block given, else an Enumerator



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/keyutils/keyring.rb', line 174

def each_recursive
  return enum_for __method__ unless block_given?

  Lib.recursive_key_scan serial, ->(parent, key, desc, desc_len, _) do
    parent = parent == 0 ? nil : Keyring.send(:new, parent, nil)
    if desc_len > 0
      attributes = Key.send :parse_describe, desc.read_string(desc_len)
      key = Key.send :new_dispatch, key, attributes[:type], attributes[:desc]
      error = nil
    else
      attributes = nil
      key = Key.send :new, key, nil, nil
      error = SystemCallError.new FFI.errno
    end
    yield key, parent, attributes, error
    0
  end, nil
  self
end

#lengthFixnum

Returns number of keys linked to this keyring.

Returns:

  • (Fixnum)

    number of keys linked to this keyring



195
196
197
# File 'lib/keyutils/keyring.rb', line 195

def length
  read.length
end

Link a key to the keyring.

Creates a link from this keyring to key, displacing any link to another key of the same type and description in this keyring if one exists.

The caller must have write permission on a keyring to be able to create links in it.

The caller must have link permission on a key to be able to create a link to it.

Parameters:

  • key (Key)

    the key to link to this keyring

Returns:

Raises:

  • (Errno::ENOKEY)

    the key or the keyring specified are invalid

  • (Errno::EKEYEXPIRED)

    the key or the keyring specified have expired

  • (Errno::EKEYREVOKED)

    the key or the keyring specified have been revoked

  • (Errno::EACCES)

    the keyring exists, but is not writable by the calling process

  • (Errno::ENOMEM)

    insufficient memory to expand the keyring

  • (Errno::EDQUOT)

    expanding the keyring would exceed the keyring owner’s quota

  • (Errno::EACCES)

    the key exists, but is not linkable by the calling process

See Also:



46
47
48
49
# File 'lib/keyutils/keyring.rb', line 46

def link key
  Lib.keyctl_link key.id, id
  self
end

#read<Key> Also known as: to_a

Read the keyring.

Reads the list of keys in this keyring.

The caller must have read permission on a key to be able to read it.

Returns:

  • (<Key>)

    the keyring members

Raises:

  • (Errno::ENOKEY)

    the keyring is invalid

  • (Errno::EKEYEXPIRED)

    the keyring has expired

  • (Errno::EKEYREVOKED)

    the keyring had been revoked

  • (Errno::EACCES)

    the keyring is not readable by the calling process

See Also:



132
133
134
135
136
137
138
# File 'lib/keyutils/keyring.rb', line 132

def read
  super.unpack('L*').map do |serial|
    # try to map to the correct class
    key = Key.send :new, serial, nil, nil
    Key.send(:new_dispatch, serial, key.type, key.description) rescue key
  end
end

#request(type, description, callout_info = '') ⇒ Key?

Request a key from the kernel’s key management facility.

Asks the kernel to find a key of the given type that matches the specified description and, if successful, to attach it this keyring.

#request first recursively searches all the keyrings attached to the calling process in the order thread-specific keyring, process-specific keyring and then session keyring for a matching key.

If #request is called from a program invoked by request_key(2) on behalf of some other process to generate a key, then the keyrings of that other process will be searched next, using that other process’s UID, GID, groups and security context to control access.

The keys in each keyring searched are checked for a match before any child keyrings are recursed into. Only keys that are searchable for the caller may be found, and only searchable keyrings may be searched.

If the key is not found then, if callout_info is not nil, this function will attempt to look further afield. In such a case, the callout_info is passed to a user-space service such as /sbin/request-key to generate the key.

If that is unsuccessful also, then an error will be raised, and a temporary negative key will be installed in the keyring. This will expire after a few seconds, but will cause subsequent calls to #request to fail until it does.

If a key is created, no matter whether it’s a valid key or a negative key, it will displace any other key of the same type and description from the keyring.

Parameters:

  • type (Symbol)

    key type

  • description (String)

    key description

  • callout_info (String, nil) (defaults to: '')

    additional parameters for the request-key(8) facility

Returns:

  • (Key, nil)

    the key, if found

Raises:

  • (Errno::EACCES)

    the keyring wasn’t available for modification by the user

  • (Errno::EINTR)

    the request was interrupted by a signal

  • (Errno::EDQUOT)

    the key quota for this user would be exceeded by creating this key or linking it to the keyring

  • (Errno::EKEYEXPIRED)

    an expired key was found, but no replacement could be obtained

  • (Errno::EKEYREJECTED)

    the attempt to generate a new key was rejected

  • (Errno::EKEYREVOKED)

    a revoked key was found, but no replacement could be obtained

  • (Errno::ENOMEM)

    insufficient memory to create a key

See Also:



289
290
291
292
293
294
295
296
297
298
# File 'lib/keyutils/keyring.rb', line 289

def request type, description, callout_info = ''
  serial = Lib.request_key \
      type.to_s,
      description,
      callout_info,
      to_i
  new_dispatch serial, type.intern, description
rescue Errno::ENOKEY
  nil
end

#search(type, description, destination = nil) ⇒ Key?

Search the keyring for a key

Recursively searches the keyring for a key of the specified type and description.

If found, the key will be attached to the destination keyring (if given), and returned.

The source keyring must grant search permission to the caller, and for a key to be found, it must also grant search permission to the caller. Child keyrings will be only be recursively searched if they grant search permission to the caller as well.

If the destination keyring is given, then the link may only be formed if the found key grants the caller link permission and the destination keyring grants the caller write permission.

If the search is successful, and if the destination keyring already contains a link to a key that matches the specified type and description, then that link will be replaced by a link to the found key.

Parameters:

  • type (Symbol)

    the type of the key to find

  • description (String)

    the description of the key to find

  • destination (Keyring, nil) (defaults to: nil)

    the keyring to attach the key if found

Returns:

  • (Key, nil)

    the key, if found

Raises:

  • (Errno::EKEYEXPIRED)

    one of the keyrings has expired, or the only key found was expired

  • (Errno::EKEYREVOKED)

    one of the keyrings has been revoked, or the only key found was revoked

  • (Errno::ENOMEM)

    insufficient memory to expand the destination keyring

  • (Errno::EDQUOT)

    the key quota for this user would be exceeded by creating a link to the found key in the destination keyring

  • (Errno::EACCES)

    the source keyring didn’t grant search permission, the destination keyring didn’t grant write permission or the found key didn’t grant link permission to the caller

See Also:



113
114
115
116
117
118
# File 'lib/keyutils/keyring.rb', line 113

def search type, description, destination = nil
  serial = Lib.keyctl_search id, type.to_s, description, destination.to_i
  Key.send :new_dispatch, serial, type.intern, description
rescue Errno::ENOKEY
  nil
end

#to_h{String => String}

Return all “user” subkeys

Keys that cannot be read are ommited.

Returns:

  • ({String => String})

    user keys’ descriptions and their payloads



330
331
332
333
334
# File 'lib/keyutils/keyring.rb', line 330

def to_h
  keys = find_all { |k| k.type == :user rescue false }
  pairs = keys.map { |k| [k.description, k.to_s] rescue nil }
  Hash[*pairs.compact.flatten]
end

Unlink a key from the keyring.

Removes a link from this keyring to key if it exists.

The caller must have write permission on a keyring to be able to remove links in it.

Parameters:

  • key (Key)

    the key to unlink from this keyring

Returns:

Raises:

  • (Errno::ENOKEY)

    the key or the keyring specified are invalid

  • (Errno::EKEYEXPIRED)

    the key or the keyring specified have expired

  • (Errno::EKEYREVOKED)

    the key or the keyring specified have been revoked

  • (Errno::EACCES)

    the keyring exists, but is not writable by the calling process

See Also:



68
69
70
71
72
73
74
# File 'lib/keyutils/keyring.rb', line 68

def unlink key
  Lib.keyctl_unlink key.id, id
  self
rescue Errno::ENOENT
  # there was no link
  self
end