[PATCH] Keys: Allow in-kernel key requestor to pass auxiliary data to upcaller
David Howells [Thu, 29 Jun 2006 09:24:28 +0000 (02:24 -0700)]
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>

Documentation/keys-request-key.txt
Documentation/keys.txt
include/linux/key.h
security/keys/internal.h
security/keys/keyctl.c
security/keys/request_key.c

index 22488d7..c1f64fd 100644 (file)
@@ -3,16 +3,23 @@
                              ===================
 
 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
-request_key():
+request_key*():
 
        struct key *request_key(const struct key_type *type,
                                const char *description,
                                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:
 
        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,
                                 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
 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
 ===========
@@ -40,8 +57,8 @@ A request proceeds in the following manner:
      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
+     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
@@ -62,7 +79,7 @@ A request proceeds in the following manner:
      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
+     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
@@ -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
      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
 /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
      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
      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
+     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
+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
@@ -153,7 +171,7 @@ 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.
+error.  Note that several errors may have come from LSM.
 
 The error priority is:
 
index 61c0fad..e373f02 100644 (file)
@@ -780,6 +780,17 @@ payload contents" for more information.
     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:
 
        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.
 
 
+ (*) 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
 ============================
index e693e72..169f05e 100644 (file)
@@ -177,7 +177,8 @@ struct key {
 /*
  * 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 {
        /* name of the type */
@@ -285,6 +286,11 @@ extern struct key *request_key(struct key_type *type,
                               const char *description,
                               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 key_ref_t key_create_or_update(key_ref_t keyring,
index 3c2877f..1bb416f 100644 (file)
@@ -99,6 +99,7 @@ extern int install_process_keyring(struct task_struct *tsk);
 extern struct key *request_key_and_link(struct key_type *type,
                                        const char *description,
                                        const char *callout_info,
+                                       void *aux,
                                        struct key *dest_keyring,
                                        unsigned long flags);
 
index 329411c..d9ca15c 100644 (file)
@@ -183,7 +183,7 @@ asmlinkage long sys_request_key(const char __user *_type,
        }
 
        /* 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_ALLOC_IN_QUOTA);
        if (IS_ERR(key)) {
index 58d1efd..f573ac1 100644 (file)
@@ -1,6 +1,6 @@
 /* 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)
  *
  * 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,
                                 struct key *authkey,
-                                const char *op)
+                                const char *op,
+                                void *aux)
 {
        struct task_struct *tsk = current;
        key_serial_t prkey, sskey;
@@ -127,6 +128,7 @@ error_alloc:
 static struct key *__request_key_construction(struct key_type *type,
                                              const char *description,
                                              const char *callout_info,
+                                             void *aux,
                                              unsigned long flags)
 {
        request_key_actor_t actor;
@@ -164,7 +166,7 @@ static struct key *__request_key_construction(struct key_type *type,
        actor = call_sbin_request_key;
        if (type->request_key)
                actor = type->request_key;
-       ret = actor(key, authkey, "create");
+       ret = actor(key, authkey, "create", aux);
        if (ret < 0)
                goto request_failed;
 
@@ -258,8 +260,9 @@ alloc_failed:
  */
 static struct key *request_key_construction(struct key_type *type,
                                            const char *description,
-                                           struct key_user *user,
                                            const char *callout_info,
+                                           void *aux,
+                                           struct key_user *user,
                                            unsigned long flags)
 {
        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 */
-       key = __request_key_construction(type, description, callout_info,
+       key = __request_key_construction(type, description, callout_info, aux,
                                         flags);
  error:
        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,
                                 const char *description,
                                 const char *callout_info,
+                                void *aux,
                                 struct key *dest_keyring,
                                 unsigned long flags)
 {
@@ -399,8 +403,9 @@ struct key *request_key_and_link(struct key_type *type,
        struct key *key;
        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 */
        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
                         * being constructed) */
                        key = request_key_construction(type, description,
-                                                      user, callout_info,
-                                                      flags);
+                                                      callout_info, aux,
+                                                      user, flags);
                        if (key)
                                break;
 
@@ -491,8 +496,27 @@ struct key *request_key(struct key_type *type,
                        const char *callout_info)
 {
        return request_key_and_link(type, description, callout_info, NULL,
-                                   KEY_ALLOC_IN_QUOTA);
+                                   NULL, KEY_ALLOC_IN_QUOTA);
 
 } /* end 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);