mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
48
fs/ecryptfs/Kconfig
Normal file
48
fs/ecryptfs/Kconfig
Normal 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
11
fs/ecryptfs/Makefile
Normal 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
2610
fs/ecryptfs/crypto.c
Normal file
File diff suppressed because it is too large
Load diff
126
fs/ecryptfs/debug.c
Normal file
126
fs/ecryptfs/debug.c
Normal 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
92
fs/ecryptfs/dentry.c
Normal 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
774
fs/ecryptfs/ecryptfs_dek.c
Executable 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
84
fs/ecryptfs/ecryptfs_dek.h
Executable 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
38
fs/ecryptfs/ecryptfs_dlp.h
Executable 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 */
|
847
fs/ecryptfs/ecryptfs_kernel.h
Normal file
847
fs/ecryptfs/ecryptfs_kernel.h
Normal 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 */
|
183
fs/ecryptfs/ecryptfs_sdp_chamber.c
Executable file
183
fs/ecryptfs/ecryptfs_sdp_chamber.c
Executable 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;
|
||||
}
|
39
fs/ecryptfs/ecryptfs_sdp_chamber.h
Executable file
39
fs/ecryptfs/ecryptfs_sdp_chamber.h
Executable 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
897
fs/ecryptfs/file.c
Normal 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
1609
fs/ecryptfs/inode.c
Normal file
File diff suppressed because it is too large
Load diff
2680
fs/ecryptfs/keystore.c
Normal file
2680
fs/ecryptfs/keystore.c
Normal file
File diff suppressed because it is too large
Load diff
172
fs/ecryptfs/kthread.c
Normal file
172
fs/ecryptfs/kthread.c
Normal 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
1196
fs/ecryptfs/main.c
Normal file
File diff suppressed because it is too large
Load diff
468
fs/ecryptfs/messaging.c
Normal file
468
fs/ecryptfs/messaging.c
Normal 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
511
fs/ecryptfs/miscdev.c
Normal 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
349
fs/ecryptfs/mm.c
Executable 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
28
fs/ecryptfs/mm.h
Executable 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
590
fs/ecryptfs/mmap.c
Normal 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
273
fs/ecryptfs/read_write.c
Normal 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
235
fs/ecryptfs/super.c
Normal 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
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue