Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

48
fs/ecryptfs/Kconfig Normal file
View file

@ -0,0 +1,48 @@
config ECRYPT_FS
tristate "eCrypt filesystem layer support"
depends on KEYS && CRYPTO && (ENCRYPTED_KEYS || ENCRYPTED_KEYS=n)
select CRYPTO_ECB
select CRYPTO_CBC
select CRYPTO_MD5
select CRYPTO_SHA256
help
Encrypted filesystem that operates on the VFS layer. See
<file:Documentation/filesystems/ecryptfs.txt> to learn more about
eCryptfs. Userspace components are required and can be
obtained from <http://ecryptfs.sf.net>.
To compile this file system support as a module, choose M here: the
module will be called ecryptfs.
config ECRYPT_FS_MESSAGING
bool "Enable notifications for userspace key wrap/unwrap"
depends on ECRYPT_FS
help
Enables the /dev/ecryptfs entry for use by ecryptfsd. This allows
for userspace to wrap/unwrap file encryption keys by other
backends, like OpenSSL.
config WTL_ENCRYPTION_FILTER
bool "Enables filtering for some files not to encrypt on eCryptfs"
default n
depends on ECRYPT_FS
help
Modification of encrypted filesystem for SD card encryption
config SDP
bool "Enables SDP"
default n
depends on ECRYPT_FS
help
Sensitive Data Protection
config SDP_KEY_DUMP
bool "SDP KEY DUMP"
default n
config DLP
bool "Enables DLP"
default n
depends on SDP
help
Data Loss Prevention

11
fs/ecryptfs/Makefile Normal file
View file

@ -0,0 +1,11 @@
#
# Makefile for the Linux eCryptfs
#
obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o
obj-$(CONFIG_SDP) += ecryptfs_dek.o mm.o ecryptfs_sdp_chamber.o
ecryptfs-y := dentry.o file.o inode.o main.o super.o mmap.o read_write.o \
crypto.o keystore.o kthread.o debug.o
ecryptfs-$(CONFIG_ECRYPT_FS_MESSAGING) += messaging.o miscdev.o

2610
fs/ecryptfs/crypto.c Normal file

File diff suppressed because it is too large Load diff

126
fs/ecryptfs/debug.c Normal file
View file

@ -0,0 +1,126 @@
/**
* eCryptfs: Linux filesystem encryption layer
* Functions only useful for debugging.
*
* Copyright (C) 2006 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "ecryptfs_kernel.h"
/**
* ecryptfs_dump_auth_tok - debug function to print auth toks
*
* This function will print the contents of an ecryptfs authentication
* token.
*/
void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok)
{
char salt[ECRYPTFS_SALT_SIZE * 2 + 1];
char sig[ECRYPTFS_SIG_SIZE_HEX + 1];
ecryptfs_printk(KERN_DEBUG, "Auth tok at mem loc [%p]:\n",
auth_tok);
if (auth_tok->flags & ECRYPTFS_PRIVATE_KEY) {
ecryptfs_printk(KERN_DEBUG, " * private key type\n");
} else {
ecryptfs_printk(KERN_DEBUG, " * passphrase type\n");
ecryptfs_to_hex(salt, auth_tok->token.password.salt,
ECRYPTFS_SALT_SIZE);
salt[ECRYPTFS_SALT_SIZE * 2] = '\0';
ecryptfs_printk(KERN_DEBUG, " * salt = [%s]\n", salt);
if (auth_tok->token.password.flags &
ECRYPTFS_PERSISTENT_PASSWORD) {
ecryptfs_printk(KERN_DEBUG, " * persistent\n");
}
memcpy(sig, auth_tok->token.password.signature,
ECRYPTFS_SIG_SIZE_HEX);
sig[ECRYPTFS_SIG_SIZE_HEX] = '\0';
ecryptfs_printk(KERN_DEBUG, " * signature = [%s]\n", sig);
}
ecryptfs_printk(KERN_DEBUG, " * session_key.flags = [0x%x]\n",
auth_tok->session_key.flags);
if (auth_tok->session_key.flags
& ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT)
ecryptfs_printk(KERN_DEBUG,
" * Userspace decrypt request set\n");
if (auth_tok->session_key.flags
& ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT)
ecryptfs_printk(KERN_DEBUG,
" * Userspace encrypt request set\n");
if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_DECRYPTED_KEY) {
ecryptfs_printk(KERN_DEBUG, " * Contains decrypted key\n");
ecryptfs_printk(KERN_DEBUG,
" * session_key.decrypted_key_size = [0x%x]\n",
auth_tok->session_key.decrypted_key_size);
ecryptfs_printk(KERN_DEBUG, " * Decrypted session key "
"dump:\n");
if (ecryptfs_verbosity > 0)
ecryptfs_dump_hex(auth_tok->session_key.decrypted_key,
ECRYPTFS_DEFAULT_KEY_BYTES);
}
if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_ENCRYPTED_KEY) {
ecryptfs_printk(KERN_DEBUG, " * Contains encrypted key\n");
ecryptfs_printk(KERN_DEBUG,
" * session_key.encrypted_key_size = [0x%x]\n",
auth_tok->session_key.encrypted_key_size);
ecryptfs_printk(KERN_DEBUG, " * Encrypted session key "
"dump:\n");
if (ecryptfs_verbosity > 0)
ecryptfs_dump_hex(auth_tok->session_key.encrypted_key,
auth_tok->session_key.
encrypted_key_size);
}
}
/**
* ecryptfs_dump_hex - debug hex printer
* @data: string of bytes to be printed
* @bytes: number of bytes to print
*
* Dump hexadecimal representation of char array
*/
#ifndef CONFIG_SDP
void ecryptfs_dump_hex(char *data, int bytes)
{
int i = 0;
int add_newline = 1;
if (ecryptfs_verbosity < 1)
return;
if (bytes != 0) {
printk(KERN_DEBUG "0x%.2x.", (unsigned char)data[i]);
i++;
}
while (i < bytes) {
printk("0x%.2x.", (unsigned char)data[i]);
i++;
if (i % 16 == 0) {
printk("\n");
add_newline = 0;
} else
add_newline = 1;
}
if (add_newline)
printk("\n");
}
#else
void ecryptfs_dump_hex(char *data, int bytes)
{
}
#endif

92
fs/ecryptfs/dentry.c Normal file
View file

@ -0,0 +1,92 @@
/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 1997-2003 Erez Zadok
* Copyright (C) 2001-2003 Stony Brook University
* Copyright (C) 2004-2006 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/dcache.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/fs_stack.h>
#include <linux/slab.h>
#include "ecryptfs_kernel.h"
/**
* ecryptfs_d_revalidate - revalidate an ecryptfs dentry
* @dentry: The ecryptfs dentry
* @flags: lookup flags
*
* Called when the VFS needs to revalidate a dentry. This
* is called whenever a name lookup finds a dentry in the
* dcache. Most filesystems leave this as NULL, because all their
* dentries in the dcache are valid.
*
* Returns 1 if valid, 0 otherwise.
*
*/
static int ecryptfs_d_revalidate(struct dentry *dentry, unsigned int flags)
{
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
int rc;
if (!(lower_dentry->d_flags & DCACHE_OP_REVALIDATE))
return 1;
if (flags & LOOKUP_RCU)
return -ECHILD;
rc = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
if (dentry->d_inode) {
struct inode *lower_inode =
ecryptfs_inode_to_lower(dentry->d_inode);
fsstack_copy_attr_all(dentry->d_inode, lower_inode);
}
return rc;
}
struct kmem_cache *ecryptfs_dentry_info_cache;
static void ecryptfs_dentry_free_rcu(struct rcu_head *head)
{
kmem_cache_free(ecryptfs_dentry_info_cache,
container_of(head, struct ecryptfs_dentry_info, rcu));
}
/**
* ecryptfs_d_release
* @dentry: The ecryptfs dentry
*
* Called when a dentry is really deallocated.
*/
static void ecryptfs_d_release(struct dentry *dentry)
{
struct ecryptfs_dentry_info *p = dentry->d_fsdata;
if (p) {
path_put(&p->lower_path);
call_rcu(&p->rcu, ecryptfs_dentry_free_rcu);
}
}
const struct dentry_operations ecryptfs_dops = {
.d_revalidate = ecryptfs_d_revalidate,
.d_release = ecryptfs_d_release,
};

774
fs/ecryptfs/ecryptfs_dek.c Executable file
View file

@ -0,0 +1,774 @@
/*
* 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/pagemap.h>
#include <linux/crypto.h>
#include <asm/unaligned.h>
#include <sdp/fs_handler.h>
#include "ecryptfs_dek.h"
#include "ecryptfs_sdp_chamber.h"
#include "mm.h"
extern int dek_encrypt_dek_efs(int engine_id, dek_t *plainDek, dek_t *encDek);
extern int dek_decrypt_dek_efs(int engine_id, dek_t *encDek, dek_t *plainDek);
extern int dek_is_locked(int engine_id);
static int ecryptfs_update_crypt_flag(struct dentry *dentry, enum sdp_op operation);
static const char* get_op_name(enum sdp_op operation) {
switch (operation)
{
case TO_SENSITIVE: return "TO_SENSITIVE";
case TO_PROTECTED: return "TO_PROTECTED";
default: return "OP_UNKNOWN";
}
}
static int ecryptfs_set_key(struct ecryptfs_crypt_stat *crypt_stat) {
int rc = 0;
mutex_lock(&crypt_stat->cs_tfm_mutex);
if (!(crypt_stat->flags & ECRYPTFS_KEY_SET)) {
rc = crypto_ablkcipher_setkey(crypt_stat->tfm, crypt_stat->key,
crypt_stat->key_size);
if (rc) {
ecryptfs_printk(KERN_ERR,
"Error setting key; rc = [%d]\n",
rc);
rc = -EINVAL;
goto out;
}
crypt_stat->flags |= ECRYPTFS_KEY_SET;
crypt_stat->flags |= ECRYPTFS_KEY_VALID;
}
out:
mutex_unlock(&crypt_stat->cs_tfm_mutex);
return rc;
}
int ecryptfs_is_sdp_locked(int engine_id)
{
if(engine_id < 0) {
DEK_LOGE("invalid engine_id[%d]\n", engine_id);
return 0;
}
return dek_is_locked(engine_id);
}
extern int32_t sdp_mm_set_process_sensitive(unsigned int proc_id);
#define PSEUDO_KEY_LEN 32
const char pseudo_key[PSEUDO_KEY_LEN] = {
// PSEUDOSDP
0x50, 0x53, 0x55, 0x45, 0x44, 0x4f, 0x53, 0x44,
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
void ecryptfs_clean_sdp_dek(struct ecryptfs_crypt_stat *crypt_stat)
{
int rc = 0;
printk("%s()\n", __func__);
if(crypt_stat->tfm) {
mutex_lock(&crypt_stat->cs_tfm_mutex);
rc = crypto_ablkcipher_setkey(crypt_stat->tfm, pseudo_key,
PSEUDO_KEY_LEN);
if (rc) {
ecryptfs_printk(KERN_ERR,
"Error cleaning tfm rc = [%d]\n",
rc);
}
mutex_unlock(&crypt_stat->cs_tfm_mutex);
}
memset(crypt_stat->key, 0, ECRYPTFS_MAX_KEY_BYTES);
crypt_stat->flags &= ~(ECRYPTFS_KEY_SET);
crypt_stat->flags &= ~(ECRYPTFS_KEY_VALID);
}
int ecryptfs_get_sdp_dek(struct ecryptfs_crypt_stat *crypt_stat)
{
int rc = 0;
if(crypt_stat->flags & ECRYPTFS_KEY_SET) {
DEK_LOGE("get_sdp_dek: key is already set (success)\n");
return 0;
}
if(crypt_stat->flags & ECRYPTFS_DEK_SDP_ENABLED) {
if(crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) {
dek_t DEK;
if(crypt_stat->engine_id < 0) {
DEK_LOGE("get_sdp_dek: invalid engine-id"
"(ECRYPTFS_DEK_IS_SENSITIVE:ON, engine_id:%d)\n", crypt_stat->engine_id);
goto out;
}
memset(crypt_stat->key, 0, ECRYPTFS_MAX_KEY_BYTES);
if (crypt_stat->sdp_dek.type != DEK_TYPE_PLAIN) {
rc = dek_decrypt_dek_efs(crypt_stat->engine_id, &crypt_stat->sdp_dek, &DEK);
} else {
DEK_LOGE("DEK already plaintext, skip decryption");
rc = 0;
goto out;
}
if (rc < 0) {
DEK_LOGE("Error decypting dek; rc = [%d]\n", rc);
memset(&DEK, 0, sizeof(dek_t));
goto out;
}
memcpy(crypt_stat->key, DEK.buf, DEK.len);
crypt_stat->key_size = DEK.len;
memset(&DEK, 0, sizeof(dek_t));
} else {
#if ECRYPTFS_DEK_DEBUG
DEK_LOGD("file is not sensitive\n");
#endif
}
}
out:
/*
* Succeeded
*/
if(!rc) {
sdp_mm_set_process_sensitive(current->pid);
rc = ecryptfs_set_key(crypt_stat);
} else {
/*
* Error
*/
ecryptfs_clean_sdp_dek(crypt_stat);
}
return rc;
}
int write_dek_packet(char *dest,
struct ecryptfs_crypt_stat *crypt_stat,
size_t *written) {
*written = 0;
dest[(*written)++] = ECRYPTFS_DEK_PACKET_TYPE;
memset(dest + *written, 0, PKG_NAME_SIZE);
memcpy(dest + *written, current->comm, PKG_NAME_SIZE);
(*written) += PKG_NAME_SIZE;
put_unaligned_be32(from_kuid(&init_user_ns, current_euid()), dest + *written);
(*written) += 4;
memset(dest + *written, 0, DEK_MAXLEN);
if (crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) {
if(crypt_stat->flags & ECRYPTFS_DEK_MULTI_ENGINE) {
put_unaligned_be32(crypt_stat->engine_id, dest + *written);
(*written) += 4;
}
put_unaligned_be32(crypt_stat->sdp_dek.type, dest + *written);
(*written) += 4;
put_unaligned_be32(crypt_stat->sdp_dek.len, dest + *written);
(*written) += 4;
memcpy(dest + *written, crypt_stat->sdp_dek.buf, crypt_stat->sdp_dek.len);
(*written) += crypt_stat->sdp_dek.len;
}
return 0;
}
int parse_dek_packet(char *data,
struct ecryptfs_crypt_stat *crypt_stat,
size_t *packet_size) {
int rc = 0;
char temp_comm[PKG_NAME_SIZE]; //test
int temp_euid;
if (crypt_stat->file_version == 0)
return -EPERM;
(*packet_size) = 0;
if (data[(*packet_size)++] != ECRYPTFS_DEK_PACKET_TYPE) {
DEK_LOGE("First byte != 0x%.2x; invalid packet\n",
ECRYPTFS_DEK_PACKET_TYPE);
rc = -EINVAL;
}
memcpy(temp_comm, &data[*packet_size], PKG_NAME_SIZE);
(*packet_size) += PKG_NAME_SIZE;
temp_euid = get_unaligned_be32(data + *packet_size);
(*packet_size) += 4;
if (crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) {
if(crypt_stat->flags & ECRYPTFS_DEK_MULTI_ENGINE) {
crypt_stat->engine_id = get_unaligned_be32(data + *packet_size);
(*packet_size) += 4;
} else {
/**
* If eCryptfs header doesn't have engine-id,
* we assign it from mount_crypt_stat->userid
* (Fils created in old version)
*/
crypt_stat->engine_id = crypt_stat->mount_crypt_stat->userid;
}
crypt_stat->sdp_dek.type = get_unaligned_be32(data + *packet_size);
(*packet_size) += 4;
if(crypt_stat->sdp_dek.type < 0 || crypt_stat->sdp_dek.type > 6)
return -EINVAL;
crypt_stat->sdp_dek.len = get_unaligned_be32(data + *packet_size);
(*packet_size) += 4;
if(crypt_stat->sdp_dek.len <= 0 || crypt_stat->sdp_dek.len > DEK_MAXLEN)
return -EFAULT;
memcpy(crypt_stat->sdp_dek.buf, &data[*packet_size], crypt_stat->sdp_dek.len);
(*packet_size) += crypt_stat->sdp_dek.len;
}
#if ECRYPTFS_DEK_DEBUG
DEK_LOGD("%s() : comm : %s [euid:%d]\n",
__func__, temp_comm, temp_euid);
#endif
return rc;
}
static int ecryptfs_dek_copy_mount_wide_sigs_to_inode_sigs(
struct ecryptfs_crypt_stat *crypt_stat,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat)
{
struct ecryptfs_global_auth_tok *global_auth_tok;
int rc = 0;
mutex_lock(&crypt_stat->keysig_list_mutex);
mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex);
list_for_each_entry(global_auth_tok,
&mount_crypt_stat->global_auth_tok_list,
mount_crypt_stat_list) {
if (global_auth_tok->flags & ECRYPTFS_AUTH_TOK_FNEK)
continue;
rc = ecryptfs_add_keysig(crypt_stat, global_auth_tok->sig);
if (rc) {
printk(KERN_ERR "Error adding keysig; rc = [%d]\n", rc);
goto out;
}
}
out:
mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex);
mutex_unlock(&crypt_stat->keysig_list_mutex);
return rc;
}
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
static void ecryptfs_propagate_flag(struct file *file, int userid, enum sdp_op operation) {
struct file *f = file;
do {
if(!f)
return ;
DEK_LOGD("%s file: %p [%s]\n",__func__, f, f->f_inode->i_sb->s_type->name);
if (operation == TO_SENSITIVE) {
mapping_set_sensitive(f->f_mapping);
} else {
mapping_clear_sensitive(f->f_mapping);
}
f->f_mapping->userid = userid;
} while (f->f_op->get_lower_file && (f = f->f_op->get_lower_file(f)));
}
#endif
void ecryptfs_set_mapping_sensitive(struct inode *ecryptfs_inode, int userid, enum sdp_op operation) {
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL;
struct ecryptfs_inode_info *inode_info;
inode_info = ecryptfs_inode_to_private(ecryptfs_inode);
DEK_LOGD("%s inode: %p lower_file_count: %d\n",__func__, ecryptfs_inode,atomic_read(&inode_info->lower_file_count));
mount_crypt_stat = &ecryptfs_superblock_to_private(ecryptfs_inode->i_sb)->mount_crypt_stat;
if (operation == TO_SENSITIVE) {
mapping_set_sensitive(ecryptfs_inode->i_mapping);
} else {
mapping_clear_sensitive(ecryptfs_inode->i_mapping);
}
ecryptfs_inode->i_mapping->userid = userid;
/*
* If FMP is in use, need to set flag to lower filesystems too recursively
*/
if (mount_crypt_stat->flags & ECRYPTFS_USE_FMP) {
if(inode_info->lower_file) {
ecryptfs_propagate_flag(inode_info->lower_file, userid, operation);
}
}
#else
if (operation == TO_SENSITIVE) {
mapping_set_sensitive(ecryptfs_inode->i_mapping);
} else {
mapping_clear_sensitive(ecryptfs_inode->i_mapping);
}
ecryptfs_inode->i_mapping->userid = userid;
#endif
}
/*
* set sensitive flag, update metadata
* Set cached inode pages to sensitive
*/
static int ecryptfs_update_crypt_flag(struct dentry *dentry, enum sdp_op operation)
{
int rc = 0;
struct inode *inode;
struct inode *lower_inode;
struct ecryptfs_crypt_stat *crypt_stat;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
u32 tmp_flags;
DEK_LOGE("%s(operation:%s) entered\n", __func__, get_op_name(operation));
crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
mount_crypt_stat = &ecryptfs_superblock_to_private(dentry->d_sb)->mount_crypt_stat;
if (!(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED))
ecryptfs_init_crypt_stat(crypt_stat);
inode = dentry->d_inode;
lower_inode = ecryptfs_inode_to_lower(inode);
/*
* To update metadata we need to make sure keysig_list contains fekek.
* Because our EDEK is stored along with key for protected file.
*/
if(list_empty(&crypt_stat->keysig_list))
ecryptfs_dek_copy_mount_wide_sigs_to_inode_sigs(crypt_stat, mount_crypt_stat);
mutex_lock(&crypt_stat->cs_mutex);
rc = ecryptfs_get_lower_file(dentry, inode);
if (rc) {
mutex_unlock(&crypt_stat->cs_mutex);
DEK_LOGE("ecryptfs_get_lower_file rc=%d\n", rc);
return rc;
}
tmp_flags = crypt_stat->flags;
if (operation == TO_SENSITIVE) {
crypt_stat->flags |= ECRYPTFS_DEK_IS_SENSITIVE;
crypt_stat->flags |= ECRYPTFS_DEK_MULTI_ENGINE;
/*
* Set sensitive to inode mapping
*/
ecryptfs_set_mapping_sensitive(inode, mount_crypt_stat->userid, TO_SENSITIVE);
} else {
crypt_stat->flags &= ~ECRYPTFS_DEK_IS_SENSITIVE;
crypt_stat->flags &= ~ECRYPTFS_DEK_MULTI_ENGINE;
/*
* Set protected to inode mapping
*/
ecryptfs_set_mapping_sensitive(inode, mount_crypt_stat->userid, TO_PROTECTED);
}
rc = ecryptfs_write_metadata(dentry, inode);
if (rc) {
crypt_stat->flags = tmp_flags;
DEK_LOGE("ecryptfs_write_metadata rc=%d\n", rc);
goto out;
}
rc = ecryptfs_write_inode_size_to_metadata(inode);
if (rc) {
DEK_LOGE("Problem with "
"ecryptfs_write_inode_size_to_metadata; "
"rc = [%d]\n", rc);
goto out;
}
out:
mutex_unlock(&crypt_stat->cs_mutex);
ecryptfs_put_lower_file(inode);
fsstack_copy_attr_all(inode, lower_inode);
return rc;
}
void ecryptfs_fs_request_callback(int opcode, int ret, unsigned long ino) {
DEK_LOGD("%s opcode<%d> ret<%d> ino<%ld>\n", __func__, opcode, ret, ino);
}
int ecryptfs_sdp_set_sensitive(int engine_id, struct dentry *dentry) {
int rc = 0;
struct inode *inode = dentry->d_inode;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(inode)->crypt_stat;
dek_t DEK;
int id_bak = crypt_stat->engine_id;
DEK_LOGD("%s(%s)\n", __func__, dentry->d_name.name);
if(S_ISDIR(inode->i_mode)) {
crypt_stat->engine_id = engine_id;
crypt_stat->flags |= ECRYPTFS_DEK_IS_SENSITIVE;
rc = 0;
} else if(S_ISREG(inode->i_mode)) {
crypt_stat->engine_id = engine_id;
if (crypt_stat->key_size > ECRYPTFS_MAX_KEY_BYTES ||
crypt_stat->key_size > DEK_MAXLEN ||
crypt_stat->key_size > UINT_MAX){
DEK_LOGE("%s Too large key_size\n", __func__);
rc = -EFAULT;
goto out;
}
memcpy(DEK.buf, crypt_stat->key, crypt_stat->key_size);
DEK.len = (unsigned int)crypt_stat->key_size;
DEK.type = DEK_TYPE_PLAIN;
rc = dek_encrypt_dek_efs(crypt_stat->engine_id, &DEK, &crypt_stat->sdp_dek);
if (rc < 0) {
DEK_LOGE("Error encrypting dek; rc = [%d]\n", rc);
memset(&crypt_stat->sdp_dek, 0, sizeof(dek_t));
goto out;
}
#if 0
/*
* We don't have to clear FEK after set-sensitive.
* FEK will be closed when the file is closed
*/
memset(crypt_stat->key, 0, crypt_stat->key_size);
crypt_stat->flags &= ~(ECRYPTFS_KEY_SET);
#else
/*
* set-key after set sensitive file.
* Well when the file is just created and we do set_sensitive, the key is not set in the
* tfm. later SDP code, set-key is done while encryption, trying to decrypt EFEK.
*
* Here is the case in locked state user process want to create/write a file.
* the process open the file, automatically becomes sensitive by vault logic,
* and do the encryption, then boom. failed to decrypt EFEK even if FEK is
* available
*/
rc = ecryptfs_set_key(crypt_stat);
if(rc) goto out;
#endif
ecryptfs_update_crypt_flag(dentry, TO_SENSITIVE);
}
out:
if(rc) crypt_stat->engine_id = id_bak;
memset(&DEK, 0, sizeof(dek_t));
return rc;
}
int ecryptfs_sdp_set_protected(struct dentry *dentry) {
int rc = 0;
struct inode *inode = dentry->d_inode;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(inode)->crypt_stat;
DEK_LOGD("%s(%s)\n", __func__, dentry->d_name.name);
if(IS_CHAMBER_DENTRY(dentry)) {
DEK_LOGE("can't set-protected to chamber directory");
return -EIO;
}
if(crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) {
if(crypt_stat->engine_id < 0) {
DEK_LOGE("%s: invalid engine-id (ECRYPTFS_DEK_IS_SENSITIVE:ON, engine_id:%d)\n",
__func__, crypt_stat->engine_id);
return -EIO;
}
if(ecryptfs_is_sdp_locked(crypt_stat->engine_id)) {
DEK_LOGE("%s: Failed. (engine_id:%d locked)\n",
__func__, crypt_stat->engine_id);
return -EIO;
}
if(S_ISDIR(inode->i_mode)) {
crypt_stat->flags &= ~ECRYPTFS_DEK_IS_SENSITIVE;
rc = 0;
} else {
rc = ecryptfs_get_sdp_dek(crypt_stat);
if (rc) {
ecryptfs_printk(KERN_ERR, "%s Get SDP key failed\n", __func__);
goto out;
}
/*
* TODO : double check if need to compute iv here
*/
rc = ecryptfs_compute_root_iv(crypt_stat);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error computing "
"the root IV\n");
goto out;
}
rc = ecryptfs_set_key(crypt_stat);
if(rc) goto out;
ecryptfs_update_crypt_flag(dentry, TO_PROTECTED);
}
} else {
rc = 0; //already protected
}
out:
return rc;
}
int ecryptfs_sdp_convert_dek(struct dentry *dentry) {
int rc = 0;
struct inode *inode = dentry->d_inode;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(inode)->crypt_stat;
dek_t DEK;
rc = dek_decrypt_dek_efs(crypt_stat->engine_id, &crypt_stat->sdp_dek, &DEK);
if (rc < 0) {
DEK_LOGE("Error converting dek [DEC]; rc = [%d]\n", rc);
goto out;
}
rc = dek_encrypt_dek_efs(crypt_stat->engine_id, &DEK, &crypt_stat->sdp_dek);
if (rc < 0) {
DEK_LOGE("Error converting dek [ENC]; rc = [%d]\n", rc);
goto out;
}
rc = ecryptfs_update_crypt_flag(dentry, TO_SENSITIVE);
if (rc < 0) {
DEK_LOGE("Error converting dek [FLAG]; rc = [%d]\n", rc);
goto out;
}
out:
memset(&DEK, 0, sizeof(dek_t));
return rc;
}
long ecryptfs_do_sdp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
char filename[NAME_MAX+1] = {0};
void __user *ubuf = (void __user *)arg;
struct dentry *ecryptfs_dentry = file->f_path.dentry;
struct inode *inode = ecryptfs_dentry->d_inode;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(inode)->crypt_stat;
struct dentry *fp_dentry =
ecryptfs_inode_to_private(inode)->lower_file->f_dentry;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
&ecryptfs_superblock_to_private(inode->i_sb)->mount_crypt_stat;
int rc;
if (fp_dentry->d_name.len <= NAME_MAX)
memcpy(filename, fp_dentry->d_name.name,
fp_dentry->d_name.len + 1);
DEK_LOGD("%s(%s)\n", __func__, ecryptfs_dentry->d_name.name);
if (!(crypt_stat->flags & ECRYPTFS_DEK_SDP_ENABLED)) {
DEK_LOGE("SDP not enabled, skip sdp ioctl\n");
return -ENOTTY;
}
switch (cmd) {
case ECRYPTFS_IOCTL_GET_SDP_INFO: {
dek_arg_get_sdp_info req;
DEK_LOGD("ECRYPTFS_IOCTL_GET_SDP_INFO\n");
memset(&req, 0, sizeof(dek_arg_get_sdp_info));
if(copy_from_user(&req, ubuf, sizeof(req))) {
DEK_LOGE("can't copy from user\n");
return -EFAULT;
} else {
mutex_lock(&crypt_stat->cs_mutex);
req.engine_id = crypt_stat->engine_id;
if (crypt_stat->flags & ECRYPTFS_DEK_SDP_ENABLED) {
req.sdp_enabled = 1;
} else {
req.sdp_enabled = 0;
}
if (crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) {
req.is_sensitive = 1;
} else {
req.is_sensitive = 0;
}
if (crypt_stat->flags & ECRYPTFS_SDP_IS_CHAMBER_DIR) {
req.is_chamber = 1;
} else {
req.is_chamber = 0;
}
req.type = crypt_stat->sdp_dek.type;
mutex_unlock(&crypt_stat->cs_mutex);
}
if(copy_to_user(ubuf, &req, sizeof(req))) {
DEK_LOGE("can't copy to user\n");
return -EFAULT;
}
break;
}
case ECRYPTFS_IOCTL_SET_PROTECTED: {
ecryptfs_printk(KERN_DEBUG, "ECRYPTFS_IOCTL_SET_PROTECTED\n");
if(!is_current_epmd()) {
DEK_LOGE("only epmd can call this\n");
DEK_LOGE("Permission denied\n");
return -EACCES;
}
if (!(crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE)) {
DEK_LOGE("already protected file\n");
return 0;
}
if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
DEK_LOGE("Set protected directory\n");
crypt_stat->flags &= ~ECRYPTFS_DEK_IS_SENSITIVE;
break;
}
rc = ecryptfs_sdp_set_protected(ecryptfs_dentry);
if (rc) {
DEK_LOGE("Failed to set protected rc(%d)\n", rc);
return rc;
}
break;
}
case ECRYPTFS_IOCTL_SET_SENSITIVE: {
dek_arg_set_sensitive req;
ecryptfs_printk(KERN_DEBUG, "ECRYPTFS_IOCTL_SET_SENSITIVE\n");
if (crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) {
DEK_LOGE("already sensitive file\n");
return 0;
}
memset(&req, 0, sizeof(dek_arg_set_sensitive));
if(copy_from_user(&req, ubuf, sizeof(req))) {
DEK_LOGE("can't copy from user\n");
memset(&req, 0, sizeof(dek_arg_set_sensitive));
return -EFAULT;
} else {
rc = ecryptfs_sdp_set_sensitive(req.engine_id, ecryptfs_dentry);
if (rc) {
DEK_LOGE("failed to set sensitive rc(%d)\n", rc);
memset(&req, 0, sizeof(dek_arg_set_sensitive));
return rc;
}
}
memset(&req, 0, sizeof(dek_arg_set_sensitive));
break;
}
case ECRYPTFS_IOCTL_ADD_CHAMBER: {
dek_arg_add_chamber req;
int engineid;
if (!S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
DEK_LOGE("Not a directory\n");
return -ENOTDIR;
}
if(!is_current_epmd()) {
DEK_LOGE("only epmd can call this\n");
DEK_LOGE("Permission denied\n");
return -EACCES;
}
memset(&req, 0, sizeof(req));
if(copy_from_user(&req, ubuf, sizeof(req))) {
DEK_LOGE("can't copy from user\n");
memset(&req, 0, sizeof(req));
return -EFAULT;
}
if(!IS_UNDER_ROOT(ecryptfs_dentry)) {
DEK_LOGE("Chamber has to be under root directory");
return -EFAULT;
}
if(is_chamber_directory(mount_crypt_stat, ecryptfs_dentry->d_name.name, &engineid)) {
DEK_LOGE("Already chamber directory [%s] engine:%d\n",
ecryptfs_dentry->d_name.name, engineid);
if(engineid != req.engine_id) {
DEK_LOGE("Attemping to change engine-id[%d] -> [%d] : Failed\n",
engineid, req.engine_id);
return -EACCES;
}
set_chamber_flag(engineid, inode);
break;
}
rc = add_chamber_directory(mount_crypt_stat, req.engine_id,
ecryptfs_dentry->d_name.name);
if(rc) {
DEK_LOGE("add_chamber_directory failed. %d\n", rc);
return rc;
}
set_chamber_flag(req.engine_id, inode);
break;
}
case ECRYPTFS_IOCTL_REMOVE_CHAMBER: {
if (!S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
DEK_LOGE("Not a directory\n");
return -ENOTDIR;
}
if(!is_current_epmd()) {
//DEK_LOGE("only epmd can call this");
DEK_LOGE("Permission denied");
return -EACCES;
}
if(!IS_UNDER_ROOT(ecryptfs_dentry)) {
DEK_LOGE("Chamber has to be under root directory");
return -EFAULT;
}
if(!is_chamber_directory(mount_crypt_stat, ecryptfs_dentry->d_name.name, NULL)) {
DEK_LOGE("Not a chamber directory [%s]\n", ecryptfs_dentry->d_name.name);
clr_chamber_flag(inode);
return 0;
}
del_chamber_directory(mount_crypt_stat, ecryptfs_dentry->d_name.name);
clr_chamber_flag(inode);
break;
}
default: {
return -EOPNOTSUPP;
break;
}
}
return 0;
}

