android_kernel_samsung_msm8226/security/sdp/dek.c

1186 lines
29 KiB
C

/*
* 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/fs.h>
#include <linux/fcntl.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
#include <linux/time.h>
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/random.h>
#include <linux/err.h>
#include <sdp/dek_common.h>
#include <sdp/dek_ioctl.h>
#include <sdp/dek_aes.h>
#include <sdp/kek_pack.h>
/*
* Need to move this to defconfig
*/
#define CONFIG_PUB_CRYPTO
//#define CONFIG_SDP_IOCTL_PRIV
#ifdef CONFIG_PUB_CRYPTO
#include <sdp/pub_crypto_emul.h>
#endif
#define DEK_LOG_COUNT 100
extern void ecryptfs_mm_drop_cache(int userid, int engineid);
/* Log buffer */
struct log_struct
{
int len;
char buf[256];
struct list_head list;
spinlock_t list_lock;
};
struct log_struct log_buffer;
static int log_count = 0;
/* Wait queue */
wait_queue_head_t wq;
static int flag = 0;
int dek_is_sdp_uid(uid_t uid) {
int userid = uid / PER_USER_RANGE;
return is_kek_pack(userid);
}
EXPORT_SYMBOL(dek_is_sdp_uid);
int is_system_server(void) {
uid_t uid = current_uid();
switch(uid) {
#if 0
case 0: //root
DEK_LOGD("allowing root to access SDP device files\n");
#endif
case 1000:
return 1;
default:
break;
}
return 0;
}
int is_root(void) {
uid_t uid = current_uid();
switch(uid) {
case 0: //root
//DEK_LOGD("allowing root to access SDP device files\n");
return 1;
default:
;
}
return 0;
}
int is_current_adbd() {
DEK_LOGD("current->comm : %s\n", current->comm);
#if 1
if(is_root()) {
// epmd/vold are 4 length string
if(strlen(current->comm) == 4)
if(strcmp(current->comm, "adbd"))
return 1;
}
return 0;
#else
return 1;
#endif
}
int is_current_epmd() {
DEK_LOGD("current->comm : %s\n", current->comm);
#if 1
if(is_root()) {
// epmd/vold are 4 length string
if(strlen(current->comm) == 4)
if(strcmp(current->comm, "vold") || strcmp(current->comm, "epmd"))
return 1;
}
return 0;
#else
return 1;
#endif
}
static int zero_out(char *buf, unsigned int len) {
char zero = 0;
int retry_cnt = 3;
int i;
retry:
if(retry_cnt > 0) {
memset((void *)buf, zero, len);
for (i = 0; i < len; ++i) {
zero |= buf[i];
if (zero) {
DEK_LOGE("the memory was not properly zeroed\n");
retry_cnt--;
goto retry;
}
}
} else {
DEK_LOGE("FATAL : can't zero out!!\n");
return -1;
}
return 0;
}
/* Log */
static void dek_add_to_log(int engine_id, char * buffer);
static int dek_open_evt(struct inode *inode, struct file *file)
{
return 0;
}
static int dek_release_evt(struct inode *ignored, struct file *file)
{
return 0;
}
static int dek_open_req(struct inode *inode, struct file *file)
{
return 0;
}
static int dek_release_req(struct inode *ignored, struct file *file)
{
return 0;
}
#ifdef CONFIG_SDP_KEY_DUMP
void key_dump(unsigned char *buf, int len) {
int i;
printk("len=%d: ", len);
for(i=0;i<len;++i) {
if((i%16) == 0)
printk("\n");
printk("%02X ", (unsigned char)buf[i]);
}
printk("\n");
}
static void kek_dump(int engine_id, int kek_type, const char *kek_name) {
kek_t *kek;
int ret;
kek = get_kek(engine_id, kek_type, &ret);
if(kek) {
printk("dek: %s: ", kek_name);
key_dump(kek->buf, kek->len);
put_kek(kek);
} else {
printk("dek: %s: empty\n", kek_name);
}
}
static void dump_all_keys(int engine_id) {
kek_dump(engine_id, KEK_TYPE_SYM, "KEK_TYPE_SYM");
kek_dump(engine_id, KEK_TYPE_RSA_PUB, "KEK_TYPE_RSA_PUB");
kek_dump(engine_id, KEK_TYPE_RSA_PRIV, "KEK_TYPE_RSA_PRIV");
kek_dump(engine_id, KEK_TYPE_DH_PUB, "KEK_TYPE_DH_PUB");
kek_dump(engine_id, KEK_TYPE_DH_PRIV, "KEK_TYPE_DH_PRIV");
kek_dump(engine_id, KEK_TYPE_ECDH256_PUB, "KEK_TYPE_ECDH256_PUB");
kek_dump(engine_id, KEK_TYPE_ECDH256_PRIV, "KEK_TYPE_ECDH256_PRIV");
}
#endif
int dek_is_locked(int engine_id) {
if(is_kek(engine_id, KEK_TYPE_SYM))
return 0;
return 1;
}
int dek_generate_dek(int engine_id, dek_t *newDek) {
newDek->len = DEK_LEN;
get_random_bytes(newDek->buf, newDek->len);
if (newDek->len <= 0 || newDek->len > DEK_LEN) {
zero_out((char *)newDek, sizeof(dek_t));
return -EFAULT;
}
#ifdef CONFIG_SDP_KEY_DUMP
else {
if(get_sdp_sysfs_key_dump()) {
DEK_LOGD("DEK: ");
key_dump(newDek->buf, newDek->len);
}
}
#endif
return 0;
}
static int dek_encrypt_dek(int engine_id, dek_t *plainDek, dek_t *encDek) {
int ret = 0;
kek_t *kek;
#ifdef CONFIG_SDP_KEY_DUMP
if(get_sdp_sysfs_key_dump()) {
DEK_LOGD("plainDek from user: ");
key_dump(plainDek->buf, plainDek->len);
}
#endif
kek = get_kek(engine_id, KEK_TYPE_SYM, &ret);
if (kek) {
if (dek_aes_encrypt(kek, plainDek->buf, encDek->buf, plainDek->len)) {
DEK_LOGE("aes encrypt failed\n");
dek_add_to_log(engine_id, "aes encrypt failed");
encDek->len = 0;
} else {
encDek->len = plainDek->len;
encDek->type = DEK_TYPE_AES_ENC;
}
put_kek(kek);
} else {
#ifdef CONFIG_PUB_CRYPTO
/*
* Do an asymmetric crypto
*/
switch(get_sdp_sysfs_asym_alg()) {
case SDPK_ALGOTYPE_ASYMM_ECDH:
kek = get_kek(engine_id, KEK_TYPE_ECDH256_PUB, &ret);
if(kek) {
ret = ecdh_encryptDEK(plainDek, encDek, kek);
put_kek(kek);
break;
}else{
if(ret == -EACCES) return ret;
DEK_LOGE("no KEK_TYPE_ECDH256_PUB : %d\n", engine_id);
dek_add_to_log(engine_id, "encrypt failed, no KEK_TYPE_ECDH256_PUB");
}
// no ECDH, try DH
/* no break */
case SDPK_ALGOTYPE_ASYMM_DH:
kek = get_kek(engine_id, KEK_TYPE_DH_PUB, &ret);
if(kek) {
ret = dh_encryptDEK(plainDek, encDek, kek);
put_kek(kek);
break;
}else{
if(ret == -EACCES) return ret;
DEK_LOGE("no KEK_TYPE_DH_PUB : %d\n", engine_id);
dek_add_to_log(engine_id, "encrypt failed, no KEK_TYPE_DH_PUB");
}
// no DH, try RSA
/* no break */
case SDPK_ALGOTYPE_ASYMM_RSA:
kek = get_kek(engine_id, KEK_TYPE_RSA_PUB, &ret);
if(kek) {
ret = rsa_encryptByPub(plainDek, encDek, kek);
put_kek(kek);
break;
}else{
if(ret == -EACCES) return ret;
DEK_LOGE("no KEK_TYPE_RSA_PUB : %d\n", engine_id);
dek_add_to_log(engine_id, "encrypt failed, no KEK_TYPE_RSA_PUB");
}
// no RSA, return error;
/* no break */
default:
DEK_LOGE("no ASYMM algo registered : %d\n", engine_id);
printk(KERN_INFO "MDM_LOG - encrypt failed, no ASYMM algo supported for id: %d\n", engine_id);
dek_add_to_log(engine_id, "no ASYMM algo supported");
return -EOPNOTSUPP;
}
#else
DEK_LOGE("pub crypto not supported : %d\n", engine_id);
dek_add_to_log(engine_id, "encrypt failed, no key");
return -EOPNOTSUPP;
#endif
}
if(ret) return ret;
if (encDek->len <= 0 || encDek->len > DEK_MAXLEN) {
DEK_LOGE("dek_encrypt_dek, incorrect len=%d\n", encDek->len);
zero_out((char *)encDek, sizeof(dek_t));
return -EFAULT;
}
#ifdef CONFIG_SDP_KEY_DUMP
else {
if(get_sdp_sysfs_key_dump()) {
DEK_LOGD("encDek to user: ");
key_dump(encDek->buf, encDek->len);
}
}
#endif
return 0;
}
int dek_encrypt_dek_efs(int engine_id, dek_t *plainDek, dek_t *encDek) {
return dek_encrypt_dek(engine_id, plainDek, encDek);
}
static int dek_decrypt_dek(int engine_id, dek_t *encDek, dek_t *plainDek) {
int dek_type = encDek->type;
kek_t *kek = NULL;
int ret = 0;
#ifdef CONFIG_SDP_KEY_DUMP
if(get_sdp_sysfs_key_dump()) {
DEK_LOGD("encDek from user: ");
key_dump(encDek->buf, encDek->len);
}
#endif
switch(dek_type) {
case DEK_TYPE_AES_ENC:
{
kek = get_kek(engine_id, KEK_TYPE_SYM, &ret);
if (kek) {
if (dek_aes_decrypt(kek, encDek->buf, plainDek->buf, encDek->len)) {
DEK_LOGE("aes decrypt failed\n");
dek_add_to_log(engine_id, "aes decrypt failed");
plainDek->len = 0;
} else {
plainDek->len = encDek->len;
plainDek->type = DEK_TYPE_PLAIN;
}
put_kek(kek);
} else {
DEK_LOGE("no KEK_TYPE_SYM for id: %d\n", engine_id);
printk(KERN_INFO "MDM_LOG - decrypt failed, no KEK_TYPE_SYM for id: %d\n", engine_id);
dek_add_to_log(engine_id, "decrypt failed, no KEK_TYPE_SYM");
return -EIO;
}
return 0;
}
case DEK_TYPE_RSA_ENC:
{
#ifdef CONFIG_PUB_CRYPTO
kek = get_kek(engine_id, KEK_TYPE_RSA_PRIV, &ret);
if(kek) {
ret = rsa_decryptByPair(encDek, plainDek, kek);
put_kek(kek);
}else{
DEK_LOGE("no KEK_TYPE_RSA_PRIV for id: %d\n", engine_id);
printk(KERN_INFO "MDM_LOG - decrypt failed, no KEK_TYPE_RSA_PRIV for id: %d\n", engine_id);
dek_add_to_log(engine_id, "decrypt failed, no KEK_TYPE_RSA_PRIV");
return -EIO;
}
#else
DEK_LOGE("Not supported key type: %d\n", encDek->type);
dek_add_to_log(engine_id, "decrypt failed, DH type not supported");
return -EOPNOTSUPP;
#endif
return ret;
}
case DEK_TYPE_DH_ENC:
{
#ifdef CONFIG_PUB_CRYPTO
kek = get_kek(engine_id, KEK_TYPE_DH_PRIV, &ret);
if(kek) {
ret = dh_decryptEDEK(encDek, plainDek, kek);
put_kek(kek);
}else{
DEK_LOGE("no KEK_TYPE_DH_PRIV for id: %d\n", engine_id);
printk(KERN_INFO "MDM_LOG - decrypt failed, no KEK_TYPE_DH_PRIV for id: %d\n", engine_id);
dek_add_to_log(engine_id, "decrypt failed, no KEK_TYPE_DH_PRIV");
return -EIO;
}
#else
DEK_LOGE("Not supported key type: %d\n", encDek->type);
dek_add_to_log(engine_id, "decrypt failed, DH type not supported");
return -EOPNOTSUPP;
#endif
return ret;
}
case DEK_TYPE_ECDH256_ENC:
{
#ifdef CONFIG_PUB_CRYPTO
kek = get_kek(engine_id, KEK_TYPE_ECDH256_PRIV, &ret);
if(kek) {
ret = ecdh_decryptEDEK(encDek, plainDek, kek);
put_kek(kek);
}else{
DEK_LOGE("no KEK_TYPE_ECDH256_PRIV for id: %d\n", engine_id);
printk(KERN_INFO "MDM_LOG - decrypt failed, no KEK_TYPE_ECDH256_PRIV for id: %d\n", engine_id);
dek_add_to_log(engine_id, "decrypt failed, no KEK_TYPE_ECDH256_PRIV");
return -EIO;
}
#else
DEK_LOGE("Not supported key type: %d\n", encDek->type);
dek_add_to_log(engine_id, "decrypt failed, ECDH type not supported");
return -EOPNOTSUPP;
#endif
return ret;
}
default:
{
DEK_LOGE("Unsupported edek type: %d\n", encDek->type);
printk(KERN_INFO "MDM_LOG - decrypt failed, unsupported key type for id: %d\n", engine_id);
dek_add_to_log(engine_id, "decrypt failed, unsupported key type");
return -EFAULT;
}
}
}
int dek_decrypt_dek_efs(int engine_id, dek_t *encDek, dek_t *plainDek) {
return dek_decrypt_dek(engine_id, encDek, plainDek);
}
static int dek_on_boot(dek_arg_on_boot *evt) {
int ret = 0;
int engine_id = evt->engine_id;
int user_id = evt->user_id;
if((evt->SDPK_Rpub.len > KEK_MAXLEN) ||
(evt->SDPK_Dpub.len > KEK_MAXLEN) ||
(evt->SDPK_EDpub.len > KEK_MAXLEN)) {
DEK_LOGE("Invalid args\n");
DEK_LOGE("SDPK_Rpub.len : %d\n", evt->SDPK_Rpub.len);
DEK_LOGE("SDPK_Dpub.len : %d\n", evt->SDPK_Dpub.len);
DEK_LOGE("SDPK_EDpub.len : %d\n", evt->SDPK_EDpub.len);
return -EINVAL;
}
if(!is_kek_pack(engine_id)) {
ret = add_kek_pack(engine_id, user_id);
if(ret && ret != -EEXIST) {
DEK_LOGE("add_kek_pack failed\n");
return ret;
}
ret = 0;
add_kek(engine_id, &evt->SDPK_Rpub);
add_kek(engine_id, &evt->SDPK_Dpub);
add_kek(engine_id, &evt->SDPK_EDpub);
#ifdef CONFIG_SDP_KEY_DUMP
if(get_sdp_sysfs_key_dump()) {
dump_all_keys(engine_id);
}
#endif
}
return ret;
}
static int dek_on_device_locked(dek_arg_on_device_locked *evt) {
int user_id = evt->user_id;
int engine_id = evt->engine_id;
del_kek(engine_id, KEK_TYPE_SYM);
del_kek(engine_id, KEK_TYPE_RSA_PRIV);
del_kek(engine_id, KEK_TYPE_DH_PRIV);
del_kek(engine_id, KEK_TYPE_ECDH256_PRIV);
ecryptfs_mm_drop_cache(user_id, engine_id);
#ifdef CONFIG_SDP_KEY_DUMP
if(get_sdp_sysfs_key_dump()) {
dump_all_keys(engine_id);
}
#endif
return 0;
}
static int dek_on_device_unlocked(dek_arg_on_device_unlocked *evt) {
int engine_id = evt->engine_id;
if((evt->SDPK_sym.len > KEK_MAXLEN) ||
(evt->SDPK_Rpri.len > KEK_MAXLEN) ||
(evt->SDPK_Dpri.len > KEK_MAXLEN) ||
(evt->SDPK_EDpri.len > KEK_MAXLEN)) {
DEK_LOGE("%s Invalid args\n", __func__);
DEK_LOGE("SDPK_sym.len : %d\n", evt->SDPK_sym.len);
DEK_LOGE("SDPK_Rpri.len : %d\n", evt->SDPK_Rpri.len);
DEK_LOGE("SDPK_Dpri.len : %d\n", evt->SDPK_Dpri.len);
DEK_LOGE("SDPK_EDpri.len : %d\n", evt->SDPK_EDpri.len);
return -EINVAL;
}
add_kek(engine_id, &evt->SDPK_sym);
add_kek(engine_id, &evt->SDPK_Rpri);
add_kek(engine_id, &evt->SDPK_Dpri);
add_kek(engine_id, &evt->SDPK_EDpri);
#ifdef CONFIG_SDP_KEY_DUMP
if(get_sdp_sysfs_key_dump()) {
dump_all_keys(engine_id);
}
#endif
return 0;
}
static int dek_on_user_added(dek_arg_on_user_added *evt) {
int ret;
int engine_id = evt->engine_id;
int user_id = evt->user_id;
if((evt->SDPK_Rpub.len > KEK_MAXLEN) ||
(evt->SDPK_Dpub.len > KEK_MAXLEN) ||
(evt->SDPK_EDpub.len > KEK_MAXLEN)) {
DEK_LOGE("Invalid args\n");
DEK_LOGE("SDPK_Rpub.len : %d\n", evt->SDPK_Rpub.len);
DEK_LOGE("SDPK_Dpub.len : %d\n", evt->SDPK_Dpub.len);
DEK_LOGE("SDPK_EDpub.len : %d\n", evt->SDPK_EDpub.len);
return -EINVAL;
}
ret = add_kek_pack(engine_id, user_id);
if(ret && ret != -EEXIST) {
DEK_LOGE("add_kek_pack failed\n");
return ret;
}
ret = 0;
add_kek(engine_id, &evt->SDPK_Rpub);
add_kek(engine_id, &evt->SDPK_Dpub);
add_kek(engine_id, &evt->SDPK_EDpub);
#ifdef CONFIG_SDP_KEY_DUMP
if(get_sdp_sysfs_key_dump()) {
dump_all_keys(engine_id);
}
#endif
return ret;
}
static int dek_on_user_removed(dek_arg_on_user_removed *evt) {
del_kek_pack(evt->engine_id);
return 0;
}
// I'm thinking... if minor id can represent persona id
static long dek_do_ioctl_evt(unsigned int minor, unsigned int cmd,
unsigned long arg) {
long ret = 0;
void __user *ubuf = (void __user *)arg;
void *cleanup = NULL;
unsigned int size = 0;
switch (cmd) {
/*
* Event while booting.
*
* This event comes per persona, the driver is responsible to
* verify things good whether it's compromised.
*/
case DEK_ON_BOOT: {
dek_arg_on_boot *evt = kzalloc(sizeof(dek_arg_on_boot), GFP_KERNEL);
DEK_LOGD("DEK_ON_BOOT\n");
if (evt == NULL) {
ret = -ENOMEM;
goto err;
}
cleanup = evt;
size = sizeof(dek_arg_on_boot);
if(copy_from_user(evt, ubuf, size)) {
DEK_LOGE("can't copy from user evt\n");
ret = -EFAULT;
goto err;
}
ret = dek_on_boot(evt);
if (ret < 0) {
dek_add_to_log(evt->engine_id, "Boot failed");
goto err;
}
dek_add_to_log(evt->engine_id, "Booted");
break;
}
/*
* Event when device is locked.
*
* Nullify private key which prevents decryption.
*/
case DEK_ON_DEVICE_LOCKED: {
dek_arg_on_device_locked *evt = kzalloc(sizeof(dek_arg_on_device_locked), GFP_KERNEL);
DEK_LOGD("DEK_ON_DEVICE_LOCKED\n");
if (evt == NULL) {
ret = -ENOMEM;
goto err;
}
cleanup = evt;
size = sizeof(dek_arg_on_device_locked);
if(copy_from_user(evt, ubuf, size)) {
DEK_LOGE("can't copy from user evt\n");
ret = -EFAULT;
goto err;
}
ret = dek_on_device_locked(evt);
if (ret < 0) {
dek_add_to_log(evt->engine_id, "Lock failed");
goto err;
}
dek_add_to_log(evt->engine_id, "Locked");
break;
}
/*
* Event when device unlocked.
*
* Read private key and decrypt with user password.
*/
case DEK_ON_DEVICE_UNLOCKED: {
dek_arg_on_device_unlocked *evt = kzalloc(sizeof(dek_arg_on_device_unlocked), GFP_KERNEL);
DEK_LOGD("DEK_ON_DEVICE_UNLOCKED\n");
if (evt == NULL) {
ret = -ENOMEM;
goto err;
}
cleanup = evt;
size = sizeof(dek_arg_on_device_unlocked);
if(copy_from_user(evt, ubuf, size)) {
DEK_LOGE("can't copy from user evt\n");
ret = -EFAULT;
goto err;
}
ret = dek_on_device_unlocked(evt);
if (ret < 0) {
dek_add_to_log(evt->engine_id, "Unlock failed");
goto err;
}
dek_add_to_log(evt->engine_id, "Unlocked");
break;
}
/*
* Event when new user(persona) added.
*
* Generate RSA public key and encrypt private key with given
* password. Also pub-key and encryped priv-key have to be stored
* in a file system.
*/
case DEK_ON_USER_ADDED: {
dek_arg_on_user_added *evt = kzalloc(sizeof(dek_arg_on_user_added), GFP_KERNEL);
DEK_LOGD("DEK_ON_USER_ADDED\n");
if (evt == NULL) {
ret = -ENOMEM;
goto err;
}
cleanup = evt;
size = sizeof(dek_arg_on_user_added);
if(copy_from_user(evt, ubuf, size)) {
DEK_LOGE("can't copy from user evt\n");
ret = -EFAULT;
goto err;
}
ret = dek_on_user_added(evt);
if (ret < 0) {
dek_add_to_log(evt->engine_id, "Add user failed");
goto err;
}
dek_add_to_log(evt->engine_id, "Added user");
break;
}
/*
* Event when user is removed.
*
* Remove pub-key file & encrypted priv-key file.
*/
case DEK_ON_USER_REMOVED: {
dek_arg_on_user_removed *evt = kzalloc(sizeof(dek_arg_on_user_removed), GFP_KERNEL);
DEK_LOGD("DEK_ON_USER_REMOVED\n");
if (evt == NULL) {
ret = -ENOMEM;
goto err;
}
cleanup = evt;
size = sizeof(dek_arg_on_user_removed);
if(copy_from_user(evt, ubuf, size)) {
DEK_LOGE("can't copy from user evt\n");
ret = -EFAULT;
goto err;
}
ret = dek_on_user_removed(evt);
if (ret < 0) {
dek_add_to_log(evt->engine_id, "Remove user failed");
goto err;
}
dek_add_to_log(evt->engine_id, "Removed user");
break;
}
/*
* Event when password changed.
*
* Encrypt SDPK_Rpri with new password and store it.
*/
case DEK_ON_CHANGE_PASSWORD: {
DEK_LOGD("DEK_ON_CHANGE_PASSWORD << deprecated. SKIP\n");
ret = 0;
dek_add_to_log(0, "Changed password << deprecated");
break;
}
case DEK_DISK_CACHE_CLEANUP: {
dek_arg_disk_cache_cleanup *evt = kzalloc(sizeof(dek_arg_disk_cache_cleanup), GFP_KERNEL);
DEK_LOGD("DEK_DISK_CACHE_CLEANUP\n");
if (evt == NULL) {
ret = -ENOMEM;
goto err;
}
cleanup = evt;
size = sizeof(dek_arg_disk_cache_cleanup);
if(copy_from_user(evt, ubuf, size)) {
DEK_LOGE("can't copy from user evt\n");
ret = -EFAULT;
goto err;
}
ecryptfs_mm_drop_cache(evt->user_id, evt->engine_id);
ret = 0;
dek_add_to_log(evt->engine_id, "Disk cache clean up");
break;
}
default:
DEK_LOGE("%s case default\n", __func__);
ret = -EINVAL;
break;
}
err:
if (cleanup) {
zero_out((char *)cleanup, size);
kfree(cleanup);
}
return ret;
}
static long dek_do_ioctl_req(unsigned int minor, unsigned int cmd,
unsigned long arg) {
long ret = 0;
void __user *ubuf = (void __user *)arg;
switch (cmd) {
case DEK_IS_KEK_AVAIL: {
dek_arg_is_kek_avail req;
DEK_LOGD("DEK_IS_KEK_AVAIL\n");
memset(&req, 0, sizeof(dek_arg_is_kek_avail));
if(copy_from_user(&req, ubuf, sizeof(req))) {
DEK_LOGE("can't copy from user\n");
ret = -EFAULT;
goto err;
}
req.ret = is_kek_available(req.engine_id, req.kek_type);
if(req.ret < 0) {
DEK_LOGE("is_kek_available(id:%d, kek:%d) error\n",
req.engine_id, req.kek_type);
ret = -ENOENT;
goto err;
}
if(copy_to_user(ubuf, &req, sizeof(req))) {
DEK_LOGE("can't copy to user req\n");
zero_out((char *)&req, sizeof(dek_arg_is_kek_avail));
ret = -EFAULT;
goto err;
}
ret = 0;
}
break;
/*
* Request to generate DEK.
* Generate DEK and return to the user
*/
case DEK_GENERATE_DEK: {
dek_arg_generate_dek req;
DEK_LOGD("DEK_GENERATE_DEK\n");
memset(&req, 0, sizeof(dek_arg_generate_dek));
if(copy_from_user(&req, ubuf, sizeof(req))) {
DEK_LOGE("can't copy from user req\n");
ret = -EFAULT;
goto err;
}
dek_generate_dek(req.engine_id, &req.dek);
if(copy_to_user(ubuf, &req, sizeof(req))) {
DEK_LOGE("can't copy to user req\n");
zero_out((char *)&req, sizeof(dek_arg_generate_dek));
ret = -EFAULT;
goto err;
}
zero_out((char *)&req, sizeof(dek_arg_generate_dek));
break;
}
/*
* Request to encrypt given DEK.
*
* encrypt dek and return to the user
*/
case DEK_ENCRYPT_DEK: {
dek_arg_encrypt_dek req;
DEK_LOGD("DEK_ENCRYPT_DEK\n");
memset(&req, 0, sizeof(dek_arg_encrypt_dek));
if(copy_from_user(&req, ubuf, sizeof(req))) {
DEK_LOGE("can't copy from user req\n");
zero_out((char *)&req, sizeof(dek_arg_encrypt_dek));
ret = -EFAULT;
goto err;
}
if(req.plain_dek.len <= 0 || req.plain_dek.len > DEK_MAXLEN) {
DEK_LOGE("Incorrect dek len\n");
zero_out((char *)&req, sizeof(dek_arg_encrypt_dek));
ret = -EFAULT;
goto err;
}
ret = dek_encrypt_dek(req.engine_id,
&req.plain_dek, &req.enc_dek);
if (ret < 0) {
zero_out((char *)&req, sizeof(dek_arg_encrypt_dek));
goto err;
}
if(copy_to_user(ubuf, &req, sizeof(req))) {
DEK_LOGE("can't copy to user req\n");
zero_out((char *)&req, sizeof(dek_arg_encrypt_dek));
ret = -EFAULT;
goto err;
}
zero_out((char *)&req, sizeof(dek_arg_encrypt_dek));
break;
}
/*
* Request to decrypt given DEK.
*
* Decrypt dek and return to the user.
* When device is locked, private key is not available, so
* the driver must return EPERM or some kind of error.
*/
case DEK_DECRYPT_DEK: {
dek_arg_decrypt_dek req;
DEK_LOGD("DEK_DECRYPT_DEK\n");
memset(&req, 0, sizeof(dek_arg_decrypt_dek));
if(copy_from_user(&req, ubuf, sizeof(req))) {
DEK_LOGE("can't copy from user req\n");
zero_out((char *)&req, sizeof(dek_arg_decrypt_dek));
ret = -EFAULT;
goto err;
}
if(req.enc_dek.len <= 0 || req.enc_dek.len > DEK_MAXLEN) {
DEK_LOGE("Incorrect dek len\n");
zero_out((char *)&req, sizeof(dek_arg_decrypt_dek));
ret = -EFAULT;
goto err;
}
ret = dek_decrypt_dek(req.engine_id,
&req.enc_dek, &req.plain_dek);
if (ret < 0) {
zero_out((char *)&req, sizeof(dek_arg_decrypt_dek));
goto err;
}
if(copy_to_user(ubuf, &req, sizeof(req))) {
DEK_LOGE("can't copy to user req\n");
zero_out((char *)&req, sizeof(dek_arg_decrypt_dek));
ret = -EFAULT;
goto err;
}
zero_out((char *)&req, sizeof(dek_arg_decrypt_dek));
break;
}
default:
DEK_LOGE("%s case default\n", __func__);
ret = -EINVAL;
break;
}
return ret;
err:
return ret;
}
int is_kek_available(int engine_id, int kek_type) {
return is_kek(engine_id, kek_type);
}
static long dek_ioctl_evt(struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int minor;
if(!is_system_server()) {
DEK_LOGE("Current process can't access evt device\n");
DEK_LOGE("Current process info :: "
"uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
"fsuid=%u fsgid=%u\n",
current_uid(), current_gid(), current_euid(),
current_egid(), current_suid(), current_sgid(),
current_fsuid(), current_fsgid());
dek_add_to_log(000, "Access denied to evt device");
return -EACCES;
}
minor = iminor(file->f_path.dentry->d_inode);
return dek_do_ioctl_evt(minor, cmd, arg);
}
static long dek_ioctl_req(struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int minor;
#if 0
if(!is_container_app() && !is_root()) {
DEK_LOGE("Current process can't access req device\n");
DEK_LOGE("Current process info :: "
"uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
"fsuid=%u fsgid=%u\n",
current_uid(), current_gid(), current_euid(),
current_egid(), current_suid(), current_sgid(),
current_fsuid(), current_fsgid());
dek_add_to_log(000, "Access denied to req device");
return -EACCES;
}
#endif
minor = iminor(file->f_path.dentry->d_inode);
return dek_do_ioctl_req(minor, cmd, arg);
}
/*
* DAR engine log
*/
static int dek_open_log(struct inode *inode, struct file *file)
{
DEK_LOGD("dek_open_log\n");
return 0;
}
static int dek_release_log(struct inode *ignored, struct file *file)
{
DEK_LOGD("dek_release_log\n");
return 0;
}
static ssize_t dek_read_log(struct file *file, char __user *buffer, size_t len, loff_t *off)
{
int ret = 0;
struct log_struct *tmp = NULL;
char log_buf[256];
int log_buf_len;
if (list_empty(&log_buffer.list)) {
DEK_LOGD("process %i (%s) going to sleep\n",
current->pid, current->comm);
flag = 0;
wait_event_interruptible(wq, flag != 0);
}
flag = 0;
spin_lock(&log_buffer.list_lock);
if (!list_empty(&log_buffer.list)) {
tmp = list_first_entry(&log_buffer.list, struct log_struct, list);
memcpy(&log_buf, tmp->buf, tmp->len);
log_buf_len = tmp->len;
list_del(&tmp->list);
kfree(tmp);
log_count--;
spin_unlock(&log_buffer.list_lock);
ret = copy_to_user(buffer, log_buf, log_buf_len);
if (ret) {
DEK_LOGE("dek_read_log, copy_to_user fail, ret=%d, len=%d\n",
ret, log_buf_len);
return -EFAULT;
}
len = log_buf_len;
*off = log_buf_len;
} else {
spin_unlock(&log_buffer.list_lock);
DEK_LOGD("dek_read_log, list empty\n");
len = 0;
}
return len;
}
static void dek_add_to_log(int engine_id, char * buffer) {
struct timespec ts;
struct log_struct *tmp = (struct log_struct*)kmalloc(sizeof(struct log_struct), GFP_KERNEL);
if (tmp) {
INIT_LIST_HEAD(&tmp->list);
getnstimeofday(&ts);
tmp->len = sprintf(tmp->buf, "%ld.%.3ld|%d|%s|%d|%s\n",
(long)ts.tv_sec,
(long)ts.tv_nsec / 1000000,
current->pid,
current->comm,
engine_id,
buffer);
spin_lock(&log_buffer.list_lock);
list_add_tail(&(tmp->list), &(log_buffer.list));
log_count++;
if (log_count > DEK_LOG_COUNT) {
DEK_LOGD("dek_add_to_log - exceeded DEK_LOG_COUNT\n");
tmp = list_first_entry(&log_buffer.list, struct log_struct, list);
list_del(&tmp->list);
kfree(tmp);
log_count--;
}
spin_unlock(&log_buffer.list_lock);
DEK_LOGD("process %i (%s) awakening the readers, log_count=%d\n",
current->pid, current->comm, log_count);
flag = 1;
wake_up_interruptible(&wq);
} else {
DEK_LOGE("dek_add_to_log - failed to allocate buffer\n");
}
}
const struct file_operations dek_fops_evt = {
.owner = THIS_MODULE,
.open = dek_open_evt,
.release = dek_release_evt,
.unlocked_ioctl = dek_ioctl_evt,
.compat_ioctl = dek_ioctl_evt,
};
static struct miscdevice dek_misc_evt = {
.minor = MISC_DYNAMIC_MINOR,
.name = "dek_evt",
.fops = &dek_fops_evt,
};
const struct file_operations dek_fops_req = {
.owner = THIS_MODULE,
.open = dek_open_req,
.release = dek_release_req,
.unlocked_ioctl = dek_ioctl_req,
.compat_ioctl = dek_ioctl_req,
};
static struct miscdevice dek_misc_req = {
.minor = MISC_DYNAMIC_MINOR,
.name = "dek_req",
.fops = &dek_fops_req,
};
const struct file_operations dek_fops_log = {
.owner = THIS_MODULE,
.open = dek_open_log,
.release = dek_release_log,
.read = dek_read_log,
};
static struct miscdevice dek_misc_log = {
.minor = MISC_DYNAMIC_MINOR,
.name = "dek_log",
.fops = &dek_fops_log,
};
static int __init dek_init(void) {
int ret;
ret = misc_register(&dek_misc_evt);
if (unlikely(ret)) {
DEK_LOGE("failed to register misc_evt device!\n");
return ret;
}
ret = misc_register(&dek_misc_req);
if (unlikely(ret)) {
DEK_LOGE("failed to register misc_req device!\n");
return ret;
}
ret = dek_create_sysfs_asym_alg(dek_misc_req.this_device);
if (unlikely(ret)) {
DEK_LOGE("failed to create sysfs_asym_alg device!\n");
return ret;
}
ret = misc_register(&dek_misc_log);
if (unlikely(ret)) {
DEK_LOGE("failed to register misc_log device!\n");
return ret;
}
ret = dek_create_sysfs_key_dump(dek_misc_log.this_device);
if (unlikely(ret)) {
DEK_LOGE("failed to create sysfs_key_dump device!\n");
return ret;
}
INIT_LIST_HEAD(&log_buffer.list);
spin_lock_init(&log_buffer.list_lock);
init_waitqueue_head(&wq);
init_kek_pack();
printk("dek: initialized\n");
dek_add_to_log(000, "Initialized");
return 0;
}
static void __exit dek_exit(void)
{
printk("dek: unloaded\n");
}
module_init(dek_init)
module_exit(dek_exit)
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SDP DEK");