summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2010-06-02 15:44:28 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2010-06-17 20:47:39 +0200
commitf72b0d07a071d20cf27ed1cc160de5bd675dc992 (patch)
treeb756eec7fd8402419580ce28d4723a3a21cf20e6
parent331c71959cab925c9a87d471f8d22be435566651 (diff)
downloadkernel-crypto-f72b0d07a071d20cf27ed1cc160de5bd675dc992.tar.gz
kernel-crypto-f72b0d07a071d20cf27ed1cc160de5bd675dc992.tar.xz
kernel-crypto-f72b0d07a071d20cf27ed1cc160de5bd675dc992.zip
Added a netlink based storage fascility (server needs
to be implemented).
-rw-r--r--ncr-storage-low.c545
-rw-r--r--ncr-storage-low.h20
-rw-r--r--ncr-storage.c12
-rw-r--r--ncr-storage.h14
-rw-r--r--ncr.c2
5 files changed, 568 insertions, 25 deletions
diff --git a/ncr-storage-low.c b/ncr-storage-low.c
index d55356d1f53..47cd8e84d8e 100644
--- a/ncr-storage-low.c
+++ b/ncr-storage-low.c
@@ -23,26 +23,555 @@
#include <linux/highmem.h>
#include "cryptodev.h"
#include <asm/uaccess.h>
-#include <asm/ioctl.h>
-#include <linux/scatterlist.h>
+#include <net/genetlink.h>
+#include <linux/kernel.h>
+#include <linux/completion.h>
#include "ncr.h"
#include "ncr_int.h"
#include "ncr-storage.h"
+#include "ncr-storage-low.h"
-/* Actual storage to somewhere function
+/* The idea here is to have communication via netlink to userspace
+ * send our commands, put an id in the list and wait for completion of the
+ * request. The server should compute our request and reply with the id.
*/
-int _ncr_store(const struct storage_item_st * tostore)
+
+struct event_item_st {
+ struct list_head list;
+ struct completion completed;
+ void* reply;
+ size_t reply_size;
+ uint32_t id;
+};
+
+
+static struct list_sem_st event_list;
+
+static int event_add(uint32_t id)
{
+struct event_item_st* item;
+
+ item = kmalloc( sizeof(*item), GFP_KERNEL);
+ if (item == NULL) {
+ err();
+ return -ENOMEM;
+ }
+ item->id = id;
+ item->reply = NULL;
+ item->reply_size = 0;
+ init_completion(&item->completed);
+ down(&event_list.sem);
+ list_add(&item->list, &event_list.list);
+ up(&event_list.sem);
+ return 0;
}
-/* Loading from somewhere function
- */
-int _ncr_load(struct storage_item_st * loaded)
+static void event_wait(uint32_t id)
+{
+struct event_item_st* item;
+struct completion* completed = NULL;
+
+ down(&event_list.sem);
+
+ list_for_each_entry(item, &event_list.list, list) {
+ if (id == item->id) {
+ completed = &item->completed;
+ break;
+ }
+ }
+ up(&event_list.sem);
+ if (completed)
+ wait_for_completion(completed);
+}
+
+static void event_complete(uint32_t id)
+{
+struct event_item_st* item;
+
+ down(&event_list.sem);
+
+ list_for_each_entry(item, &event_list.list, list) {
+ if (id == item->id) {
+ complete(&item->completed);
+ break;
+ }
+ }
+ up(&event_list.sem);
+}
+
+static void event_set_data(uint32_t id, void* data, size_t data_size)
+{
+struct event_item_st* item;
+
+ down(&event_list.sem);
+
+ list_for_each_entry(item, &event_list.list, list) {
+ if (id == item->id) {
+ item->reply = data;
+ item->reply_size = data_size;
+ break;
+ }
+ }
+ up(&event_list.sem);
+
+ return;
+}
+
+static void* event_get_data(uint32_t id, size_t *reply_size)
{
-/* loaded must have owner and group set.
+struct event_item_st* item;
+void* reply = NULL;
+
+ down(&event_list.sem);
+
+ list_for_each_entry(item, &event_list.list, list) {
+ if (id == item->id) {
+ reply = &item->reply;
+ *reply_size = item->reply_size;
+ break;
+ }
+ }
+ up(&event_list.sem);
+
+ return reply;
+}
+
+static void event_remove(uint32_t id)
+{
+struct event_item_st* item, *tmp;
+
+ down(&event_list.sem);
+
+ list_for_each_entry_safe(item, tmp, &event_list.list, list) {
+ if (id == item->id) {
+ list_del(&item->list);
+ if (item->reply)
+ kfree(item->reply);
+ kfree(item);
+ break;
+ }
+ }
+ up(&event_list.sem);
+}
+
+
+/* attributes (variables): the index in this enum is used as a reference for the type,
+ * userspace application has to indicate the corresponding type
+ * the policy is used for security considerations
+ */
+enum {
+ ATTR_UNSPEC,
+ ATTR_DATA,
+ __ATTR_MAX,
+};
+#define ATTR_MAX (__ATTR_MAX - 1)
+
+/* attribute policy: defines which attribute has which type (e.g int, char * etc)
+ * possible values defined in net/netlink.h
+ */
+static struct nla_policy ncr_genl_policy[ATTR_MAX + 1] = {
+ [ATTR_DATA] = { .type = NLA_BINARY },
+};
+
+static atomic_t ncr_event_sr;
+static uint32_t listener_pid = -1;
+
+#define VERSION_NR 1
+/* family definition */
+static struct genl_family ncr_gnl_family = {
+ .id = GENL_ID_GENERATE, //genetlink should generate an id
+ .hdrsize = 0,
+ .name = "KEY_STORAGE", //the name of this family, used by userspace application
+ .version = VERSION_NR, //version number
+ .maxattr = ATTR_MAX,
+};
+
+/* commands: enumeration of all commands (functions),
+ * used by userspace application to identify command to be ececuted
*/
+enum {
+ CMD_LISTENING,
+ CMD_STORE,
+ CMD_LOAD,
+ CMD_STORE_ACK,
+ CMD_LOADED_DATA,
+ __CMD_MAX,
+};
+#define CMD_MAX (__CMD_MAX - 1)
+
+/* an echo command, receives a message, prints it and sends another message back */
+int _ncr_store(const struct storage_item_st * tostore)
+{
+ struct sk_buff *skb;
+ int ret;
+ uint8_t *reply;
+ size_t size, reply_size;
+ struct nlattr *attr;
+ void* msg, *msg_head;
+ uint32_t id;
+
+ if (listener_pid == -1) {
+ err();
+ return -EIO;
+ }
+
+ /* send a message back*/
+ /* allocate some memory, since the size is not yet known use NLMSG_GOODSIZE*/
+ size = nla_total_size(sizeof(struct storage_item_st)) +
+ nla_total_size(0);
+ skb = genlmsg_new(size, GFP_KERNEL);
+ if (skb == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ id = atomic_long_inc_return(&ncr_event_sr);
+ msg_head = genlmsg_put(skb, 0, id,
+ &ncr_gnl_family, 0, CMD_STORE);
+ if (msg_head == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* fill the data */
+ attr = nla_reserve(skb, ATTR_DATA,
+ sizeof(struct storage_item_st));
+ if (!attr) {
+ err();
+ ret = -EINVAL;
+ goto out;
+ }
+
+ msg = nla_data(attr);
+ if (!msg) {
+ err();
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(msg, tostore, sizeof(*tostore));
+
+ /* finalize the message */
+ genlmsg_end(skb, msg_head);
+
+ /* send the message back */
+ ret = genlmsg_unicast(skb, listener_pid);
+ if (ret != 0)
+ goto out;
+
+ /* wait for an acknowledgment */
+ event_wait(id);
+
+ reply = event_get_data(id, &reply_size);
+ if (reply_size < 1)
+ BUG();
+
+ if (reply[0] != 0) {
+ /* write failed */
+ ret = -EIO;
+ } else {
+ ret = 0;
+ }
+
+ event_remove(id);
+ return ret;
+
+out:
+ nlmsg_free(skb);
+ printk("an error occured in ncr_gnl_store\n");
+
+ return ret;
}
+
+/* an echo command, receives a message, prints it and sends another message back */
+int _ncr_load(struct storage_item_st * toload)
+{
+ struct sk_buff *skb;
+ int ret;
+ void *msg_head;
+ size_t size, reply_size;
+ struct nlattr *attr;
+ void* msg, *reply;
+ struct ncr_gnl_load_cmd_st cmd;
+ uint32_t id;
+
+ if (listener_pid == -1) {
+ err();
+ return -EIO;
+ }
+
+ /* send a message back*/
+ /* allocate some memory, since the size is not yet known use NLMSG_GOODSIZE*/
+ size = nla_total_size(sizeof(struct ncr_gnl_load_cmd_st)) +
+ nla_total_size(0);
+
+ skb = genlmsg_new(size, GFP_KERNEL);
+ if (skb == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ id = atomic_long_inc_return(&ncr_event_sr);
+ msg_head = genlmsg_put(skb, 0, id,
+ &ncr_gnl_family, 0, CMD_LOAD);
+ if (msg_head == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* fill the data */
+ attr = nla_reserve(skb, ATTR_DATA,
+ sizeof(struct ncr_gnl_load_cmd_st));
+ if (!attr) {
+ err();
+ ret = -EINVAL;
+ goto out;
+ }
+
+ msg = nla_data(attr);
+ if (!msg) {
+ err();
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cmd.owner = toload->owner;
+ cmd.group = toload->group;
+ strcpy(cmd.label, toload->label);
+
+ memcpy(msg, &cmd, sizeof(cmd));
+
+ /* finalize the message */
+ genlmsg_end(skb, msg_head);
+
+ ret = event_add(id);
+ if (ret < 0) {
+ err();
+ goto out;
+ }
+
+ /* send the message */
+ ret = genlmsg_unicast(skb, listener_pid);
+ if (ret != 0)
+ goto out;
+
+ /* wait for an answer */
+ event_wait(id);
+
+ reply = event_get_data(id, &reply_size);
+ if (reply_size != sizeof(struct storage_item_st))
+ BUG();
+
+ memcpy(toload, reply, reply_size);
+
+ event_remove(id);
+
+ return 0;
+
+out:
+ nlmsg_free(skb);
+ printk("an error occured in ncr_gnl_store\n");
+
+ return ret;
+}
+
+/* with this command the userspace server registers */
+int ncr_gnl_listen(struct sk_buff *skb, struct genl_info *info)
+{
+ if (info == NULL)
+ return -EIO;
+
+ listener_pid = info->snd_pid;
+
+ return 0;
+}
+
+
+/* with this command the userspace server registers */
+int ncr_gnl_store_ack(struct sk_buff *skb, struct genl_info *info)
+{
+ uint8_t * data, *event_reply;
+ size_t len;
+ struct ncr_gnl_store_ack_st reply;
+ struct nlattr *na;
+
+ if (info == NULL)
+ return -EIO;
+
+ /*for each attribute there is an index in info->attrs which points to a nlattr structure
+ *in this structure the data is given
+ */
+ na = info->attrs[ATTR_DATA];
+ if (na) {
+ len = nla_len(na);
+ data = (void *)nla_data(na);
+ if (data == NULL || len != sizeof(struct ncr_gnl_store_ack_st))
+ printk(KERN_DEBUG"error while receiving data\n");
+ else {
+
+ memcpy( &reply, data, sizeof(reply));
+ event_reply = kmalloc(1, GFP_KERNEL);
+ if (event_reply != NULL) {
+ if (reply.reply == 0)
+ event_reply[0] = 0;
+ else event_reply[0] = 1; /* fail */
+ event_set_data(reply.id, event_reply, 1);
+ }
+ event_complete(reply.id);
+ }
+ } else
+ printk(KERN_DEBUG"no info->attrs %i\n", ATTR_DATA);
+
+ return 0;
+}
+
+/* an echo command, receives a message, prints it and sends another message back */
+int ncr_gnl_loaded_data(struct sk_buff *skb, struct genl_info *info)
+{
+ uint8_t * data, *event_reply;
+ size_t len;
+ struct ncr_gnl_loaded_st reply;
+ struct nlattr *na;
+
+ if (info == NULL)
+ return -EIO;
+
+ /*for each attribute there is an index in info->attrs which points to a nlattr structure
+ *in this structure the data is given
+ */
+ na = info->attrs[ATTR_DATA];
+ if (na) {
+ len = nla_len(na);
+ data = (void *)nla_data(na);
+ if (data == NULL || len != sizeof(struct ncr_gnl_loaded_st))
+ printk(KERN_DEBUG"error while receiving data\n");
+ else {
+
+ memcpy( &reply, data, sizeof(reply));
+ event_reply = kmalloc(sizeof(reply.storage), GFP_KERNEL);
+ if (event_reply != NULL) {
+ memcpy(event_reply, &reply.storage, sizeof(reply.storage));
+ event_set_data(reply.id, event_reply, sizeof(reply.storage));
+ }
+ event_complete(reply.id);
+ }
+ } else
+ printk(KERN_DEBUG"no info->attrs %i\n", ATTR_DATA);
+
+ return 0;
+}
+
+
+/* commands: mapping between the command enumeration and the actual function*/
+struct genl_ops ncr_gnl_ops_listen = {
+ .cmd = CMD_LISTENING,
+ .flags = 0,
+ .policy = ncr_genl_policy,
+ .doit = ncr_gnl_listen,
+ .dumpit = NULL,
+};
+
+struct genl_ops ncr_gnl_ops_load = {
+ .cmd = CMD_LOADED_DATA,
+ .flags = 0,
+ .policy = ncr_genl_policy,
+ .doit = ncr_gnl_loaded_data,
+ .dumpit = NULL,
+};
+
+struct genl_ops ncr_gnl_ops_store_ack = {
+ .cmd = CMD_STORE_ACK,
+ .flags = 0,
+ .policy = ncr_genl_policy,
+ .doit = ncr_gnl_store_ack,
+ .dumpit = NULL,
+};
+
+
+
+int ncr_gnl_init(void)
+{
+ int rc;
+ printk("INIT GENERIC NETLINK EXEMPLE MODULE\n");
+
+ init_MUTEX(&event_list.sem);
+ INIT_LIST_HEAD(&event_list.list);
+ atomic_set(&ncr_event_sr, 0);
+
+ /*register new family*/
+ rc = genl_register_family(&ncr_gnl_family);
+ if (rc != 0)
+ goto failure;
+ /*register functions (commands) of the new family*/
+
+ rc = genl_register_ops(&ncr_gnl_family, &ncr_gnl_ops_listen);
+ if (rc != 0) {
+ printk("register ops: %i\n",rc);
+ genl_unregister_family(&ncr_gnl_family);
+ goto failure;
+ }
+
+ rc = genl_register_ops(&ncr_gnl_family, &ncr_gnl_ops_store_ack);
+ if (rc != 0) {
+ printk("register ops: %i\n",rc);
+ genl_unregister_family(&ncr_gnl_family);
+ goto failure;
+ }
+
+ rc = genl_register_ops(&ncr_gnl_family, &ncr_gnl_ops_load);
+ if (rc != 0) {
+ printk("register ops: %i\n",rc);
+ genl_unregister_family(&ncr_gnl_family);
+ goto failure;
+ }
+
+ return 0;
+
+ failure:
+ printk("an error occured while inserting the generic netlink example module\n");
+ return rc;
+}
+
+void ncr_gnl_deinit(void)
+{
+ int ret;
+ struct event_item_st *item, *tmp;
+ printk("EXIT GENERIC NETLINK EXEMPLE MODULE\n");
+
+ /* deinitialize the event list */
+ down(&event_list.sem);
+ list_for_each_entry_safe(item, tmp, &event_list.list, list) {
+ list_del(&item->list);
+ kfree(item);
+ }
+ up(&event_list.sem);
+
+ ret = genl_unregister_ops(&ncr_gnl_family, &ncr_gnl_ops_store_ack);
+ if(ret != 0) {
+ printk("unregister ops: %i\n",ret);
+ return;
+ }
+
+ ret = genl_unregister_ops(&ncr_gnl_family, &ncr_gnl_ops_load);
+ if(ret != 0) {
+ printk("unregister ops: %i\n",ret);
+ return;
+ }
+
+ ret = genl_unregister_ops(&ncr_gnl_family, &ncr_gnl_ops_listen);
+ if(ret != 0) {
+ printk("unregister ops: %i\n",ret);
+ return;
+ }
+
+ /*unregister the family*/
+ ret = genl_unregister_family(&ncr_gnl_family);
+ if(ret !=0) {
+ printk("unregister family %i\n",ret);
+ }
+}
diff --git a/ncr-storage-low.h b/ncr-storage-low.h
new file mode 100644
index 00000000000..2578ac9bf7b
--- /dev/null
+++ b/ncr-storage-low.h
@@ -0,0 +1,20 @@
+#ifndef _STORAGE_LOW
+#define _STORAGE_LOW
+
+struct ncr_gnl_load_cmd_st {
+ uint8_t label[MAX_LABEL_SIZE];
+ uint32_t owner;
+ uint32_t group;
+} __attribute__ ((__packed__));
+
+struct ncr_gnl_store_ack_st {
+ uint32_t id;
+ uint8_t reply;
+} __attribute__ ((__packed__));
+
+struct ncr_gnl_loaded_st {
+ uint32_t id;
+ struct storage_item_st storage;
+} __attribute__ ((__packed__));
+
+#endif
diff --git a/ncr-storage.c b/ncr-storage.c
index b5db4b1645b..15df0debc1c 100644
--- a/ncr-storage.c
+++ b/ncr-storage.c
@@ -58,11 +58,9 @@ int _ncr_key_to_store(const struct key_item_st *key, const char* label,
switch(key->type) {
case NCR_KEY_TYPE_SECRET:
/* uint16_t size + raw key */
- output->raw_key = kmalloc(sizeof(uint8_t)+sizeof(uint16_t)+MAX_KEY_SIZE, GFP_KERNEL);
- if (output->raw_key == NULL) {
- err();
- return -ENOMEM;
- }
+ if (sizeof(output->raw_key) < key->key.secret.size + 2)
+ BUG();
+
output->raw_key[0] = (key->key.secret.size >> 8) & 0xff;
output->raw_key[1] = (key->key.secret.size) & 0xff;
memcpy(&output->raw_key[2], key->key.secret.data, key->key.secret.size);
@@ -121,8 +119,6 @@ int ncr_storage_store(struct list_sem_st* key_lst, void __user* arg)
}
ret = _ncr_store(&tostore);
- kfree(tostore.raw_key);
-
if (ret < 0) {
err();
goto fail;
@@ -175,8 +171,6 @@ int ncr_storage_load(struct list_sem_st* key_lst, void __user* arg)
}
ret = _ncr_store_to_key(&loaded, key);
- kfree(loaded.raw_key);
-
if (ret < 0) {
err();
goto fail;
diff --git a/ncr-storage.h b/ncr-storage.h
index 74a583820eb..e1016e82b1a 100644
--- a/ncr-storage.h
+++ b/ncr-storage.h
@@ -1,11 +1,13 @@
#ifndef NCR_STORAGE_H
# define NCR_STORAGE_H
+#define MAX_RAW_KEY_SIZE 4096
+
struct storage_item_st {
/* metadata */
- char label[MAX_LABEL_SIZE];
- uid_t owner;
- gid_t group;
+ uint8_t label[MAX_LABEL_SIZE];
+ uint32_t owner;
+ uint32_t group;
mode_t mode;
uint16_t algorithm;
@@ -15,9 +17,9 @@ struct storage_item_st {
uint8_t key_id_size;
/* data */
- uint8_t * raw_key;
- size_t raw_key_size;
-};
+ uint8_t raw_key[MAX_RAW_KEY_SIZE];
+ uint16_t raw_key_size;
+} __attribute__ ((__packed__));
int ncr_storage_store(struct list_sem_st* key_lst, void __user* arg);
int ncr_storage_load(struct list_sem_st* key_lst, void __user* arg);
diff --git a/ncr.c b/ncr.c
index 90ec0184cda..4e2559be67a 100644
--- a/ncr.c
+++ b/ncr.c
@@ -30,8 +30,6 @@
#include "ncr.h"
#include "ncr_int.h"
-#define err() printk(KERN_DEBUG"ncr: %s: %d\n", __func__, __LINE__)
-
void* ncr_init_lists(void)
{
struct ncr_lists *lst;