84
fs/ecryptfs/ecryptfs_dek.h Executable file
View file

@ -0,0 +1,84 @@
/*
* 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.
*/
#ifndef ECRYPTFS_DEK_H
#define ECRYPTFS_DEK_H
#include <linux/fs.h>
#include <sdp/dek_common.h>
#include "ecryptfs_kernel.h"
#define ECRYPTFS_DEK_XATTR_NAME "user.sdp"
#define ECRYPTFS_DEK_DEBUG 0
#define O_SDP 0x10000000
enum sdp_op {
TO_SENSITIVE = 0,
TO_PROTECTED
};
int ecryptfs_super_block_get_userid(struct super_block *sb);
int ecryptfs_is_sdp_locked(int engine_id);
void ecryptfs_clean_sdp_dek(struct ecryptfs_crypt_stat *crypt_stat);
int ecryptfs_get_sdp_dek(struct ecryptfs_crypt_stat *crypt_stat);
int ecryptfs_sdp_convert_dek(struct dentry *dentry);
int ecryptfs_parse_xattr_is_sensitive(const void *data, int len);
int write_dek_packet(char *dest, struct ecryptfs_crypt_stat *crypt_stat, size_t *written);
int parse_dek_packet(char *data, struct ecryptfs_crypt_stat *crypt_stat, size_t *packet_size);
long ecryptfs_do_sdp_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int ecryptfs_sdp_set_sensitive(int engine_id, struct dentry *dentry);
int ecryptfs_sdp_set_protected(struct dentry *dentry);
void ecryptfs_set_mapping_sensitive(struct inode *ecryptfs_inode, int userid, enum sdp_op operation);
void ecryptfs_fs_request_callback(int opcode, int ret, unsigned long ino);
#define ECRYPTFS_EVT_RENAME_TO_CHAMBER 1
#define ECRYPTFS_EVT_RENAME_OUT_OF_CHAMBER 2
/*
* ioctl for SDP
*/
typedef struct _dek_arg_sdp_info {
int engine_id;
int sdp_enabled;
int is_sensitive;
int is_chamber;
unsigned int type;
}dek_arg_get_sdp_info;
typedef struct _dek_arg_set_sensitive {
int engine_id;
}dek_arg_set_sensitive;
typedef struct _dek_arg_add_chamber {
int engine_id;
}dek_arg_add_chamber;
#define ECRYPTFS_IOCTL_GET_SDP_INFO _IOR('l', 0x11, __u32)
#define ECRYPTFS_IOCTL_SET_SENSITIVE _IOW('l', 0x15, __u32)
#define ECRYPTFS_IOCTL_SET_PROTECTED _IOW('l', 0x16, __u32)
#define ECRYPTFS_IOCTL_ADD_CHAMBER _IOW('l', 0x17, __u32)
#define ECRYPTFS_IOCTL_REMOVE_CHAMBER _IOW('l', 0x18, __u32)
#endif /* #ifndef ECRYPTFS_DEK_H */

38
fs/ecryptfs/ecryptfs_dlp.h Executable file
View file

@ -0,0 +1,38 @@
/*
* 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.
*/
#ifndef ECRYPTFS_DLP_H
#define ECRYPTFS_DLP_H
#define KNOX_DLP_XATTR_NAME "user.knox_dlp"
#define AID_KNOX_DLP KGIDT_INIT(8002)
#define AID_KNOX_DLP_RESTRICTED KGIDT_INIT(8003)
#define DLP_DEBUG 1
struct knox_expiry {
int64_t tv_sec;
int64_t tv_nsec;
};
struct knox_dlp_data {
struct knox_expiry expiry_time;
};
#endif /* ECRYPTFS_DLP_H */

View file

