[PATCH 10/16] keyctl: Introduce a new operation KEYCTL_VERIFY_SIGNATURE

Vivek Goyal
2013-09-10T21:45:00Z

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 <vgoyal@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