You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
kernel_samsung_sm7125/security/sdp/dek.c

1475 lines
34 KiB

/*
* 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.
*/
#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
#define DEK_LOG_BUF_SIZE 1024
#define LOG_ENTRY_BUF_SIZE 512
//#if defined(CONFIG_SDP) && !defined(CONFIG_FSCRYPT_SDP)
//extern void ecryptfs_mm_drop_cache(int userid, int engineid);
//#endif
/* Log buffer */
struct log_struct {
int len;
char buf[DEK_LOG_BUF_SIZE];
struct list_head list;
spinlock_t list_lock;
};
struct log_struct log_buffer;
static int log_count;
/* Wait queue */
wait_queue_head_t wq;
static int flag;
static struct workqueue_struct *queue_log_workqueue;
typedef struct __log_entry_t {
int engineId;
char buffer[LOG_ENTRY_BUF_SIZE];
struct work_struct work;
} log_entry_t;
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 = from_kuid(&init_user_ns, 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 = from_kuid(&init_user_ns, 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(void)
{
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(void)
{
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;
}
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;
}
#if DEK_DEBUG
void hex_key_dump(const char* tag, uint8_t *data, size_t data_len)
{
static const char *hex = "0123456789ABCDEF";
static const char delimiter = ' ';
int i;
char *buf;
size_t buf_len;
if (tag == NULL || data == NULL || data_len <= 0) {
return;
}
buf_len = data_len * 3;
buf = (char *)kmalloc(buf_len, GFP_ATOMIC);
if (buf == NULL) {
return;
}
for (i= 0 ; i < data_len ; i++) {
buf[i*3 + 0] = hex[(data[i] >> 4) & 0x0F];
buf[i*3 + 1] = hex[(data[i]) & 0x0F];
buf[i*3 + 2] = delimiter;
}
buf[buf_len - 1] = '\0';
printk(KERN_ERR
"[%s] %s(len=%zu) : %s\n", "DEK_DBG", tag, data_len, buf);
kfree(buf);
}
#endif
#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;
}
int dek_encrypt_dek_ecdh(dek_t *plainDek, dek_t *encDek, kek_t *drv_key)
{
int res;
int enc_type;
unsigned int enc_plen;
u8 enc_pbuf[SDP_CRYPTO_GCM_MAX_PLEN];
ss_payload sspay;
dek_t in;
dek_t out;
enc_type = CONV_DLEN_TO_TYPE(plainDek->len);
if (unlikely(!enc_type))
return -EINVAL;
#if DEK_DEBUG
hex_key_dump("enc_ecdh: plainDek", (u8 *)plainDek, sizeof(dek_t));
#endif
// Update Dummy DEK into input dek
in.type = DEK_TYPE_DUMMY,
in.len = 0;
// Derive shared secret
res = ecdh_deriveSS(&in, &out, drv_key);
if (res || out.len != sizeof(ss_payload))
return -EINVAL;
// Update ss_payload
memcpy(&sspay, out.buf, out.len);
res = dek_aes_encrypt_key_raw(
sspay.ss.buf, sspay.ss.len, plainDek->buf, plainDek->len, enc_pbuf, &enc_plen);
if (res)
goto clean;
/*
* Update encDek which will be stored into file system
*
* Blob Format : [ DataK.pub | GCM Pack ]
* [ dhkt_t | gcm_pack32 ]
* or [ dhkt_t | gcm_pack64 ]
*/
memset(encDek->buf, 0, DEK_MAXLEN);
memcpy(encDek->buf, &sspay.dh, sizeof(dhk_t));
memcpy((u8 *)encDek->buf + sizeof(dhk_t), enc_pbuf, enc_plen);
encDek->len = sizeof(dhk_t) + enc_plen;
encDek->type = DEK_TYPE_ECDH256_ENC;
#if DEK_DEBUG
hex_key_dump("enc_ecdh: encDek", (u8 *)encDek, sizeof(dek_t));
#endif
clean:
memset(sspay.ss.buf, 0, sspay.ss.len);
memset(out.buf, 0, out.len);
return res;
}
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) {
ret = dek_aes_encrypt_key(kek, plainDek->buf, plainDek->len, encDek->buf, &encDek->len);
if (ret) {
DEK_LOGE("aes encrypt failed\n");
dek_add_to_log(engine_id, "aes encrypt failed");
} else {
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 = dek_encrypt_dek_ecdh(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);
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);
}
int dek_decrypt_dek_ecdh(dek_t *encDek, dek_t *plainDek, kek_t *drv_key)
{
int res;
int dec_type;
unsigned int dec_plen;
u8 dec_pbuf[SDP_CRYPTO_GCM_MAX_PLEN];
ssk_t ss;
dek_t in;
dek_t out;
dec_plen = encDek->len - sizeof(dhk_t);
dec_type = CONV_PLEN_TO_TYPE(dec_plen);
if (unlikely(!dec_type))
return -EINVAL;
#if DEK_DEBUG
hex_key_dump("dec_ecdh: encDek", (u8 *)encDek, sizeof(dek_t));
#endif
// Update DataK.pub into input dek
memcpy(in.buf, encDek->buf, sizeof(dhk_t));
in.type = DEK_TYPE_ECDH256_ENC;
in.len = sizeof(dhk_t);
// Derive shared secret
res = ecdh_deriveSS(&in, &out, drv_key);
if (res || out.len != sizeof(ssk_t))
return -EINVAL;
// Update ss_payload
memcpy(&ss, out.buf, out.len);
// Update GCM pack buffer
memcpy(dec_pbuf, (u8 *)encDek->buf + sizeof(dhk_t), dec_plen);
// Clean up plain key buffer
memset(plainDek->buf, 0, DEK_MAXLEN);
res = dek_aes_decrypt_key_raw(
ss.buf, ss.len, dec_pbuf, dec_plen, plainDek->buf, &plainDek->len);
if (res)
goto clean;
plainDek->type = DEK_TYPE_PLAIN;
#if DEK_DEBUG
hex_key_dump("dec_ecdh: plainDek->buf", plainDek->buf,plainDek->len);
#endif
clean:
memset(ss.buf, 0, ss.len);
memset(out.buf, 0, out.len);
return res;
}
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) {
ret = dek_aes_decrypt_key(kek, encDek->buf, encDek->len, plainDek->buf, &plainDek->len);
if (ret) {
DEK_LOGE("aes decrypt failed\n");
dek_add_to_log(engine_id, "aes decrypt failed");
} else {
plainDek->type = DEK_TYPE_PLAIN;
}
put_kek(kek);
} else {
DEK_LOGE("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 ret;
}
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);
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);
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 = dek_decrypt_dek_ecdh(encDek, plainDek, kek);
put_kek(kek);
} else{
DEK_LOGE("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);
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);
}
int dek_encrypt_fek(unsigned char *master_key, unsigned int master_key_len,
unsigned char *fek, unsigned int fek_len,
unsigned char *efek, unsigned int *efek_len)
{
return dek_aes_encrypt_key_raw(master_key, master_key_len,
fek, fek_len,
efek, efek_len);
}
int dek_decrypt_fek(unsigned char *master_key, unsigned int master_key_len,
unsigned char *efek, unsigned int efek_len,
unsigned char *fek, unsigned int *fek_len)
{
return dek_aes_decrypt_key_raw(master_key, master_key_len,
efek, efek_len,
fek, fek_len);
}
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)
{
//#if defined(CONFIG_SDP) && !defined(CONFIG_FSCRYPT_SDP)
// int user_id = evt->user_id;
//#endif
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);
//#if defined(CONFIG_SDP) && !defined(CONFIG_FSCRYPT_SDP)
// ecryptfs_mm_drop_cache(user_id, engine_id);
//#endif
#ifdef CONFIG_FSCRYPT_SDP
fscrypt_sdp_cache_drop_inode_mappings(engine_id);
#endif
#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;
}
//#if defined(CONFIG_SDP) && !defined(CONFIG_FSCRYPT_SDP)
// ecryptfs_mm_drop_cache(evt->user_id, evt->engine_id);
//#endif
#ifdef CONFIG_FSCRYPT_SDP
fscrypt_sdp_cache_drop_inode_mappings(evt->engine_id);
#endif
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_t *tempPlain_dek, *tempEnc_dek;
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;
}
tempPlain_dek = kmalloc(sizeof(dek_t), GFP_NOFS);
if (tempPlain_dek == NULL) {
return -ENOMEM;
}
tempEnc_dek = kmalloc(sizeof(dek_t), GFP_NOFS);
if (tempEnc_dek == NULL) {
kzfree(tempPlain_dek);
return -ENOMEM;
}
tempPlain_dek->type = req.plain_dek.type;
tempPlain_dek->len = req.plain_dek.len;
memcpy(tempPlain_dek->buf, req.plain_dek.buf, req.plain_dek.len);
ret = dek_encrypt_dek(req.engine_id,
tempPlain_dek, tempEnc_dek);
memset(tempPlain_dek->buf, 0, DEK_MAXLEN);
if (ret < 0) {
DEK_LOGE("DEK_ENCRYPT_DEK: failed to encrypt dek! (err:%d)\n", ret);
zero_out((char *)&req, sizeof(dek_arg_encrypt_dek));
kzfree(tempPlain_dek);
kzfree(tempEnc_dek);
goto err;
}
req.enc_dek.type = tempEnc_dek->type;
req.enc_dek.len = tempEnc_dek->len;
memcpy(req.enc_dek.buf, tempEnc_dek->buf, tempEnc_dek->len);
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;
kzfree(tempPlain_dek);
kzfree(tempEnc_dek);
goto err;
}
zero_out((char *)&req, sizeof(dek_arg_encrypt_dek));
kzfree(tempPlain_dek);
kzfree(tempEnc_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_t *tempPlain_dek, *tempEnc_dek;
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;
}
tempPlain_dek = kmalloc(sizeof(dek_t), GFP_NOFS);
if (tempPlain_dek == NULL) {
return -ENOMEM;
}
tempEnc_dek = kmalloc(sizeof(dek_t), GFP_NOFS);
if (tempEnc_dek == NULL) {
kzfree(tempPlain_dek);
return -ENOMEM;
}
tempEnc_dek->type = req.enc_dek.type;
tempEnc_dek->len = req.enc_dek.len;
memcpy(tempEnc_dek->buf, req.enc_dek.buf, req.enc_dek.len);
ret = dek_decrypt_dek(req.engine_id,
tempEnc_dek, tempPlain_dek);
if (ret < 0) {
DEK_LOGE("DEK_DECRYPT_DEK: failed to decrypt dek! (err:%d)\n", ret);
zero_out((char *)&req, sizeof(dek_arg_decrypt_dek));
kzfree(tempPlain_dek);
kzfree(tempEnc_dek);
goto err;
}
req.plain_dek.type = tempPlain_dek->type;
req.plain_dek.len = tempPlain_dek->len;
memcpy(req.plain_dek.buf, tempPlain_dek->buf, tempPlain_dek->len);
memset(tempPlain_dek->buf, 0, DEK_MAXLEN);
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;
kzfree(tempPlain_dek);
kzfree(tempEnc_dek);
goto err;
}
zero_out((char *)&req, sizeof(dek_arg_decrypt_dek));
kzfree(tempPlain_dek);
kzfree(tempEnc_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");
#if 0
/* TODO: fix build issue first */
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());
#endif
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[DEK_LOG_BUF_SIZE];
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;
}
void queue_log_work(struct work_struct *log_work)
{
log_entry_t *logStruct = container_of(log_work, log_entry_t, work);
int engine_id = logStruct->engineId;
char *buffer = logStruct->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");
}
kfree(logStruct);
}
void dek_add_to_log(int engine_id, char *buffer)
{
log_entry_t *temp = (log_entry_t *)kmalloc(sizeof(log_entry_t), GFP_ATOMIC);
int len;
if (!temp) {
DEK_LOGE("failed to allocate memory for log entry\n");
return;
}
temp->engineId = engine_id;
len = (strlen(buffer) > LOG_ENTRY_BUF_SIZE) ? (LOG_ENTRY_BUF_SIZE - 1) : strlen(buffer);
memcpy(temp->buffer, buffer, len);
temp->buffer[len] = '\0';
INIT_WORK(&temp->work, queue_log_work);
queue_work(queue_log_workqueue, &temp->work);
}
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;
}
queue_log_workqueue = alloc_workqueue("queue_log_workqueue", WQ_HIGHPRI, 0);
if (!queue_log_workqueue) {
DEK_LOGE("failed to allocate queue_log_workqueue\n");
return -ENOMEM;
}
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");
#ifdef CONFIG_FSCRYPT_SDP
fscrypt_sdp_cache_init();
#endif
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");