@ -0,0 +1,847 @@
/**
* eCryptfs: Linux filesystem encryption layer
* Kernel declarations.
*
* Copyright (C) 1997-2003 Erez Zadok
* Copyright (C) 2001-2003 Stony Brook University
* Copyright (C) 2004-2008 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
* Trevor S. Highland <trevor.highland@gmail.com>
* Tyler Hicks <tyhicks@ou.edu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef ECRYPTFS_KERNEL_H
#define ECRYPTFS_KERNEL_H
#include <keys/user-type.h>
#include <keys/encrypted-type.h>
#include <linux/fs.h>
#include <linux/fs_stack.h>
#include <linux/namei.h>
#include <linux/scatterlist.h>
#include <linux/hash.h>
#include <linux/nsproxy.h>
#include <linux/backing-dev.h>
#include <linux/ecryptfs.h>
#include <linux/crypto.h>
#ifdef CONFIG_SDP
#include <sdp/dek_common.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#endif
#ifdef CONFIG_DLP
#include "ecryptfs_dlp.h"
#endif
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
#define ENC_NAME_FILTER_MAX_INSTANCE 5
#define ENC_NAME_FILTER_MAX_LEN (256*5)
#define ENC_EXT_FILTER_MAX_INSTANCE 60
#define ENC_EXT_FILTER_MAX_LEN 16
#endif
#define ECRYPTFS_DEFAULT_IV_BYTES 16
#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
#define ECRYPTFS_DEFAULT_MSG_CTX_ELEMS 32
#define ECRYPTFS_DEFAULT_SEND_TIMEOUT HZ
#define ECRYPTFS_MAX_MSG_CTX_TTL (HZ*3)
#define ECRYPTFS_DEFAULT_NUM_USERS 4
#define ECRYPTFS_MAX_NUM_USERS 32768
#define ECRYPTFS_XATTR_NAME "user.ecryptfs"
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
#define RA_CLEAR 0
#define RA_RESTORE 1
#define FMPINFO_CLEAR 0
#define FMPINFO_SET 1
#endif
#ifdef CONFIG_SDP
#define PKG_NAME_SIZE 16
#endif
void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size);
extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
struct ecryptfs_key_record {
unsigned char type;
size_t enc_key_size;
unsigned char sig[ECRYPTFS_SIG_SIZE];
unsigned char enc_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
};
struct ecryptfs_auth_tok_list {
struct ecryptfs_auth_tok *auth_tok;
struct list_head list;
};
struct ecryptfs_crypt_stat;
struct ecryptfs_mount_crypt_stat;
struct ecryptfs_page_crypt_context {
struct page *page;
#define ECRYPTFS_PREPARE_COMMIT_MODE 0
#define ECRYPTFS_WRITEPAGE_MODE 1
unsigned int mode;
union {
struct file *lower_file;
struct writeback_control *wbc;
} param;
};
#if defined(CONFIG_ENCRYPTED_KEYS) || defined(CONFIG_ENCRYPTED_KEYS_MODULE)
static inline struct ecryptfs_auth_tok *
ecryptfs_get_encrypted_key_payload_data(struct key *key)
{
if (key->type == &key_type_encrypted)
return (struct ecryptfs_auth_tok *)
(&((struct encrypted_key_payload *)key->payload.data)->payload_data);
else
return NULL;
}
static inline struct key *ecryptfs_get_encrypted_key(char *sig)
{
return request_key(&key_type_encrypted, sig, NULL);
}
#else
static inline struct ecryptfs_auth_tok *
ecryptfs_get_encrypted_key_payload_data(struct key *key)
{
return NULL;
}
static inline struct key *ecryptfs_get_encrypted_key(char *sig)
{
return ERR_PTR(-ENOKEY);
}
#endif /* CONFIG_ENCRYPTED_KEYS */
static inline struct ecryptfs_auth_tok *
ecryptfs_get_key_payload_data(struct key *key)
{
struct ecryptfs_auth_tok *auth_tok;
auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
if (!auth_tok)
return (struct ecryptfs_auth_tok *)
(((struct user_key_payload *)key->payload.data)->data);
else
return auth_tok;
}
#ifdef CONFIG_CRYPTO_FIPS
#define ECRYPTFS_MAX_CIPHER_MODE_SIZE 3
#define ECRYPTFS_AES_CBC_MODE "cbc"
#define ECRYPTFS_AES_ECB_MODE "ecb"
#endif
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
#define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32
#define ECRYPTFS_MAX_NUM_ENC_KEYS 64
#define ECRYPTFS_MAX_IV_BYTES 16 /* 128 bits */
#define ECRYPTFS_SALT_BYTES 2
#define MAGIC_ECRYPTFS_MARKER 0x3c81b7f5
#define MAGIC_ECRYPTFS_MARKER_SIZE_BYTES 8 /* 4*2 */
#define ECRYPTFS_FILE_SIZE_BYTES (sizeof(u64))
#define ECRYPTFS_SIZE_AND_MARKER_BYTES (ECRYPTFS_FILE_SIZE_BYTES \
+ MAGIC_ECRYPTFS_MARKER_SIZE_BYTES)
#define ECRYPTFS_DEFAULT_CIPHER "aes"
#define ECRYPTFS_DEFAULT_KEY_BYTES 16
#define ECRYPTFS_DEFAULT_HASH "md5"
#ifdef CONFIG_CRYPTO_FIPS
#define ECRYPTFS_SHA256_HASH "sha256"
#endif
#define ECRYPTFS_TAG_70_DIGEST ECRYPTFS_DEFAULT_HASH
#define ECRYPTFS_TAG_1_PACKET_TYPE 0x01
#define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C
#define ECRYPTFS_DEK_PACKET_TYPE 0xD0 /* dek ecryptfs packet block */
#define ECRYPTFS_TAG_11_PACKET_TYPE 0xED
#define ECRYPTFS_TAG_64_PACKET_TYPE 0x40
#define ECRYPTFS_TAG_65_PACKET_TYPE 0x41
#define ECRYPTFS_TAG_66_PACKET_TYPE 0x42
#define ECRYPTFS_TAG_67_PACKET_TYPE 0x43
#define ECRYPTFS_TAG_70_PACKET_TYPE 0x46 /* FNEK-encrypted filename
* as dentry name */
#define ECRYPTFS_TAG_71_PACKET_TYPE 0x47 /* FNEK-encrypted filename in
* metadata */
#define ECRYPTFS_TAG_72_PACKET_TYPE 0x48 /* FEK-encrypted filename as
* dentry name */
#define ECRYPTFS_TAG_73_PACKET_TYPE 0x49 /* FEK-encrypted filename as
* metadata */
#define ECRYPTFS_MIN_PKT_LEN_SIZE 1 /* Min size to specify packet length */
#define ECRYPTFS_MAX_PKT_LEN_SIZE 2 /* Pass at least this many bytes to
* ecryptfs_parse_packet_length() and
* ecryptfs_write_packet_length()
*/
/* Constraint: ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES >=
* ECRYPTFS_MAX_IV_BYTES */
#define ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES 16
#define ECRYPTFS_NON_NULL 0x42 /* A reasonable substitute for NULL */
#define MD5_DIGEST_SIZE 16
#ifdef CONFIG_CRYPTO_FIPS
#define SHA256_HASH_SIZE 32
#endif
#define ECRYPTFS_TAG_70_DIGEST_SIZE MD5_DIGEST_SIZE
#define ECRYPTFS_TAG_70_MIN_METADATA_SIZE (1 + ECRYPTFS_MIN_PKT_LEN_SIZE \
+ ECRYPTFS_SIG_SIZE + 1 + 1)
#define ECRYPTFS_TAG_70_MAX_METADATA_SIZE (1 + ECRYPTFS_MAX_PKT_LEN_SIZE \
+ ECRYPTFS_SIG_SIZE + 1 + 1)
#define ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FEK_ENCRYPTED."
#define ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX_SIZE 23
//#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FNEK_ENCRYPTED."
//#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE 24
#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX "EN."
#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE 3
#define ECRYPTFS_ENCRYPTED_DENTRY_NAME_LEN (18 + 1 + 4 + 1 + 32)
#ifdef CONFIG_ECRYPT_FS_MESSAGING
# define ECRYPTFS_VERSIONING_MASK_MESSAGING (ECRYPTFS_VERSIONING_DEVMISC \
| ECRYPTFS_VERSIONING_PUBKEY)
#else
# define ECRYPTFS_VERSIONING_MASK_MESSAGING 0
#endif
#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
| ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
| ECRYPTFS_VERSIONING_XATTR \
| ECRYPTFS_VERSIONING_MULTKEY \
| ECRYPTFS_VERSIONING_MASK_MESSAGING \
| ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
struct ecryptfs_key_sig {
struct list_head crypt_stat_list;
char keysig[ECRYPTFS_SIG_SIZE_HEX + 1];
};
struct ecryptfs_filename {
struct list_head crypt_stat_list;
#define ECRYPTFS_FILENAME_CONTAINS_DECRYPTED 0x00000001
u32 flags;
u32 seq_no;
char *filename;
char *encrypted_filename;
size_t filename_size;
size_t encrypted_filename_size;
char fnek_sig[ECRYPTFS_SIG_SIZE_HEX];
char dentry_name[ECRYPTFS_ENCRYPTED_DENTRY_NAME_LEN + 1];
};
/**
* This is the primary struct associated with each encrypted file.
*
* TODO: cache align/pack?
*/
struct ecryptfs_crypt_stat {
#define ECRYPTFS_STRUCT_INITIALIZED 0x00000001
#define ECRYPTFS_POLICY_APPLIED 0x00000002
#define ECRYPTFS_ENCRYPTED 0x00000004
#define ECRYPTFS_SECURITY_WARNING 0x00000008
#define ECRYPTFS_ENABLE_HMAC 0x00000010
#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000020
#define ECRYPTFS_KEY_VALID 0x00000040
#define ECRYPTFS_METADATA_IN_XATTR 0x00000080
#define ECRYPTFS_VIEW_AS_ENCRYPTED 0x00000100
#define ECRYPTFS_KEY_SET 0x00000200
#define ECRYPTFS_ENCRYPT_FILENAMES 0x00000400
#define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00000800
#define ECRYPTFS_ENCFN_USE_FEK 0x00001000
#define ECRYPTFS_UNLINK_SIGS 0x00002000
#define ECRYPTFS_I_SIZE_INITIALIZED 0x00004000
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
#define ECRYPTFS_ENCRYPTED_OTHER_DEVICE 0x00008000
#endif
#ifdef CONFIG_SDP
#define ECRYPTFS_DEK_SDP_ENABLED 0x00100000
#define ECRYPTFS_DEK_IS_SENSITIVE 0x00200000
#define ECRYPTFS_DEK_MULTI_ENGINE 0x00400000 // eCryptfs header contains engine id.
#define ECRYPTFS_SDP_IS_CHAMBER_DIR 0x02000000
#endif
#ifdef CONFIG_DLP
#define ECRYPTFS_DLP_ENABLED 0x04000000
#endif
u32 flags;
unsigned int file_version;
size_t iv_bytes;
size_t metadata_size;
size_t extent_size; /* Data extent size; default is 4096 */
size_t key_size;
size_t extent_shift;
unsigned int extent_mask;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
struct crypto_ablkcipher *tfm;
struct crypto_hash *hash_tfm; /* Crypto context for generating
* the initialization vectors */
unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
unsigned char key[ECRYPTFS_MAX_KEY_BYTES];
unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES];
struct list_head keysig_list;
struct mutex keysig_list_mutex;
struct mutex cs_tfm_mutex;
struct mutex cs_hash_tfm_mutex;
struct mutex cs_mutex;
#ifdef CONFIG_SDP
int engine_id;
dek_t sdp_dek;
#endif
#ifdef CONFIG_DLP
struct knox_dlp_data expiry;
#endif
};
/* inode private data. */
struct ecryptfs_inode_info {
struct inode vfs_inode;
struct inode *wii_inode;
struct mutex lower_file_mutex;
atomic_t lower_file_count;
struct file *lower_file;
struct ecryptfs_crypt_stat crypt_stat;
};
/* dentry private data. Each dentry must keep track of a lower
* vfsmount too. */
struct ecryptfs_dentry_info {
struct path lower_path;
union {
struct ecryptfs_crypt_stat *crypt_stat;
struct rcu_head rcu;
};
};
/**
* ecryptfs_global_auth_tok - A key used to encrypt all new files under the mountpoint
* @flags: Status flags
* @mount_crypt_stat_list: These auth_toks hang off the mount-wide
* cryptographic context. Every time a new
* inode comes into existence, eCryptfs copies
* the auth_toks on that list to the set of
* auth_toks on the inode's crypt_stat
* @global_auth_tok_key: The key from the user's keyring for the sig
* @global_auth_tok: The key contents
* @sig: The key identifier
*
* ecryptfs_global_auth_tok structs refer to authentication token keys
* in the user keyring that apply to newly created files. A list of
* these objects hangs off of the mount_crypt_stat struct for any
* given eCryptfs mount. This struct maintains a reference to both the
* key contents and the key itself so that the key can be put on
* unmount.
*/
struct ecryptfs_global_auth_tok {
#define ECRYPTFS_AUTH_TOK_INVALID 0x00000001
#define ECRYPTFS_AUTH_TOK_FNEK 0x00000002
u32 flags;
struct list_head mount_crypt_stat_list;
struct key *global_auth_tok_key;
unsigned char sig[ECRYPTFS_SIG_SIZE_HEX + 1];
};
/**
* ecryptfs_key_tfm - Persistent key tfm
* @key_tfm: crypto API handle to the key
* @key_size: Key size in bytes
* @key_tfm_mutex: Mutex to ensure only one operation in eCryptfs is
* using the persistent TFM at any point in time
* @key_tfm_list: Handle to hang this off the module-wide TFM list
* @cipher_name: String name for the cipher for this TFM
*
* Typically, eCryptfs will use the same ciphers repeatedly throughout
* the course of its operations. In order to avoid unnecessarily
* destroying and initializing the same cipher repeatedly, eCryptfs
* keeps a list of crypto API contexts around to use when needed.
*/
struct ecryptfs_key_tfm {
struct crypto_blkcipher *key_tfm;
size_t key_size;
struct mutex key_tfm_mutex;
struct list_head key_tfm_list;
unsigned char cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
#ifdef CONFIG_CRYPTO_FIPS
unsigned char cipher_mode[ECRYPTFS_MAX_CIPHER_MODE_SIZE + 1];
#endif
};
extern struct mutex key_tfm_list_mutex;
/**
* This struct is to enable a mount-wide passphrase/salt combo. This
* is more or less a stopgap to provide similar functionality to other
* crypto filesystems like EncFS or CFS until full policy support is
* implemented in eCryptfs.
*/
struct ecryptfs_mount_crypt_stat {
/* Pointers to memory we do not own, do not free these */
#define ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED 0x00000001
#define ECRYPTFS_XATTR_METADATA_ENABLED 0x00000002
#define ECRYPTFS_ENCRYPTED_VIEW_ENABLED 0x00000004
#define ECRYPTFS_MOUNT_CRYPT_STAT_INITIALIZED 0x00000008
#define ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES 0x00000010
#define ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK 0x00000020
#define ECRYPTFS_GLOBAL_ENCFN_USE_FEK 0x00000040
#define ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY 0x00000080
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
#define ECRYPTFS_USE_FMP 0x00001000
#endif
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
#define ECRYPTFS_ENABLE_FILTERING 0x00000100
#define ECRYPTFS_ENABLE_NEW_PASSTHROUGH 0x00000200
#endif
#ifdef CONFIG_CRYPTO_FIPS
#define ECRYPTFS_ENABLE_CC 0x00000400
#endif
#ifdef CONFIG_SDP
#define ECRYPTFS_MOUNT_SDP_ENABLED 0x80000000
#endif
#ifdef CONFIG_DLP
#define ECRYPTFS_MOUNT_DLP_ENABLED 0x40000000
#endif
u32 flags;
struct list_head global_auth_tok_list;
struct mutex global_auth_tok_list_mutex;
size_t global_default_cipher_key_size;
size_t global_default_fn_cipher_key_bytes;
unsigned char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE
+ 1];
unsigned char global_default_fn_cipher_name[
ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
char global_default_fnek_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
u8 cipher_code;
#endif
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
int max_name_filter_len;
char enc_filter_name[ENC_NAME_FILTER_MAX_INSTANCE]
[ENC_NAME_FILTER_MAX_LEN + 1];
char enc_filter_ext[ENC_EXT_FILTER_MAX_INSTANCE]
[ENC_EXT_FILTER_MAX_LEN + 1];
#endif
#ifdef CONFIG_SDP
int userid;
struct list_head chamber_dir_list;
spinlock_t chamber_dir_list_lock;
int partition_id;
#endif
};
/* superblock private data. */
struct ecryptfs_sb_info {
struct super_block *wsi_sb;
struct ecryptfs_mount_crypt_stat mount_crypt_stat;
struct backing_dev_info bdi;
#ifdef CONFIG_SDP
int userid;
#endif
};
/* file private data. */
struct ecryptfs_file_info {
struct file *wfi_file;
struct ecryptfs_crypt_stat *crypt_stat;
};
/* auth_tok <=> encrypted_session_key mappings */
struct ecryptfs_auth_tok_list_item {
unsigned char encrypted_session_key[ECRYPTFS_MAX_KEY_BYTES];
struct list_head list;
struct ecryptfs_auth_tok auth_tok;
};
struct ecryptfs_message {
/* Can never be greater than ecryptfs_message_buf_len */
/* Used to find the parent msg_ctx */
/* Inherits from msg_ctx->index */
u32 index;
u32 data_len;
u8 data[];
};
struct ecryptfs_msg_ctx {
#define ECRYPTFS_MSG_CTX_STATE_FREE 0x01
#define ECRYPTFS_MSG_CTX_STATE_PENDING 0x02
#define ECRYPTFS_MSG_CTX_STATE_DONE 0x03
#define ECRYPTFS_MSG_CTX_STATE_NO_REPLY 0x04
u8 state;
#define ECRYPTFS_MSG_HELO 100
#define ECRYPTFS_MSG_QUIT 101
#define ECRYPTFS_MSG_REQUEST 102
#define ECRYPTFS_MSG_RESPONSE 103
u8 type;
u32 index;
/* Counter converts to a sequence number. Each message sent
* out for which we expect a response has an associated
* sequence number. The response must have the same sequence
* number as the counter for the msg_stc for the message to be
* valid. */
u32 counter;
size_t msg_size;
struct ecryptfs_message *msg;
struct task_struct *task;
struct list_head node;
struct list_head daemon_out_list;
struct mutex mux;
};
struct ecryptfs_daemon {
#define ECRYPTFS_DAEMON_IN_READ 0x00000001
#define ECRYPTFS_DAEMON_IN_POLL 0x00000002
#define ECRYPTFS_DAEMON_ZOMBIE 0x00000004
#define ECRYPTFS_DAEMON_MISCDEV_OPEN 0x00000008
u32 flags;
u32 num_queued_msg_ctx;
struct file *file;
struct mutex mux;
struct list_head msg_ctx_out_queue;
wait_queue_head_t wait;
struct hlist_node euid_chain;
};
#ifdef CONFIG_ECRYPT_FS_MESSAGING
extern struct mutex ecryptfs_daemon_hash_mux;
#endif
static inline size_t
ecryptfs_lower_header_size(struct ecryptfs_crypt_stat *crypt_stat)
{
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
return 0;
return crypt_stat->metadata_size;
}
static inline struct ecryptfs_file_info *
ecryptfs_file_to_private(struct file *file)
{
return file->private_data;
}
static inline void
ecryptfs_set_file_private(struct file *file,
struct ecryptfs_file_info *file_info)
{
file->private_data = file_info;
}
static inline struct file *ecryptfs_file_to_lower(struct file *file)
{
return ((struct ecryptfs_file_info *)file->private_data)->wfi_file;
}
static inline void
ecryptfs_set_file_lower(struct file *file, struct file *lower_file)
{
((struct ecryptfs_file_info *)file->private_data)->wfi_file =
lower_file;
}
static inline struct ecryptfs_inode_info *
ecryptfs_inode_to_private(struct inode *inode)
{
return container_of(inode, struct ecryptfs_inode_info, vfs_inode);
}
static inline struct inode *ecryptfs_inode_to_lower(struct inode *inode)
{
return ecryptfs_inode_to_private(inode)->wii_inode;
}
static inline void
ecryptfs_set_inode_lower(struct inode *inode, struct inode *lower_inode)
{
ecryptfs_inode_to_private(inode)->wii_inode = lower_inode;
}
static inline struct ecryptfs_sb_info *
ecryptfs_superblock_to_private(struct super_block *sb)
{
return (struct ecryptfs_sb_info *)sb->s_fs_info;
}
static inline void
ecryptfs_set_superblock_private(struct super_block *sb,
struct ecryptfs_sb_info *sb_info)
{
sb->s_fs_info = sb_info;
}
static inline struct super_block *
ecryptfs_superblock_to_lower(struct super_block *sb)
{
return ((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb;
}
static inline void
ecryptfs_set_superblock_lower(struct super_block *sb,
struct super_block *lower_sb)
{
((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb = lower_sb;
}
static inline struct ecryptfs_dentry_info *
ecryptfs_dentry_to_private(struct dentry *dentry)
{
return (struct ecryptfs_dentry_info *)dentry->d_fsdata;
}
static inline void
ecryptfs_set_dentry_private(struct dentry *dentry,
struct ecryptfs_dentry_info *dentry_info)
{
dentry->d_fsdata = dentry_info;
}
static inline struct dentry *
ecryptfs_dentry_to_lower(struct dentry *dentry)
{
return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path.dentry;
}
static inline struct vfsmount *
ecryptfs_dentry_to_lower_mnt(struct dentry *dentry)
{
return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path.mnt;
}
static inline struct path *
ecryptfs_dentry_to_lower_path(struct dentry *dentry)
{
return &((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path;
}
#define ecryptfs_printk(type, fmt, arg...) \
__ecryptfs_printk(type "%s: " fmt, __func__, ## arg);
__printf(1, 2)
void __ecryptfs_printk(const char *fmt, ...);
extern const struct file_operations ecryptfs_main_fops;
extern const struct file_operations ecryptfs_dir_fops;
extern const struct inode_operations ecryptfs_main_iops;
extern const struct inode_operations ecryptfs_dir_iops;
extern const struct inode_operations ecryptfs_symlink_iops;
extern const struct super_operations ecryptfs_sops;
extern const struct dentry_operations ecryptfs_dops;
extern const struct address_space_operations ecryptfs_aops;
extern int ecryptfs_verbosity;
extern unsigned int ecryptfs_message_buf_len;
extern signed long ecryptfs_message_wait_timeout;
extern unsigned int ecryptfs_number_of_users;
extern struct kmem_cache *ecryptfs_auth_tok_list_item_cache;
extern struct kmem_cache *ecryptfs_file_info_cache;
extern struct kmem_cache *ecryptfs_dentry_info_cache;
extern struct kmem_cache *ecryptfs_inode_info_cache;
extern struct kmem_cache *ecryptfs_sb_info_cache;
extern struct kmem_cache *ecryptfs_header_cache;
extern struct kmem_cache *ecryptfs_xattr_cache;
extern struct kmem_cache *ecryptfs_key_record_cache;
extern struct kmem_cache *ecryptfs_key_sig_cache;
extern struct kmem_cache *ecryptfs_global_auth_tok_cache;
extern struct kmem_cache *ecryptfs_key_tfm_cache;
struct inode *ecryptfs_get_inode(struct inode *lower_inode,
struct super_block *sb);
void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
struct inode *ecryptfs_inode);
int ecryptfs_decode_and_decrypt_filename(char **decrypted_name,
size_t *decrypted_name_size,
struct super_block *sb,
const char *name, size_t name_size);
int ecryptfs_fill_zeros(struct file *file, loff_t new_length);
int ecryptfs_encrypt_and_encode_filename(
char **encoded_name,
size_t *encoded_name_size,
struct ecryptfs_crypt_stat *crypt_stat,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
const char *name, size_t name_size);
struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry);
void ecryptfs_dump_hex(char *data, int bytes);
int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
int sg_size);
int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat);
void ecryptfs_rotate_iv(unsigned char *iv);
void ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat);
void ecryptfs_destroy_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat);
void ecryptfs_destroy_mount_crypt_stat(
struct ecryptfs_mount_crypt_stat *mount_crypt_stat);
int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat);
int ecryptfs_write_inode_size_to_metadata(struct inode *ecryptfs_inode);
int ecryptfs_encrypt_page(struct page *page);
int ecryptfs_decrypt_page(struct page *page);
int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry,
struct inode *ecryptfs_inode);
int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry);
int ecryptfs_new_file_context(struct inode *ecryptfs_inode);
void ecryptfs_write_crypt_stat_flags(char *page_virt,
struct ecryptfs_crypt_stat *crypt_stat,
size_t *written);
int ecryptfs_read_and_validate_header_region(struct inode *inode);
int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry,
struct inode *inode);
u8 ecryptfs_code_for_cipher_string(char *cipher_name, size_t key_bytes);
int ecryptfs_cipher_code_to_string(char *str, u8 cipher_code);
void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat);
int ecryptfs_generate_key_packet_set(char *dest_base,
struct ecryptfs_crypt_stat *crypt_stat,
struct dentry *ecryptfs_dentry,
size_t *len, size_t max);
int
ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
unsigned char *src, struct dentry *ecryptfs_dentry);
int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
ssize_t
ecryptfs_getxattr_lower(struct dentry *lower_dentry, const char *name,
void *value, size_t size);
int
ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags);
int ecryptfs_read_xattr_region(char *page_virt, struct inode *ecryptfs_inode);
#ifdef CONFIG_ECRYPT_FS_MESSAGING
int ecryptfs_process_response(struct ecryptfs_daemon *daemon,
struct ecryptfs_message *msg, u32 seq);
int ecryptfs_send_message(char *data, int data_len,
struct ecryptfs_msg_ctx **msg_ctx);
int ecryptfs_wait_for_response(struct ecryptfs_msg_ctx *msg_ctx,
struct ecryptfs_message **emsg);
int ecryptfs_init_messaging(void);
void ecryptfs_release_messaging(void);
#else
static inline int ecryptfs_init_messaging(void)
{
return 0;
}
static inline void ecryptfs_release_messaging(void)
{ }
static inline int ecryptfs_send_message(char *data, int data_len,
struct ecryptfs_msg_ctx **msg_ctx)
{
return -ENOTCONN;
}
static inline int ecryptfs_wait_for_response(struct ecryptfs_msg_ctx *msg_ctx,
struct ecryptfs_message **emsg)
{
return -ENOMSG;
}
#endif
void
ecryptfs_write_header_metadata(char *virt,
struct ecryptfs_crypt_stat *crypt_stat,
size_t *written);
int ecryptfs_add_keysig(struct ecryptfs_crypt_stat *crypt_stat, char *sig);
int
ecryptfs_add_global_auth_tok(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
char *sig, u32 global_auth_tok_flags);
int ecryptfs_get_global_auth_tok_for_sig(
struct ecryptfs_global_auth_tok **global_auth_tok,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *sig);
#ifdef CONFIG_CRYPTO_FIPS
int
ecryptfs_add_new_key_tfm(struct ecryptfs_key_tfm **key_tfm, char *cipher_name,
size_t key_size, u32 mount_flags);
#else
int
ecryptfs_add_new_key_tfm(struct ecryptfs_key_tfm **key_tfm, char *cipher_name,
size_t key_size);
#endif
int ecryptfs_init_crypto(void);
int ecryptfs_destroy_crypto(void);
#ifdef CONFIG_CRYPTO_FIPS
int ecryptfs_tfm_exists(char *cipher_name, char *cipher_mode, struct ecryptfs_key_tfm **key_tfm);
int ecryptfs_get_tfm_and_mutex_for_cipher_name(struct crypto_blkcipher **tfm,
struct mutex **tfm_mutex,
char *cipher_name,
u32 mount_flags);
#else
int ecryptfs_tfm_exists(char *cipher_name, struct ecryptfs_key_tfm **key_tfm);
int ecryptfs_get_tfm_and_mutex_for_cipher_name(struct crypto_blkcipher **tfm,
struct mutex **tfm_mutex,
char *cipher_name);
#endif
int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key,
struct ecryptfs_auth_tok **auth_tok,
char *sig);
int ecryptfs_write_lower(struct inode *ecryptfs_inode, char *data,
loff_t offset, size_t size);
int ecryptfs_write_lower_page_segment(struct inode *ecryptfs_inode,
struct page *page_for_lower,
size_t offset_in_page, size_t size);
int ecryptfs_write(struct inode *inode, char *data, loff_t offset, size_t size);
int ecryptfs_read_lower(char *data, loff_t offset, size_t size,
struct inode *ecryptfs_inode);
int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs,
pgoff_t page_index,
size_t offset_in_page, size_t size,
struct inode *ecryptfs_inode);
struct page *ecryptfs_get_locked_page(struct inode *inode, loff_t index);
int ecryptfs_parse_packet_length(unsigned char *data, size_t *size,
size_t *length_size);
int ecryptfs_write_packet_length(char *dest, size_t size,
size_t *packet_size_length);
#ifdef CONFIG_ECRYPT_FS_MESSAGING
int ecryptfs_init_ecryptfs_miscdev(void);
void ecryptfs_destroy_ecryptfs_miscdev(void);
int ecryptfs_send_miscdev(char *data, size_t data_size,
struct ecryptfs_msg_ctx *msg_ctx, u8 msg_type,
u16 msg_flags, struct ecryptfs_daemon *daemon);
void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx);
int
ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, struct file *file);
int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon);
int ecryptfs_find_daemon_by_euid(struct ecryptfs_daemon **daemon);
#endif
int ecryptfs_init_kthread(void);
void ecryptfs_destroy_kthread(void);
int ecryptfs_privileged_open(struct file **lower_file,
struct dentry *lower_dentry,
struct vfsmount *lower_mnt,
const struct cred *cred);
int ecryptfs_get_lower_file(struct dentry *dentry, struct inode *inode);
void ecryptfs_put_lower_file(struct inode *inode);
int
ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
size_t *packet_size,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
char *filename, size_t filename_size);
int
ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
size_t *packet_size,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
char *data, size_t max_packet_size);
int ecryptfs_set_f_namelen(long *namelen, long lower_namelen,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat);
int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
loff_t offset);
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
void ecryptfs_propagate_rapages(struct file *file, unsigned int crypt);
int ecryptfs_propagate_fmpinfo(struct inode *inode, unsigned int flag);
#endif
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
extern int is_file_name_match(struct ecryptfs_mount_crypt_stat *mcs,
struct dentry *fp_dentry);
extern int is_file_ext_match(struct ecryptfs_mount_crypt_stat *mcs,
char *str);
#endif
#endif /* #ifndef ECRYPTFS_KERNEL_H */

