This is based on a patch david howells sent me. I have modified that
patch to meet my needs.

Extend kecytl() to add an option to verify signature of a user buffer.
One needs to pass in the signature type also so that respective handler
can be called. Currently I have defined a new signature type
KEYCTL_SIG_TYPE_IMA_DIGSIG, which sinifies signatures generated by IMA
subsystem.

Signed-off-by: Vivek Goyal <vgo...@redhat.com>
---
 include/uapi/linux/keyctl.h | 16 +++++++++
 security/keys/compat.c      | 28 ++++++++++++++++
 security/keys/internal.h    |  2 ++
 security/keys/keyctl.c      | 79 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 125 insertions(+)

diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 840cb99..d7c7471 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -12,6 +12,17 @@
 #ifndef _LINUX_KEYCTL_H
 #define _LINUX_KEYCTL_H
 
+/* Data required to verify signature of a user buffer */
+struct keyctl_sig_data {
+       void *data;
+       size_t datalen;
+       void *sig;
+       size_t siglen;
+       unsigned long sig_type;
+       unsigned long keyring_id;
+       unsigned long flags;
+};
+
 /* special process keyring shortcut IDs */
 #define KEY_SPEC_THREAD_KEYRING                -1      /* - key ID for 
thread-specific keyring */
 #define KEY_SPEC_PROCESS_KEYRING       -2      /* - key ID for 
process-specific keyring */
@@ -57,5 +68,10 @@
 #define KEYCTL_INSTANTIATE_IOV         20      /* instantiate a partially 
constructed key */
 #define KEYCTL_INVALIDATE              21      /* invalidate a key */
 #define KEYCTL_GET_PERSISTENT          22      /* get a user's persistent 
keyring */
+#define KEYCTL_VERIFY_SIGNATURE                23      /* use a key to verify 
a signature */
+
+/* Type of signatures */
+#define KEYCTL_SIG_TYPE_UNKNOWN                        0
+#define KEYCTL_SIG_TYPE_INTEGRITY_DIGSIG       1       /* Digital Signature 
generated by integrity subsystem utilities */
 
 #endif /*  _LINUX_KEYCTL_H */
diff --git a/security/keys/compat.c b/security/keys/compat.c
index bbd32c7..3af2cf2 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -15,6 +15,31 @@
 #include <linux/slab.h>
 #include "internal.h"
 
+struct compat_keyctl_sig_data {
+       compat_uptr_t data;
+       compat_size_t datalen;
+       compat_uptr_t sig;
+       compat_size_t siglen;
+       compat_ulong_t sig_type;
+       compat_ulong_t keyring_id;
+       compat_ulong_t flags;
+};
+
+static long compat_keyctl_verify_signature(const void __user *_sig_data)
+{
+       struct compat_keyctl_sig_data csig_data;
+       int result;
+
+       result = copy_from_user(&csig_data, _sig_data, sizeof(csig_data));
+       if (result)
+               return -EFAULT;
+
+       return __keyctl_verify_signature(csig_data.keyring_id,
+                       compat_ptr(csig_data.data), csig_data.datalen,
+                       compat_ptr(csig_data.sig), csig_data.siglen,
+                       csig_data.sig_type, csig_data.flags);
+}
+
 /*
  * Instantiate a key with the specified compatibility multipart payload and
  * link the key into the destination keyring if one is given.
@@ -141,6 +166,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
        case KEYCTL_GET_PERSISTENT:
                return keyctl_get_persistent(arg2, arg3);
 
+       case KEYCTL_VERIFY_SIGNATURE:
+               return compat_keyctl_verify_signature(compat_ptr(arg2));
+
        default:
                return -EOPNOTSUPP;
        }
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 80b2aac..f15acee 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -255,6 +255,8 @@ extern long keyctl_invalidate_key(key_serial_t);
 extern long keyctl_instantiate_key_common(key_serial_t,
                                          const struct iovec *,
                                          unsigned, size_t, key_serial_t);
+extern long keyctl_verify_signature(const void __user *_sig_data);
+extern long __keyctl_verify_signature(key_serial_t keyring_id, void __user 
*_data, size_t dlen, void __user *_sig, size_t siglen, unsigned long sig_type, 
unsigned long flags);
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 extern long keyctl_get_persistent(uid_t, key_serial_t);
 extern unsigned persistent_keyring_expiry;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index cee72ce..84b7c3d 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -23,6 +23,8 @@
 #include <linux/vmalloc.h>
 #include <linux/security.h>
 #include <linux/uio.h>
+#include <linux/ima.h>
+#include <keys/system_keyring.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
@@ -1564,6 +1566,80 @@ error_keyring:
        return ret;
 }
 
+long __keyctl_verify_signature(key_serial_t keyring_id, void __user *_data,
+                               size_t dlen, void __user *_sig, size_t siglen,
+                               unsigned long sig_type, unsigned long flags)
+{
+       void *sig;
+       long ret;
+       key_ref_t keyring_ref;
+
+       pr_devel("-->keyctl_verify_signature(,%zu,,%zu,%lu)\n",
+                       dlen, siglen, sig_type);
+
+       if (!_data || !dlen || !_sig || !siglen || !keyring_id)
+               return -EINVAL;
+       /*
+        * Possibly various signature handlers could scan signature and
+        * claim it belongs to them and verify.
+        */
+       if (sig_type == KEYCTL_SIG_TYPE_UNKNOWN)
+               return -EOPNOTSUPP;
+
+       /* Get the keyring which should be used */
+       keyring_ref = lookup_user_key(keyring_id, 0, KEY_SEARCH);
+       if (IS_ERR(keyring_ref))
+               return PTR_ERR(keyring_ref);
+
+
+       ret = -ENOMEM;
+       sig = kmalloc(siglen, GFP_KERNEL);
+       if (!sig)
+               goto error_keyref_put;
+
+       ret = -EFAULT;
+       if (copy_from_user(sig, _sig, siglen) != 0)
+               goto error_free_sig;
+
+       switch(sig_type) {
+       case KEYCTL_SIG_TYPE_INTEGRITY_DIGSIG:
+               ret = integrity_verify_user_buffer_digsig(
+                                       key_ref_to_ptr(keyring_ref),
+                                       _data, dlen, sig, siglen);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+error_free_sig:
+       kfree(sig);
+error_keyref_put:
+       key_ref_put(keyring_ref);
+       return ret;
+}
+
+/*
+ * Use a key to verify a signature.
+ *
+ * The key argument gives a key to use or a keyring in which a suitable key
+ * might be found.  The signature will be examined and an attempt will be made
+ * to determine the key to use from the information contained therein.
+ */
+long keyctl_verify_signature(const void __user *_sig_data)
+{
+       struct keyctl_sig_data sig_data;
+       int result;
+
+       result = copy_from_user(&sig_data, _sig_data, sizeof(sig_data));
+       if (result)
+               return -EFAULT;
+
+       return __keyctl_verify_signature(sig_data.keyring_id, sig_data.data,
+                               sig_data.datalen, sig_data.sig, sig_data.siglen,
+                               sig_data.sig_type, sig_data.flags);
+
+}
+
 /*
  * The key control system call
  */
@@ -1670,6 +1746,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, 
unsigned long, arg3,
        case KEYCTL_GET_PERSISTENT:
                return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
 
+       case KEYCTL_VERIFY_SIGNATURE:
+               return keyctl_verify_signature((const void __user *)arg2);
+
        default:
                return -EOPNOTSUPP;
        }
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to