[PATCH 18/19] Add ioctl handlers

From: Miloslav TrmaÄ
Date: Fri Aug 20 2010 - 04:47:40 EST


Add ioctl and compat_ioctl handling. This is the only file that
directly accesses structured data from userspace (other files may access
unformated data such as cipher input or multiple-precision integers).

Also add the last operation, ncr_master_key_set.
---
crypto/userspace/ncr.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 405 insertions(+), 0 deletions(-)

diff --git a/crypto/userspace/ncr.c b/crypto/userspace/ncr.c
index 9fb56ad..ee812ab 100644
--- a/crypto/userspace/ncr.c
+++ b/crypto/userspace/ncr.c
@@ -44,6 +44,411 @@
*/
struct key_item_st master_key;

+static struct mutex lists_ida_mutex;
+static DEFINE_IDA(lists_ida);
+
+struct ncr_lists *ncr_init_lists(void)
+{
+ struct ncr_lists *lst;
+
+ lst = kmalloc(sizeof(*lst), GFP_KERNEL);
+ if(!lst) {
+ err();
+ return NULL;
+ }
+
+ memset(lst, 0, sizeof(*lst));
+
+ mutex_init(&lists_ida_mutex);
+ mutex_lock(&lists_ida_mutex);
+ /* ida_pre_get() should preallocate enough, and, due to lists_ida_mutex,
+ nobody else can use the preallocated data. Therefore the loop
+ recommended in idr_get_new() documentation is not necessary. */
+ if (ida_pre_get(&lists_ida, GFP_KERNEL) == 0 ||
+ ida_get_new(&lists_ida, &lst->id) != 0) {
+ mutex_unlock(&lists_ida_mutex);
+ kfree(lst);
+ return NULL;
+ }
+ mutex_unlock(&lists_ida_mutex);
+
+ mutex_init(&lst->key_idr_mutex);
+ idr_init(&lst->key_idr);
+
+ mutex_init(&lst->session_idr_mutex);
+ idr_init(&lst->session_idr);
+
+ return lst;
+}
+
+void ncr_deinit_lists(struct ncr_lists *lst)
+{
+ if(lst) {
+ ncr_key_list_deinit(lst);
+ ncr_sessions_list_deinit(lst);
+
+ mutex_lock(&lists_ida_mutex);
+ ida_remove(&lists_ida, lst->id);
+ mutex_unlock(&lists_ida_mutex);
+
+ kfree(lst);
+ }
+}
+
+void ncr_master_key_reset(void)
+{
+ memset(&master_key, 0, sizeof(master_key));
+}
+
+static int ncr_master_key_set(const struct ncr_master_key_set *st,
+ struct nlattr *tb[])
+{
+struct audit_buffer *ab;
+int ret;
+
+ /* This will also cause auditing of the syscall, including information
+ about the process, and success/failure indication. Note that on
+ error the AUDIT_CRYPTO_STORAGE_KEY record will be empty. */
+ ab = audit_log_start(current->audit_context, GFP_KERNEL,
+ AUDIT_CRYPTO_STORAGE_KEY);
+
+ if (current_euid() != 0 && !capable(CAP_SYS_ADMIN)) {
+ err();
+ ret = -EPERM;
+ goto end;
+ }
+
+ if (st->key_size > sizeof(master_key.key.secret.data)) {
+ err();
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (st->key_size != 16 && st->key_size != 24 && st->key_size != 32) {
+ dprintk(0, KERN_DEBUG, "Master key size must be 16,24 or 32.\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (master_key.type != NCR_KEY_TYPE_INVALID) {
+ dprintk(0, KERN_DEBUG, "Master key was previously initialized.\n");
+ }
+
+ if (unlikely(copy_from_user(master_key.key.secret.data, st->key,
+ st->key_size))) {
+ err();
+ ret = -EFAULT;
+ goto end;
+ }
+
+ dprintk(0, KERN_INFO, "Initializing master key.\n");
+ /* Not much we can reveal... */
+ audit_log_format(ab, "key_size=%u", (unsigned)st->key_size);
+
+ master_key.type = NCR_KEY_TYPE_SECRET;
+ master_key.key.secret.size = st->key_size;
+
+ ret = 0;
+
+end:
+ audit_log_end(ab);
+
+ return ret;
+}
+
+int
+ncr_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg_)
+{
+ void __user *arg = (void __user *)arg_;
+ struct nlattr *tb[NCR_ATTR_MAX + 1];
+ void *attr_buf;
+ int ret;
+
+ if (unlikely(!lst))
+ BUG();
+
+ switch (cmd) {
+#define CASE_(LABEL, STRUCT, FUNCTION, ARGS) \
+ case (LABEL): { \
+ struct STRUCT data; \
+ \
+ attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&data, tb, arg); \
+ if (IS_ERR(attr_buf)) { \
+ err(); \
+ return PTR_ERR(attr_buf); \
+ } \
+ ret = (FUNCTION)ARGS; \
+ break; \
+ }
+#define CASE_NO_OUTPUT(LABEL, STRUCT, FUNCTION) \
+ CASE_(LABEL, STRUCT, FUNCTION, (lst, &data, tb))
+#define CASE_NO_OUTPUT_COMPAT(LABEL, STRUCT, FUNCTION) \
+ CASE_(LABEL, STRUCT, FUNCTION, (lst, &data, tb, 0))
+
+ case NCRIO_KEY_INIT:
+ return ncr_key_init(lst);
+ CASE_NO_OUTPUT(NCRIO_KEY_GENERATE, ncr_key_generate, ncr_key_generate);
+ CASE_NO_OUTPUT(NCRIO_KEY_GENERATE_PAIR, ncr_key_generate_pair,
+ ncr_key_generate_pair);
+ CASE_NO_OUTPUT(NCRIO_KEY_DERIVE, ncr_key_derive, ncr_key_derive);
+ case NCRIO_KEY_GET_INFO: {
+ struct ncr_key_get_info data;
+ struct ncr_out out;
+
+ attr_buf = NCR_GET_INPUT_ARGS(&data, tb, arg);
+ if (IS_ERR(attr_buf)) {
+ err();
+ return PTR_ERR(attr_buf);
+ }
+ ret = NCR_OUT_INIT(&out, &data, arg);
+ if (ret != 0) {
+ err();
+ break;
+ }
+ ret = ncr_key_get_info(lst, &out, &data, tb);
+ ncr_out_free(&out);
+ break;
+ }
+ CASE_NO_OUTPUT(NCRIO_KEY_EXPORT, ncr_key_export, ncr_key_export);
+ CASE_NO_OUTPUT(NCRIO_KEY_IMPORT, ncr_key_import, ncr_key_import);
+ case NCRIO_KEY_DEINIT: {
+ ncr_key_t key;
+
+ ret = get_user(key, (const ncr_key_t __user *)arg);
+ if (unlikely(ret)) {
+ err();
+ return ret;
+ }
+ return ncr_key_deinit(lst, key);
+ }
+ CASE_NO_OUTPUT(NCRIO_KEY_WRAP, ncr_key_wrap, ncr_key_wrap);
+ CASE_NO_OUTPUT(NCRIO_KEY_UNWRAP, ncr_key_unwrap, ncr_key_unwrap);
+ CASE_NO_OUTPUT(NCRIO_KEY_STORAGE_WRAP, ncr_key_storage_wrap,
+ ncr_key_storage_wrap);
+ CASE_NO_OUTPUT(NCRIO_KEY_STORAGE_UNWRAP, ncr_key_storage_unwrap,
+ ncr_key_storage_unwrap);
+ CASE_NO_OUTPUT(NCRIO_SESSION_INIT, ncr_session_init, ncr_session_init);
+ CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_UPDATE, ncr_session_update,
+ ncr_session_update);
+ CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_FINAL, ncr_session_final,
+ ncr_session_final);
+ CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_ONCE, ncr_session_once,
+ ncr_session_once);
+ CASE_(NCRIO_MASTER_KEY_SET, ncr_master_key_set, ncr_master_key_set,
+ (&data, tb));
+ default:
+ return -EINVAL;
+#undef CASE_
+#undef CASE_NO_OUTPUT
+#undef CASE_NO_OUTPUT_COMPAT
+ }
+ kfree(attr_buf);
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+struct compat_ncr_key_export {
+ __u32 input_size, output_size;
+ ncr_key_t key;
+ compat_uptr_t buffer;
+ compat_int_t buffer_size;
+ __NL_ATTRIBUTES;
+};
+#define COMPAT_NCRIO_KEY_EXPORT _IOWR('c', 209, struct compat_ncr_key_export)
+
+static void convert_ncr_key_export(struct ncr_key_export *new,
+ const struct compat_ncr_key_export *old)
+{
+ new->key = old->key;
+ new->buffer = compat_ptr(old->buffer);
+ new->buffer_size = old->buffer_size;
+}
+
+struct compat_ncr_key_import {
+ __u32 input_size, output_size;
+ ncr_key_t key;
+ compat_uptr_t data;
+ __u32 data_size;
+ __NL_ATTRIBUTES;
+};
+#define COMPAT_NCRIO_KEY_IMPORT _IOWR('c', 210, struct compat_ncr_key_import)
+
+static void convert_ncr_key_import(struct ncr_key_import *new,
+ const struct compat_ncr_key_import *old)
+{
+ new->key = old->key;
+ new->data = compat_ptr(old->data);
+ new->data_size = old->data_size;
+}
+
+struct compat_ncr_key_wrap {
+ __u32 input_size, output_size;
+ ncr_key_t wrapping_key;
+ ncr_key_t source_key;
+ compat_uptr_t buffer;
+ compat_int_t buffer_size;
+ __NL_ATTRIBUTES;
+};
+#define COMPAT_NCRIO_KEY_WRAP _IOWR('c', 250, struct compat_ncr_key_wrap)
+
+static void convert_ncr_key_wrap(struct ncr_key_wrap *new,
+ const struct compat_ncr_key_wrap *old)
+{
+ new->wrapping_key = old->wrapping_key;
+ new->source_key = old->source_key;
+ new->buffer = compat_ptr(old->buffer);
+ new->buffer_size = old->buffer_size;
+}
+
+struct compat_ncr_key_unwrap {
+ __u32 input_size, output_size;
+ ncr_key_t wrapping_key;
+ ncr_key_t dest_key;
+ compat_uptr_t data;
+ __u32 data_size;
+ __NL_ATTRIBUTES;
+};
+#define COMPAT_NCRIO_KEY_UNWRAP _IOWR('c', 251, struct compat_ncr_key_unwrap)
+
+static void convert_ncr_key_unwrap(struct ncr_key_unwrap *new,
+ const struct compat_ncr_key_unwrap *old)
+{
+ new->wrapping_key = old->wrapping_key;
+ new->dest_key = old->dest_key;
+ new->data = compat_ptr(old->data);
+ new->data_size = old->data_size;
+}
+
+struct compat_ncr_key_storage_wrap {
+ __u32 input_size, output_size;
+ ncr_key_t key;
+ compat_uptr_t buffer;
+ compat_int_t buffer_size;
+ __NL_ATTRIBUTES;
+};
+#define COMPAT_NCRIO_KEY_STORAGE_WRAP \
+ _IOWR('c', 261, struct compat_ncr_key_storage_wrap)
+
+static void convert_ncr_key_storage_wrap(struct ncr_key_storage_wrap *new,
+ const struct compat_ncr_key_storage_wrap *old)
+{
+ new->key = old->key;
+ new->buffer = compat_ptr(old->buffer);
+ new->buffer_size = old->buffer_size;
+}
+
+struct compat_ncr_key_storage_unwrap {
+ __u32 input_size, output_size;
+ ncr_key_t key;
+ compat_uptr_t data;
+ __u32 data_size;
+ __NL_ATTRIBUTES;
+};
+#define COMPAT_NCRIO_KEY_STORAGE_UNWRAP \
+ _IOWR('c', 262, struct compat_ncr_key_storage_wrap)
+
+static void convert_ncr_key_storage_unwrap(struct ncr_key_storage_unwrap *new,
+ const struct compat_ncr_key_storage_unwrap *old)
+{
+ new->key = old->key;
+ new->data = compat_ptr(old->data);
+ new->data_size = old->data_size;
+}
+
+struct compat_ncr_master_key_set {
+ __u32 input_size, output_size;
+ compat_uptr_t key;
+ __u32 key_size;
+ __NL_ATTRIBUTES;
+};
+#define COMPAT_NCRIO_MASTER_KEY_SET \
+ _IOWR('c', 260, struct compat_ncr_master_key_set)
+
+static void convert_ncr_master_key_set(struct ncr_master_key_set *new,
+ const struct compat_ncr_master_key_set *old)
+{
+ new->key = compat_ptr(old->key);
+ new->key_size = old->key_size;
+}
+
+long
+ncr_compat_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg_)
+{
+ void __user *arg = (void __user *)arg_;
+ struct nlattr *tb[NCR_ATTR_MAX + 1];
+ void *attr_buf;
+ int ret;
+
+ if (unlikely(!lst))
+ BUG();
+
+ switch (cmd) {
+ case NCRIO_KEY_INIT:
+ case NCRIO_KEY_GENERATE:
+ case NCRIO_KEY_GENERATE_PAIR:
+ case NCRIO_KEY_DERIVE:
+ case NCRIO_KEY_GET_INFO:
+ case NCRIO_KEY_DEINIT:
+ case NCRIO_SESSION_INIT:
+ return ncr_ioctl(lst, cmd, arg_);
+
+#define CASE_(LABEL, STRUCT, FUNCTION, ARGS) \
+ case (LABEL): { \
+ struct compat_##STRUCT old; \
+ struct STRUCT new; \
+ \
+ attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&old, tb, arg); \
+ if (IS_ERR(attr_buf)) { \
+ err(); \
+ return PTR_ERR(attr_buf); \
+ } \
+ convert_##STRUCT(&new, &old); \
+ ret = (FUNCTION)ARGS; \
+ break; \
+ }
+#define CASE_NO_OUTPUT(LABEL, STRUCT, FUNCTION) \
+ CASE_(LABEL, STRUCT, FUNCTION, (lst, &new, tb))
+
+#define CASE_COMPAT_ONLY(LABEL, STRUCT, FUNCTION) \
+ case (LABEL): { \
+ struct STRUCT data; \
+ \
+ attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&data, tb, arg); \
+ if (IS_ERR(attr_buf)) { \
+ err(); \
+ return PTR_ERR(attr_buf); \
+ } \
+ ret = (FUNCTION)(lst, &data, tb, 1); \
+ break; \
+ }
+
+ CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_EXPORT, ncr_key_export, ncr_key_export);
+ CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_IMPORT, ncr_key_import, ncr_key_import);
+ CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_WRAP, ncr_key_wrap, ncr_key_wrap);
+ CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_UNWRAP, ncr_key_unwrap, ncr_key_unwrap);
+ CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_STORAGE_WRAP, ncr_key_storage_wrap,
+ ncr_key_storage_wrap);
+ CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_STORAGE_UNWRAP, ncr_key_storage_unwrap,
+ ncr_key_storage_unwrap);
+ CASE_COMPAT_ONLY(NCRIO_SESSION_UPDATE, ncr_session_update,
+ ncr_session_update);
+ CASE_COMPAT_ONLY(NCRIO_SESSION_FINAL, ncr_session_final,
+ ncr_session_final);
+ CASE_COMPAT_ONLY(NCRIO_SESSION_ONCE, ncr_session_once,
+ ncr_session_once);
+ CASE_(COMPAT_NCRIO_MASTER_KEY_SET, ncr_master_key_set,
+ ncr_master_key_set, (&new, tb));
+ default:
+ return -EINVAL;
+#undef CASE_
+#undef CASE_NO_OUTPUT
+#undef CASE_COMPAT_ONLY
+ }
+ kfree(attr_buf);
+ return ret;
+}
+#endif
+
int ncr_session_input_data_from_nla(struct ncr_session_input_data *dest,
const struct nlattr *nla, int compat)
{
--
1.7.2.1

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