View file

@ -0,0 +1,183 @@
/*
* 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/list.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include "ecryptfs_sdp_chamber.h"
#include <sdp/common.h>
#define CHAMBER_PATH_MAX 512
typedef struct __chamber_info {
int partition_id;
int engine_id;
struct list_head list;
char path[CHAMBER_PATH_MAX];
}chamber_info_t;
#define NO_DIRECTORY_SEPARATOR_IN_CHAMBER_PATH 1
/* Debug */
#define CHAMBER_DEBUG 0
#if CHAMBER_DEBUG
#define CHAMBER_LOGD(FMT, ...) printk("SDP_CHAMBER[%d] %s :: " FMT , current->pid, __func__, ##__VA_ARGS__)
#else
#define CHAMBER_LOGD(FMT, ...)
#endif /* PUB_CRYPTO_DEBUG */
#define CHAMBER_LOGE(FMT, ...) printk("SDP_CHAMBER[%d] %s :: " FMT , current->pid, __func__, ##__VA_ARGS__)
chamber_info_t *alloc_chamber_info(int partition_id, int engine_id, const unsigned char *path) {
chamber_info_t *new_chamber = kmalloc(sizeof(chamber_info_t), GFP_KERNEL);
if(new_chamber == NULL) {
CHAMBER_LOGE("can't alloc memory for chamber_info\n");
return NULL;
}
new_chamber->partition_id = partition_id;
new_chamber->engine_id = engine_id;
snprintf(new_chamber->path, CHAMBER_PATH_MAX, "%s", path);
return new_chamber;
}
int add_chamber_directory(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
int engine_id, const unsigned char *path) {
chamber_info_t *new_chamber = NULL;
#if NO_DIRECTORY_SEPARATOR_IN_CHAMBER_PATH
if(strchr(path, '/') != NULL) {
CHAMBER_LOGE("Chamber directory cannot contain '/'\n");
return -EINVAL;
}
#endif
new_chamber = alloc_chamber_info(mount_crypt_stat->partition_id, engine_id, path);
if(new_chamber == NULL) {
return -ENOMEM;
}
spin_lock(&(mount_crypt_stat->chamber_dir_list_lock));
CHAMBER_LOGD("Adding %s into chamber list\n", new_chamber->path);
list_add_tail(&new_chamber->list, &(mount_crypt_stat->chamber_dir_list));
spin_unlock(&(mount_crypt_stat->chamber_dir_list_lock));
return 0;
}
chamber_info_t *find_chamber_info(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
const unsigned char *path) {
struct list_head *entry;
spin_lock(&(mount_crypt_stat->chamber_dir_list_lock));
CHAMBER_LOGD("%s\n", path);
list_for_each(entry, &mount_crypt_stat->chamber_dir_list) {
chamber_info_t *info;
info = list_entry(entry, chamber_info_t, list);
// Check path
if(!strncmp(path, info->path, CHAMBER_PATH_MAX)) {
CHAMBER_LOGD("Found %s from chamber list\n", info->path);
spin_unlock(&(mount_crypt_stat->chamber_dir_list_lock));
return info;
}
}
spin_unlock(&(mount_crypt_stat->chamber_dir_list_lock));
CHAMBER_LOGD("Not found\n");
return NULL;
}
void del_chamber_directory(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
const unsigned char *path) {
chamber_info_t *info = find_chamber_info(mount_crypt_stat, path);
if(info == NULL) {
CHAMBER_LOGD("nothing to remove\n");
return;
}
spin_lock(&mount_crypt_stat->chamber_dir_list_lock);
CHAMBER_LOGD("%s removed\n", info->path);
list_del(&info->list);
kfree(info);
spin_unlock(&mount_crypt_stat->chamber_dir_list_lock);
}
int is_chamber_directory(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
const unsigned char *path, int *engineid) {
chamber_info_t *info;
#if NO_DIRECTORY_SEPARATOR_IN_CHAMBER_PATH
if(strchr(path, '/') != NULL) {
CHAMBER_LOGD("%s containes '/'\n", path);
return 0;
}
#endif
info = find_chamber_info(mount_crypt_stat, path);
if(info == NULL)
return 0;
if(engineid) *engineid = info->engine_id;
return 1;
}
void set_chamber_flag(int engineid, struct inode *inode) {
struct ecryptfs_crypt_stat *crypt_stat;
if(inode == NULL) {
CHAMBER_LOGE("invalid inode\n");
return;
}
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
crypt_stat->engine_id = engineid;
crypt_stat->flags |= ECRYPTFS_SDP_IS_CHAMBER_DIR;
crypt_stat->flags |= ECRYPTFS_DEK_IS_SENSITIVE;
}
void clr_chamber_flag(struct inode *inode) {
struct ecryptfs_crypt_stat *crypt_stat;
if(inode == NULL) {
CHAMBER_LOGE("invalid inode\n");
return;
}
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
crypt_stat->engine_id = -1;
crypt_stat->flags &= ~ECRYPTFS_DEK_IS_SENSITIVE;
crypt_stat->flags &= ~ECRYPTFS_SDP_IS_CHAMBER_DIR;
}

View file

@ -0,0 +1,39 @@
/*
* 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.
*/
#ifndef ECRYPTFS_SDP_CHAMBER_H_
#define ECRYPTFS_SDP_CHAMBER_H_
#include "ecryptfs_kernel.h"
int add_chamber_directory(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
int engine_id, const unsigned char *path);
void del_chamber_directory(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
const unsigned char *path);
int is_chamber_directory(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
const unsigned char *path, int *engineid);
void set_chamber_flag(int engineid, struct inode *inode);
void clr_chamber_flag(struct inode *inode);
#define IS_UNDER_ROOT(dentry) (dentry->d_parent->d_inode == dentry->d_sb->s_root->d_inode)
#define IS_CHAMBER_DENTRY(dentry) (ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat.flags & ECRYPTFS_SDP_IS_CHAMBER_DIR)
#define IS_SENSITIVE_DENTRY(dentry) (ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat.flags & ECRYPTFS_DEK_IS_SENSITIVE)
#endif /* ECRYPTFS_SDP_CHAMBER_H_ */

897
fs/ecryptfs/file.c Normal file
View file

@ -0,0 +1,897 @@
/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 1997-2004 Erez Zadok
* Copyright (C) 2001-2004 Stony Brook University
* Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
* Michael C. Thompson <mcthomps@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/file.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/security.h>
#include <linux/compat.h>
#include <linux/fs_stack.h>
#include <linux/aio.h>
#include "ecryptfs_kernel.h"
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
#include <linux/ctype.h>
#define ECRYPTFS_IOCTL_GET_ATTRIBUTES _IOR('l', 0x10, __u32)
#define ECRYPTFS_WAS_ENCRYPTED 0x0080
#define ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE 0x0100
#endif
#ifdef CONFIG_SDP
#if 0
#include <linux/fs.h>
#include <linux/syscalls.h>
#include <linux/atomic.h>
#endif
#include "ecryptfs_dek.h"
#include "mm.h"
#endif
#ifdef CONFIG_DLP
#include "ecryptfs_dlp.h"
#include <sdp/sdp_dlp.h>
#include <sdp/fs_request.h>
#endif
/**
* ecryptfs_read_update_atime
*
* generic_file_read updates the atime of upper layer inode. But, it
* doesn't give us a chance to update the atime of the lower layer
* inode. This function is a wrapper to generic_file_read. It
* updates the atime of the lower level inode if generic_file_read
* returns without any errors. This is to be used only for file reads.
* The function to be used for directory reads is ecryptfs_read.
*/
static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb,
struct iov_iter *to)
{
ssize_t rc;
struct path *path;
struct file *file = iocb->ki_filp;
rc = generic_file_read_iter(iocb, to);
/*
* Even though this is a async interface, we need to wait
* for IO to finish to update atime
*/
if (-EIOCBQUEUED == rc)
rc = wait_on_sync_kiocb(iocb);
if (rc >= 0) {
path = ecryptfs_dentry_to_lower_path(file->f_path.dentry);
touch_atime(path);
}
return rc;
}
struct ecryptfs_getdents_callback {
struct dir_context ctx;
struct dir_context *caller;
struct super_block *sb;
int filldir_called;
int entries_written;
};
/* Inspired by generic filldir in fs/readdir.c */
static int
ecryptfs_filldir(void *dirent, const char *lower_name, int lower_namelen,
loff_t offset, u64 ino, unsigned int d_type)
{
struct ecryptfs_getdents_callback *buf =
(struct ecryptfs_getdents_callback *)dirent;
size_t name_size;
char *name;
int rc;
buf->filldir_called++;
rc = ecryptfs_decode_and_decrypt_filename(&name, &name_size,
buf->sb, lower_name,
lower_namelen);
if (rc) {
printk(KERN_ERR "%s: Error attempting to decode and decrypt "
"filename [%s]; rc = [%d]\n", __func__, lower_name,
rc);
goto out;
}
buf->caller->pos = buf->ctx.pos;
rc = !dir_emit(buf->caller, name, name_size, ino, d_type);
kfree(name);
if (!rc)
buf->entries_written++;
out:
return rc;
}
/**
* ecryptfs_readdir
* @file: The eCryptfs directory file
* @ctx: The actor to feed the entries to
*/
static int ecryptfs_readdir(struct file *file, struct dir_context *ctx)
{
int rc;
struct file *lower_file;
struct inode *inode = file_inode(file);
struct ecryptfs_getdents_callback buf = {
.ctx.actor = ecryptfs_filldir,
.caller = ctx,
.sb = inode->i_sb,
};
lower_file = ecryptfs_file_to_lower(file);
lower_file->f_pos = ctx->pos;
rc = iterate_dir(lower_file, &buf.ctx);
ctx->pos = buf.ctx.pos;
if (rc < 0)
goto out;
if (buf.filldir_called && !buf.entries_written)
goto out;
if (rc >= 0)
fsstack_copy_attr_atime(inode,
file_inode(lower_file));
out:
return rc;
}
struct kmem_cache *ecryptfs_file_info_cache;
static int read_or_initialize_metadata(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
struct ecryptfs_crypt_stat *crypt_stat;
int rc;
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
mount_crypt_stat = &ecryptfs_superblock_to_private(
inode->i_sb)->mount_crypt_stat;
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
if (crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED
&& crypt_stat->flags & ECRYPTFS_POLICY_APPLIED
&& crypt_stat->flags & ECRYPTFS_ENCRYPTED
&& !(crypt_stat->flags & ECRYPTFS_KEY_VALID)
&& !(crypt_stat->flags & ECRYPTFS_KEY_SET)
&& crypt_stat->flags & ECRYPTFS_I_SIZE_INITIALIZED) {
crypt_stat->flags |= ECRYPTFS_ENCRYPTED_OTHER_DEVICE;
}
mutex_lock(&crypt_stat->cs_mutex);
if ((mount_crypt_stat->flags & ECRYPTFS_ENABLE_NEW_PASSTHROUGH)
&& (crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
if (ecryptfs_read_metadata(dentry)) {
crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
| ECRYPTFS_ENCRYPTED);
rc = 0;
goto out;
}
} else if ((mount_crypt_stat->flags & ECRYPTFS_ENABLE_FILTERING)
&& (crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
struct dentry *fp_dentry =
ecryptfs_inode_to_private(inode)->lower_file->f_dentry;
char filename[NAME_MAX+1] = {0};
if (fp_dentry->d_name.len <= NAME_MAX)
memcpy(filename, fp_dentry->d_name.name,
fp_dentry->d_name.len + 1);
if (is_file_name_match(mount_crypt_stat, fp_dentry)
|| is_file_ext_match(mount_crypt_stat, filename)) {
if (ecryptfs_read_metadata(dentry))
crypt_stat->flags &=
~(ECRYPTFS_I_SIZE_INITIALIZED
| ECRYPTFS_ENCRYPTED);
rc = 0;
goto out;
}
}
mutex_unlock(&crypt_stat->cs_mutex);
#endif
mutex_lock(&crypt_stat->cs_mutex);
if (crypt_stat->flags & ECRYPTFS_POLICY_APPLIED &&
crypt_stat->flags & ECRYPTFS_KEY_VALID) {
rc = 0;
goto out;
}
rc = ecryptfs_read_metadata(dentry);
if (!rc)
goto out;
#ifdef CONFIG_SDP
/*
* no passthrough/xattr for sensitive files
*/
if ((rc) && crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE)
goto out;
#endif
if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED) {
crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
| ECRYPTFS_ENCRYPTED);
rc = 0;
goto out;
}
if (!(mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) &&
!i_size_read(ecryptfs_inode_to_lower(inode))) {
rc = ecryptfs_initialize_file(dentry, inode);
if (!rc)
goto out;
}
rc = -EIO;
out:
mutex_unlock(&crypt_stat->cs_mutex);
#ifdef CONFIG_SDP
if(!rc)
{
/*
* SDP v2.0 : sensitive directory (SDP vault)
* Files under sensitive directory automatically becomes sensitive
*/
struct dentry *p = dentry->d_parent;
struct inode *parent_inode = p->d_inode;
struct ecryptfs_crypt_stat *parent_crypt_stat =
&ecryptfs_inode_to_private(parent_inode)->crypt_stat;
if (!(crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) &&
((S_ISDIR(parent_inode->i_mode)) &&
(parent_crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE))) {
rc = ecryptfs_sdp_set_sensitive(parent_crypt_stat->engine_id, dentry);
}
}
#endif
return rc;
}
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
static void ecryptfs_set_rapages(struct file *file, unsigned int flag)
{
if (!flag)
file->f_ra.ra_pages = 0;
else
file->f_ra.ra_pages = (unsigned int)file->f_mapping->backing_dev_info->ra_pages;
}
static int ecryptfs_set_fmpinfo(struct file *file, struct inode *inode, unsigned int set_flag)
{
struct address_space *mapping = file->f_mapping;
if (set_flag) {
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(inode)->crypt_stat;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
&ecryptfs_superblock_to_private(inode->i_sb)->mount_crypt_stat;
if (strncmp(crypt_stat->cipher, "aesxts", sizeof("aesxts"))
&& strncmp(crypt_stat->cipher, "aes", sizeof("aes"))) {
if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
mapping->plain_text = 1;
return 0;
} else {
ecryptfs_printk(KERN_ERR,
"%s: Error invalid file encryption algorithm, inode %lu, filename %s alg %s\n"
, __func__, inode->i_ino, file->f_dentry->d_name.name, crypt_stat->cipher);
return -EINVAL;
}
}
mapping->iv = crypt_stat->root_iv;
mapping->key = crypt_stat->key;
mapping->sensitive_data_index = crypt_stat->metadata_size/4096;
if (mount_crypt_stat->cipher_code == RFC2440_CIPHER_AES_XTS_256) {
mapping->key_length = crypt_stat->key_size * 2;
mapping->alg = "aesxts";
} else {
mapping->key_length = crypt_stat->key_size;
mapping->alg = crypt_stat->cipher;
}
mapping->hash_tfm = crypt_stat->hash_tfm;
#ifdef CONFIG_CRYPTO_FIPS
mapping->cc_enable =
(mount_crypt_stat->flags & ECRYPTFS_ENABLE_CC)?1:0;
#endif
} else {
mapping->iv = NULL;
mapping->key = NULL;
mapping->key_length = 0;
mapping->sensitive_data_index = 0;
mapping->alg = NULL;
mapping->hash_tfm = NULL;
#ifdef CONFIG_CRYPTO_FIPS
mapping->cc_enable = 0;
#endif
mapping->plain_text = 0;
}
return 0;
}
void ecryptfs_propagate_rapages(struct file *file, unsigned int flag)
{
struct file *f = file;
do {
if (!f)
return;
ecryptfs_set_rapages(f, flag);
} while(f->f_op->get_lower_file && (f = f->f_op->get_lower_file(f)));
}
int ecryptfs_propagate_fmpinfo(struct inode *inode, unsigned int flag)
{
struct file *f = ecryptfs_inode_to_private(inode)->lower_file;
do {
if (!f)
return 0;
if (ecryptfs_set_fmpinfo(f, inode, flag))
return -EINVAL;
} while(f->f_op->get_lower_file && (f = f->f_op->get_lower_file(f)));
return 0;
}
#endif
/**
* ecryptfs_open
* @inode: inode speciying file to open
* @file: Structure to return filled in
*
* Opens the file specified by inode.
*
* Returns zero on success; non-zero otherwise
*/
static int ecryptfs_open(struct inode *inode, struct file *file)
{
int rc = 0;
struct ecryptfs_crypt_stat *crypt_stat = NULL;
struct dentry *ecryptfs_dentry = file->f_path.dentry;
/* Private value of ecryptfs_dentry allocated in
* ecryptfs_lookup() */
struct ecryptfs_file_info *file_info;
#ifdef CONFIG_DLP
sdp_fs_command_t *cmd = NULL;
ssize_t dlp_len = 0;
struct knox_dlp_data dlp_data;
struct timespec ts;
#endif
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS) || defined(CONFIG_SDP)
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
mount_crypt_stat = &ecryptfs_superblock_to_private(
inode->i_sb)->mount_crypt_stat;
#endif
/* Released in ecryptfs_release or end of function if failure */
file_info = kmem_cache_zalloc(ecryptfs_file_info_cache, GFP_KERNEL);
ecryptfs_set_file_private(file, file_info);
if (!file_info) {
ecryptfs_printk(KERN_ERR,
"Error attempting to allocate memory\n");
rc = -ENOMEM;
goto out;
}
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
mutex_lock(&crypt_stat->cs_mutex);
if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)) {
ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n");
/* Policy code enabled in future release */
crypt_stat->flags |= (ECRYPTFS_POLICY_APPLIED
| ECRYPTFS_ENCRYPTED);
}
mutex_unlock(&crypt_stat->cs_mutex);
rc = ecryptfs_get_lower_file(ecryptfs_dentry, inode);
if (rc) {
printk(KERN_ERR "%s: Error attempting to initialize "
"the lower file for the dentry with name "
"[%pd]; rc = [%d]\n", __func__,
ecryptfs_dentry, rc);
goto out_free;
}
if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_ACCMODE)
== O_RDONLY && (file->f_flags & O_ACCMODE) != O_RDONLY) {
rc = -EPERM;
printk(KERN_WARNING "%s: Lower file is RO; eCryptfs "
"file must hence be opened RO\n", __func__);
goto out_put;
}
ecryptfs_set_file_lower(
file, ecryptfs_inode_to_private(inode)->lower_file);
if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
#ifdef CONFIG_SDP
/*
* it's possible to have a sensitive directory. (vault)
*/
if (mount_crypt_stat->flags & ECRYPTFS_MOUNT_SDP_ENABLED)
crypt_stat->flags |= ECRYPTFS_DEK_SDP_ENABLED;
#endif
ecryptfs_printk(KERN_DEBUG, "This is a directory\n");
mutex_lock(&crypt_stat->cs_mutex);
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
mutex_unlock(&crypt_stat->cs_mutex);
rc = 0;
goto out;
}
rc = read_or_initialize_metadata(ecryptfs_dentry);
if (rc) {
#ifdef CONFIG_SDP
if(file->f_flags & O_SDP){
printk("Failed to initialize metadata, "
"but let it continue cause current call is from SDP API\n");
mutex_lock(&crypt_stat->cs_mutex);
crypt_stat->flags &= ~(ECRYPTFS_KEY_VALID);
mutex_unlock(&crypt_stat->cs_mutex);
rc = 0;
/*
* Letting this continue doesn't mean to allow read/writing. It will anyway fail later.
*
* 1. In this stage, ecryptfs_stat won't have key/iv and encryption ctx.
* 2. ECRYPTFS_KEY_VALID bit is off, next attempt will try reading metadata again.
* 3. Skip DEK conversion. it cannot be done anyway.
*/
goto out;
}
#endif
goto out_put;
}
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
if (mount_crypt_stat->flags & ECRYPTFS_USE_FMP)
rc = ecryptfs_propagate_fmpinfo(inode, FMPINFO_SET);
else
rc = ecryptfs_propagate_fmpinfo(inode, FMPINFO_CLEAR);
#endif
if (rc)
goto out_put;
#ifdef CONFIG_SDP
if (crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) {
#ifdef CONFIG_SDP_KEY_DUMP
if (S_ISREG(ecryptfs_dentry->d_inode->i_mode)) {
if(get_sdp_sysfs_key_dump()) {
printk("FEK[%s] : ", ecryptfs_dentry->d_name.name);
key_dump(crypt_stat->key, 32);
}
}
#endif
/*
* Need to update sensitive mapping on file open
*/
if (S_ISREG(ecryptfs_dentry->d_inode->i_mode)) {
ecryptfs_set_mapping_sensitive(inode, mount_crypt_stat->userid, TO_SENSITIVE);
}
if (ecryptfs_is_sdp_locked(crypt_stat->engine_id)) {
ecryptfs_printk(KERN_INFO, "ecryptfs_open: persona is locked, rc=%d\n", rc);
} else {
int dek_type = crypt_stat->sdp_dek.type;
ecryptfs_printk(KERN_INFO, "ecryptfs_open: persona is unlocked, rc=%d\n", rc);
if(dek_type != DEK_TYPE_AES_ENC) {
ecryptfs_printk(KERN_DEBUG, "converting dek...\n");
rc = ecryptfs_sdp_convert_dek(ecryptfs_dentry);
ecryptfs_printk(KERN_DEBUG, "conversion ready, rc=%d\n", rc);
rc = 0; // TODO: Do we need to return error if conversion fails?
}
}
}
#if ECRYPTFS_DEK_DEBUG
else {
ecryptfs_printk(KERN_INFO, "ecryptfs_open: dek_file_type is protected\n");
}
#endif
#endif
#ifdef CONFIG_DLP
if(crypt_stat->flags & ECRYPTFS_DLP_ENABLED) {
#if DLP_DEBUG
printk("DLP %s: try to open %s [%lu] with crypt_stat->flags %d\n",
__func__, ecryptfs_dentry->d_name.name, inode->i_ino, crypt_stat->flags);
#endif
if (dlp_is_locked(mount_crypt_stat->userid)) {
printk("%s: DLP locked\n", __func__);
rc = -EPERM;
goto out_put;
}
if(in_egroup_p(AID_KNOX_DLP) || in_egroup_p(AID_KNOX_DLP_RESTRICTED)) {
dlp_len = ecryptfs_dentry->d_inode->i_op->getxattr(
ecryptfs_dentry,
KNOX_DLP_XATTR_NAME,
&dlp_data, sizeof(dlp_data));
if (dlp_len == sizeof(dlp_data)) {
getnstimeofday(&ts);
#if DLP_DEBUG
printk("DLP %s: current time [%ld/%ld] %s\n",
__func__, (long)ts.tv_sec, (long)dlp_data.expiry_time.tv_sec, ecryptfs_dentry->d_name.name);
#endif
if ((ts.tv_sec > dlp_data.expiry_time.tv_sec) &&
dlp_isInterestedFile(mount_crypt_stat->userid, ecryptfs_dentry->d_name.name)==0) {
/* Command to delete expired file */
cmd = sdp_fs_command_alloc(FSOP_DLP_FILE_REMOVE,
current->tgid, mount_crypt_stat->userid, mount_crypt_stat->partition_id,
inode->i_ino, GFP_KERNEL);
rc = -ENOENT;
goto out_put;
}
} else if (dlp_len == -ENODATA) {
/* DLP flag is set, but no DLP data. Let it continue, xattr will be set later */
printk("DLP %s: normal file [%s]\n",
__func__, ecryptfs_dentry->d_name.name);
} else {
printk("DLP %s: Error, len [%ld], [%s]\n",
__func__, (long)dlp_len, ecryptfs_dentry->d_name.name);
rc = -EFAULT;
goto out_put;
}
#if DLP_DEBUG
printk("DLP %s: DLP file [%s] opened with tgid %d, %d\n" ,
__func__, ecryptfs_dentry->d_name.name, current->tgid, in_egroup_p(AID_KNOX_DLP_RESTRICTED));
#endif
if(in_egroup_p(AID_KNOX_DLP_RESTRICTED)) {
cmd = sdp_fs_command_alloc(FSOP_DLP_FILE_OPENED,
current->tgid, mount_crypt_stat->userid, mount_crypt_stat->partition_id,
inode->i_ino, GFP_KERNEL);
}
} else {
printk("DLP %s: not DLP app [%s]\n", __func__, current->comm);
rc = -EPERM;
goto out_put;
}
}
#endif
ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = "
"[0x%.16lx] size: [0x%.16llx]\n", inode, inode->i_ino,
(unsigned long long)i_size_read(inode));
goto out;
out_put:
ecryptfs_put_lower_file(inode);
out_free:
kmem_cache_free(ecryptfs_file_info_cache,
ecryptfs_file_to_private(file));
out:
#ifdef CONFIG_DLP
if(cmd) {
sdp_fs_request(cmd, NULL);
sdp_fs_command_free(cmd);
}
#endif
return rc;
}
static int ecryptfs_flush(struct file *file, fl_owner_t td)
{
struct file *lower_file = ecryptfs_file_to_lower(file);
if (lower_file->f_op->flush) {
filemap_write_and_wait(file->f_mapping);
return lower_file->f_op->flush(lower_file, td);
}
return 0;
}
static int ecryptfs_release(struct inode *inode, struct file *file)
{
#ifdef CONFIG_SDP
struct ecryptfs_crypt_stat *crypt_stat;
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
mutex_lock(&crypt_stat->cs_mutex);
#endif
ecryptfs_put_lower_file(inode);
#ifdef CONFIG_SDP
mutex_unlock(&crypt_stat->cs_mutex);
#endif
kmem_cache_free(ecryptfs_file_info_cache,
ecryptfs_file_to_private(file));
return 0;
}
static int
ecryptfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
int rc;
rc = filemap_write_and_wait(file->f_mapping);
if (rc)
return rc;
return vfs_fsync(ecryptfs_file_to_lower(file), datasync);
}
static int ecryptfs_fasync(int fd, struct file *file, int flag)
{
int rc = 0;
struct file *lower_file = NULL;
lower_file = ecryptfs_file_to_lower(file);
if (lower_file->f_op->fasync)
rc = lower_file->f_op->fasync(fd, lower_file, flag);
return rc;
}
static long
ecryptfs_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct file *lower_file = ecryptfs_file_to_lower(file);
long rc = -ENOTTY;
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
if (cmd == ECRYPTFS_IOCTL_GET_ATTRIBUTES) {
u32 __user *user_attr = (u32 __user *)arg;
u32 attr = 0;
char filename[NAME_MAX+1] = {0};
struct dentry *ecryptfs_dentry = file->f_path.dentry;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
&ecryptfs_superblock_to_private(ecryptfs_dentry->d_sb)
->mount_crypt_stat;
struct inode *inode = ecryptfs_dentry->d_inode;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(inode)->crypt_stat;
struct dentry *fp_dentry =
ecryptfs_inode_to_private(inode)->lower_file->f_dentry;
if (fp_dentry->d_name.len <= NAME_MAX)
memcpy(filename, fp_dentry->d_name.name,
fp_dentry->d_name.len + 1);
mutex_lock(&crypt_stat->cs_mutex);
if ((crypt_stat->flags & ECRYPTFS_ENCRYPTED
|| crypt_stat->flags & ECRYPTFS_ENCRYPTED_OTHER_DEVICE)
|| ((mount_crypt_stat->flags
& ECRYPTFS_ENABLE_FILTERING)
&& (is_file_name_match
(mount_crypt_stat, fp_dentry)
|| is_file_ext_match
(mount_crypt_stat, filename)))) {
if (crypt_stat->flags & ECRYPTFS_KEY_VALID)
attr = ECRYPTFS_WAS_ENCRYPTED;
else
attr = ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE;
}
mutex_unlock(&crypt_stat->cs_mutex);
put_user(attr, user_attr);
return 0;
}
#endif
#ifdef CONFIG_SDP
rc = ecryptfs_do_sdp_ioctl(file, cmd, arg);
if (rc != EOPNOTSUPP)
return rc;
#else
printk("%s CONFIG_SDP not enabled \n", __func__);
#endif
if (!lower_file->f_op->unlocked_ioctl)
return rc;
switch (cmd) {
case FITRIM:
case FS_IOC_GETFLAGS:
case FS_IOC_SETFLAGS:
case FS_IOC_GETVERSION:
case FS_IOC_SETVERSION:
rc = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
fsstack_copy_attr_all(file_inode(file), file_inode(lower_file));
return rc;
default:
return rc;
}
}
#ifdef CONFIG_COMPAT
static long
ecryptfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct file *lower_file = ecryptfs_file_to_lower(file);
long rc = -ENOIOCTLCMD;
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
if (cmd == ECRYPTFS_IOCTL_GET_ATTRIBUTES) {
u32 __user *user_attr = (u32 __user *)arg;
u32 attr = 0;
char filename[NAME_MAX+1] = {0};
struct dentry *ecryptfs_dentry = file->f_path.dentry;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
&ecryptfs_superblock_to_private(ecryptfs_dentry->d_sb)
->mount_crypt_stat;
struct inode *inode = ecryptfs_dentry->d_inode;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(inode)->crypt_stat;
struct dentry *fp_dentry =
ecryptfs_inode_to_private(inode)->lower_file->f_dentry;
if (fp_dentry->d_name.len <= NAME_MAX)
memcpy(filename, fp_dentry->d_name.name,
fp_dentry->d_name.len + 1);
mutex_lock(&crypt_stat->cs_mutex);
if ((crypt_stat->flags & ECRYPTFS_ENCRYPTED
|| crypt_stat->flags & ECRYPTFS_ENCRYPTED_OTHER_DEVICE)
|| ((mount_crypt_stat->flags
& ECRYPTFS_ENABLE_FILTERING)
&& (is_file_name_match
(mount_crypt_stat, fp_dentry)
|| is_file_ext_match
(mount_crypt_stat, filename)))) {
if (crypt_stat->flags & ECRYPTFS_KEY_VALID)
attr = ECRYPTFS_WAS_ENCRYPTED;
else
attr = ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE;
}
mutex_unlock(&crypt_stat->cs_mutex);
put_user(attr, user_attr);
return 0;
}
#endif
#ifdef CONFIG_SDP
rc = ecryptfs_do_sdp_ioctl(file, cmd, arg);
if (rc != EOPNOTSUPP)
return rc;
#else
printk("%s CONFIG_SDP not enabled \n", __func__);
#endif
if (!lower_file->f_op->compat_ioctl)
return rc;
switch (cmd) {
case FITRIM:
case FS_IOC32_GETFLAGS:
case FS_IOC32_SETFLAGS:
case FS_IOC32_GETVERSION:
case FS_IOC32_SETVERSION:
rc = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
fsstack_copy_attr_all(file_inode(file), file_inode(lower_file));
return rc;
default:
return rc;
}
}
#endif
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
int is_file_name_match(struct ecryptfs_mount_crypt_stat *mcs,
struct dentry *fp_dentry)
{
int i;
char *str = NULL;
if (!(strcmp("/", fp_dentry->d_name.name))
|| !(strcmp("", fp_dentry->d_name.name)))
return 0;
str = kzalloc(mcs->max_name_filter_len + 1, GFP_KERNEL);
if (!str) {
printk(KERN_ERR "%s: Out of memory whilst attempting "
"to kzalloc [%d] bytes\n", __func__,
(mcs->max_name_filter_len + 1));
return 0;
}
for (i = 0; i < ENC_NAME_FILTER_MAX_INSTANCE; i++) {
int len = 0;
struct dentry *p = fp_dentry;
if (!strlen(mcs->enc_filter_name[i]))
break;
while (1) {
if (len == 0) {
len = strlen(p->d_name.name);
if (len > mcs->max_name_filter_len)
break;
strcpy(str, p->d_name.name);
} else {
len = len + 1 + strlen(p->d_name.name) ;
if (len > mcs->max_name_filter_len)
break;
strcat(str, "/");
strcat(str, p->d_name.name);
}
if (strnicmp(str, mcs->enc_filter_name[i], len))
break;
p = p->d_parent;
if (!(strcmp("/", p->d_name.name))
|| !(strcmp("", p->d_name.name))) {
if (len == strlen(mcs->enc_filter_name[i])) {
kfree(str);
return 1;
}
break;
}
}
}
kfree(str);
return 0;
}
int is_file_ext_match(struct ecryptfs_mount_crypt_stat *mcs, char *str)
{
int i;
char ext[NAME_MAX + 1] = {0};
char *token;
int count = 0;
while ((token = strsep(&str, ".")) != NULL) {
strncpy(ext, token, NAME_MAX);
count++;
}
if (count <= 1)
return 0;
for (i = 0; i < ENC_EXT_FILTER_MAX_INSTANCE; i++) {
if (!strlen(mcs->enc_filter_ext[i]))
return 0;
if (strlen(ext) != strlen(mcs->enc_filter_ext[i]))
continue;
if (!strnicmp(ext, mcs->enc_filter_ext[i], strlen(ext)))
return 1;
}
return 0;
}
#endif
const struct file_operations ecryptfs_dir_fops = {
.iterate = ecryptfs_readdir,
.read = generic_read_dir,
.unlocked_ioctl = ecryptfs_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ecryptfs_compat_ioctl,
#endif
.open = ecryptfs_open,
.flush = ecryptfs_flush,
.release = ecryptfs_release,
.fsync = ecryptfs_fsync,
.fasync = ecryptfs_fasync,
.splice_read = generic_file_splice_read,
.llseek = default_llseek,
};
const struct file_operations ecryptfs_main_fops = {
.llseek = generic_file_llseek,
.read = new_sync_read,
.read_iter = ecryptfs_read_update_atime,
.write = new_sync_write,
.write_iter = generic_file_write_iter,
.iterate = ecryptfs_readdir,
.unlocked_ioctl = ecryptfs_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ecryptfs_compat_ioctl,
#endif
.mmap = generic_file_mmap,
.open = ecryptfs_open,
.flush = ecryptfs_flush,
.release = ecryptfs_release,
.fsync = ecryptfs_fsync,
.fasync = ecryptfs_fasync,
.splice_read = generic_file_splice_read,
};

