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@xxxxxxxxxx> --- include/linux/compat.h | 4 +-- include/linux/syscalls.h | 3 +- include/uapi/linux/keyctl.h | 16 +++++++++ security/keys/compat.c | 31 ++++++++++++++++- security/keys/internal.h | 2 ++ security/keys/keyctl.c | 83 +++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 133 insertions(+), 6 deletions(-) diff --git a/include/linux/compat.h b/include/linux/compat.h index 7f0c1dd..fdb20cc 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -322,8 +322,8 @@ long compat_sys_msgctl(int first, int second, void __user *uptr); long compat_sys_shmctl(int first, int second, void __user *uptr); long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, unsigned nsems, const struct compat_timespec __user *timeout); -asmlinkage long compat_sys_keyctl(u32 option, - u32 arg2, u32 arg3, u32 arg4, u32 arg5); +asmlinkage long compat_sys_keyctl(u32 option, u32 arg2, u32 arg3, u32 arg4, + u32 arg5, u32 arg6); asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u32); asmlinkage ssize_t compat_sys_readv(unsigned long fd, diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 84662ec..f04e62a 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -690,7 +690,8 @@ asmlinkage long sys_request_key(const char __user *_type, key_serial_t destringid); asmlinkage long sys_keyctl(int cmd, unsigned long arg2, unsigned long arg3, - unsigned long arg4, unsigned long arg5); + unsigned long arg4, unsigned long arg5, + unsigned long arg6); asmlinkage long sys_ioprio_set(int which, int who, int ioprio); asmlinkage long sys_ioprio_get(int which, int who); 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..37c7afa 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. @@ -66,7 +91,8 @@ no_payload: * directly. */ asmlinkage long compat_sys_keyctl(u32 option, - u32 arg2, u32 arg3, u32 arg4, u32 arg5) + u32 arg2, u32 arg3, u32 arg4, u32 arg5, + u32 arg6) { switch (option) { case KEYCTL_GET_KEYRING_ID: @@ -141,6 +167,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..8ee6035 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,11 +1566,85 @@ 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 */ -SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, - unsigned long, arg4, unsigned long, arg5) +SYSCALL_DEFINE6(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, + unsigned long, arg4, unsigned long, arg5, unsigned long, arg6) { switch (option) { case KEYCTL_GET_KEYRING_ID: @@ -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 _______________________________________________ kernel mailing list kernel@xxxxxxxxxxxxxxxxxxxxxxx https://admin.fedoraproject.org/mailman/listinfo/kernel