keys: add keyctl function to get a security label
authorDavid Howells <dhowells@redhat.com>
Tue, 29 Apr 2008 08:01:26 +0000 (01:01 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 Apr 2008 15:06:16 +0000 (08:06 -0700)
Add a keyctl() function to get the security label of a key.

The following is added to Documentation/keys.txt:

 (*) Get the LSM security context attached to a key.

long keyctl(KEYCTL_GET_SECURITY, key_serial_t key, char *buffer,
    size_t buflen)

     This function returns a string that represents the LSM security context
     attached to a key in the buffer provided.

     Unless there's an error, it always returns the amount of data it could
     produce, even if that's too big for the buffer, but it won't copy more
     than requested to userspace. If the buffer pointer is NULL then no copy
     will take place.

     A NUL character is included at the end of the string if the buffer is
     sufficiently big.  This is included in the returned count.  If no LSM is
     in force then an empty string will be returned.

     A process must have view permission on the key for this function to be
     successful.

[akpm@linux-foundation.org: declare keyctl_get_security()]
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
Cc: Paul Moore <paul.moore@hp.com>
Cc: Chris Wright <chrisw@sous-sol.org>
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>
Documentation/keys.txt
include/linux/keyctl.h
include/linux/security.h
security/dummy.c
security/keys/compat.c
security/keys/internal.h
security/keys/keyctl.c
security/security.c
security/selinux/hooks.c

index b82d38de8b89667c0a3a3cc59f47cd26b3e23bc9..be424b02437ddf975bc7d97512e05950d6083915 100644 (file)
@@ -711,6 +711,27 @@ The keyctl syscall functions are:
      The assumed authoritative key is inherited across fork and exec.
 
 
+ (*) Get the LSM security context attached to a key.
+
+       long keyctl(KEYCTL_GET_SECURITY, key_serial_t key, char *buffer,
+                   size_t buflen)
+
+     This function returns a string that represents the LSM security context
+     attached to a key in the buffer provided.
+
+     Unless there's an error, it always returns the amount of data it could
+     produce, even if that's too big for the buffer, but it won't copy more
+     than requested to userspace. If the buffer pointer is NULL then no copy
+     will take place.
+
+     A NUL character is included at the end of the string if the buffer is
+     sufficiently big.  This is included in the returned count.  If no LSM is
+     in force then an empty string will be returned.
+
+     A process must have view permission on the key for this function to be
+     successful.
+
+
 ===============
 KERNEL SERVICES
 ===============
index 3365945640c9a93d907efedb5c3ca707737797d2..656ee6b77a4a337d2dcdd728e8a1d79af83ba5ad 100644 (file)
@@ -49,5 +49,6 @@
 #define KEYCTL_SET_REQKEY_KEYRING      14      /* set default request-key keyring */
 #define KEYCTL_SET_TIMEOUT             15      /* set key timeout */
 #define KEYCTL_ASSUME_AUTHORITY                16      /* assume request_key() authorisation */
+#define KEYCTL_GET_SECURITY            17      /* get key security label */
 
 #endif /*  _LINUX_KEYCTL_H */
index 3ebcdd00b17d0bc420e36132ad8ebbe0001d0b44..adb09d893ae0eea36dffbcf374a61bdddbabe476 100644 (file)
@@ -1009,6 +1009,17 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @perm describes the combination of permissions required of this key.
  *     Return 1 if permission granted, 0 if permission denied and -ve it the
  *     normal permissions model should be effected.
+ * @key_getsecurity:
+ *     Get a textual representation of the security context attached to a key
+ *     for the purposes of honouring KEYCTL_GETSECURITY.  This function
+ *     allocates the storage for the NUL-terminated string and the caller
+ *     should free it.
+ *     @key points to the key to be queried.
+ *     @_buffer points to a pointer that should be set to point to the
+ *      resulting string (if no label or an error occurs).
+ *     Return the length of the string (including terminating NUL) or -ve if
+ *      an error.
+ *     May also return 0 (and a NULL buffer pointer) if there is no label.
  *
  * Security hooks affecting all System V IPC operations.
  *
@@ -1538,7 +1549,7 @@ struct security_operations {
        int (*key_permission) (key_ref_t key_ref,
                               struct task_struct *context,
                               key_perm_t perm);
-
+       int (*key_getsecurity)(struct key *key, char **_buffer);
 #endif /* CONFIG_KEYS */
 
 #ifdef CONFIG_AUDIT
@@ -2732,6 +2743,7 @@ int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long f
 void security_key_free(struct key *key);
 int security_key_permission(key_ref_t key_ref,
                            struct task_struct *context, key_perm_t perm);
+int security_key_getsecurity(struct key *key, char **_buffer);
 
 #else
 
@@ -2753,6 +2765,12 @@ static inline int security_key_permission(key_ref_t key_ref,
        return 0;
 }
 
+static inline int security_key_getsecurity(struct key *key, char **_buffer)
+{
+       *_buffer = NULL;
+       return 0;
+}
+
 #endif
 #endif /* CONFIG_KEYS */
 
index 26ee06ef0e93bc86447815af659845db726e84f4..48cf30226e16522f0bc5be2803442afe23ea1883 100644 (file)
@@ -994,6 +994,13 @@ static inline int dummy_key_permission(key_ref_t key_ref,
 {
        return 0;
 }
+
+static int dummy_key_getsecurity(struct key *key, char **_buffer)
+{
+       *_buffer = NULL;
+       return 0;
+}
+
 #endif /* CONFIG_KEYS */
 
 #ifdef CONFIG_AUDIT
@@ -1210,6 +1217,7 @@ void security_fixup_ops (struct security_operations *ops)
        set_to_dummy_if_null(ops, key_alloc);
        set_to_dummy_if_null(ops, key_free);
        set_to_dummy_if_null(ops, key_permission);
+       set_to_dummy_if_null(ops, key_getsecurity);
 #endif /* CONFIG_KEYS */
 #ifdef CONFIG_AUDIT
        set_to_dummy_if_null(ops, audit_rule_init);
index e10ec995f2751101d39a7dfbd145cd8ce52cc6ee..c766c68a63bc2078c62bf256730e05e14b36268b 100644 (file)
@@ -79,6 +79,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
        case KEYCTL_ASSUME_AUTHORITY:
                return keyctl_assume_authority(arg2);
 
+       case KEYCTL_GET_SECURITY:
+               return keyctl_get_security(arg2, compat_ptr(arg3), arg4);
+
        default:
                return -EOPNOTSUPP;
        }
index 3cc04c2afe1ccea59a51ec6e0084a25f3ac61f36..6361d3736dbc7404d9e67d2facf0b8f91b7157f3 100644 (file)
@@ -155,7 +155,8 @@ extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
 extern long keyctl_set_reqkey_keyring(int);
 extern long keyctl_set_timeout(key_serial_t, unsigned);
 extern long keyctl_assume_authority(key_serial_t);
-
+extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,
+                               size_t buflen);
 
 /*
  * debugging key validation
index 1698bf90ee84a76e3f5cb0671e936fdba4efadb3..56e963b700b92cfe494294617a5b2e56895dd7c3 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/string.h>
 #include <linux/err.h>
 #include <linux/vmalloc.h>
+#include <linux/security.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
@@ -1080,6 +1081,66 @@ error:
 
 } /* end keyctl_assume_authority() */
 
+/*
+ * get the security label of a key
+ * - the key must grant us view permission
+ * - if there's a buffer, we place up to buflen bytes of data into it
+ * - unless there's an error, we return the amount of information available,
+ *   irrespective of how much we may have copied (including the terminal NUL)
+ * - implements keyctl(KEYCTL_GET_SECURITY)
+ */
+long keyctl_get_security(key_serial_t keyid,
+                        char __user *buffer,
+                        size_t buflen)
+{
+       struct key *key, *instkey;
+       key_ref_t key_ref;
+       char *context;
+       long ret;
+
+       key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW);
+       if (IS_ERR(key_ref)) {
+               if (PTR_ERR(key_ref) != -EACCES)
+                       return PTR_ERR(key_ref);
+
+               /* viewing a key under construction is also permitted if we
+                * have the authorisation token handy */
+               instkey = key_get_instantiation_authkey(keyid);
+               if (IS_ERR(instkey))
+                       return PTR_ERR(key_ref);
+               key_put(instkey);
+
+               key_ref = lookup_user_key(NULL, keyid, 0, 1, 0);
+               if (IS_ERR(key_ref))
+                       return PTR_ERR(key_ref);
+       }
+
+       key = key_ref_to_ptr(key_ref);
+       ret = security_key_getsecurity(key, &context);
+       if (ret == 0) {
+               /* if no information was returned, give userspace an empty
+                * string */
+               ret = 1;
+               if (buffer && buflen > 0 &&
+                   copy_to_user(buffer, "", 1) != 0)
+                       ret = -EFAULT;
+       } else if (ret > 0) {
+               /* return as much data as there's room for */
+               if (buffer && buflen > 0) {
+                       if (buflen > ret)
+                               buflen = ret;
+
+                       if (copy_to_user(buffer, context, buflen) != 0)
+                               ret = -EFAULT;
+               }
+
+               kfree(context);
+       }
+
+       key_ref_put(key_ref);
+       return ret;
+}
+
 /*****************************************************************************/
 /*
  * the key control system call
@@ -1160,6 +1221,11 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
        case KEYCTL_ASSUME_AUTHORITY:
                return keyctl_assume_authority((key_serial_t) arg2);
 
+       case KEYCTL_GET_SECURITY:
+               return keyctl_get_security((key_serial_t) arg2,
+                                          (char *) arg3,
+                                          (size_t) arg4);
+
        default:
                return -EOPNOTSUPP;
        }
index a809035441ab25a93bb93611f247f406f176fb8a..8e64a29dc55d447c489765f808d56827546175cd 100644 (file)
@@ -1156,6 +1156,11 @@ int security_key_permission(key_ref_t key_ref,
        return security_ops->key_permission(key_ref, context, perm);
 }
 
+int security_key_getsecurity(struct key *key, char **_buffer)
+{
+       return security_ops->key_getsecurity(key, _buffer);
+}
+
 #endif /* CONFIG_KEYS */
 
 #ifdef CONFIG_AUDIT
index 047365ac9faa62458ea09edbaece15fb453d02d8..838d1e5e63a195f507fedc59646cc3740aea66c8 100644 (file)
@@ -5300,6 +5300,20 @@ static int selinux_key_permission(key_ref_t key_ref,
                            SECCLASS_KEY, perm, NULL);
 }
 
+static int selinux_key_getsecurity(struct key *key, char **_buffer)
+{
+       struct key_security_struct *ksec = key->security;
+       char *context = NULL;
+       unsigned len;
+       int rc;
+
+       rc = security_sid_to_context(ksec->sid, &context, &len);
+       if (!rc)
+               rc = len;
+       *_buffer = context;
+       return rc;
+}
+
 #endif
 
 static struct security_operations selinux_ops = {
@@ -5488,6 +5502,7 @@ static struct security_operations selinux_ops = {
        .key_alloc =                    selinux_key_alloc,
        .key_free =                     selinux_key_free,
        .key_permission =               selinux_key_permission,
+       .key_getsecurity =              selinux_key_getsecurity,
 #endif
 
 #ifdef CONFIG_AUDIT