1609
fs/ecryptfs/inode.c Normal file

File diff suppressed because it is too large Load diff

2680
fs/ecryptfs/keystore.c Normal file

File diff suppressed because it is too large Load diff

172
fs/ecryptfs/kthread.c Normal file
View file

@ -0,0 +1,172 @@
/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 2008 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/mount.h>
#include "ecryptfs_kernel.h"
struct ecryptfs_open_req {
struct file **lower_file;
struct path path;
struct completion done;
struct list_head kthread_ctl_list;
};
static struct ecryptfs_kthread_ctl {
#define ECRYPTFS_KTHREAD_ZOMBIE 0x00000001
u32 flags;
struct mutex mux;
struct list_head req_list;
wait_queue_head_t wait;
} ecryptfs_kthread_ctl;
static struct task_struct *ecryptfs_kthread;
/**
* ecryptfs_threadfn
* @ignored: ignored
*
* The eCryptfs kernel thread that has the responsibility of getting
* the lower file with RW permissions.
*
* Returns zero on success; non-zero otherwise
*/
static int ecryptfs_threadfn(void *ignored)
{
set_freezable();
while (1) {
struct ecryptfs_open_req *req;
wait_event_freezable(
ecryptfs_kthread_ctl.wait,
(!list_empty(&ecryptfs_kthread_ctl.req_list)
|| kthread_should_stop()));
mutex_lock(&ecryptfs_kthread_ctl.mux);
if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
mutex_unlock(&ecryptfs_kthread_ctl.mux);
goto out;
}
while (!list_empty(&ecryptfs_kthread_ctl.req_list)) {
req = list_first_entry(&ecryptfs_kthread_ctl.req_list,
struct ecryptfs_open_req,
kthread_ctl_list);
list_del(&req->kthread_ctl_list);
*req->lower_file = dentry_open(&req->path,
(O_RDWR | O_LARGEFILE), current_cred());
complete(&req->done);
}
mutex_unlock(&ecryptfs_kthread_ctl.mux);
}
out:
return 0;
}
int __init ecryptfs_init_kthread(void)
{
int rc = 0;
mutex_init(&ecryptfs_kthread_ctl.mux);
init_waitqueue_head(&ecryptfs_kthread_ctl.wait);
INIT_LIST_HEAD(&ecryptfs_kthread_ctl.req_list);
ecryptfs_kthread = kthread_run(&ecryptfs_threadfn, NULL,
"ecryptfs-kthread");
if (IS_ERR(ecryptfs_kthread)) {
rc = PTR_ERR(ecryptfs_kthread);
printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]"
"\n", __func__, rc);
}
return rc;
}
void ecryptfs_destroy_kthread(void)
{
struct ecryptfs_open_req *req, *tmp;
mutex_lock(&ecryptfs_kthread_ctl.mux);
ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
list_for_each_entry_safe(req, tmp, &ecryptfs_kthread_ctl.req_list,
kthread_ctl_list) {
list_del(&req->kthread_ctl_list);
*req->lower_file = ERR_PTR(-EIO);
complete(&req->done);
}
mutex_unlock(&ecryptfs_kthread_ctl.mux);
kthread_stop(ecryptfs_kthread);
wake_up(&ecryptfs_kthread_ctl.wait);
}
/**
* ecryptfs_privileged_open
* @lower_file: Result of dentry_open by root on lower dentry
* @lower_dentry: Lower dentry for file to open
* @lower_mnt: Lower vfsmount for file to open
*
* This function gets a r/w file opened againt the lower dentry.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_privileged_open(struct file **lower_file,
struct dentry *lower_dentry,
struct vfsmount *lower_mnt,
const struct cred *cred)
{
struct ecryptfs_open_req req;
int flags = O_LARGEFILE;
int rc = 0;
init_completion(&req.done);
req.lower_file = lower_file;
req.path.dentry = lower_dentry;
req.path.mnt = lower_mnt;
/* Corresponding dput() and mntput() are done when the
* lower file is fput() when all eCryptfs files for the inode are
* released. */
flags |= IS_RDONLY(lower_dentry->d_inode) ? O_RDONLY : O_RDWR;
(*lower_file) = dentry_open(&req.path, flags, cred);
if (!IS_ERR(*lower_file))
goto out;
if ((flags & O_ACCMODE) == O_RDONLY) {
rc = PTR_ERR((*lower_file));
goto out;
}
mutex_lock(&ecryptfs_kthread_ctl.mux);
if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
rc = -EIO;
mutex_unlock(&ecryptfs_kthread_ctl.mux);
printk(KERN_ERR "%s: We are in the middle of shutting down; "
"aborting privileged request to open lower file\n",
__func__);
goto out;
}
list_add_tail(&req.kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
mutex_unlock(&ecryptfs_kthread_ctl.mux);
wake_up(&ecryptfs_kthread_ctl.wait);
wait_for_completion(&req.done);
if (IS_ERR(*lower_file))
rc = PTR_ERR(*lower_file);
out:
return rc;
}

1196
fs/ecryptfs/main.c Normal file

File diff suppressed because it is too large Load diff

468
fs/ecryptfs/messaging.c Normal file
View file

