Allow the callout data to be passed as a blob rather than a string for internal kernel services that call any request_key_*() interface other than request_key(). request_key() itself still takes a NUL-terminated string. The functions that change are: request_key_with_auxdata() request_key_async() request_key_async_with_auxdata() Signed-off-by: David Howells <dhowells@redhat.com> Cc: Paul Moore <paul.moore@hp.com> Cc: Chris Wright <chrisw@sous-sol.org> Cc: Stephen Smalley <sds@tycho.nsa.gov> Cc: James Morris <jmorris@namei.org> Cc: Kevin Coffman <kwc@citi.umich.edu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
			
				
	
	
		
			201 lines
		
	
	
	
		
			7.4 KiB
			
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
	
		
			7.4 KiB
			
		
	
	
	
		
			Text
		
	
	
	
	
	
			      ===================
 | 
						|
			      KEY REQUEST SERVICE
 | 
						|
			      ===================
 | 
						|
 | 
						|
The key request service is part of the key retention service (refer to
 | 
						|
Documentation/keys.txt).  This document explains more fully how the requesting
 | 
						|
algorithm works.
 | 
						|
 | 
						|
The process starts by either the kernel requesting a service by calling
 | 
						|
request_key*():
 | 
						|
 | 
						|
	struct key *request_key(const struct key_type *type,
 | 
						|
				const char *description,
 | 
						|
				const char *callout_info);
 | 
						|
 | 
						|
or:
 | 
						|
 | 
						|
	struct key *request_key_with_auxdata(const struct key_type *type,
 | 
						|
					     const char *description,
 | 
						|
					     const char *callout_info,
 | 
						|
					     size_t callout_len,
 | 
						|
					     void *aux);
 | 
						|
 | 
						|
or:
 | 
						|
 | 
						|
	struct key *request_key_async(const struct key_type *type,
 | 
						|
				      const char *description,
 | 
						|
				      const char *callout_info,
 | 
						|
				      size_t callout_len);
 | 
						|
 | 
						|
or:
 | 
						|
 | 
						|
	struct key *request_key_async_with_auxdata(const struct key_type *type,
 | 
						|
						   const char *description,
 | 
						|
						   const char *callout_info,
 | 
						|
					     	   size_t callout_len,
 | 
						|
						   void *aux);
 | 
						|
 | 
						|
Or by userspace invoking the request_key system call:
 | 
						|
 | 
						|
	key_serial_t request_key(const char *type,
 | 
						|
				 const char *description,
 | 
						|
				 const char *callout_info,
 | 
						|
				 key_serial_t dest_keyring);
 | 
						|
 | 
						|
The main difference between the access points is that the in-kernel interface
 | 
						|
does not need to link the key to a keyring to prevent it from being immediately
 | 
						|
destroyed.  The kernel interface returns a pointer directly to the key, and
 | 
						|
it's up to the caller to destroy the key.
 | 
						|
 | 
						|
The request_key*_with_auxdata() calls are like the in-kernel request_key*()
 | 
						|
calls, except that they permit auxiliary data to be passed to the upcaller (the
 | 
						|
default is NULL).  This is only useful for those key types that define their
 | 
						|
own upcall mechanism rather than using /sbin/request-key.
 | 
						|
 | 
						|
The two async in-kernel calls may return keys that are still in the process of
 | 
						|
being constructed.  The two non-async ones will wait for construction to
 | 
						|
complete first.
 | 
						|
 | 
						|
The userspace interface links the key to a keyring associated with the process
 | 
						|
to prevent the key from going away, and returns the serial number of the key to
 | 
						|
the caller.
 | 
						|
 | 
						|
 | 
						|
The following example assumes that the key types involved don't define their
 | 
						|
own upcall mechanisms.  If they do, then those should be substituted for the
 | 
						|
forking and execution of /sbin/request-key.
 | 
						|
 | 
						|
 | 
						|
===========
 | 
						|
THE PROCESS
 | 
						|
===========
 | 
						|
 | 
						|
A request proceeds in the following manner:
 | 
						|
 | 
						|
 (1) Process A calls request_key() [the userspace syscall calls the kernel
 | 
						|
     interface].
 | 
						|
 | 
						|
 (2) request_key() searches the process's subscribed keyrings to see if there's
 | 
						|
     a suitable key there.  If there is, it returns the key.  If there isn't,
 | 
						|
     and callout_info is not set, an error is returned.  Otherwise the process
 | 
						|
     proceeds to the next step.
 | 
						|
 | 
						|
 (3) request_key() sees that A doesn't have the desired key yet, so it creates
 | 
						|
     two things:
 | 
						|
 | 
						|
     (a) An uninstantiated key U of requested type and description.
 | 
						|
 | 
						|
     (b) An authorisation key V that refers to key U and notes that process A
 | 
						|
     	 is the context in which key U should be instantiated and secured, and
 | 
						|
     	 from which associated key requests may be satisfied.
 | 
						|
 | 
						|
 (4) request_key() then forks and executes /sbin/request-key with a new session
 | 
						|
     keyring that contains a link to auth key V.
 | 
						|
 | 
						|
 (5) /sbin/request-key assumes the authority associated with key U.
 | 
						|
 | 
						|
 (6) /sbin/request-key execs an appropriate program to perform the actual
 | 
						|
     instantiation.
 | 
						|
 | 
						|
 (7) The program may want to access another key from A's context (say a
 | 
						|
     Kerberos TGT key).  It just requests the appropriate key, and the keyring
 | 
						|
     search notes that the session keyring has auth key V in its bottom level.
 | 
						|
 | 
						|
     This will permit it to then search the keyrings of process A with the
 | 
						|
     UID, GID, groups and security info of process A as if it was process A,
 | 
						|
     and come up with key W.
 | 
						|
 | 
						|
 (8) The program then does what it must to get the data with which to
 | 
						|
     instantiate key U, using key W as a reference (perhaps it contacts a
 | 
						|
     Kerberos server using the TGT) and then instantiates key U.
 | 
						|
 | 
						|
 (9) Upon instantiating key U, auth key V is automatically revoked so that it
 | 
						|
     may not be used again.
 | 
						|
 | 
						|
