blob: e794277d3d11c0a89d09eb07a5a9429b33574a99 [file] [log] [blame]
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
*
* Sensitive Data Protection
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <sdp/dek_common.h>
typedef struct __kek_pack {
int engine_id;
int user_id;
struct list_head list;
struct list_head kek_list_head;
spinlock_t kek_list_lock;
}kek_pack_t;
typedef struct __kek_item {
struct list_head list;
int kek_type;
kek_t kek;
}kek_item_t;
#define KEK_PACK_DEBUG 0
#if KEK_PACK_DEBUG
#define KEK_PACK_LOGD(FMT, ...) printk("KEK_PACK[%d] %s :: " FMT , current->pid, __func__, ##__VA_ARGS__)
#else
#define KEK_PACK_LOGD(FMT, ...)
#endif /* PUB_CRYPTO_DEBUG */
#define KEK_PACK_LOGE(FMT, ...) printk("KEK_PACK[%d] %s :: " FMT , current->pid, __func__, ##__VA_ARGS__)
struct list_head kek_pack_list_head;
spinlock_t kek_pack_list_lock;
spinlock_t del_kek_pack_lock;
void init_kek_pack(void) {
spin_lock_init(&kek_pack_list_lock);
spin_lock_init(&del_kek_pack_lock);
INIT_LIST_HEAD(&kek_pack_list_head);
}
static kek_pack_t *find_kek_pack(int engine_id) {
struct list_head *entry;
spin_lock(&kek_pack_list_lock);
list_for_each(entry, &kek_pack_list_head) {
kek_pack_t *pack = list_entry(entry, kek_pack_t, list);
if(pack->engine_id == engine_id) {
KEK_PACK_LOGD("Found kek-pack : %d\n", engine_id);
spin_unlock(&kek_pack_list_lock);
return pack;
}
}
spin_unlock(&kek_pack_list_lock);
KEK_PACK_LOGE("Can't find kek-pack : %d\n", engine_id);
return NULL;
}
static int __add_kek(kek_pack_t *pack, kek_t *kek, kek_item_t *item) {
if(kek == NULL) return -EINVAL;
if(pack == NULL) return -EINVAL;
INIT_LIST_HEAD(&item->list);
item->kek_type = kek->type;
memcpy(&item->kek, kek, sizeof(kek_t));
list_add_tail(&item->list, &pack->kek_list_head);
KEK_PACK_LOGD("item %p\n", item);
return 0;
}
static kek_item_t *find_kek_item(kek_pack_t *pack, int kek_type) {
struct list_head *entry;
if(pack == NULL) return NULL;
list_for_each(entry, &pack->kek_list_head) {
kek_item_t *item = list_entry(entry, kek_item_t, list);
if(item->kek_type == kek_type) {
KEK_PACK_LOGD("Found kek-item : %d\n", kek_type);
return item;
}
}
KEK_PACK_LOGD("Can't find kek %d : %d\n", kek_type, pack->engine_id);
return NULL;
}
static void del_kek_item(kek_item_t *item) {
KEK_PACK_LOGD("entered\n");
if(item) {
list_del(&item->list);
kzfree(item);
} else {
KEK_PACK_LOGD("given item is NULL\n");
}
}
int add_kek_pack(int engine_id, int user_id) {
kek_pack_t *new_kek_pack;
KEK_PACK_LOGD("entered\n");
if(find_kek_pack(engine_id)) {
KEK_PACK_LOGE("kek-pack for %d already exists\n", engine_id);
return -EEXIST;
}
new_kek_pack = kmalloc(sizeof(kek_pack_t), GFP_KERNEL);
if(new_kek_pack == NULL) {
KEK_PACK_LOGE("can't alloc memory for kek_pack_t\n");
return -EINVAL;
}
new_kek_pack->engine_id = engine_id;
new_kek_pack->user_id = user_id;
spin_lock_init(&new_kek_pack->kek_list_lock);
INIT_LIST_HEAD(&new_kek_pack->kek_list_head);
spin_lock(&kek_pack_list_lock);
list_add_tail(&new_kek_pack->list, &kek_pack_list_head);
spin_unlock(&kek_pack_list_lock);
return 0;
}
void del_kek_pack(int engine_id) {
struct list_head *entry, *q;
kek_pack_t *pack;
spin_lock(&del_kek_pack_lock);
KEK_PACK_LOGD("entered\n");
pack = find_kek_pack(engine_id);
if(pack == NULL) {
spin_unlock(&del_kek_pack_lock);
return;
}
spin_lock(&pack->kek_list_lock);
list_for_each_safe(entry, q, &pack->kek_list_head) {
kek_item_t *item = list_entry(entry, kek_item_t, list);
KEK_PACK_LOGD("entry %p item %p\n", entry, item);
del_kek_item(item);
}
spin_unlock(&pack->kek_list_lock);
list_del(&pack->list);
kzfree(pack);
spin_unlock(&del_kek_pack_lock);
}
int add_kek(int engine_id, kek_t *kek) {
int rc;
kek_pack_t *pack;
kek_item_t *item;
KEK_PACK_LOGD("entered\n");
pack = find_kek_pack(engine_id);
if(pack == NULL) return -ENOENT;
item = kmalloc(sizeof(kek_item_t), GFP_KERNEL);
if(item == NULL) {
rc = -ENOMEM;
} else {
spin_lock(&pack->kek_list_lock);
if(find_kek_item(pack, kek->type)) {
spin_unlock(&pack->kek_list_lock);
kzfree(item);
return -EEXIST;
}
rc = __add_kek(pack, kek, item);
spin_unlock(&pack->kek_list_lock);
}
if(rc) KEK_PACK_LOGE("%s failed. rc = %d", __func__, rc);
return rc;
}
int del_kek(int engine_id, int kek_type) {
kek_pack_t *pack;
kek_item_t *item;
KEK_PACK_LOGD("entered\n");
pack = find_kek_pack(engine_id);
if(pack == NULL) return -ENOENT;
spin_lock(&pack->kek_list_lock);
item = find_kek_item(pack, kek_type);
if(item == NULL) {
spin_unlock(&pack->kek_list_lock);
return -ENOENT;
}
del_kek_item(item);
spin_unlock(&pack->kek_list_lock);
return 0;
}
/*
* I wanted to have some ref-count for kek_item_t that doesn't disturb
* ongoing dek process. But the returned kek won't be zero-freed if the process
* never returns.
*
* So allocate new memory and let the user call put accordingly
*/
kek_t *get_kek(int engine_id, int kek_type, int *rc) {
kek_pack_t *pack;
kek_item_t *item;
kek_t *kek;
int userid = from_kuid(&init_user_ns, current_uid()) / PER_USER_RANGE;
KEK_PACK_LOGD("entered [%d]\n", from_kuid(&init_user_ns, current_uid()));
pack = find_kek_pack(engine_id);
if(pack == NULL) {
*rc = -ENOENT;
return NULL;
}
// Across user engine access denied for Knox containers.
if(!is_root() &&
(pack->user_id >= 100 && pack->user_id < 200) &&
(pack->user_id != userid)) {
KEK_PACK_LOGE("Permission denied to get kek\n");
KEK_PACK_LOGE("pack->user_id[%d] != userid[%d]\n",
pack->user_id, userid);
*rc = -EACCES;
return NULL;
}
kek = kmalloc(sizeof(kek_t), GFP_KERNEL);
if (kek == NULL) {
*rc = -ENOMEM;
return NULL;
}
spin_lock(&pack->kek_list_lock);
item = find_kek_item(pack, kek_type);
if (item) {
*rc = 0;
memcpy(kek, &item->kek, sizeof(kek_t));
spin_unlock(&pack->kek_list_lock);
return kek;
} else {
spin_unlock(&pack->kek_list_lock);
kzfree(kek);
}
*rc = -ENOENT;
return NULL;
}
void put_kek(kek_t *kek) {
KEK_PACK_LOGD("entered\n");
if(kek) kzfree(kek);
}
int is_kek_pack(int engine_id) {
kek_pack_t *pack;
KEK_PACK_LOGD("entered\n");
pack = find_kek_pack(engine_id);
if(pack) return 1;
return 0;
}
int is_kek(int engine_id, int kek_type) {
kek_pack_t *pack;
kek_item_t *item;
KEK_PACK_LOGD("entered\n");
pack = find_kek_pack(engine_id);
if(pack == NULL) return 0;
spin_lock(&pack->kek_list_lock);
item = find_kek_item(pack, kek_type);
spin_unlock(&pack->kek_list_lock);
if(item) {
return 1;
}
return 0;
}