@ -0,0 +1,468 @@
/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 2004-2008 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
* Tyler Hicks <tyhicks@ou.edu>
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/user_namespace.h>
#include <linux/nsproxy.h>
#include "ecryptfs_kernel.h"
static LIST_HEAD(ecryptfs_msg_ctx_free_list);
static LIST_HEAD(ecryptfs_msg_ctx_alloc_list);
static struct mutex ecryptfs_msg_ctx_lists_mux;
static struct hlist_head *ecryptfs_daemon_hash;
struct mutex ecryptfs_daemon_hash_mux;
static int ecryptfs_hash_bits;
#define ecryptfs_current_euid_hash(uid) \
hash_long((unsigned long)from_kuid(&init_user_ns, current_euid()), ecryptfs_hash_bits)
static u32 ecryptfs_msg_counter;
static struct ecryptfs_msg_ctx *ecryptfs_msg_ctx_arr;
/**
* ecryptfs_acquire_free_msg_ctx
* @msg_ctx: The context that was acquired from the free list
*
* Acquires a context element from the free list and locks the mutex
* on the context. Sets the msg_ctx task to current. Returns zero on
* success; non-zero on error or upon failure to acquire a free
* context element. Must be called with ecryptfs_msg_ctx_lists_mux
* held.
*/
static int ecryptfs_acquire_free_msg_ctx(struct ecryptfs_msg_ctx **msg_ctx)
{
struct list_head *p;
int rc;
if (list_empty(&ecryptfs_msg_ctx_free_list)) {
printk(KERN_WARNING "%s: The eCryptfs free "
"context list is empty. It may be helpful to "
"specify the ecryptfs_message_buf_len "
"parameter to be greater than the current "
"value of [%d]\n", __func__, ecryptfs_message_buf_len);
rc = -ENOMEM;
goto out;
}
list_for_each(p, &ecryptfs_msg_ctx_free_list) {
*msg_ctx = list_entry(p, struct ecryptfs_msg_ctx, node);
if (mutex_trylock(&(*msg_ctx)->mux)) {
(*msg_ctx)->task = current;
rc = 0;
goto out;
}
}
rc = -ENOMEM;
out:
return rc;
}
/**
* ecryptfs_msg_ctx_free_to_alloc
* @msg_ctx: The context to move from the free list to the alloc list
*
* Must be called with ecryptfs_msg_ctx_lists_mux held.
*/
static void ecryptfs_msg_ctx_free_to_alloc(struct ecryptfs_msg_ctx *msg_ctx)
{
list_move(&msg_ctx->node, &ecryptfs_msg_ctx_alloc_list);
msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_PENDING;
msg_ctx->counter = ++ecryptfs_msg_counter;
}
/**
* ecryptfs_msg_ctx_alloc_to_free
* @msg_ctx: The context to move from the alloc list to the free list
*
* Must be called with ecryptfs_msg_ctx_lists_mux held.
*/
void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx)
{
list_move(&(msg_ctx->node), &ecryptfs_msg_ctx_free_list);
kfree(msg_ctx->msg);
msg_ctx->msg = NULL;
msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_FREE;
}
/**
* ecryptfs_find_daemon_by_euid
* @daemon: If return value is zero, points to the desired daemon pointer
*
* Must be called with ecryptfs_daemon_hash_mux held.
*
* Search the hash list for the current effective user id.
*
* Returns zero if the user id exists in the list; non-zero otherwise.
*/
int ecryptfs_find_daemon_by_euid(struct ecryptfs_daemon **daemon)
{
int rc;
hlist_for_each_entry(*daemon,
&ecryptfs_daemon_hash[ecryptfs_current_euid_hash()],
euid_chain) {
if (uid_eq((*daemon)->file->f_cred->euid, current_euid())) {
rc = 0;
goto out;
}
}
rc = -EINVAL;
out:
return rc;
}
/**
* ecryptfs_spawn_daemon - Create and initialize a new daemon struct
* @daemon: Pointer to set to newly allocated daemon struct
* @file: File used when opening /dev/ecryptfs
*
* Must be called ceremoniously while in possession of
* ecryptfs_sacred_daemon_hash_mux
*
* Returns zero on success; non-zero otherwise
*/
int
ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, struct file *file)
{
int rc = 0;
(*daemon) = kzalloc(sizeof(**daemon), GFP_KERNEL);
if (!(*daemon)) {
rc = -ENOMEM;
printk(KERN_ERR "%s: Failed to allocate [%zd] bytes of "
"GFP_KERNEL memory\n", __func__, sizeof(**daemon));
goto out;
}
(*daemon)->file = file;
mutex_init(&(*daemon)->mux);
INIT_LIST_HEAD(&(*daemon)->msg_ctx_out_queue);
init_waitqueue_head(&(*daemon)->wait);
(*daemon)->num_queued_msg_ctx = 0;
hlist_add_head(&(*daemon)->euid_chain,
&ecryptfs_daemon_hash[ecryptfs_current_euid_hash()]);
out:
return rc;
}
/**
* ecryptfs_exorcise_daemon - Destroy the daemon struct
*
* Must be called ceremoniously while in possession of
* ecryptfs_daemon_hash_mux and the daemon's own mux.
*/
int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon)
{
struct ecryptfs_msg_ctx *msg_ctx, *msg_ctx_tmp;
int rc = 0;
mutex_lock(&daemon->mux);
if ((daemon->flags & ECRYPTFS_DAEMON_IN_READ)
|| (daemon->flags & ECRYPTFS_DAEMON_IN_POLL)) {
rc = -EBUSY;
mutex_unlock(&daemon->mux);
goto out;
}
list_for_each_entry_safe(msg_ctx, msg_ctx_tmp,
&daemon->msg_ctx_out_queue, daemon_out_list) {
list_del(&msg_ctx->daemon_out_list);
daemon->num_queued_msg_ctx--;
printk(KERN_WARNING "%s: Warning: dropping message that is in "
"the out queue of a dying daemon\n", __func__);
ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
}
hlist_del(&daemon->euid_chain);
mutex_unlock(&daemon->mux);
kzfree(daemon);
out:
return rc;
}
/**
* ecryptfs_process_reponse
* @msg: The ecryptfs message received; the caller should sanity check
* msg->data_len and free the memory
* @seq: The sequence number of the message; must match the sequence
* number for the existing message context waiting for this
* response
*
* Processes a response message after sending an operation request to
* userspace. Some other process is awaiting this response. Before
* sending out its first communications, the other process allocated a
* msg_ctx from the ecryptfs_msg_ctx_arr at a particular index. The
* response message contains this index so that we can copy over the
* response message into the msg_ctx that the process holds a
* reference to. The other process is going to wake up, check to see
* that msg_ctx->state == ECRYPTFS_MSG_CTX_STATE_DONE, and then
* proceed to read off and process the response message. Returns zero
* upon delivery to desired context element; non-zero upon delivery
* failure or error.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_process_response(struct ecryptfs_daemon *daemon,
struct ecryptfs_message *msg, u32 seq)
{
struct ecryptfs_msg_ctx *msg_ctx;
size_t msg_size;
int rc;
if (msg->index >= ecryptfs_message_buf_len) {
rc = -EINVAL;
printk(KERN_ERR "%s: Attempt to reference "
"context buffer at index [%d]; maximum "
"allowable is [%d]\n", __func__, msg->index,
(ecryptfs_message_buf_len - 1));
goto out;
}
msg_ctx = &ecryptfs_msg_ctx_arr[msg->index];
mutex_lock(&msg_ctx->mux);
if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_PENDING) {
rc = -EINVAL;
printk(KERN_WARNING "%s: Desired context element is not "
"pending a response\n", __func__);
goto unlock;
} else if (msg_ctx->counter != seq) {
rc = -EINVAL;
printk(KERN_WARNING "%s: Invalid message sequence; "
"expected [%d]; received [%d]\n", __func__,
msg_ctx->counter, seq);
goto unlock;
}
msg_size = (sizeof(*msg) + msg->data_len);
msg_ctx->msg = kmemdup(msg, msg_size, GFP_KERNEL);
if (!msg_ctx->msg) {
rc = -ENOMEM;
printk(KERN_ERR "%s: Failed to allocate [%zd] bytes of "
"GFP_KERNEL memory\n", __func__, msg_size);
goto unlock;
}
msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_DONE;
wake_up_process(msg_ctx->task);
rc = 0;
unlock:
mutex_unlock(&msg_ctx->mux);
out:
return rc;
}
/**
* ecryptfs_send_message_locked
* @data: The data to send
* @data_len: The length of data
* @msg_ctx: The message context allocated for the send
*
* Must be called with ecryptfs_daemon_hash_mux held.
*
* Returns zero on success; non-zero otherwise
*/
static int
ecryptfs_send_message_locked(char *data, int data_len, u8 msg_type,
struct ecryptfs_msg_ctx **msg_ctx)
{
struct ecryptfs_daemon *daemon;
int rc;
rc = ecryptfs_find_daemon_by_euid(&daemon);
if (rc) {
rc = -ENOTCONN;
goto out;
}
mutex_lock(&ecryptfs_msg_ctx_lists_mux);
rc = ecryptfs_acquire_free_msg_ctx(msg_ctx);
if (rc) {
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
printk(KERN_WARNING "%s: Could not claim a free "
"context element\n", __func__);
goto out;
}
ecryptfs_msg_ctx_free_to_alloc(*msg_ctx);
mutex_unlock(&(*msg_ctx)->mux);
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
rc = ecryptfs_send_miscdev(data, data_len, *msg_ctx, msg_type, 0,
daemon);
if (rc)
printk(KERN_ERR "%s: Error attempting to send message to "
"userspace daemon; rc = [%d]\n", __func__, rc);
out:
return rc;
}
/**
* ecryptfs_send_message
* @data: The data to send
* @data_len: The length of data
* @msg_ctx: The message context allocated for the send
*
* Grabs ecryptfs_daemon_hash_mux.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_send_message(char *data, int data_len,
struct ecryptfs_msg_ctx **msg_ctx)
{
int rc;
mutex_lock(&ecryptfs_daemon_hash_mux);
rc = ecryptfs_send_message_locked(data, data_len, ECRYPTFS_MSG_REQUEST,
msg_ctx);
mutex_unlock(&ecryptfs_daemon_hash_mux);
return rc;
}
/**
* ecryptfs_wait_for_response
* @msg_ctx: The context that was assigned when sending a message
* @msg: The incoming message from userspace; not set if rc != 0
*
* Sleeps until awaken by ecryptfs_receive_message or until the amount
* of time exceeds ecryptfs_message_wait_timeout. If zero is
* returned, msg will point to a valid message from userspace; a
* non-zero value is returned upon failure to receive a message or an
* error occurs. Callee must free @msg on success.
*/
int ecryptfs_wait_for_response(struct ecryptfs_msg_ctx *msg_ctx,
struct ecryptfs_message **msg)
{
signed long timeout = ecryptfs_message_wait_timeout * HZ;
int rc = 0;
sleep:
timeout = schedule_timeout_interruptible(timeout);
mutex_lock(&ecryptfs_msg_ctx_lists_mux);
mutex_lock(&msg_ctx->mux);
if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_DONE) {
if (timeout) {
mutex_unlock(&msg_ctx->mux);
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
goto sleep;
}
rc = -ENOMSG;
} else {
*msg = msg_ctx->msg;
msg_ctx->msg = NULL;
}
ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
mutex_unlock(&msg_ctx->mux);
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
return rc;
}
int __init ecryptfs_init_messaging(void)
{
int i;
int rc = 0;
if (ecryptfs_number_of_users > ECRYPTFS_MAX_NUM_USERS) {
ecryptfs_number_of_users = ECRYPTFS_MAX_NUM_USERS;
printk(KERN_WARNING "%s: Specified number of users is "
"too large, defaulting to [%d] users\n", __func__,
ecryptfs_number_of_users);
}
mutex_init(&ecryptfs_daemon_hash_mux);
mutex_lock(&ecryptfs_daemon_hash_mux);
ecryptfs_hash_bits = 1;
while (ecryptfs_number_of_users >> ecryptfs_hash_bits)
ecryptfs_hash_bits++;
ecryptfs_daemon_hash = kmalloc((sizeof(struct hlist_head)
* (1 << ecryptfs_hash_bits)),
GFP_KERNEL);
if (!ecryptfs_daemon_hash) {
rc = -ENOMEM;
printk(KERN_ERR "%s: Failed to allocate memory\n", __func__);
mutex_unlock(&ecryptfs_daemon_hash_mux);
goto out;
}
for (i = 0; i < (1 << ecryptfs_hash_bits); i++)
INIT_HLIST_HEAD(&ecryptfs_daemon_hash[i]);
mutex_unlock(&ecryptfs_daemon_hash_mux);
ecryptfs_msg_ctx_arr = kmalloc((sizeof(struct ecryptfs_msg_ctx)
* ecryptfs_message_buf_len),
GFP_KERNEL);
if (!ecryptfs_msg_ctx_arr) {
rc = -ENOMEM;
printk(KERN_ERR "%s: Failed to allocate memory\n", __func__);
goto out;
}
mutex_init(&ecryptfs_msg_ctx_lists_mux);
mutex_lock(&ecryptfs_msg_ctx_lists_mux);
ecryptfs_msg_counter = 0;
for (i = 0; i < ecryptfs_message_buf_len; i++) {
INIT_LIST_HEAD(&ecryptfs_msg_ctx_arr[i].node);
INIT_LIST_HEAD(&ecryptfs_msg_ctx_arr[i].daemon_out_list);
mutex_init(&ecryptfs_msg_ctx_arr[i].mux);
mutex_lock(&ecryptfs_msg_ctx_arr[i].mux);
ecryptfs_msg_ctx_arr[i].index = i;
ecryptfs_msg_ctx_arr[i].state = ECRYPTFS_MSG_CTX_STATE_FREE;
ecryptfs_msg_ctx_arr[i].counter = 0;
ecryptfs_msg_ctx_arr[i].task = NULL;
ecryptfs_msg_ctx_arr[i].msg = NULL;
list_add_tail(&ecryptfs_msg_ctx_arr[i].node,
&ecryptfs_msg_ctx_free_list);
mutex_unlock(&ecryptfs_msg_ctx_arr[i].mux);
}
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
rc = ecryptfs_init_ecryptfs_miscdev();
if (rc)
ecryptfs_release_messaging();
out:
return rc;
}
void ecryptfs_release_messaging(void)
{
if (ecryptfs_msg_ctx_arr) {
int i;
mutex_lock(&ecryptfs_msg_ctx_lists_mux);
for (i = 0; i < ecryptfs_message_buf_len; i++) {
mutex_lock(&ecryptfs_msg_ctx_arr[i].mux);
kfree(ecryptfs_msg_ctx_arr[i].msg);
mutex_unlock(&ecryptfs_msg_ctx_arr[i].mux);
}
kfree(ecryptfs_msg_ctx_arr);
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
}
if (ecryptfs_daemon_hash) {
struct ecryptfs_daemon *daemon;
int i;
mutex_lock(&ecryptfs_daemon_hash_mux);
for (i = 0; i < (1 << ecryptfs_hash_bits); i++) {
int rc;
hlist_for_each_entry(daemon,
&ecryptfs_daemon_hash[i],
euid_chain) {
rc = ecryptfs_exorcise_daemon(daemon);
if (rc)
printk(KERN_ERR "%s: Error whilst "
"attempting to destroy daemon; "
"rc = [%d]. Dazed and confused, "
"but trying to continue.\n",
__func__, rc);
}
}
kfree(ecryptfs_daemon_hash);
mutex_unlock(&ecryptfs_daemon_hash_mux);
}
ecryptfs_destroy_ecryptfs_miscdev();
return;
}

511
fs/ecryptfs/miscdev.c Normal file
View file

@ -0,0 +1,511 @@
/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 2008 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/fs.h>
#include <linux/hash.h>
#include <linux/random.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/module.h>
#include "ecryptfs_kernel.h"
static atomic_t ecryptfs_num_miscdev_opens;
/**
* ecryptfs_miscdev_poll
* @file: dev file
* @pt: dev poll table (ignored)
*
* Returns the poll mask
*/
static unsigned int
ecryptfs_miscdev_poll(struct file *file, poll_table *pt)
{
struct ecryptfs_daemon *daemon = file->private_data;
unsigned int mask = 0;
mutex_lock(&daemon->mux);
if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
printk(KERN_WARNING "%s: Attempt to poll on zombified "
"daemon\n", __func__);
goto out_unlock_daemon;
}
if (daemon->flags & ECRYPTFS_DAEMON_IN_READ)
goto out_unlock_daemon;
if (daemon->flags & ECRYPTFS_DAEMON_IN_POLL)
goto out_unlock_daemon;
daemon->flags |= ECRYPTFS_DAEMON_IN_POLL;
mutex_unlock(&daemon->mux);
poll_wait(file, &daemon->wait, pt);
mutex_lock(&daemon->mux);
if (!list_empty(&daemon->msg_ctx_out_queue))
mask |= POLLIN | POLLRDNORM;
out_unlock_daemon:
daemon->flags &= ~ECRYPTFS_DAEMON_IN_POLL;
mutex_unlock(&daemon->mux);
return mask;
}
/**
* ecryptfs_miscdev_open
* @inode: inode of miscdev handle (ignored)
* @file: file for miscdev handle
*
* Returns zero on success; non-zero otherwise
*/
static int
ecryptfs_miscdev_open(struct inode *inode, struct file *file)
{
struct ecryptfs_daemon *daemon = NULL;
int rc;
mutex_lock(&ecryptfs_daemon_hash_mux);
rc = ecryptfs_find_daemon_by_euid(&daemon);
if (!rc) {
rc = -EINVAL;
goto out_unlock_daemon_list;
}
rc = ecryptfs_spawn_daemon(&daemon, file);
if (rc) {
printk(KERN_ERR "%s: Error attempting to spawn daemon; "
"rc = [%d]\n", __func__, rc);
goto out_unlock_daemon_list;
}
mutex_lock(&daemon->mux);
if (daemon->flags & ECRYPTFS_DAEMON_MISCDEV_OPEN) {
rc = -EBUSY;
goto out_unlock_daemon;
}
daemon->flags |= ECRYPTFS_DAEMON_MISCDEV_OPEN;
file->private_data = daemon;
atomic_inc(&ecryptfs_num_miscdev_opens);
out_unlock_daemon:
mutex_unlock(&daemon->mux);
out_unlock_daemon_list:
mutex_unlock(&ecryptfs_daemon_hash_mux);
return rc;
}
/**
* ecryptfs_miscdev_release
* @inode: inode of fs/ecryptfs/euid handle (ignored)
* @file: file for fs/ecryptfs/euid handle
*
* This keeps the daemon registered until the daemon sends another
* ioctl to fs/ecryptfs/ctl or until the kernel module unregisters.
*
* Returns zero on success; non-zero otherwise
*/
static int
ecryptfs_miscdev_release(struct inode *inode, struct file *file)
{
struct ecryptfs_daemon *daemon = file->private_data;
int rc;
mutex_lock(&daemon->mux);
BUG_ON(!(daemon->flags & ECRYPTFS_DAEMON_MISCDEV_OPEN));
daemon->flags &= ~ECRYPTFS_DAEMON_MISCDEV_OPEN;
atomic_dec(&ecryptfs_num_miscdev_opens);
mutex_unlock(&daemon->mux);
mutex_lock(&ecryptfs_daemon_hash_mux);
rc = ecryptfs_exorcise_daemon(daemon);
mutex_unlock(&ecryptfs_daemon_hash_mux);
if (rc) {
printk(KERN_CRIT "%s: Fatal error whilst attempting to "
"shut down daemon; rc = [%d]. Please report this "
"bug.\n", __func__, rc);
BUG();
}
return rc;
}
/**
* ecryptfs_send_miscdev
* @data: Data to send to daemon; may be NULL
* @data_size: Amount of data to send to daemon
* @msg_ctx: Message context, which is used to handle the reply. If
* this is NULL, then we do not expect a reply.
* @msg_type: Type of message
* @msg_flags: Flags for message
* @daemon: eCryptfs daemon object
*
* Add msg_ctx to queue and then, if it exists, notify the blocked
* miscdevess about the data being available. Must be called with
* ecryptfs_daemon_hash_mux held.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_send_miscdev(char *data, size_t data_size,
struct ecryptfs_msg_ctx *msg_ctx, u8 msg_type,
u16 msg_flags, struct ecryptfs_daemon *daemon)
{
struct ecryptfs_message *msg;
msg = kmalloc((sizeof(*msg) + data_size), GFP_KERNEL);
if (!msg) {
printk(KERN_ERR "%s: Out of memory whilst attempting "
"to kmalloc(%zd, GFP_KERNEL)\n", __func__,
(sizeof(*msg) + data_size));
return -ENOMEM;
}
mutex_lock(&msg_ctx->mux);
msg_ctx->msg = msg;
msg_ctx->msg->index = msg_ctx->index;
msg_ctx->msg->data_len = data_size;
msg_ctx->type = msg_type;
memcpy(msg_ctx->msg->data, data, data_size);
msg_ctx->msg_size = (sizeof(*msg_ctx->msg) + data_size);
list_add_tail(&msg_ctx->daemon_out_list, &daemon->msg_ctx_out_queue);
mutex_unlock(&msg_ctx->mux);
mutex_lock(&daemon->mux);
daemon->num_queued_msg_ctx++;
wake_up_interruptible(&daemon->wait);
mutex_unlock(&daemon->mux);
return 0;
}
/*
* miscdevfs packet format:
* Octet 0: Type
* Octets 1-4: network byte order msg_ctx->counter
* Octets 5-N0: Size of struct ecryptfs_message to follow
* Octets N0-N1: struct ecryptfs_message (including data)
*
* Octets 5-N1 not written if the packet type does not include a message
*/
#define PKT_TYPE_SIZE 1
#define PKT_CTR_SIZE 4
#define MIN_NON_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE)
#define MIN_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE \
+ ECRYPTFS_MIN_PKT_LEN_SIZE)
/* 4 + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES comes from tag 65 packet format */
#define MAX_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE \
+ ECRYPTFS_MAX_PKT_LEN_SIZE \
+ sizeof(struct ecryptfs_message) \
+ 4 + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES)
#define PKT_TYPE_OFFSET 0
#define PKT_CTR_OFFSET PKT_TYPE_SIZE
#define PKT_LEN_OFFSET (PKT_TYPE_SIZE + PKT_CTR_SIZE)
/**
* ecryptfs_miscdev_read - format and send message from queue
* @file: miscdevfs handle
* @buf: User buffer into which to copy the next message on the daemon queue
* @count: Amount of space available in @buf
* @ppos: Offset in file (ignored)
*
* Pulls the most recent message from the daemon queue, formats it for
* being sent via a miscdevfs handle, and copies it into @buf
*
* Returns the number of bytes copied into the user buffer
*/
static ssize_t
ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct ecryptfs_daemon *daemon = file->private_data;
struct ecryptfs_msg_ctx *msg_ctx;
size_t packet_length_size;
char packet_length[ECRYPTFS_MAX_PKT_LEN_SIZE];
size_t i;
size_t total_length;
int rc;
mutex_lock(&daemon->mux);
if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
rc = 0;
printk(KERN_WARNING "%s: Attempt to read from zombified "
"daemon\n", __func__);
goto out_unlock_daemon;
}
if (daemon->flags & ECRYPTFS_DAEMON_IN_READ) {
rc = 0;
goto out_unlock_daemon;
}
/* This daemon will not go away so long as this flag is set */
daemon->flags |= ECRYPTFS_DAEMON_IN_READ;
check_list:
if (list_empty(&daemon->msg_ctx_out_queue)) {
mutex_unlock(&daemon->mux);
rc = wait_event_interruptible(
daemon->wait, !list_empty(&daemon->msg_ctx_out_queue));
mutex_lock(&daemon->mux);
if (rc < 0) {
rc = 0;
goto out_unlock_daemon;
}
}
if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
rc = 0;
goto out_unlock_daemon;
}
if (list_empty(&daemon->msg_ctx_out_queue)) {
/* Something else jumped in since the
* wait_event_interruptable() and removed the
* message from the queue; try again */
goto check_list;
}
msg_ctx = list_first_entry(&daemon->msg_ctx_out_queue,
struct ecryptfs_msg_ctx, daemon_out_list);
BUG_ON(!msg_ctx);
mutex_lock(&msg_ctx->mux);
if (msg_ctx->msg) {
rc = ecryptfs_write_packet_length(packet_length,
msg_ctx->msg_size,
&packet_length_size);
if (rc) {
rc = 0;
printk(KERN_WARNING "%s: Error writing packet length; "
"rc = [%d]\n", __func__, rc);
goto out_unlock_msg_ctx;
}
} else {
packet_length_size = 0;
msg_ctx->msg_size = 0;
}
total_length = (PKT_TYPE_SIZE + PKT_CTR_SIZE + packet_length_size
+ msg_ctx->msg_size);
if (count < total_length) {
rc = 0;
printk(KERN_WARNING "%s: Only given user buffer of "
"size [%zd], but we need [%zd] to read the "
"pending message\n", __func__, count, total_length);
goto out_unlock_msg_ctx;
}
rc = -EFAULT;
if (put_user(msg_ctx->type, buf))
goto out_unlock_msg_ctx;
if (put_user(cpu_to_be32(msg_ctx->counter),
(__be32 __user *)(&buf[PKT_CTR_OFFSET])))
goto out_unlock_msg_ctx;
i = PKT_TYPE_SIZE + PKT_CTR_SIZE;
if (msg_ctx->msg) {
if (copy_to_user(&buf[i], packet_length, packet_length_size))
goto out_unlock_msg_ctx;
i += packet_length_size;
if (copy_to_user(&buf[i], msg_ctx->msg, msg_ctx->msg_size))
goto out_unlock_msg_ctx;
i += msg_ctx->msg_size;
}
rc = i;
list_del(&msg_ctx->daemon_out_list);
kfree(msg_ctx->msg);
msg_ctx->msg = NULL;
/* We do not expect a reply from the userspace daemon for any
* message type other than ECRYPTFS_MSG_REQUEST */
if (msg_ctx->type != ECRYPTFS_MSG_REQUEST)
ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
out_unlock_msg_ctx:
mutex_unlock(&msg_ctx->mux);
out_unlock_daemon:
daemon->flags &= ~ECRYPTFS_DAEMON_IN_READ;
mutex_unlock(&daemon->mux);
return rc;
}
/**
* ecryptfs_miscdev_response - miscdevess response to message previously sent to daemon
* @data: Bytes comprising struct ecryptfs_message
* @data_size: sizeof(struct ecryptfs_message) + data len
* @seq: Sequence number for miscdev response packet
*
* Returns zero on success; non-zero otherwise
*/
static int ecryptfs_miscdev_response(struct ecryptfs_daemon *daemon, char *data,
size_t data_size, u32 seq)
{
struct ecryptfs_message *msg = (struct ecryptfs_message *)data;
int rc;
if ((sizeof(*msg) + msg->data_len) != data_size) {
printk(KERN_WARNING "%s: (sizeof(*msg) + msg->data_len) = "
"[%zd]; data_size = [%zd]. Invalid packet.\n", __func__,
(sizeof(*msg) + msg->data_len), data_size);
rc = -EINVAL;
goto out;
}
rc = ecryptfs_process_response(daemon, msg, seq);
if (rc)
printk(KERN_ERR
"Error processing response message; rc = [%d]\n", rc);
out:
return rc;
}
/**
* ecryptfs_miscdev_write - handle write to daemon miscdev handle
* @file: File for misc dev handle
* @buf: Buffer containing user data
* @count: Amount of data in @buf
* @ppos: Pointer to offset in file (ignored)
*
* Returns the number of bytes read from @buf
*/
static ssize_t
ecryptfs_miscdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
__be32 counter_nbo;
u32 seq;
size_t packet_size, packet_size_length;
char *data;
unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE];
ssize_t rc;
if (count == 0) {
return 0;
} else if (count == MIN_NON_MSG_PKT_SIZE) {
/* Likely a harmless MSG_HELO or MSG_QUIT - no packet length */
goto memdup;
} else if (count < MIN_MSG_PKT_SIZE || count > MAX_MSG_PKT_SIZE) {
printk(KERN_WARNING "%s: Acceptable packet size range is "
"[%d-%zu], but amount of data written is [%zu].",
__func__, MIN_MSG_PKT_SIZE, MAX_MSG_PKT_SIZE, count);
return -EINVAL;
}
if (copy_from_user(packet_size_peek, &buf[PKT_LEN_OFFSET],
sizeof(packet_size_peek))) {
printk(KERN_WARNING "%s: Error while inspecting packet size\n",
__func__);
return -EFAULT;
}
rc = ecryptfs_parse_packet_length(packet_size_peek, &packet_size,
&packet_size_length);
if (rc) {
printk(KERN_WARNING "%s: Error parsing packet length; "
"rc = [%zd]\n", __func__, rc);
return rc;
}
if ((PKT_TYPE_SIZE + PKT_CTR_SIZE + packet_size_length + packet_size)
!= count) {
printk(KERN_WARNING "%s: Invalid packet size [%zu]\n", __func__,
packet_size);
return -EINVAL;
}
memdup:
data = memdup_user(buf, count);
if (IS_ERR(data)) {
printk(KERN_ERR "%s: memdup_user returned error [%ld]\n",
__func__, PTR_ERR(data));
return PTR_ERR(data);
}
switch (data[PKT_TYPE_OFFSET]) {
case ECRYPTFS_MSG_RESPONSE:
if (count < (MIN_MSG_PKT_SIZE
+ sizeof(struct ecryptfs_message))) {
printk(KERN_WARNING "%s: Minimum acceptable packet "
"size is [%zd], but amount of data written is "
"only [%zd]. Discarding response packet.\n",
__func__,
(MIN_MSG_PKT_SIZE
+ sizeof(struct ecryptfs_message)), count);
rc = -EINVAL;
goto out_free;
}
memcpy(&counter_nbo, &data[PKT_CTR_OFFSET], PKT_CTR_SIZE);
seq = be32_to_cpu(counter_nbo);
rc = ecryptfs_miscdev_response(file->private_data,
&data[PKT_LEN_OFFSET + packet_size_length],
packet_size, seq);
if (rc) {
printk(KERN_WARNING "%s: Failed to deliver miscdev "
"response to requesting operation; rc = [%zd]\n",
__func__, rc);
goto out_free;
}
break;
case ECRYPTFS_MSG_HELO:
case ECRYPTFS_MSG_QUIT:
break;
default:
ecryptfs_printk(KERN_WARNING, "Dropping miscdev "
"message of unrecognized type [%d]\n",
data[0]);
rc = -EINVAL;
goto out_free;
}
rc = count;
out_free:
kfree(data);
return rc;
}
static const struct file_operations ecryptfs_miscdev_fops = {
.owner = THIS_MODULE,
.open = ecryptfs_miscdev_open,
.poll = ecryptfs_miscdev_poll,
.read = ecryptfs_miscdev_read,
.write = ecryptfs_miscdev_write,
.release = ecryptfs_miscdev_release,
.llseek = noop_llseek,
};
static struct miscdevice ecryptfs_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "ecryptfs",
.fops = &ecryptfs_miscdev_fops
};
/**
* ecryptfs_init_ecryptfs_miscdev
*
* Messages sent to the userspace daemon from the kernel are placed on
* a queue associated with the daemon. The next read against the
* miscdev handle by that daemon will return the oldest message placed
* on the message queue for the daemon.
*
* Returns zero on success; non-zero otherwise
*/
int __init ecryptfs_init_ecryptfs_miscdev(void)
{
int rc;
atomic_set(&ecryptfs_num_miscdev_opens, 0);
rc = misc_register(&ecryptfs_miscdev);
if (rc)
printk(KERN_ERR "%s: Failed to register miscellaneous device "
"for communications with userspace daemons; rc = [%d]\n",
__func__, rc);
return rc;
}
/**
* ecryptfs_destroy_ecryptfs_miscdev
*
* All of the daemons must be exorcised prior to calling this
* function.
*/
void ecryptfs_destroy_ecryptfs_miscdev(void)
{
BUG_ON(atomic_read(&ecryptfs_num_miscdev_opens) != 0);
misc_deregister(&ecryptfs_miscdev);
}