(10) The program then exits 0 and request_key() deletes key V and returns key
 | 
						|
     U to the caller.
 | 
						|
 | 
						|
This also extends further.  If key W (step 7 above) didn't exist, key W would
 | 
						|
be created uninstantiated, another auth key (X) would be created (as per step
 | 
						|
3) and another copy of /sbin/request-key spawned (as per step 4); but the
 | 
						|
context specified by auth key X will still be process A, as it was in auth key
 | 
						|
V.
 | 
						|
 | 
						|
This is because process A's keyrings can't simply be attached to
 | 
						|
/sbin/request-key at the appropriate places because (a) execve will discard two
 | 
						|
of them, and (b) it requires the same UID/GID/Groups all the way through.
 | 
						|
 | 
						|
 | 
						|
======================
 | 
						|
NEGATIVE INSTANTIATION
 | 
						|
======================
 | 
						|
 | 
						|
Rather than instantiating a key, it is possible for the possessor of an
 | 
						|
authorisation key to negatively instantiate a key that's under construction.
 | 
						|
This is a short duration placeholder that causes any attempt at re-requesting
 | 
						|
the key whilst it exists to fail with error ENOKEY.
 | 
						|
 | 
						|
This is provided to prevent excessive repeated spawning of /sbin/request-key
 | 
						|
processes for a key that will never be obtainable.
 | 
						|
 | 
						|
Should the /sbin/request-key process exit anything other than 0 or die on a
 | 
						|
signal, the key under construction will be automatically negatively
 | 
						|
instantiated for a short amount of time.
 | 
						|
 | 
						|
 | 
						|
====================
 | 
						|
THE SEARCH ALGORITHM
 | 
						|
====================
 | 
						|
 | 
						|
A search of any particular keyring proceeds in the following fashion:
 | 
						|
 | 
						|
 (1) When the key management code searches for a key (keyring_search_aux) it
 | 
						|
     firstly calls key_permission(SEARCH) on the keyring it's starting with,
 | 
						|
     if this denies permission, it doesn't search further.
 | 
						|
 | 
						|
 (2) It considers all the non-keyring keys within that keyring and, if any key
 | 
						|
     matches the criteria specified, calls key_permission(SEARCH) on it to see
 | 
						|
     if the key is allowed to be found.  If it is, that key is returned; if
 | 
						|
     not, the search continues, and the error code is retained if of higher
 | 
						|
     priority than the one currently set.
 | 
						|
 | 
						|
 (3) It then considers all the keyring-type keys in the keyring it's currently
 | 
						|
     searching.  It calls key_permission(SEARCH) on each keyring, and if this
 | 
						|
     grants permission, it recurses, executing steps (2) and (3) on that
 | 
						|
     keyring.
 | 
						|
 | 
						|
The process stops immediately a valid key is found with permission granted to
 | 
						|
use it.  Any error from a previous match attempt is discarded and the key is
 | 
						|
returned.
 | 
						|
 | 
						|
When search_process_keyrings() is invoked, it performs the following searches
 | 
						|
until one succeeds:
 | 
						|
 | 
						|
 (1) If extant, the process's thread keyring is searched.
 | 
						|
 | 
						|
 (2) If extant, the process's process keyring is searched.
 | 
						|
 | 
						|
 (3) The process's session keyring is searched.
 | 
						|
 | 
						|
 (4) If the process has assumed the authority associated with a request_key()
 | 
						|
     authorisation key then:
 | 
						|
 | 
						|
     (a) If extant, the calling process's thread keyring is searched.
 | 
						|
 | 
						|
     (b) If extant, the calling process's process keyring is searched.
 | 
						|
 | 
						|
     (c) The calling process's session keyring is searched.
 | 
						|
 | 
						|
The moment one succeeds, all pending errors are discarded and the found key is
 | 
						|
returned.
 | 
						|
 | 
						|
Only if all these fail does the whole thing fail with the highest priority
 | 
						|
error.  Note that several errors may have come from LSM.
 | 
						|
 | 
						|
The error priority is:
 | 
						|
 | 
						|
	EKEYREVOKED > EKEYEXPIRED > ENOKEY
 | 
						|
 | 
						|
EACCES/EPERM are only returned on a direct search of a specific keyring where
 | 
						|
the basal keyring does not grant Search permission.
 |