Browse Source

[PATCH] Keys: Allow in-kernel key requestor to pass auxiliary data to upcaller

The proposed NFS key type uses its own method of passing key requests to
userspace (upcalling) rather than invoking /sbin/request-key.  This is
because the responsible userspace daemon should already be running and will
be contacted through rpc_pipefs.

This patch permits the NFS filesystem to pass auxiliary data to the upcall
operation (struct key_type::request_key) so that the upcaller can use a
pre-existing communications channel more easily.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-By: Kevin Coffman <kwc@citi.umich.edu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
David Howells 19 years ago
parent
commit
4e54f08543

+ 36 - 18
Documentation/keys-request-key.txt

@@ -3,16 +3,23 @@
 			      ===================
 			      ===================
 
 
 The key request service is part of the key retention service (refer to
 The key request service is part of the key retention service (refer to
-Documentation/keys.txt). This document explains more fully how that the
-requesting algorithm works.
+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
 The process starts by either the kernel requesting a service by calling
-request_key():
+request_key*():
 
 
 	struct key *request_key(const struct key_type *type,
 	struct key *request_key(const struct key_type *type,
 				const char *description,
 				const char *description,
 				const char *callout_string);
 				const char *callout_string);
 
 
+or:
+
+	struct key *request_key_with_auxdata(const struct key_type *type,
+					     const char *description,
+					     const char *callout_string,
+					     void *aux);
+
 Or by userspace invoking the request_key system call:
 Or by userspace invoking the request_key system call:
 
 
 	key_serial_t request_key(const char *type,
 	key_serial_t request_key(const char *type,
@@ -20,16 +27,26 @@ Or by userspace invoking the request_key system call:
 				 const char *callout_info,
 				 const char *callout_info,
 				 key_serial_t dest_keyring);
 				 key_serial_t dest_keyring);
 
 
-The main difference between the two 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 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() call is like the in-kernel request_key() call,
+except that it permits 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 userspace interface links the key to a keyring associated with the process
 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
 to prevent the key from going away, and returns the serial number of the key to
 the caller.
 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
 THE PROCESS
 ===========
 ===========
@@ -40,8 +57,8 @@ A request proceeds in the following manner:
      interface].
      interface].
 
 
  (2) request_key() searches the process's subscribed keyrings to see if there's
  (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
+     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.
      proceeds to the next step.
 
 
  (3) request_key() sees that A doesn't have the desired key yet, so it creates
  (3) request_key() sees that A doesn't have the desired key yet, so it creates
@@ -62,7 +79,7 @@ A request proceeds in the following manner:
      instantiation.
      instantiation.
 
 
  (7) The program may want to access another key from A's context (say a
  (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
+     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.
      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
      This will permit it to then search the keyrings of process A with the
@@ -79,10 +96,11 @@ A request proceeds in the following manner:
 (10) The program then exits 0 and request_key() deletes key V and returns key
 (10) The program then exits 0 and request_key() deletes key V and returns key
      U to the caller.
      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 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
 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
 /sbin/request-key at the appropriate places because (a) execve will discard two
@@ -118,17 +136,17 @@ A search of any particular keyring proceeds in the following fashion:
 
 
  (2) It considers all the non-keyring keys within that keyring and, if any key
  (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
      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
+     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
      not, the search continues, and the error code is retained if of higher
      priority than the one currently set.
      priority than the one currently set.
 
 
  (3) It then considers all the keyring-type keys in the keyring it's currently
  (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
+     searching.  It calls key_permission(SEARCH) on each keyring, and if this
      grants permission, it recurses, executing steps (2) and (3) on that
      grants permission, it recurses, executing steps (2) and (3) on that
      keyring.
      keyring.
 
 
 The process stops immediately a valid key is found with permission granted to
 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
+use it.  Any error from a previous match attempt is discarded and the key is
 returned.
 returned.
 
 
 When search_process_keyrings() is invoked, it performs the following searches
 When search_process_keyrings() is invoked, it performs the following searches
@@ -153,7 +171,7 @@ The moment one succeeds, all pending errors are discarded and the found key is
 returned.
 returned.
 
 
 Only if all these fail does the whole thing fail with the highest priority
 Only if all these fail does the whole thing fail with the highest priority
-error. Note that several errors may have come from LSM.
+error.  Note that several errors may have come from LSM.
 
 
 The error priority is:
 The error priority is:
 
 

+ 29 - 0
Documentation/keys.txt

@@ -780,6 +780,17 @@ payload contents" for more information.
     See also Documentation/keys-request-key.txt.
     See also Documentation/keys-request-key.txt.
 
 
 
 
+(*) To search for a key, passing auxiliary data to the upcaller, call:
+
+	struct key *request_key_with_auxdata(const struct key_type *type,
+					     const char *description,
+					     const char *callout_string,
+					     void *aux);
+
+    This is identical to request_key(), except that the auxiliary data is
+    passed to the key_type->request_key() op if it exists.
+
+
 (*) When it is no longer required, the key should be released using:
 (*) When it is no longer required, the key should be released using:
 
 
 	void key_put(struct key *key);
 	void key_put(struct key *key);
@@ -1031,6 +1042,24 @@ The structure has a number of fields, some of which are mandatory:
      as might happen when the userspace buffer is accessed.
      as might happen when the userspace buffer is accessed.
 
 
 
 
+ (*) int (*request_key)(struct key *key, struct key *authkey, const char *op,
+			void *aux);
+
+     This method is optional.  If provided, request_key() and
+     request_key_with_auxdata() will invoke this function rather than
+     upcalling to /sbin/request-key to operate upon a key of this type.
+
+     The aux parameter is as passed to request_key_with_auxdata() or is NULL
+     otherwise.  Also passed are the key to be operated upon, the
+     authorisation key for this operation and the operation type (currently
+     only "create").
+
+     This function should return only when the upcall is complete.  Upon return
+     the authorisation key will be revoked, and the target key will be
+     negatively instantiated if it is still uninstantiated.  The error will be
+     returned to the caller of request_key*().
+
+
 ============================
 ============================
 REQUEST-KEY CALLBACK SERVICE
 REQUEST-KEY CALLBACK SERVICE
 ============================
 ============================

+ 7 - 1
include/linux/key.h

@@ -177,7 +177,8 @@ struct key {
 /*
 /*
  * kernel managed key type definition
  * kernel managed key type definition
  */
  */
-typedef int (*request_key_actor_t)(struct key *key, struct key *authkey, const char *op);
+typedef int (*request_key_actor_t)(struct key *key, struct key *authkey,
+				   const char *op, void *aux);
 
 
 struct key_type {
 struct key_type {
 	/* name of the type */
 	/* name of the type */
@@ -285,6 +286,11 @@ extern struct key *request_key(struct key_type *type,
 			       const char *description,
 			       const char *description,
 			       const char *callout_info);
 			       const char *callout_info);
 
 
+extern struct key *request_key_with_auxdata(struct key_type *type,
+					    const char *description,
+					    const char *callout_info,
+					    void *aux);
+
 extern int key_validate(struct key *key);
 extern int key_validate(struct key *key);
 
 
 extern key_ref_t key_create_or_update(key_ref_t keyring,
 extern key_ref_t key_create_or_update(key_ref_t keyring,

+ 1 - 0
security/keys/internal.h

@@ -99,6 +99,7 @@ extern int install_process_keyring(struct task_struct *tsk);
 extern struct key *request_key_and_link(struct key_type *type,
 extern struct key *request_key_and_link(struct key_type *type,
 					const char *description,
 					const char *description,
 					const char *callout_info,
 					const char *callout_info,
+					void *aux,
 					struct key *dest_keyring,
 					struct key *dest_keyring,
 					unsigned long flags);
 					unsigned long flags);
 
 

+ 1 - 1
security/keys/keyctl.c

@@ -183,7 +183,7 @@ asmlinkage long sys_request_key(const char __user *_type,
 	}
 	}
 
 
 	/* do the search */
 	/* do the search */
-	key = request_key_and_link(ktype, description, callout_info,
+	key = request_key_and_link(ktype, description, callout_info, NULL,
 				   key_ref_to_ptr(dest_ref),
 				   key_ref_to_ptr(dest_ref),
 				   KEY_ALLOC_IN_QUOTA);
 				   KEY_ALLOC_IN_QUOTA);
 	if (IS_ERR(key)) {
 	if (IS_ERR(key)) {

+ 34 - 10
security/keys/request_key.c

@@ -1,6 +1,6 @@
 /* request_key.c: request a key from userspace
 /* request_key.c: request a key from userspace
  *
  *
- * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004-6 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  * Written by David Howells (dhowells@redhat.com)
  *
  *
  * This program is free software; you can redistribute it and/or
  * This program is free software; you can redistribute it and/or
@@ -33,7 +33,8 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_conswq);
  */
  */
 static int call_sbin_request_key(struct key *key,
 static int call_sbin_request_key(struct key *key,
 				 struct key *authkey,
 				 struct key *authkey,
-				 const char *op)
+				 const char *op,
+				 void *aux)
 {
 {
 	struct task_struct *tsk = current;
 	struct task_struct *tsk = current;
 	key_serial_t prkey, sskey;
 	key_serial_t prkey, sskey;
@@ -127,6 +128,7 @@ error_alloc:
 static struct key *__request_key_construction(struct key_type *type,
 static struct key *__request_key_construction(struct key_type *type,
 					      const char *description,
 					      const char *description,
 					      const char *callout_info,
 					      const char *callout_info,
+					      void *aux,
 					      unsigned long flags)
 					      unsigned long flags)
 {
 {
 	request_key_actor_t actor;
 	request_key_actor_t actor;
@@ -164,7 +166,7 @@ static struct key *__request_key_construction(struct key_type *type,
 	actor = call_sbin_request_key;
 	actor = call_sbin_request_key;
 	if (type->request_key)
 	if (type->request_key)
 		actor = type->request_key;
 		actor = type->request_key;
-	ret = actor(key, authkey, "create");
+	ret = actor(key, authkey, "create", aux);
 	if (ret < 0)
 	if (ret < 0)
 		goto request_failed;
 		goto request_failed;
 
 
@@ -258,8 +260,9 @@ alloc_failed:
  */
  */
 static struct key *request_key_construction(struct key_type *type,
 static struct key *request_key_construction(struct key_type *type,
 					    const char *description,
 					    const char *description,
-					    struct key_user *user,
 					    const char *callout_info,
 					    const char *callout_info,
+					    void *aux,
+					    struct key_user *user,
 					    unsigned long flags)
 					    unsigned long flags)
 {
 {
 	struct key_construction *pcons;
 	struct key_construction *pcons;
@@ -284,7 +287,7 @@ static struct key *request_key_construction(struct key_type *type,
 	}
 	}
 
 
 	/* see about getting userspace to construct the key */
 	/* see about getting userspace to construct the key */
-	key = __request_key_construction(type, description, callout_info,
+	key = __request_key_construction(type, description, callout_info, aux,
 					 flags);
 					 flags);
  error:
  error:
 	kleave(" = %p", key);
 	kleave(" = %p", key);
@@ -392,6 +395,7 @@ static void request_key_link(struct key *key, struct key *dest_keyring)
 struct key *request_key_and_link(struct key_type *type,
 struct key *request_key_and_link(struct key_type *type,
 				 const char *description,
 				 const char *description,
 				 const char *callout_info,
 				 const char *callout_info,
+				 void *aux,
 				 struct key *dest_keyring,
 				 struct key *dest_keyring,
 				 unsigned long flags)
 				 unsigned long flags)
 {
 {
@@ -399,8 +403,9 @@ struct key *request_key_and_link(struct key_type *type,
 	struct key *key;
 	struct key *key;
 	key_ref_t key_ref;
 	key_ref_t key_ref;
 
 
-	kenter("%s,%s,%s,%p,%lx",
-	       type->name, description, callout_info, dest_keyring, flags);
+	kenter("%s,%s,%s,%p,%p,%lx",
+	       type->name, description, callout_info, aux,
+	       dest_keyring, flags);
 
 
 	/* search all the process keyrings for a key */
 	/* search all the process keyrings for a key */
 	key_ref = search_process_keyrings(type, description, type->match,
 	key_ref = search_process_keyrings(type, description, type->match,
@@ -433,8 +438,8 @@ struct key *request_key_and_link(struct key_type *type,
 			/* ask userspace (returns NULL if it waited on a key
 			/* ask userspace (returns NULL if it waited on a key
 			 * being constructed) */
 			 * being constructed) */
 			key = request_key_construction(type, description,
 			key = request_key_construction(type, description,
-						       user, callout_info,
-						       flags);
+						       callout_info, aux,
+						       user, flags);
 			if (key)
 			if (key)
 				break;
 				break;
 
 
@@ -491,8 +496,27 @@ struct key *request_key(struct key_type *type,
 			const char *callout_info)
 			const char *callout_info)
 {
 {
 	return request_key_and_link(type, description, callout_info, NULL,
 	return request_key_and_link(type, description, callout_info, NULL,
-				    KEY_ALLOC_IN_QUOTA);
+				    NULL, KEY_ALLOC_IN_QUOTA);
 
 
 } /* end request_key() */
 } /* end request_key() */
 
 
 EXPORT_SYMBOL(request_key);
 EXPORT_SYMBOL(request_key);
+
+/*****************************************************************************/
+/*
+ * request a key with auxiliary data for the upcaller
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if supplementary info was provided
+ */
+struct key *request_key_with_auxdata(struct key_type *type,
+				     const char *description,
+				     const char *callout_info,
+				     void *aux)
+{
+	return request_key_and_link(type, description, callout_info, aux,
+				    NULL, KEY_ALLOC_IN_QUOTA);
+
+} /* end request_key_with_auxdata() */
+
+EXPORT_SYMBOL(request_key_with_auxdata);