349
fs/ecryptfs/mm.c Executable file
View file

@ -0,0 +1,349 @@
/*
* 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/mm.h>
#include <linux/fs.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/swap.h>
#include <linux/pagemap.h>
#include "ecryptfs_kernel.h"
#include "ecryptfs_dek.h"
extern spinlock_t inode_sb_list_lock;
static int ecryptfs_mm_debug = 0;
DEFINE_MUTEX(ecryptfs_mm_mutex);
struct ecryptfs_mm_drop_cache_param {
int user_id;
int engine_id;
};
#define INVALIDATE_MAPPING_RETRY_CNT 3
static unsigned long invalidate_mapping_pages_retry(struct address_space *mapping,
pgoff_t start, pgoff_t end, int retries) {
unsigned long ret;
if(ecryptfs_mm_debug)
printk("freeing [%s] sensitive inode[mapped pagenum = %lu]\n",
mapping->host->i_sb->s_type->name,
mapping->nrpages);
retry:
ret = invalidate_mapping_pages(mapping, start, end);
if(ecryptfs_mm_debug)
printk("invalidate_mapping_pages ret = %lu, [%lu] remained\n",
ret, mapping->nrpages);
if(mapping->nrpages != 0) {
if(retries > 0) {
printk("[%lu] mapped pages remained in sensitive inode, retry..\n",
mapping->nrpages);
retries--;
msleep(100);
goto retry;
}
}
return ret;
}
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
static unsigned long invalidate_lower_mapping_pages_retry(struct file *lower_file, int retries) {
unsigned long ret, invalidated = 0;
struct address_space *mapping;
mapping = lower_file->f_mapping;
if(ecryptfs_mm_debug)
printk("%s:freeing [%s] sensitive inode[mapped pagenum = %lu]\n",__func__,
mapping->host->i_sb->s_type->name,mapping->nrpages);
for (; retries > 0; retries--) {
// !! TODO !!
//if (lower_file && lower_file->f_op && lower_file->f_op->unlocked_ioctl)
// ret = lower_file->f_op->unlocked_ioctl(lower_file, FS_IOC_INVAL_MAPPING, 0);
ret = do_vfs_ioctl(lower_file,0, FS_IOC_INVAL_MAPPING, 0); // lower_file is sdcardfs file
invalidated += ret;
if(ecryptfs_mm_debug)
printk("invalidate_mapping_pages ret = %lu, [%lu] remained\n",
ret, mapping->nrpages);
if (mapping->nrpages == 0)
break;
printk("[%lu] mapped pages remained in sensitive inode, retry..\n",
mapping->nrpages);
msleep(100);
}
return invalidated;
}
#endif
void ecryptfs_mm_do_sdp_cleanup(struct inode *inode) {
struct ecryptfs_crypt_stat *crypt_stat;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL;
struct ecryptfs_inode_info *inode_info;
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
mount_crypt_stat = &ecryptfs_superblock_to_private(inode->i_sb)->mount_crypt_stat;
inode_info = ecryptfs_inode_to_private(inode);
if(crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) {
int rc;
if(S_ISDIR(inode->i_mode)) {
DEK_LOGD("%s: inode: %p is dir, return\n",__func__, inode);
return;
}
DEK_LOGD("%s: inode: %p clean up start\n",__func__, inode);
rc = vfs_fsync(inode_info->lower_file, 0);
if(rc)
DEK_LOGE("%s: vfs_sync returned error rc: %d\n", __func__, rc);
if(ecryptfs_is_sdp_locked(crypt_stat->engine_id)) {
DEK_LOGD("%s: persona locked inode: %lu useid: %d\n",
__func__, inode->i_ino, crypt_stat->engine_id);
invalidate_mapping_pages_retry(inode->i_mapping, 0, -1, 3);
}
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
if (mount_crypt_stat->flags & ECRYPTFS_USE_FMP) {
DEK_LOGD("%s inode: %p calling invalidate_lower_mapping_pages_retry\n",__func__, inode);
invalidate_lower_mapping_pages_retry(inode_info->lower_file, 3);
}
#endif
if(ecryptfs_is_sdp_locked(crypt_stat->engine_id)) {
ecryptfs_clean_sdp_dek(crypt_stat);
}
DEK_LOGD("%s: inode: %p clean up stop\n",__func__, inode);
}
return;
}
static unsigned long drop_inode_pagecache(struct inode *inode) {
int rc = 0;
spin_lock(&inode->i_lock);
if(ecryptfs_mm_debug)
printk("%s() cleaning [%s] pages: %lu\n", __func__,
inode->i_sb->s_type->name,inode->i_mapping->nrpages);
if ((inode->i_mapping->nrpages == 0)) {
spin_unlock(&inode->i_lock);
printk("%s inode having zero nrpages\n", __func__);
return 0;
}
spin_unlock(&inode->i_lock);
/*
* flush mapped dirty pages.
*/
rc = filemap_write_and_wait(inode->i_mapping);
if(rc)
printk("filemap_flush failed, rc=%d\n", rc);
if (inode->i_mapping->nrpages != 0)
lru_add_drain_all();
rc = invalidate_mapping_pages_retry(inode->i_mapping, 0, -1,
INVALIDATE_MAPPING_RETRY_CNT);
if(inode->i_mapping->nrpages)
printk("%s() uncleaned [%s] pages: %lu\n", __func__,
inode->i_sb->s_type->name,inode->i_mapping->nrpages);
return rc;
}
static void ecryptfs_mm_drop_pagecache(struct super_block *sb, void *arg)
{
struct inode *inode;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
struct ecryptfs_mm_drop_cache_param *param = arg;
if(strcmp("ecryptfs", sb->s_type->name)) {
printk("%s sb:%s is not ecryptfs superblock\n", __func__,
sb->s_type->name);
return;
}
mount_crypt_stat = &ecryptfs_superblock_to_private(sb)->mount_crypt_stat;
printk("%s start() sb:%s [%d], userid:%d\n", __func__,
sb->s_type->name, mount_crypt_stat->userid, param->user_id);
if (param->user_id >= 100 && param->user_id < 200) {
if(mount_crypt_stat->userid != param->user_id)
return;
}
spin_lock(&inode_sb_list_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list)
{
struct ecryptfs_crypt_stat *crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
if(crypt_stat == NULL)
continue;
if(crypt_stat->engine_id != param->engine_id) {
continue;
}
if (!inode->i_mapping) {
continue;
}
spin_lock(&inode->i_lock);
if (inode->i_mapping->nrpages == 0) {
spin_unlock(&inode->i_lock);
if(ecryptfs_mm_debug)
printk("%s() ecryptfs inode [ino:%lu]\n",__func__, inode->i_ino);
if((crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE) &&
!atomic_read(&ecryptfs_inode_to_private(inode)->lower_file_count))
ecryptfs_clean_sdp_dek(crypt_stat);
continue;
}
spin_unlock(&inode->i_lock);
spin_unlock(&inode_sb_list_lock);
if(ecryptfs_mm_debug)
printk(KERN_ERR "inode number: %lu i_mapping: %p [%s] userid:%d\n",inode->i_ino,
inode->i_mapping,inode->i_sb->s_type->name,inode->i_mapping->userid);
if(mapping_sensitive(inode->i_mapping) &&
!atomic_read(&ecryptfs_inode_to_private(inode)->lower_file_count)) {
drop_inode_pagecache(inode);
if(ecryptfs_mm_debug)
printk(KERN_ERR "lower inode: %p lower inode: %p nrpages: %lu\n",ecryptfs_inode_to_lower(inode),
ecryptfs_inode_to_private(inode), ecryptfs_inode_to_lower(inode)->i_mapping->nrpages);
if(crypt_stat->flags & ECRYPTFS_DEK_IS_SENSITIVE)
ecryptfs_clean_sdp_dek(crypt_stat);
}
spin_lock(&inode_sb_list_lock);
}
spin_unlock(&inode_sb_list_lock);
}
static int ecryptfs_mm_task(void *arg)
{
struct file_system_type *type;
struct ecryptfs_mm_drop_cache_param *param = arg;
type = get_fs_type("ecryptfs");
if(type) {
if(ecryptfs_mm_debug)
printk("%s type name: %s flags: %d\n", __func__, type->name, type->fs_flags);
mutex_lock(&ecryptfs_mm_mutex);
iterate_supers_type(type,ecryptfs_mm_drop_pagecache, param);
mutex_unlock(&ecryptfs_mm_mutex);
put_filesystem(type);
}
kfree(param);
return 0;
}
void ecryptfs_mm_drop_cache(int userid, int engineid) {
#if 1
struct task_struct *task;
struct ecryptfs_mm_drop_cache_param *param =
kzalloc(sizeof(*param), GFP_KERNEL);
if (!param) {
printk("%s :: skip. no memory to alloc param\n", __func__);
return;
}
param->user_id = userid;
param->engine_id = engineid;
printk("running cache cleanup thread - sdp-id : %d\n", userid);
task = kthread_run(ecryptfs_mm_task, param, "sdp_cached");
if (IS_ERR(task)) {
printk(KERN_ERR "SDP : unable to create kernel thread: %ld\n",
PTR_ERR(task));
}
#else
ecryptfs_mm_task(&userid);
#endif
}
#include <linux/pagevec.h>
#include <linux/pagemap.h>
#include <linux/memcontrol.h>
#include <linux/atomic.h>
static void __page_dump(unsigned char *buf, int len, const char* str)
{
unsigned int i;
char s[512];
s[0] = 0;
for(i=0;i<len && i<16;++i) {
char tmp[8];
sprintf(tmp, " %02x", buf[i]);
strcat(s, tmp);
}
if (len > 16) {
char tmp[8];
sprintf(tmp, " ...");
strcat(s, tmp);
}
DEK_LOGD("%s [%s len=%d]\n", s, str, len);
}
#ifdef DEK_DEBUG
/*
* This dump will appear in ramdump
*/
void page_dump (struct page *p) {
void *d;
d = kmap_atomic(p);
if(d) {
__page_dump((unsigned char *)d, PAGE_SIZE, "freeing");
kunmap_atomic(d);
}
}
#else
void page_dump (struct page *p) {
// Do nothing
}
#endif

28
fs/ecryptfs/mm.h Executable file
View file

@ -0,0 +1,28 @@
/*
* 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.
*/
#ifndef ECRYPTFS_MM_H_
#define ECRYPTFS_MM_H_
void ecryptfs_mm_drop_cache(int userid, int engineid);
void ecryptfs_mm_do_sdp_cleanup(struct inode *inode);
void page_dump (struct page *p);
#endif /* ECRYPTFS_MM_H_ */

590
fs/ecryptfs/mmap.c Normal file
View file

@ -0,0 +1,590 @@
/**
* eCryptfs: Linux filesystem encryption layer
* This is where eCryptfs coordinates the symmetric encryption and
* decryption of the file data as it passes between the lower
* encrypted file and the upper decrypted file.
*
* Copyright (C) 1997-2003 Erez Zadok
* Copyright (C) 2001-2003 Stony Brook University
* Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/pagemap.h>
#include <linux/writeback.h>
#include <linux/page-flags.h>
#include <linux/mount.h>
#include <linux/file.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include "ecryptfs_kernel.h"
/**
* ecryptfs_get_locked_page
*
* Get one page from cache or lower f/s, return error otherwise.
*
* Returns locked and up-to-date page (if ok), with increased
* refcnt.
*/
struct page *ecryptfs_get_locked_page(struct inode *inode, loff_t index)
{
struct page *page = read_mapping_page(inode->i_mapping, index, NULL);
if (!IS_ERR(page))
lock_page(page);
return page;
}
/**
* ecryptfs_writepage
* @page: Page that is locked before this call is made
*
* Returns zero on success; non-zero otherwise
*
* This is where we encrypt the data and pass the encrypted data to
* the lower filesystem. In OpenPGP-compatible mode, we operate on
* entire underlying packets.
*/
static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
{
int rc;
// WTL_EDM_START
/* MDM 3.1 START */
struct inode *inode;
struct ecryptfs_crypt_stat *crypt_stat;
inode = page->mapping->host;
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
size_t size;
loff_t file_size = i_size_read(inode);
pgoff_t end_page_index = file_size >> PAGE_CACHE_SHIFT;
if (end_page_index < page->index)
size = 0;
else if (end_page_index == page->index)
size = file_size & ~PAGE_CACHE_MASK;
else
size = PAGE_CACHE_SIZE;
rc = ecryptfs_write_lower_page_segment(inode, page, 0, size);
if (unlikely(rc)) {
ecryptfs_printk(KERN_WARNING, "Error write ""page (upper index [0x%.16lx])\n", page->index);
ClearPageUptodate(page);
} else
SetPageUptodate(page);
goto out;
}
/* MDM 3.1 END */
// WTL_EDM_END
rc = ecryptfs_encrypt_page(page);
if (rc) {
ecryptfs_printk(KERN_WARNING, "Error encrypting "
"page (upper index [0x%.16lx])\n", page->index);
ClearPageUptodate(page);
goto out;
}
SetPageUptodate(page);
out:
unlock_page(page);
return rc;
}
static void strip_xattr_flag(char *page_virt,
struct ecryptfs_crypt_stat *crypt_stat)
{
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
size_t written;
crypt_stat->flags &= ~ECRYPTFS_METADATA_IN_XATTR;
ecryptfs_write_crypt_stat_flags(page_virt, crypt_stat,
&written);
crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
}
}
/**
* Header Extent:
* Octets 0-7: Unencrypted file size (big-endian)
* Octets 8-15: eCryptfs special marker
* Octets 16-19: Flags
* Octet 16: File format version number (between 0 and 255)
* Octets 17-18: Reserved
* Octet 19: Bit 1 (lsb): Reserved
* Bit 2: Encrypted?
* Bits 3-8: Reserved
* Octets 20-23: Header extent size (big-endian)
* Octets 24-25: Number of header extents at front of file
* (big-endian)
* Octet 26: Begin RFC 2440 authentication token packet set
*/
/**
* ecryptfs_copy_up_encrypted_with_header
* @page: Sort of a ``virtual'' representation of the encrypted lower
* file. The actual lower file does not have the metadata in
* the header. This is locked.
* @crypt_stat: The eCryptfs inode's cryptographic context
*
* The ``view'' is the version of the file that userspace winds up
* seeing, with the header information inserted.
*/
static int
ecryptfs_copy_up_encrypted_with_header(struct page *page,
struct ecryptfs_crypt_stat *crypt_stat)
{
loff_t extent_num_in_page = 0;
loff_t num_extents_per_page = (PAGE_CACHE_SIZE
/ crypt_stat->extent_size);
int rc = 0;
while (extent_num_in_page < num_extents_per_page) {
loff_t view_extent_num = ((((loff_t)page->index)
* num_extents_per_page)
+ extent_num_in_page);
size_t num_header_extents_at_front =
(crypt_stat->metadata_size / crypt_stat->extent_size);
if (view_extent_num < num_header_extents_at_front) {
/* This is a header extent */
char *page_virt;
page_virt = kmap_atomic(page);
memset(page_virt, 0, PAGE_CACHE_SIZE);
/* TODO: Support more than one header extent */
if (view_extent_num == 0) {
size_t written;
rc = ecryptfs_read_xattr_region(
page_virt, page->mapping->host);
strip_xattr_flag(page_virt + 16, crypt_stat);
ecryptfs_write_header_metadata(page_virt + 20,
crypt_stat,
&written);
}
kunmap_atomic(page_virt);
flush_dcache_page(page);
if (rc) {
printk(KERN_ERR "%s: Error reading xattr "
"region; rc = [%d]\n", __func__, rc);
goto out;
}
} else {
/* This is an encrypted data extent */
loff_t lower_offset =
((view_extent_num * crypt_stat->extent_size)
- crypt_stat->metadata_size);
rc = ecryptfs_read_lower_page_segment(
page, (lower_offset >> PAGE_CACHE_SHIFT),
(lower_offset & ~PAGE_CACHE_MASK),
crypt_stat->extent_size, page->mapping->host);
if (rc) {
printk(KERN_ERR "%s: Error attempting to read "
"extent at offset [%lld] in the lower "
"file; rc = [%d]\n", __func__,
lower_offset, rc);
goto out;
}
}
extent_num_in_page++;
}
out:
return rc;
}
/**
* ecryptfs_readpage
* @file: An eCryptfs file
* @page: Page from eCryptfs inode mapping into which to stick the read data
*
* Read in a page, decrypting if necessary.
*
* Returns zero on success; non-zero on error.
*/
static int ecryptfs_readpage(struct file *file, struct page *page)
{
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(page->mapping->host)->crypt_stat;
int rc = 0;
if (!crypt_stat || !(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
rc = ecryptfs_read_lower_page_segment(page, page->index, 0,
PAGE_CACHE_SIZE,
page->mapping->host);
} else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
rc = ecryptfs_copy_up_encrypted_with_header(page,
crypt_stat);
if (rc) {
printk(KERN_ERR "%s: Error attempting to copy "
"the encrypted content from the lower "
"file whilst inserting the metadata "
"from the xattr into the header; rc = "
"[%d]\n", __func__, rc);
goto out;
}
} else {
rc = ecryptfs_read_lower_page_segment(
page, page->index, 0, PAGE_CACHE_SIZE,
page->mapping->host);
if (rc) {
printk(KERN_ERR "Error reading page; rc = "
"[%d]\n", rc);
goto out;
}
}
} else {
rc = ecryptfs_decrypt_page(page);
if (rc) {
ecryptfs_printk(KERN_ERR, "Error decrypting page; "
"rc = [%d]\n", rc);
goto out;
}
}
out:
if (rc)
ClearPageUptodate(page);
else
SetPageUptodate(page);
ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16lx]\n",
page->index);
unlock_page(page);
return rc;
}
/**
* Called with lower inode mutex held.
*/
static int fill_zeros_to_end_of_page(struct page *page, unsigned int to)
{
struct inode *inode = page->mapping->host;
int end_byte_in_page;
if ((i_size_read(inode) / PAGE_CACHE_SIZE) != page->index)
goto out;
end_byte_in_page = i_size_read(inode) % PAGE_CACHE_SIZE;
if (to > end_byte_in_page)
end_byte_in_page = to;
zero_user_segment(page, end_byte_in_page, PAGE_CACHE_SIZE);
out:
return 0;
}
/**
* ecryptfs_write_begin
* @file: The eCryptfs file
* @mapping: The eCryptfs object
* @pos: The file offset at which to start writing
* @len: Length of the write
* @flags: Various flags
* @pagep: Pointer to return the page
* @fsdata: Pointer to return fs data (unused)
*
* This function must zero any hole we create
*
* Returns zero on success; non-zero otherwise
*/
static int ecryptfs_write_begin(struct file *file,
struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
struct page *page;
loff_t prev_page_end_size;
int rc = 0;
page = grab_cache_page_write_begin(mapping, index, flags);
if (!page)
return -ENOMEM;
*pagep = page;
prev_page_end_size = ((loff_t)index << PAGE_CACHE_SHIFT);
if (!PageUptodate(page)) {
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(mapping->host)->crypt_stat;
if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
rc = ecryptfs_read_lower_page_segment(
page, index, 0, PAGE_CACHE_SIZE, mapping->host);
if (rc) {
printk(KERN_ERR "%s: Error attemping to read "
"lower page segment; rc = [%d]\n",
__func__, rc);
ClearPageUptodate(page);
goto out;
} else
SetPageUptodate(page);
} else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
rc = ecryptfs_copy_up_encrypted_with_header(
page, crypt_stat);
if (rc) {
printk(KERN_ERR "%s: Error attempting "
"to copy the encrypted content "
"from the lower file whilst "
"inserting the metadata from "
"the xattr into the header; rc "
"= [%d]\n", __func__, rc);
ClearPageUptodate(page);
goto out;
}
SetPageUptodate(page);
} else {
rc = ecryptfs_read_lower_page_segment(
page, index, 0, PAGE_CACHE_SIZE,
mapping->host);
if (rc) {
printk(KERN_ERR "%s: Error reading "
"page; rc = [%d]\n",
__func__, rc);
ClearPageUptodate(page);
goto out;
}
SetPageUptodate(page);
}
} else {
if (prev_page_end_size
>= i_size_read(page->mapping->host)) {
zero_user(page, 0, PAGE_CACHE_SIZE);
SetPageUptodate(page);
} else if (len < PAGE_CACHE_SIZE) {
rc = ecryptfs_decrypt_page(page);
if (rc) {
printk(KERN_ERR "%s: Error decrypting "
"page at index [%ld]; "
"rc = [%d]\n",
__func__, page->index, rc);
ClearPageUptodate(page);
goto out;
}
SetPageUptodate(page);
}
}
}
/* If creating a page or more of holes, zero them out via truncate.
* Note, this will increase i_size. */
if (index != 0) {
if (prev_page_end_size > i_size_read(page->mapping->host)) {
rc = ecryptfs_truncate(file->f_path.dentry,
prev_page_end_size);
if (rc) {
printk(KERN_ERR "%s: Error on attempt to "
"truncate to (higher) offset [%lld];"
" rc = [%d]\n", __func__,
prev_page_end_size, rc);
goto out;
}
}
}
/* Writing to a new page, and creating a small hole from start
* of page? Zero it out. */
if ((i_size_read(mapping->host) == prev_page_end_size)
&& (pos != 0))
zero_user(page, 0, PAGE_CACHE_SIZE);
out:
if (unlikely(rc)) {
unlock_page(page);
page_cache_release(page);
*pagep = NULL;
}
return rc;
}
/**
* ecryptfs_write_inode_size_to_header
*
* Writes the lower file size to the first 8 bytes of the header.
*
* Returns zero on success; non-zero on error.
*/
static int ecryptfs_write_inode_size_to_header(struct inode *ecryptfs_inode)
{
char *file_size_virt;
int rc;
file_size_virt = kmalloc(sizeof(u64), GFP_KERNEL);
if (!file_size_virt) {
rc = -ENOMEM;
goto out;
}
put_unaligned_be64(i_size_read(ecryptfs_inode), file_size_virt);
rc = ecryptfs_write_lower(ecryptfs_inode, file_size_virt, 0,
sizeof(u64));
kfree(file_size_virt);
if (rc < 0)
printk(KERN_ERR "%s: Error writing file size to header; "
"rc = [%d]\n", __func__, rc);
else
rc = 0;
out:
return rc;
}
struct kmem_cache *ecryptfs_xattr_cache;
static int ecryptfs_write_inode_size_to_xattr(struct inode *ecryptfs_inode)
{
ssize_t size;
void *xattr_virt;
struct dentry *lower_dentry =
ecryptfs_inode_to_private(ecryptfs_inode)->lower_file->f_dentry;
struct inode *lower_inode = lower_dentry->d_inode;
int rc;
if (!lower_inode->i_op->getxattr || !lower_inode->i_op->setxattr) {
printk(KERN_WARNING
"No support for setting xattr in lower filesystem\n");
rc = -ENOSYS;
goto out;
}
xattr_virt = kmem_cache_alloc(ecryptfs_xattr_cache, GFP_KERNEL);
if (!xattr_virt) {
printk(KERN_ERR "Out of memory whilst attempting to write "
"inode size to xattr\n");
rc = -ENOMEM;
goto out;
}
mutex_lock(&lower_inode->i_mutex);
size = lower_inode->i_op->getxattr(lower_dentry, ECRYPTFS_XATTR_NAME,
xattr_virt, PAGE_CACHE_SIZE);
if (size < 0)
size = 8;
put_unaligned_be64(i_size_read(ecryptfs_inode), xattr_virt);
rc = lower_inode->i_op->setxattr(lower_dentry, ECRYPTFS_XATTR_NAME,
xattr_virt, size, 0);
mutex_unlock(&lower_inode->i_mutex);
if (rc)
printk(KERN_ERR "Error whilst attempting to write inode size "
"to lower file xattr; rc = [%d]\n", rc);
kmem_cache_free(ecryptfs_xattr_cache, xattr_virt);
out:
return rc;
}
int ecryptfs_write_inode_size_to_metadata(struct inode *ecryptfs_inode)
{
struct ecryptfs_crypt_stat *crypt_stat;
crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
BUG_ON(!(crypt_stat->flags & ECRYPTFS_ENCRYPTED));
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
return ecryptfs_write_inode_size_to_xattr(ecryptfs_inode);
else
return ecryptfs_write_inode_size_to_header(ecryptfs_inode);
}
/**
* ecryptfs_write_end
* @file: The eCryptfs file object
* @mapping: The eCryptfs object
* @pos: The file position
* @len: The length of the data (unused)
* @copied: The amount of data copied
* @page: The eCryptfs page
* @fsdata: The fsdata (unused)
*/
static int ecryptfs_write_end(struct file *file,
struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
unsigned to = from + copied;
struct inode *ecryptfs_inode = mapping->host;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
int rc;
ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page"
"(page w/ index = [0x%.16lx], to = [%d])\n", index, to);
if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
rc = ecryptfs_write_lower_page_segment(ecryptfs_inode, page, 0,
to);
if (!rc) {
rc = copied;
fsstack_copy_inode_size(ecryptfs_inode,
ecryptfs_inode_to_lower(ecryptfs_inode));
}
goto out;
}
if (!PageUptodate(page)) {
if (copied < PAGE_CACHE_SIZE) {
rc = 0;
goto out;
}
SetPageUptodate(page);
}
/* Fills in zeros if 'to' goes beyond inode size */
rc = fill_zeros_to_end_of_page(page, to);
if (rc) {
ecryptfs_printk(KERN_WARNING, "Error attempting to fill "
"zeros in page with index = [0x%.16lx]\n", index);
goto out;
}
rc = ecryptfs_encrypt_page(page);
if (rc) {
ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper "
"index [0x%.16lx])\n", index);
goto out;
}
if (pos + copied > i_size_read(ecryptfs_inode)) {
i_size_write(ecryptfs_inode, pos + copied);
ecryptfs_printk(KERN_DEBUG, "Expanded file size to "
"[0x%.16llx]\n",
(unsigned long long)i_size_read(ecryptfs_inode));
}
rc = ecryptfs_write_inode_size_to_metadata(ecryptfs_inode);
if (rc)
printk(KERN_ERR "Error writing inode size to metadata; "
"rc = [%d]\n", rc);
else
rc = copied;
out:
unlock_page(page);
page_cache_release(page);
return rc;
}
static sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block)
{
int rc = 0;
struct inode *inode;
struct inode *lower_inode;
inode = (struct inode *)mapping->host;
lower_inode = ecryptfs_inode_to_lower(inode);
if (lower_inode->i_mapping->a_ops->bmap)
rc = lower_inode->i_mapping->a_ops->bmap(lower_inode->i_mapping,
block);
return rc;
}
const struct address_space_operations ecryptfs_aops = {
.writepage = ecryptfs_writepage,
.readpage = ecryptfs_readpage,
.write_begin = ecryptfs_write_begin,
.write_end = ecryptfs_write_end,
.bmap = ecryptfs_bmap,
};

