1170 lines
28 KiB
C
1170 lines
28 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 = 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() {
|
|
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);
|
|
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);
|
|
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);
|
|
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 = ecdh_decryptEDEK(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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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");
|
|
#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[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");
|