273
fs/ecryptfs/read_write.c Normal file
View file

@ -0,0 +1,273 @@
/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/fs.h>
#include <linux/pagemap.h>
#include "ecryptfs_kernel.h"
/**
* ecryptfs_write_lower
* @ecryptfs_inode: The eCryptfs inode
* @data: Data to write
* @offset: Byte offset in the lower file to which to write the data
* @size: Number of bytes from @data to write at @offset in the lower
* file
*
* Write data to the lower file.
*
* Returns bytes written on success; less than zero on error
*/
int ecryptfs_write_lower(struct inode *ecryptfs_inode, char *data,
loff_t offset, size_t size)
{
struct file *lower_file;
ssize_t rc;
lower_file = ecryptfs_inode_to_private(ecryptfs_inode)->lower_file;
if (!lower_file)
return -EIO;
rc = kernel_write(lower_file, data, size, offset);
mark_inode_dirty_sync(ecryptfs_inode);
return rc;
}
/**
* ecryptfs_write_lower_page_segment
* @ecryptfs_inode: The eCryptfs inode
* @page_for_lower: The page containing the data to be written to the
* lower file
* @offset_in_page: The offset in the @page_for_lower from which to
* start writing the data
* @size: The amount of data from @page_for_lower to write to the
* lower file
*
* Determines the byte offset in the file for the given page and
* offset within the page, maps the page, and makes the call to write
* the contents of @page_for_lower to the lower inode.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_write_lower_page_segment(struct inode *ecryptfs_inode,
struct page *page_for_lower,
size_t offset_in_page, size_t size)
{
char *virt;
loff_t offset;
int rc;
offset = ((((loff_t)page_for_lower->index) << PAGE_CACHE_SHIFT)
+ offset_in_page);
virt = kmap(page_for_lower);
rc = ecryptfs_write_lower(ecryptfs_inode, virt, offset, size);
if (rc > 0)
rc = 0;
kunmap(page_for_lower);
return rc;
}
/**
* ecryptfs_write
* @ecryptfs_inode: The eCryptfs file into which to write
* @data: Virtual address where data to write is located
* @offset: Offset in the eCryptfs file at which to begin writing the
* data from @data
* @size: The number of bytes to write from @data
*
* Write an arbitrary amount of data to an arbitrary location in the
* eCryptfs inode page cache. This is done on a page-by-page, and then
* by an extent-by-extent, basis; individual extents are encrypted and
* written to the lower page cache (via VFS writes). This function
* takes care of all the address translation to locations in the lower
* filesystem; it also handles truncate events, writing out zeros
* where necessary.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset,
size_t size)
{
struct page *ecryptfs_page;
struct ecryptfs_crypt_stat *crypt_stat;
char *ecryptfs_page_virt;
loff_t ecryptfs_file_size = i_size_read(ecryptfs_inode);
loff_t data_offset = 0;
loff_t pos;
int rc = 0;
crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
/*
* if we are writing beyond current size, then start pos
* at the current size - we'll fill in zeros from there.
*/
if (offset > ecryptfs_file_size)
pos = ecryptfs_file_size;
else
pos = offset;
while (pos < (offset + size)) {
pgoff_t ecryptfs_page_idx = (pos >> PAGE_CACHE_SHIFT);
size_t start_offset_in_page = (pos & ~PAGE_CACHE_MASK);
size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page);
loff_t total_remaining_bytes = ((offset + size) - pos);
if (fatal_signal_pending(current)) {
rc = -EINTR;
break;
}
if (num_bytes > total_remaining_bytes)
num_bytes = total_remaining_bytes;
if (pos < offset) {
/* remaining zeros to write, up to destination offset */
loff_t total_remaining_zeros = (offset - pos);
if (num_bytes > total_remaining_zeros)
num_bytes = total_remaining_zeros;
}
ecryptfs_page = ecryptfs_get_locked_page(ecryptfs_inode,
ecryptfs_page_idx);
if (IS_ERR(ecryptfs_page)) {
rc = PTR_ERR(ecryptfs_page);
printk(KERN_ERR "%s: Error getting page at "
"index [%ld] from eCryptfs inode "
"mapping; rc = [%d]\n", __func__,
ecryptfs_page_idx, rc);
goto out;
}
ecryptfs_page_virt = kmap_atomic(ecryptfs_page);
/*
* pos: where we're now writing, offset: where the request was
* If current pos is before request, we are filling zeros
* If we are at or beyond request, we are writing the *data*
* If we're in a fresh page beyond eof, zero it in either case
*/
if (pos < offset || !start_offset_in_page) {
/* We are extending past the previous end of the file.
* Fill in zero values to the end of the page */
memset(((char *)ecryptfs_page_virt
+ start_offset_in_page), 0,
PAGE_CACHE_SIZE - start_offset_in_page);
}
/* pos >= offset, we are now writing the data request */
if (pos >= offset) {
memcpy(((char *)ecryptfs_page_virt
+ start_offset_in_page),
(data + data_offset), num_bytes);
data_offset += num_bytes;
}
kunmap_atomic(ecryptfs_page_virt);
flush_dcache_page(ecryptfs_page);
SetPageUptodate(ecryptfs_page);
unlock_page(ecryptfs_page);
if (crypt_stat->flags & ECRYPTFS_ENCRYPTED)
rc = ecryptfs_encrypt_page(ecryptfs_page);
else
rc = ecryptfs_write_lower_page_segment(ecryptfs_inode,
ecryptfs_page,
start_offset_in_page,
data_offset);
page_cache_release(ecryptfs_page);
if (rc) {
printk(KERN_ERR "%s: Error encrypting "
"page; rc = [%d]\n", __func__, rc);
goto out;
}
pos += num_bytes;
}
if (pos > ecryptfs_file_size) {
i_size_write(ecryptfs_inode, pos);
if (crypt_stat->flags & ECRYPTFS_ENCRYPTED) {
int rc2;
rc2 = ecryptfs_write_inode_size_to_metadata(
ecryptfs_inode);
if (rc2) {
printk(KERN_ERR "Problem with "
"ecryptfs_write_inode_size_to_metadata; "
"rc = [%d]\n", rc2);
if (!rc)
rc = rc2;
goto out;
}
}
}
out:
return rc;
}
/**
* ecryptfs_read_lower
* @data: The read data is stored here by this function
* @offset: Byte offset in the lower file from which to read the data
* @size: Number of bytes to read from @offset of the lower file and
* store into @data
* @ecryptfs_inode: The eCryptfs inode
*
* Read @size bytes of data at byte offset @offset from the lower
* inode into memory location @data.
*
* Returns bytes read on success; 0 on EOF; less than zero on error
*/
int ecryptfs_read_lower(char *data, loff_t offset, size_t size,
struct inode *ecryptfs_inode)
{
struct file *lower_file;
lower_file = ecryptfs_inode_to_private(ecryptfs_inode)->lower_file;
if (!lower_file)
return -EIO;
return kernel_read(lower_file, offset, data, size);
}
/**
* ecryptfs_read_lower_page_segment
* @page_for_ecryptfs: The page into which data for eCryptfs will be
* written
* @offset_in_page: Offset in @page_for_ecryptfs from which to start
* writing
* @size: The number of bytes to write into @page_for_ecryptfs
* @ecryptfs_inode: The eCryptfs inode
*
* Determines the byte offset in the file for the given page and
* offset within the page, maps the page, and makes the call to read
* the contents of @page_for_ecryptfs from the lower inode.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs,
pgoff_t page_index,
size_t offset_in_page, size_t size,
struct inode *ecryptfs_inode)
{
char *virt;
loff_t offset;
int rc;
offset = ((((loff_t)page_index) << PAGE_CACHE_SHIFT) + offset_in_page);
virt = kmap(page_for_ecryptfs);
rc = ecryptfs_read_lower(virt, offset, size, ecryptfs_inode);
if (rc > 0)
rc = 0;
kunmap(page_for_ecryptfs);
flush_dcache_page(page_for_ecryptfs);
return rc;
}

235
fs/ecryptfs/super.c Normal file
View file

@ -0,0 +1,235 @@
/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 1997-2003 Erez Zadok
* Copyright (C) 2001-2003 Stony Brook University
* Copyright (C) 2004-2006 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
* Michael C. Thompson <mcthomps@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/key.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/file.h>
#include <linux/crypto.h>
#include <linux/statfs.h>
#include <linux/magic.h>
#include "ecryptfs_kernel.h"
#ifdef CONFIG_SDP
#include "ecryptfs_dek.h"
#endif
struct kmem_cache *ecryptfs_inode_info_cache;
/**
* ecryptfs_alloc_inode - allocate an ecryptfs inode
* @sb: Pointer to the ecryptfs super block
*
* Called to bring an inode into existence.
*
* Only handle allocation, setting up structures should be done in
* ecryptfs_read_inode. This is because the kernel, between now and
* then, will 0 out the private data pointer.
*
* Returns a pointer to a newly allocated inode, NULL otherwise
*/
static struct inode *ecryptfs_alloc_inode(struct super_block *sb)
{
struct ecryptfs_inode_info *inode_info;
struct inode *inode = NULL;
inode_info = kmem_cache_alloc(ecryptfs_inode_info_cache, GFP_KERNEL);
if (unlikely(!inode_info))
goto out;
ecryptfs_init_crypt_stat(&inode_info->crypt_stat);
mutex_init(&inode_info->lower_file_mutex);
atomic_set(&inode_info->lower_file_count, 0);
inode_info->lower_file = NULL;
#ifdef CONFIG_SDP
// get userid from super block
inode_info->crypt_stat.engine_id = -1;
#endif
inode = &inode_info->vfs_inode;
out:
return inode;
}
static void ecryptfs_i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
struct ecryptfs_inode_info *inode_info;
inode_info = ecryptfs_inode_to_private(inode);
kmem_cache_free(ecryptfs_inode_info_cache, inode_info);
}
/**
* ecryptfs_destroy_inode
* @inode: The ecryptfs inode
*
* This is used during the final destruction of the inode. All
* allocation of memory related to the inode, including allocated
* memory in the crypt_stat struct, will be released here.
* There should be no chance that this deallocation will be missed.
*/
static void ecryptfs_destroy_inode(struct inode *inode)
{
struct ecryptfs_inode_info *inode_info;
inode_info = ecryptfs_inode_to_private(inode);
BUG_ON(inode_info->lower_file);
ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat);
call_rcu(&inode->i_rcu, ecryptfs_i_callback);
}
/**
* ecryptfs_statfs
* @sb: The ecryptfs super block
* @buf: The struct kstatfs to fill in with stats
*
* Get the filesystem statistics. Currently, we let this pass right through
* to the lower filesystem and take no action ourselves.
*/
static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
int rc;
if (!lower_dentry->d_sb->s_op->statfs)
return -ENOSYS;
rc = lower_dentry->d_sb->s_op->statfs(lower_dentry, buf);
if (rc)
return rc;
buf->f_type = ECRYPTFS_SUPER_MAGIC;
rc = ecryptfs_set_f_namelen(&buf->f_namelen, buf->f_namelen,
&ecryptfs_superblock_to_private(dentry->d_sb)->mount_crypt_stat);
return rc;
}
/**
* ecryptfs_evict_inode
* @inode - The ecryptfs inode
*
* Called by iput() when the inode reference count reached zero
* and the inode is not hashed anywhere. Used to clear anything
* that needs to be, before the inode is completely destroyed and put
* on the inode free list. We use this to drop out reference to the
* lower inode.
*/
static void ecryptfs_evict_inode(struct inode *inode)
{
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
iput(ecryptfs_inode_to_lower(inode));
}
/**
* ecryptfs_show_options
*
* Prints the mount options for a given superblock.
* Returns zero; does not fail.
*/
static int ecryptfs_show_options(struct seq_file *m, struct dentry *root)
{
struct super_block *sb = root->d_sb;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
&ecryptfs_superblock_to_private(sb)->mount_crypt_stat;
struct ecryptfs_global_auth_tok *walker;
mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex);
list_for_each_entry(walker,
&mount_crypt_stat->global_auth_tok_list,
mount_crypt_stat_list) {
if (walker->flags & ECRYPTFS_AUTH_TOK_FNEK)
seq_printf(m, ",ecryptfs_fnek_sig=%s", walker->sig);
else
seq_printf(m, ",ecryptfs_sig=%s", walker->sig);
}
mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex);
#ifdef CONFIG_SDP
seq_printf(m, ",userid=%d", mount_crypt_stat->userid);
if (mount_crypt_stat->flags & ECRYPTFS_MOUNT_SDP_ENABLED){
seq_printf(m, ",sdp_enabled");
}
if (mount_crypt_stat->partition_id >= 0){
seq_printf(m, ",partition_id=%d", mount_crypt_stat->partition_id);
}
#endif
#ifdef CONFIG_DLP
if (mount_crypt_stat->flags & ECRYPTFS_MOUNT_DLP_ENABLED){
seq_printf(m, ",dlp_enabled");
}
#endif
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
if (mount_crypt_stat->cipher_code == RFC2440_CIPHER_AES_XTS_256)
seq_printf(m, ",ecryptfs_cipher=%s",
"aesxts");
else
#endif
seq_printf(m, ",ecryptfs_cipher=%s",
mount_crypt_stat->global_default_cipher_name);
if (mount_crypt_stat->global_default_cipher_key_size)
seq_printf(m, ",ecryptfs_key_bytes=%zd",
mount_crypt_stat->global_default_cipher_key_size);
#ifdef CONFIG_WTL_ENCRYPTION_FILTER
if (mount_crypt_stat->flags & ECRYPTFS_ENABLE_FILTERING)
seq_printf(m, ",ecryptfs_enable_filtering");
#endif
#ifdef CONFIG_CRYPTO_FIPS
if (mount_crypt_stat->flags & ECRYPTFS_ENABLE_CC)
seq_printf(m, ",ecryptfs_enable_cc");
#endif
if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)
seq_printf(m, ",ecryptfs_passthrough");
if (mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED)
seq_printf(m, ",ecryptfs_xattr_metadata");
if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)
seq_printf(m, ",ecryptfs_encrypted_view");
if (mount_crypt_stat->flags & ECRYPTFS_UNLINK_SIGS)
seq_printf(m, ",ecryptfs_unlink_sigs");
if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY)
seq_printf(m, ",ecryptfs_mount_auth_tok_only");
#if defined(CONFIG_MMC_DW_FMP_ECRYPT_FS) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
if (mount_crypt_stat->flags & ECRYPTFS_USE_FMP)
seq_printf(m, ",ecryptfs_use_fmp");
#endif
return 0;
}
const struct super_operations ecryptfs_sops = {
.alloc_inode = ecryptfs_alloc_inode,
.destroy_inode = ecryptfs_destroy_inode,
.statfs = ecryptfs_statfs,
.remount_fs = NULL,
.evict_inode = ecryptfs_evict_inode,
.show_options = ecryptfs_show_options
};