Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
7
fs/ecryptfs/Makefile
Normal file
7
fs/ecryptfs/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for the Linux 2.6 eCryptfs
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o
|
||||
|
||||
ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o messaging.o netlink.o debug.o
|
||||
1831
fs/ecryptfs/crypto.c
Normal file
1831
fs/ecryptfs/crypto.c
Normal file
File diff suppressed because it is too large
Load Diff
123
fs/ecryptfs/debug.c
Normal file
123
fs/ecryptfs/debug.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* 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");
|
||||
ecryptfs_printk(KERN_DEBUG, " * (NO PRIVATE KEY SUPPORT "
|
||||
"IN ECRYPTFS VERSION 0.1)\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
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
95
fs/ecryptfs/dentry.c
Normal file
95
fs/ecryptfs/dentry.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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 "ecryptfs_kernel.h"
|
||||
|
||||
/**
|
||||
* ecryptfs_d_revalidate - revalidate an ecryptfs dentry
|
||||
* @dentry: The ecryptfs dentry
|
||||
* @nd: The associated nameidata
|
||||
*
|
||||
* 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, struct nameidata *nd)
|
||||
{
|
||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
|
||||
struct dentry *dentry_save;
|
||||
struct vfsmount *vfsmount_save;
|
||||
int rc = 1;
|
||||
|
||||
if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
|
||||
goto out;
|
||||
dentry_save = nd->dentry;
|
||||
vfsmount_save = nd->mnt;
|
||||
nd->dentry = lower_dentry;
|
||||
nd->mnt = lower_mnt;
|
||||
rc = lower_dentry->d_op->d_revalidate(lower_dentry, nd);
|
||||
nd->dentry = dentry_save;
|
||||
nd->mnt = vfsmount_save;
|
||||
if (dentry->d_inode) {
|
||||
struct inode *lower_inode =
|
||||
ecryptfs_inode_to_lower(dentry->d_inode);
|
||||
|
||||
fsstack_copy_attr_all(dentry->d_inode, lower_inode, NULL);
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct kmem_cache *ecryptfs_dentry_info_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_d_release
|
||||
* @dentry: The ecryptfs dentry
|
||||
*
|
||||
* Called when a dentry is really deallocated.
|
||||
*/
|
||||
static void ecryptfs_d_release(struct dentry *dentry)
|
||||
{
|
||||
if (ecryptfs_dentry_to_private(dentry)) {
|
||||
if (ecryptfs_dentry_to_lower(dentry)) {
|
||||
mntput(ecryptfs_dentry_to_lower_mnt(dentry));
|
||||
dput(ecryptfs_dentry_to_lower(dentry));
|
||||
}
|
||||
kmem_cache_free(ecryptfs_dentry_info_cache,
|
||||
ecryptfs_dentry_to_private(dentry));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct dentry_operations ecryptfs_dops = {
|
||||
.d_revalidate = ecryptfs_d_revalidate,
|
||||
.d_release = ecryptfs_d_release,
|
||||
};
|
||||
584
fs/ecryptfs/ecryptfs_kernel.h
Normal file
584
fs/ecryptfs/ecryptfs_kernel.h
Normal file
@@ -0,0 +1,584 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
* Kernel declarations.
|
||||
*
|
||||
* 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>
|
||||
* 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 <linux/fs.h>
|
||||
#include <linux/fs_stack.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/hash.h>
|
||||
|
||||
/* Version verification for shared data structures w/ userspace */
|
||||
#define ECRYPTFS_VERSION_MAJOR 0x00
|
||||
#define ECRYPTFS_VERSION_MINOR 0x04
|
||||
#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x02
|
||||
/* These flags indicate which features are supported by the kernel
|
||||
* module; userspace tools such as the mount helper read
|
||||
* ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
|
||||
* how to behave. */
|
||||
#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
|
||||
#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
|
||||
#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
|
||||
#define ECRYPTFS_VERSIONING_POLICY 0x00000008
|
||||
#define ECRYPTFS_VERSIONING_XATTR 0x00000010
|
||||
#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
|
||||
| ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
|
||||
| ECRYPTFS_VERSIONING_PUBKEY \
|
||||
| ECRYPTFS_VERSIONING_XATTR)
|
||||
#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
|
||||
#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
|
||||
#define ECRYPTFS_SALT_SIZE 8
|
||||
#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
|
||||
/* The original signature size is only for what is stored on disk; all
|
||||
* in-memory representations are expanded hex, so it better adapted to
|
||||
* be passed around or referenced on the command line */
|
||||
#define ECRYPTFS_SIG_SIZE 8
|
||||
#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
|
||||
#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
|
||||
#define ECRYPTFS_MAX_KEY_BYTES 64
|
||||
#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
|
||||
#define ECRYPTFS_DEFAULT_IV_BYTES 16
|
||||
#define ECRYPTFS_FILE_VERSION 0x02
|
||||
#define ECRYPTFS_DEFAULT_HEADER_EXTENT_SIZE 8192
|
||||
#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_NLMSG_HELO 100
|
||||
#define ECRYPTFS_NLMSG_QUIT 101
|
||||
#define ECRYPTFS_NLMSG_REQUEST 102
|
||||
#define ECRYPTFS_NLMSG_RESPONSE 103
|
||||
#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
|
||||
#define ECRYPTFS_DEFAULT_NUM_USERS 4
|
||||
#define ECRYPTFS_MAX_NUM_USERS 32768
|
||||
#define ECRYPTFS_TRANSPORT_NETLINK 0
|
||||
#define ECRYPTFS_TRANSPORT_CONNECTOR 1
|
||||
#define ECRYPTFS_TRANSPORT_RELAYFS 2
|
||||
#define ECRYPTFS_DEFAULT_TRANSPORT ECRYPTFS_TRANSPORT_NETLINK
|
||||
#define ECRYPTFS_XATTR_NAME "user.ecryptfs"
|
||||
|
||||
#define RFC2440_CIPHER_DES3_EDE 0x02
|
||||
#define RFC2440_CIPHER_CAST_5 0x03
|
||||
#define RFC2440_CIPHER_BLOWFISH 0x04
|
||||
#define RFC2440_CIPHER_AES_128 0x07
|
||||
#define RFC2440_CIPHER_AES_192 0x08
|
||||
#define RFC2440_CIPHER_AES_256 0x09
|
||||
#define RFC2440_CIPHER_TWOFISH 0x0a
|
||||
#define RFC2440_CIPHER_CAST_6 0x0b
|
||||
|
||||
#define RFC2440_CIPHER_RSA 0x01
|
||||
|
||||
/**
|
||||
* For convenience, we may need to pass around the encrypted session
|
||||
* key between kernel and userspace because the authentication token
|
||||
* may not be extractable. For example, the TPM may not release the
|
||||
* private key, instead requiring the encrypted data and returning the
|
||||
* decrypted data.
|
||||
*/
|
||||
struct ecryptfs_session_key {
|
||||
#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
|
||||
#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
|
||||
#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
|
||||
#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
|
||||
u32 flags;
|
||||
u32 encrypted_key_size;
|
||||
u32 decrypted_key_size;
|
||||
u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
|
||||
u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
|
||||
};
|
||||
|
||||
struct ecryptfs_password {
|
||||
u32 password_bytes;
|
||||
s32 hash_algo;
|
||||
u32 hash_iterations;
|
||||
u32 session_key_encryption_key_bytes;
|
||||
#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
|
||||
#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
|
||||
u32 flags;
|
||||
/* Iterated-hash concatenation of salt and passphrase */
|
||||
u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
|
||||
u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
|
||||
/* Always in expanded hex */
|
||||
u8 salt[ECRYPTFS_SALT_SIZE];
|
||||
};
|
||||
|
||||
enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
|
||||
|
||||
struct ecryptfs_private_key {
|
||||
u32 key_size;
|
||||
u32 data_len;
|
||||
u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
|
||||
char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
/* May be a password or a private key */
|
||||
struct ecryptfs_auth_tok {
|
||||
u16 version; /* 8-bit major and 8-bit minor */
|
||||
u16 token_type;
|
||||
u32 flags;
|
||||
struct ecryptfs_session_key session_key;
|
||||
u8 reserved[32];
|
||||
union {
|
||||
struct ecryptfs_password password;
|
||||
struct ecryptfs_private_key private_key;
|
||||
} token;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
static inline struct ecryptfs_auth_tok *
|
||||
ecryptfs_get_key_payload_data(struct key *key)
|
||||
{
|
||||
return (struct ecryptfs_auth_tok *)
|
||||
(((struct user_key_payload*)key->payload.data)->data);
|
||||
}
|
||||
|
||||
#define ECRYPTFS_SUPER_MAGIC 0xf15f
|
||||
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
|
||||
#define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32
|
||||
#define ECRYPTFS_MAX_NUM_ENC_KEYS 64
|
||||
#define ECRYPTFS_MAX_NUM_KEYSIGS 2 /* TODO: Make this a linked list */
|
||||
#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 8
|
||||
#define ECRYPTFS_DEFAULT_CIPHER "aes"
|
||||
#define ECRYPTFS_DEFAULT_KEY_BYTES 16
|
||||
#define ECRYPTFS_DEFAULT_HASH "md5"
|
||||
#define ECRYPTFS_TAG_1_PACKET_TYPE 0x01
|
||||
#define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C
|
||||
#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 MD5_DIGEST_SIZE 16
|
||||
|
||||
/**
|
||||
* 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_NEW_FILE 0x00000004
|
||||
#define ECRYPTFS_ENCRYPTED 0x00000008
|
||||
#define ECRYPTFS_SECURITY_WARNING 0x00000010
|
||||
#define ECRYPTFS_ENABLE_HMAC 0x00000020
|
||||
#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040
|
||||
#define ECRYPTFS_KEY_VALID 0x00000080
|
||||
#define ECRYPTFS_METADATA_IN_XATTR 0x00000100
|
||||
#define ECRYPTFS_VIEW_AS_ENCRYPTED 0x00000200
|
||||
u32 flags;
|
||||
unsigned int file_version;
|
||||
size_t iv_bytes;
|
||||
size_t num_keysigs;
|
||||
size_t header_extent_size;
|
||||
size_t num_header_extents_at_front;
|
||||
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_blkcipher *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];
|
||||
unsigned char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX];
|
||||
struct mutex cs_tfm_mutex;
|
||||
struct mutex cs_hash_tfm_mutex;
|
||||
struct mutex cs_mutex;
|
||||
};
|
||||
|
||||
/* inode private data. */
|
||||
struct ecryptfs_inode_info {
|
||||
struct inode vfs_inode;
|
||||
struct inode *wii_inode;
|
||||
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;
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
u32 flags;
|
||||
struct ecryptfs_auth_tok *global_auth_tok;
|
||||
struct key *global_auth_tok_key;
|
||||
size_t global_default_cipher_key_size;
|
||||
struct crypto_blkcipher *global_key_tfm;
|
||||
struct mutex global_key_tfm_mutex;
|
||||
unsigned char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE
|
||||
+ 1];
|
||||
unsigned char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
|
||||
};
|
||||
|
||||
/* superblock private data. */
|
||||
struct ecryptfs_sb_info {
|
||||
struct super_block *wsi_sb;
|
||||
struct ecryptfs_mount_crypt_stat mount_crypt_stat;
|
||||
};
|
||||
|
||||
/* 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 {
|
||||
u32 index;
|
||||
u32 data_len;
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
struct ecryptfs_msg_ctx {
|
||||
#define ECRYPTFS_MSG_CTX_STATE_FREE 0x0001
|
||||
#define ECRYPTFS_MSG_CTX_STATE_PENDING 0x0002
|
||||
#define ECRYPTFS_MSG_CTX_STATE_DONE 0x0003
|
||||
u32 state;
|
||||
unsigned int index;
|
||||
unsigned int counter;
|
||||
struct ecryptfs_message *msg;
|
||||
struct task_struct *task;
|
||||
struct list_head node;
|
||||
struct mutex mux;
|
||||
};
|
||||
|
||||
extern unsigned int ecryptfs_transport;
|
||||
|
||||
struct ecryptfs_daemon_id {
|
||||
pid_t pid;
|
||||
uid_t uid;
|
||||
struct hlist_node id_chain;
|
||||
};
|
||||
|
||||
static inline struct ecryptfs_file_info *
|
||||
ecryptfs_file_to_private(struct file *file)
|
||||
{
|
||||
return (struct ecryptfs_file_info *)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 void
|
||||
ecryptfs_set_dentry_lower(struct dentry *dentry, struct dentry *lower_dentry)
|
||||
{
|
||||
((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path.dentry =
|
||||
lower_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 void
|
||||
ecryptfs_set_dentry_lower_mnt(struct dentry *dentry, struct vfsmount *lower_mnt)
|
||||
{
|
||||
((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path.mnt =
|
||||
lower_mnt;
|
||||
}
|
||||
|
||||
#define ecryptfs_printk(type, fmt, arg...) \
|
||||
__ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg);
|
||||
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 struct dentry_operations ecryptfs_dops;
|
||||
extern 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_0;
|
||||
extern struct kmem_cache *ecryptfs_header_cache_1;
|
||||
extern struct kmem_cache *ecryptfs_header_cache_2;
|
||||
extern struct kmem_cache *ecryptfs_xattr_cache;
|
||||
extern struct kmem_cache *ecryptfs_lower_page_cache;
|
||||
extern struct kmem_cache *ecryptfs_key_record_cache;
|
||||
|
||||
int ecryptfs_interpose(struct dentry *hidden_dentry,
|
||||
struct dentry *this_dentry, struct super_block *sb,
|
||||
int flag);
|
||||
int ecryptfs_fill_zeros(struct file *file, loff_t new_length);
|
||||
int ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat,
|
||||
const char *name, int length,
|
||||
char **decrypted_name);
|
||||
int ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat,
|
||||
const char *name, int length,
|
||||
char **encoded_name);
|
||||
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_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
void ecryptfs_destruct_mount_crypt_stat(
|
||||
struct ecryptfs_mount_crypt_stat *mount_crypt_stat);
|
||||
int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
int ecryptfs_crypto_api_algify_cipher_name(char **algified_name,
|
||||
char *cipher_name,
|
||||
char *chaining_modifier);
|
||||
#define ECRYPTFS_LOWER_I_MUTEX_NOT_HELD 0
|
||||
#define ECRYPTFS_LOWER_I_MUTEX_HELD 1
|
||||
int ecryptfs_write_inode_size_to_metadata(struct file *lower_file,
|
||||
struct inode *lower_inode,
|
||||
struct inode *inode,
|
||||
struct dentry *ecryptfs_dentry,
|
||||
int lower_i_mutex_held);
|
||||
int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
|
||||
struct file *lower_file,
|
||||
unsigned long lower_page_index, int byte_offset,
|
||||
int region_bytes);
|
||||
int
|
||||
ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode,
|
||||
struct file *lower_file, int byte_offset,
|
||||
int region_size);
|
||||
int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode,
|
||||
struct file *lower_file);
|
||||
int ecryptfs_do_readpage(struct file *file, struct page *page,
|
||||
pgoff_t lower_page_index);
|
||||
int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
|
||||
struct inode *lower_inode,
|
||||
struct writeback_control *wbc);
|
||||
int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx);
|
||||
int ecryptfs_decrypt_page(struct file *file, struct page *page);
|
||||
int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry,
|
||||
struct file *lower_file);
|
||||
int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry,
|
||||
struct file *lower_file);
|
||||
int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry);
|
||||
int ecryptfs_read_and_validate_header_region(char *data, struct dentry *dentry,
|
||||
struct vfsmount *mnt);
|
||||
int ecryptfs_read_and_validate_xattr_region(char *page_virt,
|
||||
struct dentry *ecryptfs_dentry);
|
||||
u16 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
int ecryptfs_cipher_code_to_string(char *str, u16 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 process_request_key_err(long err_code);
|
||||
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);
|
||||
int
|
||||
ecryptfs_process_cipher(struct crypto_blkcipher **key_tfm, char *cipher_name,
|
||||
size_t *key_size);
|
||||
int ecryptfs_inode_test(struct inode *inode, void *candidate_lower_inode);
|
||||
int ecryptfs_inode_set(struct inode *inode, void *lower_inode);
|
||||
void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode);
|
||||
int ecryptfs_open_lower_file(struct file **lower_file,
|
||||
struct dentry *lower_dentry,
|
||||
struct vfsmount *lower_mnt, int flags);
|
||||
int ecryptfs_close_lower_file(struct file *lower_file);
|
||||
ssize_t ecryptfs_getxattr(struct dentry *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 dentry *ecryptfs_dentry);
|
||||
int ecryptfs_process_helo(unsigned int transport, uid_t uid, pid_t pid);
|
||||
int ecryptfs_process_quit(uid_t uid, pid_t pid);
|
||||
int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t uid,
|
||||
pid_t pid, u32 seq);
|
||||
int ecryptfs_send_message(unsigned int transport, 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(unsigned int transport);
|
||||
void ecryptfs_release_messaging(unsigned int transport);
|
||||
|
||||
int ecryptfs_send_netlink(char *data, int data_len,
|
||||
struct ecryptfs_msg_ctx *msg_ctx, u16 msg_type,
|
||||
u16 msg_flags, pid_t daemon_pid);
|
||||
int ecryptfs_init_netlink(void);
|
||||
void ecryptfs_release_netlink(void);
|
||||
|
||||
int ecryptfs_send_connector(char *data, int data_len,
|
||||
struct ecryptfs_msg_ctx *msg_ctx, u16 msg_type,
|
||||
u16 msg_flags, pid_t daemon_pid);
|
||||
int ecryptfs_init_connector(void);
|
||||
void ecryptfs_release_connector(void);
|
||||
void
|
||||
ecryptfs_write_header_metadata(char *virt,
|
||||
struct ecryptfs_crypt_stat *crypt_stat,
|
||||
size_t *written);
|
||||
|
||||
#endif /* #ifndef ECRYPTFS_KERNEL_H */
|
||||
460
fs/ecryptfs/file.c
Normal file
460
fs/ecryptfs/file.c
Normal file
@@ -0,0 +1,460 @@
|
||||
/**
|
||||
* 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/mount.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/fs_stack.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
/**
|
||||
* ecryptfs_llseek
|
||||
* @file: File we are seeking in
|
||||
* @offset: The offset to seek to
|
||||
* @origin: 2 - offset from i_size; 1 - offset from f_pos
|
||||
*
|
||||
* Returns the position we have seeked to, or negative on error
|
||||
*/
|
||||
static loff_t ecryptfs_llseek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
loff_t rv;
|
||||
loff_t new_end_pos;
|
||||
int rc;
|
||||
int expanding_file = 0;
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
|
||||
/* If our offset is past the end of our file, we're going to
|
||||
* need to grow it so we have a valid length of 0's */
|
||||
new_end_pos = offset;
|
||||
switch (origin) {
|
||||
case 2:
|
||||
new_end_pos += i_size_read(inode);
|
||||
expanding_file = 1;
|
||||
break;
|
||||
case 1:
|
||||
new_end_pos += file->f_pos;
|
||||
if (new_end_pos > i_size_read(inode)) {
|
||||
ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) "
|
||||
"> i_size_read(inode)(=[0x%.16x])\n",
|
||||
new_end_pos, i_size_read(inode));
|
||||
expanding_file = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (new_end_pos > i_size_read(inode)) {
|
||||
ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) "
|
||||
"> i_size_read(inode)(=[0x%.16x])\n",
|
||||
new_end_pos, i_size_read(inode));
|
||||
expanding_file = 1;
|
||||
}
|
||||
}
|
||||
ecryptfs_printk(KERN_DEBUG, "new_end_pos = [0x%.16x]\n", new_end_pos);
|
||||
if (expanding_file) {
|
||||
rc = ecryptfs_truncate(file->f_path.dentry, new_end_pos);
|
||||
if (rc) {
|
||||
rv = rc;
|
||||
ecryptfs_printk(KERN_ERR, "Error on attempt to "
|
||||
"truncate to (higher) offset [0x%.16x];"
|
||||
" rc = [%d]\n", new_end_pos, rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rv = generic_file_llseek(file, offset, origin);
|
||||
out:
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
const struct iovec *iov,
|
||||
unsigned long nr_segs, loff_t pos)
|
||||
{
|
||||
int rc;
|
||||
struct dentry *lower_dentry;
|
||||
struct vfsmount *lower_vfsmount;
|
||||
struct file *file = iocb->ki_filp;
|
||||
|
||||
rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
|
||||
/*
|
||||
* 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) {
|
||||
lower_dentry = ecryptfs_dentry_to_lower(file->f_path.dentry);
|
||||
lower_vfsmount = ecryptfs_dentry_to_lower_mnt(file->f_path.dentry);
|
||||
touch_atime(lower_vfsmount, lower_dentry);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct ecryptfs_getdents_callback {
|
||||
void *dirent;
|
||||
struct dentry *dentry;
|
||||
filldir_t filldir;
|
||||
int err;
|
||||
int filldir_called;
|
||||
int entries_written;
|
||||
};
|
||||
|
||||
/* Inspired by generic filldir in fs/readir.c */
|
||||
static int
|
||||
ecryptfs_filldir(void *dirent, const char *name, int namelen, loff_t offset,
|
||||
u64 ino, unsigned int d_type)
|
||||
{
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
struct ecryptfs_getdents_callback *buf =
|
||||
(struct ecryptfs_getdents_callback *)dirent;
|
||||
int rc;
|
||||
int decoded_length;
|
||||
char *decoded_name;
|
||||
|
||||
crypt_stat = ecryptfs_dentry_to_private(buf->dentry)->crypt_stat;
|
||||
buf->filldir_called++;
|
||||
decoded_length = ecryptfs_decode_filename(crypt_stat, name, namelen,
|
||||
&decoded_name);
|
||||
if (decoded_length < 0) {
|
||||
rc = decoded_length;
|
||||
goto out;
|
||||
}
|
||||
rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset,
|
||||
ino, d_type);
|
||||
kfree(decoded_name);
|
||||
if (rc >= 0)
|
||||
buf->entries_written++;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_readdir
|
||||
* @file: The ecryptfs file struct
|
||||
* @dirent: Directory entry
|
||||
* @filldir: The filldir callback function
|
||||
*/
|
||||
static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir)
|
||||
{
|
||||
int rc;
|
||||
struct file *lower_file;
|
||||
struct inode *inode;
|
||||
struct ecryptfs_getdents_callback buf;
|
||||
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
lower_file->f_pos = file->f_pos;
|
||||
inode = file->f_path.dentry->d_inode;
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.dirent = dirent;
|
||||
buf.dentry = file->f_path.dentry;
|
||||
buf.filldir = filldir;
|
||||
retry:
|
||||
buf.filldir_called = 0;
|
||||
buf.entries_written = 0;
|
||||
buf.err = 0;
|
||||
rc = vfs_readdir(lower_file, ecryptfs_filldir, (void *)&buf);
|
||||
if (buf.err)
|
||||
rc = buf.err;
|
||||
if (buf.filldir_called && !buf.entries_written)
|
||||
goto retry;
|
||||
file->f_pos = lower_file->f_pos;
|
||||
if (rc >= 0)
|
||||
fsstack_copy_attr_atime(inode, lower_file->f_path.dentry->d_inode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct kmem_cache *ecryptfs_file_info_cache;
|
||||
|
||||
int ecryptfs_open_lower_file(struct file **lower_file,
|
||||
struct dentry *lower_dentry,
|
||||
struct vfsmount *lower_mnt, int flags)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
flags |= O_LARGEFILE;
|
||||
dget(lower_dentry);
|
||||
mntget(lower_mnt);
|
||||
*lower_file = dentry_open(lower_dentry, lower_mnt, flags);
|
||||
if (IS_ERR(*lower_file)) {
|
||||
printk(KERN_ERR "Error opening lower file for lower_dentry "
|
||||
"[0x%p], lower_mnt [0x%p], and flags [0x%x]\n",
|
||||
lower_dentry, lower_mnt, flags);
|
||||
rc = PTR_ERR(*lower_file);
|
||||
*lower_file = NULL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ecryptfs_close_lower_file(struct file *lower_file)
|
||||
{
|
||||
fput(lower_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ecryptfs_mount_crypt_stat *mount_crypt_stat;
|
||||
struct dentry *ecryptfs_dentry = file->f_path.dentry;
|
||||
/* Private value of ecryptfs_dentry allocated in
|
||||
* ecryptfs_lookup() */
|
||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
|
||||
struct inode *lower_inode = NULL;
|
||||
struct file *lower_file = NULL;
|
||||
struct vfsmount *lower_mnt;
|
||||
struct ecryptfs_file_info *file_info;
|
||||
int lower_flags;
|
||||
|
||||
mount_crypt_stat = &ecryptfs_superblock_to_private(
|
||||
ecryptfs_dentry->d_sb)->mount_crypt_stat;
|
||||
if ((mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)
|
||||
&& ((file->f_flags & O_WRONLY) || (file->f_flags & O_RDWR)
|
||||
|| (file->f_flags & O_CREAT) || (file->f_flags & O_TRUNC)
|
||||
|| (file->f_flags & O_APPEND))) {
|
||||
printk(KERN_WARNING "Mount has encrypted view enabled; "
|
||||
"files may only be read\n");
|
||||
rc = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
|
||||
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;
|
||||
crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
|
||||
}
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
lower_flags = file->f_flags;
|
||||
if ((lower_flags & O_ACCMODE) == O_WRONLY)
|
||||
lower_flags = (lower_flags & O_ACCMODE) | O_RDWR;
|
||||
if (file->f_flags & O_APPEND)
|
||||
lower_flags &= ~O_APPEND;
|
||||
lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry);
|
||||
/* Corresponding fput() in ecryptfs_release() */
|
||||
if ((rc = ecryptfs_open_lower_file(&lower_file, lower_dentry, lower_mnt,
|
||||
lower_flags))) {
|
||||
ecryptfs_printk(KERN_ERR, "Error opening lower file\n");
|
||||
goto out_puts;
|
||||
}
|
||||
ecryptfs_set_file_lower(file, lower_file);
|
||||
/* Isn't this check the same as the one in lookup? */
|
||||
lower_inode = lower_dentry->d_inode;
|
||||
if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
|
||||
ecryptfs_printk(KERN_DEBUG, "This is a directory\n");
|
||||
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
mutex_lock(&crypt_stat->cs_mutex);
|
||||
if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)
|
||||
|| !(crypt_stat->flags & ECRYPTFS_KEY_VALID)) {
|
||||
rc = ecryptfs_read_metadata(ecryptfs_dentry, lower_file);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"Valid headers not found\n");
|
||||
if (!(mount_crypt_stat->flags
|
||||
& ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) {
|
||||
rc = -EIO;
|
||||
printk(KERN_WARNING "Attempt to read file that "
|
||||
"is not in a valid eCryptfs format, "
|
||||
"and plaintext passthrough mode is not "
|
||||
"enabled; returning -EIO\n");
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
goto out_puts;
|
||||
}
|
||||
rc = 0;
|
||||
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
|
||||
"size: [0x%.16x]\n", inode, inode->i_ino,
|
||||
i_size_read(inode));
|
||||
ecryptfs_set_file_lower(file, lower_file);
|
||||
goto out;
|
||||
out_puts:
|
||||
mntput(lower_mnt);
|
||||
dput(lower_dentry);
|
||||
kmem_cache_free(ecryptfs_file_info_cache,
|
||||
ecryptfs_file_to_private(file));
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_flush(struct file *file, fl_owner_t td)
|
||||
{
|
||||
int rc = 0;
|
||||
struct file *lower_file = NULL;
|
||||
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
if (lower_file->f_op && lower_file->f_op->flush)
|
||||
rc = lower_file->f_op->flush(lower_file, td);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct file *lower_file = ecryptfs_file_to_lower(file);
|
||||
struct ecryptfs_file_info *file_info = ecryptfs_file_to_private(file);
|
||||
struct inode *lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
int rc;
|
||||
|
||||
if ((rc = ecryptfs_close_lower_file(lower_file))) {
|
||||
printk(KERN_ERR "Error closing lower_file\n");
|
||||
goto out;
|
||||
}
|
||||
inode->i_blocks = lower_inode->i_blocks;
|
||||
kmem_cache_free(ecryptfs_file_info_cache, file_info);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync)
|
||||
{
|
||||
struct file *lower_file = ecryptfs_file_to_lower(file);
|
||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
struct inode *lower_inode = lower_dentry->d_inode;
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (lower_inode->i_fop->fsync) {
|
||||
mutex_lock(&lower_inode->i_mutex);
|
||||
rc = lower_inode->i_fop->fsync(lower_file, lower_dentry,
|
||||
datasync);
|
||||
mutex_unlock(&lower_inode->i_mutex);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
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 && lower_file->f_op->fasync)
|
||||
rc = lower_file->f_op->fasync(fd, lower_file, flag);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t ecryptfs_sendfile(struct file *file, loff_t * ppos,
|
||||
size_t count, read_actor_t actor, void *target)
|
||||
{
|
||||
struct file *lower_file = NULL;
|
||||
int rc = -EINVAL;
|
||||
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
if (lower_file->f_op && lower_file->f_op->sendfile)
|
||||
rc = lower_file->f_op->sendfile(lower_file, ppos, count,
|
||||
actor, target);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
|
||||
const struct file_operations ecryptfs_dir_fops = {
|
||||
.readdir = ecryptfs_readdir,
|
||||
.ioctl = ecryptfs_ioctl,
|
||||
.mmap = generic_file_mmap,
|
||||
.open = ecryptfs_open,
|
||||
.flush = ecryptfs_flush,
|
||||
.release = ecryptfs_release,
|
||||
.fsync = ecryptfs_fsync,
|
||||
.fasync = ecryptfs_fasync,
|
||||
.sendfile = ecryptfs_sendfile,
|
||||
};
|
||||
|
||||
const struct file_operations ecryptfs_main_fops = {
|
||||
.llseek = ecryptfs_llseek,
|
||||
.read = do_sync_read,
|
||||
.aio_read = ecryptfs_read_update_atime,
|
||||
.write = do_sync_write,
|
||||
.aio_write = generic_file_aio_write,
|
||||
.readdir = ecryptfs_readdir,
|
||||
.ioctl = ecryptfs_ioctl,
|
||||
.mmap = generic_file_mmap,
|
||||
.open = ecryptfs_open,
|
||||
.flush = ecryptfs_flush,
|
||||
.release = ecryptfs_release,
|
||||
.fsync = ecryptfs_fsync,
|
||||
.fasync = ecryptfs_fasync,
|
||||
.sendfile = ecryptfs_sendfile,
|
||||
};
|
||||
|
||||
static int
|
||||
ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct file *lower_file = NULL;
|
||||
|
||||
if (ecryptfs_file_to_private(file))
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
if (lower_file && lower_file->f_op && lower_file->f_op->ioctl)
|
||||
rc = lower_file->f_op->ioctl(ecryptfs_inode_to_lower(inode),
|
||||
lower_file, cmd, arg);
|
||||
else
|
||||
rc = -ENOTTY;
|
||||
return rc;
|
||||
}
|
||||
1026
fs/ecryptfs/inode.c
Normal file
1026
fs/ecryptfs/inode.c
Normal file
File diff suppressed because it is too large
Load Diff
1734
fs/ecryptfs/keystore.c
Normal file
1734
fs/ecryptfs/keystore.c
Normal file
File diff suppressed because it is too large
Load Diff
881
fs/ecryptfs/main.c
Normal file
881
fs/ecryptfs/main.c
Normal file
@@ -0,0 +1,881 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
*
|
||||
* 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>
|
||||
* Michael C. Thompson <mcthomps@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 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/file.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/fs_stack.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
/**
|
||||
* Module parameter that defines the ecryptfs_verbosity level.
|
||||
*/
|
||||
int ecryptfs_verbosity = 0;
|
||||
|
||||
module_param(ecryptfs_verbosity, int, 0);
|
||||
MODULE_PARM_DESC(ecryptfs_verbosity,
|
||||
"Initial verbosity level (0 or 1; defaults to "
|
||||
"0, which is Quiet)");
|
||||
|
||||
/**
|
||||
* Module parameter that defines the number of netlink message buffer
|
||||
* elements
|
||||
*/
|
||||
unsigned int ecryptfs_message_buf_len = ECRYPTFS_DEFAULT_MSG_CTX_ELEMS;
|
||||
|
||||
module_param(ecryptfs_message_buf_len, uint, 0);
|
||||
MODULE_PARM_DESC(ecryptfs_message_buf_len,
|
||||
"Number of message buffer elements");
|
||||
|
||||
/**
|
||||
* Module parameter that defines the maximum guaranteed amount of time to wait
|
||||
* for a response through netlink. The actual sleep time will be, more than
|
||||
* likely, a small amount greater than this specified value, but only less if
|
||||
* the netlink message successfully arrives.
|
||||
*/
|
||||
signed long ecryptfs_message_wait_timeout = ECRYPTFS_MAX_MSG_CTX_TTL / HZ;
|
||||
|
||||
module_param(ecryptfs_message_wait_timeout, long, 0);
|
||||
MODULE_PARM_DESC(ecryptfs_message_wait_timeout,
|
||||
"Maximum number of seconds that an operation will "
|
||||
"sleep while waiting for a message response from "
|
||||
"userspace");
|
||||
|
||||
/**
|
||||
* Module parameter that is an estimate of the maximum number of users
|
||||
* that will be concurrently using eCryptfs. Set this to the right
|
||||
* value to balance performance and memory use.
|
||||
*/
|
||||
unsigned int ecryptfs_number_of_users = ECRYPTFS_DEFAULT_NUM_USERS;
|
||||
|
||||
module_param(ecryptfs_number_of_users, uint, 0);
|
||||
MODULE_PARM_DESC(ecryptfs_number_of_users, "An estimate of the number of "
|
||||
"concurrent users of eCryptfs");
|
||||
|
||||
unsigned int ecryptfs_transport = ECRYPTFS_DEFAULT_TRANSPORT;
|
||||
|
||||
void __ecryptfs_printk(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if (fmt[1] == '7') { /* KERN_DEBUG */
|
||||
if (ecryptfs_verbosity >= 1)
|
||||
vprintk(fmt, args);
|
||||
} else
|
||||
vprintk(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_interpose
|
||||
* @lower_dentry: Existing dentry in the lower filesystem
|
||||
* @dentry: ecryptfs' dentry
|
||||
* @sb: ecryptfs's super_block
|
||||
* @flag: If set to true, then d_add is called, else d_instantiate is called
|
||||
*
|
||||
* Interposes upper and lower dentries.
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
|
||||
struct super_block *sb, int flag)
|
||||
{
|
||||
struct inode *lower_inode;
|
||||
struct inode *inode;
|
||||
int rc = 0;
|
||||
|
||||
lower_inode = lower_dentry->d_inode;
|
||||
if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) {
|
||||
rc = -EXDEV;
|
||||
goto out;
|
||||
}
|
||||
if (!igrab(lower_inode)) {
|
||||
rc = -ESTALE;
|
||||
goto out;
|
||||
}
|
||||
inode = iget5_locked(sb, (unsigned long)lower_inode,
|
||||
ecryptfs_inode_test, ecryptfs_inode_set,
|
||||
lower_inode);
|
||||
if (!inode) {
|
||||
rc = -EACCES;
|
||||
iput(lower_inode);
|
||||
goto out;
|
||||
}
|
||||
if (inode->i_state & I_NEW)
|
||||
unlock_new_inode(inode);
|
||||
else
|
||||
iput(lower_inode);
|
||||
if (S_ISLNK(lower_inode->i_mode))
|
||||
inode->i_op = &ecryptfs_symlink_iops;
|
||||
else if (S_ISDIR(lower_inode->i_mode))
|
||||
inode->i_op = &ecryptfs_dir_iops;
|
||||
if (S_ISDIR(lower_inode->i_mode))
|
||||
inode->i_fop = &ecryptfs_dir_fops;
|
||||
if (special_file(lower_inode->i_mode))
|
||||
init_special_inode(inode, lower_inode->i_mode,
|
||||
lower_inode->i_rdev);
|
||||
dentry->d_op = &ecryptfs_dops;
|
||||
if (flag)
|
||||
d_add(dentry, inode);
|
||||
else
|
||||
d_instantiate(dentry, inode);
|
||||
fsstack_copy_attr_all(inode, lower_inode, NULL);
|
||||
/* This size will be overwritten for real files w/ headers and
|
||||
* other metadata */
|
||||
fsstack_copy_inode_size(inode, lower_inode);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_debug,
|
||||
ecryptfs_opt_ecryptfs_debug, ecryptfs_opt_cipher,
|
||||
ecryptfs_opt_ecryptfs_cipher, ecryptfs_opt_ecryptfs_key_bytes,
|
||||
ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata,
|
||||
ecryptfs_opt_encrypted_view, ecryptfs_opt_err };
|
||||
|
||||
static match_table_t tokens = {
|
||||
{ecryptfs_opt_sig, "sig=%s"},
|
||||
{ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"},
|
||||
{ecryptfs_opt_debug, "debug=%u"},
|
||||
{ecryptfs_opt_ecryptfs_debug, "ecryptfs_debug=%u"},
|
||||
{ecryptfs_opt_cipher, "cipher=%s"},
|
||||
{ecryptfs_opt_ecryptfs_cipher, "ecryptfs_cipher=%s"},
|
||||
{ecryptfs_opt_ecryptfs_key_bytes, "ecryptfs_key_bytes=%u"},
|
||||
{ecryptfs_opt_passthrough, "ecryptfs_passthrough"},
|
||||
{ecryptfs_opt_xattr_metadata, "ecryptfs_xattr_metadata"},
|
||||
{ecryptfs_opt_encrypted_view, "ecryptfs_encrypted_view"},
|
||||
{ecryptfs_opt_err, NULL}
|
||||
};
|
||||
|
||||
/**
|
||||
* ecryptfs_verify_version
|
||||
* @version: The version number to confirm
|
||||
*
|
||||
* Returns zero on good version; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_verify_version(u16 version)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
|
||||
major = ((version >> 8) & 0xFF);
|
||||
minor = (version & 0xFF);
|
||||
if (major != ECRYPTFS_VERSION_MAJOR) {
|
||||
ecryptfs_printk(KERN_ERR, "Major version number mismatch. "
|
||||
"Expected [%d]; got [%d]\n",
|
||||
ECRYPTFS_VERSION_MAJOR, major);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (minor != ECRYPTFS_VERSION_MINOR) {
|
||||
ecryptfs_printk(KERN_ERR, "Minor version number mismatch. "
|
||||
"Expected [%d]; got [%d]\n",
|
||||
ECRYPTFS_VERSION_MINOR, minor);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_parse_options
|
||||
* @sb: The ecryptfs super block
|
||||
* @options: The options pased to the kernel
|
||||
*
|
||||
* Parse mount options:
|
||||
* debug=N - ecryptfs_verbosity level for debug output
|
||||
* sig=XXX - description(signature) of the key to use
|
||||
*
|
||||
* Returns the dentry object of the lower-level (lower/interposed)
|
||||
* directory; We want to mount our stackable file system on top of
|
||||
* that lower directory.
|
||||
*
|
||||
* The signature of the key to use must be the description of a key
|
||||
* already in the keyring. Mounting will fail if the key can not be
|
||||
* found.
|
||||
*
|
||||
* Returns zero on success; non-zero on error
|
||||
*/
|
||||
static int ecryptfs_parse_options(struct super_block *sb, char *options)
|
||||
{
|
||||
char *p;
|
||||
int rc = 0;
|
||||
int sig_set = 0;
|
||||
int cipher_name_set = 0;
|
||||
int cipher_key_bytes;
|
||||
int cipher_key_bytes_set = 0;
|
||||
struct key *auth_tok_key = NULL;
|
||||
struct ecryptfs_auth_tok *auth_tok = NULL;
|
||||
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
|
||||
&ecryptfs_superblock_to_private(sb)->mount_crypt_stat;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int token;
|
||||
char *sig_src;
|
||||
char *sig_dst;
|
||||
char *debug_src;
|
||||
char *cipher_name_dst;
|
||||
char *cipher_name_src;
|
||||
char *cipher_key_bytes_src;
|
||||
int cipher_name_len;
|
||||
|
||||
if (!options) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
if (!*p)
|
||||
continue;
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case ecryptfs_opt_sig:
|
||||
case ecryptfs_opt_ecryptfs_sig:
|
||||
sig_src = args[0].from;
|
||||
sig_dst =
|
||||
mount_crypt_stat->global_auth_tok_sig;
|
||||
memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX);
|
||||
sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0';
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"The mount_crypt_stat "
|
||||
"global_auth_tok_sig set to: "
|
||||
"[%s]\n", sig_dst);
|
||||
sig_set = 1;
|
||||
break;
|
||||
case ecryptfs_opt_debug:
|
||||
case ecryptfs_opt_ecryptfs_debug:
|
||||
debug_src = args[0].from;
|
||||
ecryptfs_verbosity =
|
||||
(int)simple_strtol(debug_src, &debug_src,
|
||||
0);
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"Verbosity set to [%d]" "\n",
|
||||
ecryptfs_verbosity);
|
||||
break;
|
||||
case ecryptfs_opt_cipher:
|
||||
case ecryptfs_opt_ecryptfs_cipher:
|
||||
cipher_name_src = args[0].from;
|
||||
cipher_name_dst =
|
||||
mount_crypt_stat->
|
||||
global_default_cipher_name;
|
||||
strncpy(cipher_name_dst, cipher_name_src,
|
||||
ECRYPTFS_MAX_CIPHER_NAME_SIZE);
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"The mount_crypt_stat "
|
||||
"global_default_cipher_name set to: "
|
||||
"[%s]\n", cipher_name_dst);
|
||||
cipher_name_set = 1;
|
||||
break;
|
||||
case ecryptfs_opt_ecryptfs_key_bytes:
|
||||
cipher_key_bytes_src = args[0].from;
|
||||
cipher_key_bytes =
|
||||
(int)simple_strtol(cipher_key_bytes_src,
|
||||
&cipher_key_bytes_src, 0);
|
||||
mount_crypt_stat->global_default_cipher_key_size =
|
||||
cipher_key_bytes;
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"The mount_crypt_stat "
|
||||
"global_default_cipher_key_size "
|
||||
"set to: [%d]\n", mount_crypt_stat->
|
||||
global_default_cipher_key_size);
|
||||
cipher_key_bytes_set = 1;
|
||||
break;
|
||||
case ecryptfs_opt_passthrough:
|
||||
mount_crypt_stat->flags |=
|
||||
ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED;
|
||||
break;
|
||||
case ecryptfs_opt_xattr_metadata:
|
||||
mount_crypt_stat->flags |=
|
||||
ECRYPTFS_XATTR_METADATA_ENABLED;
|
||||
break;
|
||||
case ecryptfs_opt_encrypted_view:
|
||||
mount_crypt_stat->flags |=
|
||||
ECRYPTFS_XATTR_METADATA_ENABLED;
|
||||
mount_crypt_stat->flags |=
|
||||
ECRYPTFS_ENCRYPTED_VIEW_ENABLED;
|
||||
break;
|
||||
case ecryptfs_opt_err:
|
||||
default:
|
||||
ecryptfs_printk(KERN_WARNING,
|
||||
"eCryptfs: unrecognized option '%s'\n",
|
||||
p);
|
||||
}
|
||||
}
|
||||
/* Do not support lack of mount-wide signature in 0.1
|
||||
* release */
|
||||
if (!sig_set) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_ERR, "You must supply a valid "
|
||||
"passphrase auth tok signature as a mount "
|
||||
"parameter; see the eCryptfs README\n");
|
||||
goto out;
|
||||
}
|
||||
if (!cipher_name_set) {
|
||||
cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER);
|
||||
if (unlikely(cipher_name_len
|
||||
>= ECRYPTFS_MAX_CIPHER_NAME_SIZE)) {
|
||||
rc = -EINVAL;
|
||||
BUG();
|
||||
goto out;
|
||||
}
|
||||
memcpy(mount_crypt_stat->global_default_cipher_name,
|
||||
ECRYPTFS_DEFAULT_CIPHER, cipher_name_len);
|
||||
mount_crypt_stat->global_default_cipher_name[cipher_name_len]
|
||||
= '\0';
|
||||
}
|
||||
if (!cipher_key_bytes_set) {
|
||||
mount_crypt_stat->global_default_cipher_key_size = 0;
|
||||
}
|
||||
rc = ecryptfs_process_cipher(
|
||||
&mount_crypt_stat->global_key_tfm,
|
||||
mount_crypt_stat->global_default_cipher_name,
|
||||
&mount_crypt_stat->global_default_cipher_key_size);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error attempting to initialize cipher [%s] "
|
||||
"with key size [%Zd] bytes; rc = [%d]\n",
|
||||
mount_crypt_stat->global_default_cipher_name,
|
||||
mount_crypt_stat->global_default_cipher_key_size, rc);
|
||||
mount_crypt_stat->global_key_tfm = NULL;
|
||||
mount_crypt_stat->global_auth_tok_key = NULL;
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mutex_init(&mount_crypt_stat->global_key_tfm_mutex);
|
||||
ecryptfs_printk(KERN_DEBUG, "Requesting the key with description: "
|
||||
"[%s]\n", mount_crypt_stat->global_auth_tok_sig);
|
||||
/* The reference to this key is held until umount is done The
|
||||
* call to key_put is done in ecryptfs_put_super() */
|
||||
auth_tok_key = request_key(&key_type_user,
|
||||
mount_crypt_stat->global_auth_tok_sig,
|
||||
NULL);
|
||||
if (!auth_tok_key || IS_ERR(auth_tok_key)) {
|
||||
ecryptfs_printk(KERN_ERR, "Could not find key with "
|
||||
"description: [%s]\n",
|
||||
mount_crypt_stat->global_auth_tok_sig);
|
||||
process_request_key_err(PTR_ERR(auth_tok_key));
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
auth_tok = ecryptfs_get_key_payload_data(auth_tok_key);
|
||||
if (ecryptfs_verify_version(auth_tok->version)) {
|
||||
ecryptfs_printk(KERN_ERR, "Data structure version mismatch. "
|
||||
"Userspace tools must match eCryptfs kernel "
|
||||
"module with major version [%d] and minor "
|
||||
"version [%d]\n", ECRYPTFS_VERSION_MAJOR,
|
||||
ECRYPTFS_VERSION_MINOR);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (auth_tok->token_type != ECRYPTFS_PASSWORD
|
||||
&& auth_tok->token_type != ECRYPTFS_PRIVATE_KEY) {
|
||||
ecryptfs_printk(KERN_ERR, "Invalid auth_tok structure "
|
||||
"returned from key query\n");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mount_crypt_stat->global_auth_tok_key = auth_tok_key;
|
||||
mount_crypt_stat->global_auth_tok = auth_tok;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct kmem_cache *ecryptfs_sb_info_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_fill_super
|
||||
* @sb: The ecryptfs super block
|
||||
* @raw_data: The options passed to mount
|
||||
* @silent: Not used but required by function prototype
|
||||
*
|
||||
* Sets up what we can of the sb, rest is done in ecryptfs_read_super
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int
|
||||
ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* Released in ecryptfs_put_super() */
|
||||
ecryptfs_set_superblock_private(sb,
|
||||
kmem_cache_zalloc(ecryptfs_sb_info_cache,
|
||||
GFP_KERNEL));
|
||||
if (!ecryptfs_superblock_to_private(sb)) {
|
||||
ecryptfs_printk(KERN_WARNING, "Out of memory\n");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sb->s_op = &ecryptfs_sops;
|
||||
/* Released through deactivate_super(sb) from get_sb_nodev */
|
||||
sb->s_root = d_alloc(NULL, &(const struct qstr) {
|
||||
.hash = 0,.name = "/",.len = 1});
|
||||
if (!sb->s_root) {
|
||||
ecryptfs_printk(KERN_ERR, "d_alloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sb->s_root->d_op = &ecryptfs_dops;
|
||||
sb->s_root->d_sb = sb;
|
||||
sb->s_root->d_parent = sb->s_root;
|
||||
/* Released in d_release when dput(sb->s_root) is called */
|
||||
/* through deactivate_super(sb) from get_sb_nodev() */
|
||||
ecryptfs_set_dentry_private(sb->s_root,
|
||||
kmem_cache_zalloc(ecryptfs_dentry_info_cache,
|
||||
GFP_KERNEL));
|
||||
if (!ecryptfs_dentry_to_private(sb->s_root)) {
|
||||
ecryptfs_printk(KERN_ERR,
|
||||
"dentry_info_cache alloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = 0;
|
||||
out:
|
||||
/* Should be able to rely on deactivate_super called from
|
||||
* get_sb_nodev */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_read_super
|
||||
* @sb: The ecryptfs super block
|
||||
* @dev_name: The path to mount over
|
||||
*
|
||||
* Read the super block of the lower filesystem, and use
|
||||
* ecryptfs_interpose to create our initial inode and super block
|
||||
* struct.
|
||||
*/
|
||||
static int ecryptfs_read_super(struct super_block *sb, const char *dev_name)
|
||||
{
|
||||
int rc;
|
||||
struct nameidata nd;
|
||||
struct dentry *lower_root;
|
||||
struct vfsmount *lower_mnt;
|
||||
|
||||
memset(&nd, 0, sizeof(struct nameidata));
|
||||
rc = path_lookup(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "path_lookup() failed\n");
|
||||
goto out;
|
||||
}
|
||||
lower_root = nd.dentry;
|
||||
lower_mnt = nd.mnt;
|
||||
ecryptfs_set_superblock_lower(sb, lower_root->d_sb);
|
||||
sb->s_maxbytes = lower_root->d_sb->s_maxbytes;
|
||||
ecryptfs_set_dentry_lower(sb->s_root, lower_root);
|
||||
ecryptfs_set_dentry_lower_mnt(sb->s_root, lower_mnt);
|
||||
if ((rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0)))
|
||||
goto out_free;
|
||||
rc = 0;
|
||||
goto out;
|
||||
out_free:
|
||||
path_release(&nd);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_get_sb
|
||||
* @fs_type
|
||||
* @flags
|
||||
* @dev_name: The path to mount over
|
||||
* @raw_data: The options passed into the kernel
|
||||
*
|
||||
* The whole ecryptfs_get_sb process is broken into 4 functions:
|
||||
* ecryptfs_parse_options(): handle options passed to ecryptfs, if any
|
||||
* ecryptfs_fill_super(): used by get_sb_nodev, fills out the super_block
|
||||
* with as much information as it can before needing
|
||||
* the lower filesystem.
|
||||
* ecryptfs_read_super(): this accesses the lower filesystem and uses
|
||||
* ecryptfs_interpolate to perform most of the linking
|
||||
* ecryptfs_interpolate(): links the lower filesystem into ecryptfs
|
||||
*/
|
||||
static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags,
|
||||
const char *dev_name, void *raw_data,
|
||||
struct vfsmount *mnt)
|
||||
{
|
||||
int rc;
|
||||
struct super_block *sb;
|
||||
|
||||
rc = get_sb_nodev(fs_type, flags, raw_data, ecryptfs_fill_super, mnt);
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR "Getting sb failed; rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
sb = mnt->mnt_sb;
|
||||
rc = ecryptfs_parse_options(sb, raw_data);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error parsing options; rc = [%d]\n", rc);
|
||||
goto out_abort;
|
||||
}
|
||||
rc = ecryptfs_read_super(sb, dev_name);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Reading sb failed; rc = [%d]\n", rc);
|
||||
goto out_abort;
|
||||
}
|
||||
goto out;
|
||||
out_abort:
|
||||
dput(sb->s_root);
|
||||
up_write(&sb->s_umount);
|
||||
deactivate_super(sb);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_kill_block_super
|
||||
* @sb: The ecryptfs super block
|
||||
*
|
||||
* Used to bring the superblock down and free the private data.
|
||||
* Private data is free'd in ecryptfs_put_super()
|
||||
*/
|
||||
static void ecryptfs_kill_block_super(struct super_block *sb)
|
||||
{
|
||||
generic_shutdown_super(sb);
|
||||
}
|
||||
|
||||
static struct file_system_type ecryptfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ecryptfs",
|
||||
.get_sb = ecryptfs_get_sb,
|
||||
.kill_sb = ecryptfs_kill_block_super,
|
||||
.fs_flags = 0
|
||||
};
|
||||
|
||||
/**
|
||||
* inode_info_init_once
|
||||
*
|
||||
* Initializes the ecryptfs_inode_info_cache when it is created
|
||||
*/
|
||||
static void
|
||||
inode_info_init_once(void *vptr, struct kmem_cache *cachep, unsigned long flags)
|
||||
{
|
||||
struct ecryptfs_inode_info *ei = (struct ecryptfs_inode_info *)vptr;
|
||||
|
||||
if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
|
||||
SLAB_CTOR_CONSTRUCTOR)
|
||||
inode_init_once(&ei->vfs_inode);
|
||||
}
|
||||
|
||||
static struct ecryptfs_cache_info {
|
||||
struct kmem_cache **cache;
|
||||
const char *name;
|
||||
size_t size;
|
||||
void (*ctor)(void*, struct kmem_cache *, unsigned long);
|
||||
} ecryptfs_cache_infos[] = {
|
||||
{
|
||||
.cache = &ecryptfs_auth_tok_list_item_cache,
|
||||
.name = "ecryptfs_auth_tok_list_item",
|
||||
.size = sizeof(struct ecryptfs_auth_tok_list_item),
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_file_info_cache,
|
||||
.name = "ecryptfs_file_cache",
|
||||
.size = sizeof(struct ecryptfs_file_info),
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_dentry_info_cache,
|
||||
.name = "ecryptfs_dentry_info_cache",
|
||||
.size = sizeof(struct ecryptfs_dentry_info),
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_inode_info_cache,
|
||||
.name = "ecryptfs_inode_cache",
|
||||
.size = sizeof(struct ecryptfs_inode_info),
|
||||
.ctor = inode_info_init_once,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_sb_info_cache,
|
||||
.name = "ecryptfs_sb_cache",
|
||||
.size = sizeof(struct ecryptfs_sb_info),
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_header_cache_0,
|
||||
.name = "ecryptfs_headers_0",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_header_cache_1,
|
||||
.name = "ecryptfs_headers_1",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_header_cache_2,
|
||||
.name = "ecryptfs_headers_2",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_xattr_cache,
|
||||
.name = "ecryptfs_xattr_cache",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_lower_page_cache,
|
||||
.name = "ecryptfs_lower_page_cache",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_key_record_cache,
|
||||
.name = "ecryptfs_key_record_cache",
|
||||
.size = sizeof(struct ecryptfs_key_record),
|
||||
},
|
||||
};
|
||||
|
||||
static void ecryptfs_free_kmem_caches(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ecryptfs_cache_infos); i++) {
|
||||
struct ecryptfs_cache_info *info;
|
||||
|
||||
info = &ecryptfs_cache_infos[i];
|
||||
if (*(info->cache))
|
||||
kmem_cache_destroy(*(info->cache));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_init_kmem_caches
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_init_kmem_caches(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ecryptfs_cache_infos); i++) {
|
||||
struct ecryptfs_cache_info *info;
|
||||
|
||||
info = &ecryptfs_cache_infos[i];
|
||||
*(info->cache) = kmem_cache_create(info->name, info->size,
|
||||
0, SLAB_HWCACHE_ALIGN, info->ctor, NULL);
|
||||
if (!*(info->cache)) {
|
||||
ecryptfs_free_kmem_caches();
|
||||
ecryptfs_printk(KERN_WARNING, "%s: "
|
||||
"kmem_cache_create failed\n",
|
||||
info->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ecryptfs_obj {
|
||||
char *name;
|
||||
struct list_head slot_list;
|
||||
struct kobject kobj;
|
||||
};
|
||||
|
||||
struct ecryptfs_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t(*show) (struct ecryptfs_obj *, char *);
|
||||
ssize_t(*store) (struct ecryptfs_obj *, const char *, size_t);
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
ecryptfs_attr_store(struct kobject *kobj,
|
||||
struct attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj,
|
||||
kobj);
|
||||
struct ecryptfs_attribute *attribute =
|
||||
container_of(attr, struct ecryptfs_attribute, attr);
|
||||
|
||||
return (attribute->store ? attribute->store(obj, buf, len) : 0);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ecryptfs_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
|
||||
{
|
||||
struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj,
|
||||
kobj);
|
||||
struct ecryptfs_attribute *attribute =
|
||||
container_of(attr, struct ecryptfs_attribute, attr);
|
||||
|
||||
return (attribute->show ? attribute->show(obj, buf) : 0);
|
||||
}
|
||||
|
||||
static struct sysfs_ops ecryptfs_sysfs_ops = {
|
||||
.show = ecryptfs_attr_show,
|
||||
.store = ecryptfs_attr_store
|
||||
};
|
||||
|
||||
static struct kobj_type ecryptfs_ktype = {
|
||||
.sysfs_ops = &ecryptfs_sysfs_ops
|
||||
};
|
||||
|
||||
static decl_subsys(ecryptfs, &ecryptfs_ktype, NULL);
|
||||
|
||||
static ssize_t version_show(struct ecryptfs_obj *obj, char *buff)
|
||||
{
|
||||
return snprintf(buff, PAGE_SIZE, "%d\n", ECRYPTFS_VERSIONING_MASK);
|
||||
}
|
||||
|
||||
static struct ecryptfs_attribute sysfs_attr_version = __ATTR_RO(version);
|
||||
|
||||
static struct ecryptfs_version_str_map_elem {
|
||||
u32 flag;
|
||||
char *str;
|
||||
} ecryptfs_version_str_map[] = {
|
||||
{ECRYPTFS_VERSIONING_PASSPHRASE, "passphrase"},
|
||||
{ECRYPTFS_VERSIONING_PUBKEY, "pubkey"},
|
||||
{ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH, "plaintext passthrough"},
|
||||
{ECRYPTFS_VERSIONING_POLICY, "policy"},
|
||||
{ECRYPTFS_VERSIONING_XATTR, "metadata in extended attribute"}
|
||||
};
|
||||
|
||||
static ssize_t version_str_show(struct ecryptfs_obj *obj, char *buff)
|
||||
{
|
||||
int i;
|
||||
int remaining = PAGE_SIZE;
|
||||
int total_written = 0;
|
||||
|
||||
buff[0] = '\0';
|
||||
for (i = 0; i < ARRAY_SIZE(ecryptfs_version_str_map); i++) {
|
||||
int entry_size;
|
||||
|
||||
if (!(ECRYPTFS_VERSIONING_MASK
|
||||
& ecryptfs_version_str_map[i].flag))
|
||||
continue;
|
||||
entry_size = strlen(ecryptfs_version_str_map[i].str);
|
||||
if ((entry_size + 2) > remaining)
|
||||
goto out;
|
||||
memcpy(buff, ecryptfs_version_str_map[i].str, entry_size);
|
||||
buff[entry_size++] = '\n';
|
||||
buff[entry_size] = '\0';
|
||||
buff += entry_size;
|
||||
total_written += entry_size;
|
||||
remaining -= entry_size;
|
||||
}
|
||||
out:
|
||||
return total_written;
|
||||
}
|
||||
|
||||
static struct ecryptfs_attribute sysfs_attr_version_str = __ATTR_RO(version_str);
|
||||
|
||||
static int do_sysfs_registration(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = subsystem_register(&ecryptfs_subsys))) {
|
||||
printk(KERN_ERR
|
||||
"Unable to register ecryptfs sysfs subsystem\n");
|
||||
goto out;
|
||||
}
|
||||
rc = sysfs_create_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version.attr);
|
||||
if (rc) {
|
||||
printk(KERN_ERR
|
||||
"Unable to create ecryptfs version attribute\n");
|
||||
subsystem_unregister(&ecryptfs_subsys);
|
||||
goto out;
|
||||
}
|
||||
rc = sysfs_create_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version_str.attr);
|
||||
if (rc) {
|
||||
printk(KERN_ERR
|
||||
"Unable to create ecryptfs version_str attribute\n");
|
||||
sysfs_remove_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version.attr);
|
||||
subsystem_unregister(&ecryptfs_subsys);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __init ecryptfs_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (ECRYPTFS_DEFAULT_EXTENT_SIZE > PAGE_CACHE_SIZE) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_ERR, "The eCryptfs extent size is "
|
||||
"larger than the host's page size, and so "
|
||||
"eCryptfs cannot run on this system. The "
|
||||
"default eCryptfs extent size is [%d] bytes; "
|
||||
"the page size is [%d] bytes.\n",
|
||||
ECRYPTFS_DEFAULT_EXTENT_SIZE, PAGE_CACHE_SIZE);
|
||||
goto out;
|
||||
}
|
||||
rc = ecryptfs_init_kmem_caches();
|
||||
if (rc) {
|
||||
printk(KERN_ERR
|
||||
"Failed to allocate one or more kmem_cache objects\n");
|
||||
goto out;
|
||||
}
|
||||
rc = register_filesystem(&ecryptfs_fs_type);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Failed to register filesystem\n");
|
||||
ecryptfs_free_kmem_caches();
|
||||
goto out;
|
||||
}
|
||||
kset_set_kset_s(&ecryptfs_subsys, fs_subsys);
|
||||
sysfs_attr_version.attr.owner = THIS_MODULE;
|
||||
sysfs_attr_version_str.attr.owner = THIS_MODULE;
|
||||
rc = do_sysfs_registration();
|
||||
if (rc) {
|
||||
printk(KERN_ERR "sysfs registration failed\n");
|
||||
unregister_filesystem(&ecryptfs_fs_type);
|
||||
ecryptfs_free_kmem_caches();
|
||||
goto out;
|
||||
}
|
||||
rc = ecryptfs_init_messaging(ecryptfs_transport);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Failure occured while attempting to "
|
||||
"initialize the eCryptfs netlink socket\n");
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit ecryptfs_exit(void)
|
||||
{
|
||||
sysfs_remove_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version.attr);
|
||||
sysfs_remove_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version_str.attr);
|
||||
subsystem_unregister(&ecryptfs_subsys);
|
||||
ecryptfs_release_messaging(ecryptfs_transport);
|
||||
unregister_filesystem(&ecryptfs_fs_type);
|
||||
ecryptfs_free_kmem_caches();
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Michael A. Halcrow <mhalcrow@us.ibm.com>");
|
||||
MODULE_DESCRIPTION("eCryptfs");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(ecryptfs_init)
|
||||
module_exit(ecryptfs_exit)
|
||||
516
fs/ecryptfs/messaging.c
Normal file
516
fs/ecryptfs/messaging.c
Normal file
@@ -0,0 +1,516 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
*
|
||||
* Copyright (C) 2004-2006 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 "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_id_hash;
|
||||
static struct mutex ecryptfs_daemon_id_hash_mux;
|
||||
static int ecryptfs_hash_buckets;
|
||||
#define ecryptfs_uid_hash(uid) \
|
||||
hash_long((unsigned long)uid, ecryptfs_hash_buckets)
|
||||
|
||||
static unsigned int 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. Returns zero on success; non-zero on error or upon
|
||||
* failure to acquire a free context element. Be sure to lock the
|
||||
* list mutex before calling.
|
||||
*/
|
||||
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)) {
|
||||
ecryptfs_printk(KERN_WARNING, "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", 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
|
||||
*
|
||||
* Be sure to lock the list mutex and the context mutex before
|
||||
* calling.
|
||||
*/
|
||||
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
|
||||
*
|
||||
* Be sure to lock the list mutex and the context mutex before
|
||||
* calling.
|
||||
*/
|
||||
static void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx)
|
||||
{
|
||||
list_move(&(msg_ctx->node), &ecryptfs_msg_ctx_free_list);
|
||||
if (msg_ctx->msg)
|
||||
kfree(msg_ctx->msg);
|
||||
msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_FREE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_find_daemon_id
|
||||
* @uid: The user id which maps to the desired daemon id
|
||||
* @id: If return value is zero, points to the desired daemon id
|
||||
* pointer
|
||||
*
|
||||
* Search the hash list for the given user id. Returns zero if the
|
||||
* user id exists in the list; non-zero otherwise. The daemon id hash
|
||||
* mutex should be held before calling this function.
|
||||
*/
|
||||
static int ecryptfs_find_daemon_id(uid_t uid, struct ecryptfs_daemon_id **id)
|
||||
{
|
||||
struct hlist_node *elem;
|
||||
int rc;
|
||||
|
||||
hlist_for_each_entry(*id, elem,
|
||||
&ecryptfs_daemon_id_hash[ecryptfs_uid_hash(uid)],
|
||||
id_chain) {
|
||||
if ((*id)->uid == uid) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rc = -EINVAL;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_send_raw_message(unsigned int transport, u16 msg_type,
|
||||
pid_t pid)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch(transport) {
|
||||
case ECRYPTFS_TRANSPORT_NETLINK:
|
||||
rc = ecryptfs_send_netlink(NULL, 0, NULL, msg_type, 0, pid);
|
||||
break;
|
||||
case ECRYPTFS_TRANSPORT_CONNECTOR:
|
||||
case ECRYPTFS_TRANSPORT_RELAYFS:
|
||||
default:
|
||||
rc = -ENOSYS;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_process_helo
|
||||
* @transport: The underlying transport (netlink, etc.)
|
||||
* @uid: The user ID owner of the message
|
||||
* @pid: The process ID for the userspace program that sent the
|
||||
* message
|
||||
*
|
||||
* Adds the uid and pid values to the daemon id hash. If a uid
|
||||
* already has a daemon pid registered, the daemon will be
|
||||
* unregistered before the new daemon id is put into the hash list.
|
||||
* Returns zero after adding a new daemon id to the hash list;
|
||||
* non-zero otherwise.
|
||||
*/
|
||||
int ecryptfs_process_helo(unsigned int transport, uid_t uid, pid_t pid)
|
||||
{
|
||||
struct ecryptfs_daemon_id *new_id;
|
||||
struct ecryptfs_daemon_id *old_id;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&ecryptfs_daemon_id_hash_mux);
|
||||
new_id = kmalloc(sizeof(*new_id), GFP_KERNEL);
|
||||
if (!new_id) {
|
||||
rc = -ENOMEM;
|
||||
ecryptfs_printk(KERN_ERR, "Failed to allocate memory; unable "
|
||||
"to register daemon [%d] for user [%d]\n",
|
||||
pid, uid);
|
||||
goto unlock;
|
||||
}
|
||||
if (!ecryptfs_find_daemon_id(uid, &old_id)) {
|
||||
printk(KERN_WARNING "Received request from user [%d] "
|
||||
"to register daemon [%d]; unregistering daemon "
|
||||
"[%d]\n", uid, pid, old_id->pid);
|
||||
hlist_del(&old_id->id_chain);
|
||||
rc = ecryptfs_send_raw_message(transport, ECRYPTFS_NLMSG_QUIT,
|
||||
old_id->pid);
|
||||
if (rc)
|
||||
printk(KERN_WARNING "Failed to send QUIT "
|
||||
"message to daemon [%d]; rc = [%d]\n",
|
||||
old_id->pid, rc);
|
||||
kfree(old_id);
|
||||
}
|
||||
new_id->uid = uid;
|
||||
new_id->pid = pid;
|
||||
hlist_add_head(&new_id->id_chain,
|
||||
&ecryptfs_daemon_id_hash[ecryptfs_uid_hash(uid)]);
|
||||
rc = 0;
|
||||
unlock:
|
||||
mutex_unlock(&ecryptfs_daemon_id_hash_mux);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_process_quit
|
||||
* @uid: The user ID owner of the message
|
||||
* @pid: The process ID for the userspace program that sent the
|
||||
* message
|
||||
*
|
||||
* Deletes the corresponding daemon id for the given uid and pid, if
|
||||
* it is the registered that is requesting the deletion. Returns zero
|
||||
* after deleting the desired daemon id; non-zero otherwise.
|
||||
*/
|
||||
int ecryptfs_process_quit(uid_t uid, pid_t pid)
|
||||
{
|
||||
struct ecryptfs_daemon_id *id;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&ecryptfs_daemon_id_hash_mux);
|
||||
if (ecryptfs_find_daemon_id(uid, &id)) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_ERR, "Received request from user [%d] to "
|
||||
"unregister unrecognized daemon [%d]\n", uid,
|
||||
pid);
|
||||
goto unlock;
|
||||
}
|
||||
if (id->pid != pid) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_WARNING, "Received request from user [%d] "
|
||||
"with pid [%d] to unregister daemon [%d]\n",
|
||||
uid, pid, id->pid);
|
||||
goto unlock;
|
||||
}
|
||||
hlist_del(&id->id_chain);
|
||||
kfree(id);
|
||||
rc = 0;
|
||||
unlock:
|
||||
mutex_unlock(&ecryptfs_daemon_id_hash_mux);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_process_reponse
|
||||
* @msg: The ecryptfs message received; the caller should sanity check
|
||||
* msg->data_len
|
||||
* @pid: The process ID of the userspace application that sent the
|
||||
* message
|
||||
* @seq: The sequence number of the message
|
||||
*
|
||||
* Processes a response message after sending a operation request to
|
||||
* userspace. Returns zero upon delivery to desired context element;
|
||||
* non-zero upon delivery failure or error.
|
||||
*/
|
||||
int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t uid,
|
||||
pid_t pid, u32 seq)
|
||||
{
|
||||
struct ecryptfs_daemon_id *id;
|
||||
struct ecryptfs_msg_ctx *msg_ctx;
|
||||
int msg_size;
|
||||
int rc;
|
||||
|
||||
if (msg->index >= ecryptfs_message_buf_len) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_ERR, "Attempt to reference "
|
||||
"context buffer at index [%d]; maximum "
|
||||
"allowable is [%d]\n", msg->index,
|
||||
(ecryptfs_message_buf_len - 1));
|
||||
goto out;
|
||||
}
|
||||
msg_ctx = &ecryptfs_msg_ctx_arr[msg->index];
|
||||
mutex_lock(&msg_ctx->mux);
|
||||
if (ecryptfs_find_daemon_id(msg_ctx->task->euid, &id)) {
|
||||
rc = -EBADMSG;
|
||||
ecryptfs_printk(KERN_WARNING, "User [%d] received a "
|
||||
"message response from process [%d] but does "
|
||||
"not have a registered daemon\n",
|
||||
msg_ctx->task->euid, pid);
|
||||
goto wake_up;
|
||||
}
|
||||
if (msg_ctx->task->euid != uid) {
|
||||
rc = -EBADMSG;
|
||||
ecryptfs_printk(KERN_WARNING, "Received message from user "
|
||||
"[%d]; expected message from user [%d]\n",
|
||||
uid, msg_ctx->task->euid);
|
||||
goto unlock;
|
||||
}
|
||||
if (id->pid != pid) {
|
||||
rc = -EBADMSG;
|
||||
ecryptfs_printk(KERN_ERR, "User [%d] received a "
|
||||
"message response from an unrecognized "
|
||||
"process [%d]\n", msg_ctx->task->euid, pid);
|
||||
goto unlock;
|
||||
}
|
||||
if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_PENDING) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_WARNING, "Desired context element is not "
|
||||
"pending a response\n");
|
||||
goto unlock;
|
||||
} else if (msg_ctx->counter != seq) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_WARNING, "Invalid message sequence; "
|
||||
"expected [%d]; received [%d]\n",
|
||||
msg_ctx->counter, seq);
|
||||
goto unlock;
|
||||
}
|
||||
msg_size = sizeof(*msg) + msg->data_len;
|
||||
msg_ctx->msg = kmalloc(msg_size, GFP_KERNEL);
|
||||
if (!msg_ctx->msg) {
|
||||
rc = -ENOMEM;
|
||||
ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
|
||||
goto unlock;
|
||||
}
|
||||
memcpy(msg_ctx->msg, msg, msg_size);
|
||||
msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_DONE;
|
||||
rc = 0;
|
||||
wake_up:
|
||||
wake_up_process(msg_ctx->task);
|
||||
unlock:
|
||||
mutex_unlock(&msg_ctx->mux);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_send_message
|
||||
* @transport: The transport over which to send the message (i.e.,
|
||||
* netlink)
|
||||
* @data: The data to send
|
||||
* @data_len: The length of data
|
||||
* @msg_ctx: The message context allocated for the send
|
||||
*/
|
||||
int ecryptfs_send_message(unsigned int transport, char *data, int data_len,
|
||||
struct ecryptfs_msg_ctx **msg_ctx)
|
||||
{
|
||||
struct ecryptfs_daemon_id *id;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&ecryptfs_daemon_id_hash_mux);
|
||||
if (ecryptfs_find_daemon_id(current->euid, &id)) {
|
||||
mutex_unlock(&ecryptfs_daemon_id_hash_mux);
|
||||
rc = -ENOTCONN;
|
||||
ecryptfs_printk(KERN_ERR, "User [%d] does not have a daemon "
|
||||
"registered\n", current->euid);
|
||||
goto out;
|
||||
}
|
||||
mutex_unlock(&ecryptfs_daemon_id_hash_mux);
|
||||
mutex_lock(&ecryptfs_msg_ctx_lists_mux);
|
||||
rc = ecryptfs_acquire_free_msg_ctx(msg_ctx);
|
||||
if (rc) {
|
||||
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
|
||||
ecryptfs_printk(KERN_WARNING, "Could not claim a free "
|
||||
"context element\n");
|
||||
goto out;
|
||||
}
|
||||
ecryptfs_msg_ctx_free_to_alloc(*msg_ctx);
|
||||
mutex_unlock(&(*msg_ctx)->mux);
|
||||
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
|
||||
switch (transport) {
|
||||
case ECRYPTFS_TRANSPORT_NETLINK:
|
||||
rc = ecryptfs_send_netlink(data, data_len, *msg_ctx,
|
||||
ECRYPTFS_NLMSG_REQUEST, 0, id->pid);
|
||||
break;
|
||||
case ECRYPTFS_TRANSPORT_CONNECTOR:
|
||||
case ECRYPTFS_TRANSPORT_RELAYFS:
|
||||
default:
|
||||
rc = -ENOSYS;
|
||||
}
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error attempting to send message to userspace "
|
||||
"daemon; rc = [%d]\n", rc);
|
||||
}
|
||||
out:
|
||||
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.
|
||||
*/
|
||||
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 ecryptfs_init_messaging(unsigned int transport)
|
||||
{
|
||||
int i;
|
||||
int rc = 0;
|
||||
|
||||
if (ecryptfs_number_of_users > ECRYPTFS_MAX_NUM_USERS) {
|
||||
ecryptfs_number_of_users = ECRYPTFS_MAX_NUM_USERS;
|
||||
ecryptfs_printk(KERN_WARNING, "Specified number of users is "
|
||||
"too large, defaulting to [%d] users\n",
|
||||
ecryptfs_number_of_users);
|
||||
}
|
||||
mutex_init(&ecryptfs_daemon_id_hash_mux);
|
||||
mutex_lock(&ecryptfs_daemon_id_hash_mux);
|
||||
ecryptfs_hash_buckets = 0;
|
||||
while (ecryptfs_number_of_users >> ++ecryptfs_hash_buckets);
|
||||
ecryptfs_daemon_id_hash = kmalloc(sizeof(struct hlist_head)
|
||||
* ecryptfs_hash_buckets, GFP_KERNEL);
|
||||
if (!ecryptfs_daemon_id_hash) {
|
||||
rc = -ENOMEM;
|
||||
ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < ecryptfs_hash_buckets; i++)
|
||||
INIT_HLIST_HEAD(&ecryptfs_daemon_id_hash[i]);
|
||||
mutex_unlock(&ecryptfs_daemon_id_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;
|
||||
ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
|
||||
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);
|
||||
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);
|
||||
switch(transport) {
|
||||
case ECRYPTFS_TRANSPORT_NETLINK:
|
||||
rc = ecryptfs_init_netlink();
|
||||
if (rc)
|
||||
ecryptfs_release_messaging(transport);
|
||||
break;
|
||||
case ECRYPTFS_TRANSPORT_CONNECTOR:
|
||||
case ECRYPTFS_TRANSPORT_RELAYFS:
|
||||
default:
|
||||
rc = -ENOSYS;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void ecryptfs_release_messaging(unsigned int transport)
|
||||
{
|
||||
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);
|
||||
if (ecryptfs_msg_ctx_arr[i].msg)
|
||||
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_id_hash) {
|
||||
struct hlist_node *elem;
|
||||
struct ecryptfs_daemon_id *id;
|
||||
int i;
|
||||
|
||||
mutex_lock(&ecryptfs_daemon_id_hash_mux);
|
||||
for (i = 0; i < ecryptfs_hash_buckets; i++) {
|
||||
hlist_for_each_entry(id, elem,
|
||||
&ecryptfs_daemon_id_hash[i],
|
||||
id_chain) {
|
||||
hlist_del(elem);
|
||||
kfree(id);
|
||||
}
|
||||
}
|
||||
kfree(ecryptfs_daemon_id_hash);
|
||||
mutex_unlock(&ecryptfs_daemon_id_hash_mux);
|
||||
}
|
||||
switch(transport) {
|
||||
case ECRYPTFS_TRANSPORT_NETLINK:
|
||||
ecryptfs_release_netlink();
|
||||
break;
|
||||
case ECRYPTFS_TRANSPORT_CONNECTOR:
|
||||
case ECRYPTFS_TRANSPORT_RELAYFS:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
830
fs/ecryptfs/mmap.c
Normal file
830
fs/ecryptfs/mmap.c
Normal file
@@ -0,0 +1,830 @@
|
||||
/**
|
||||
* 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 "ecryptfs_kernel.h"
|
||||
|
||||
struct kmem_cache *ecryptfs_lower_page_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_get1page
|
||||
*
|
||||
* Get one page from cache or lower f/s, return error otherwise.
|
||||
*
|
||||
* Returns unlocked and up-to-date page (if ok), with increased
|
||||
* refcnt.
|
||||
*/
|
||||
static struct page *ecryptfs_get1page(struct file *file, int index)
|
||||
{
|
||||
struct page *page;
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
struct address_space *mapping;
|
||||
|
||||
dentry = file->f_path.dentry;
|
||||
inode = dentry->d_inode;
|
||||
mapping = inode->i_mapping;
|
||||
page = read_cache_page(mapping, index,
|
||||
(filler_t *)mapping->a_ops->readpage,
|
||||
(void *)file);
|
||||
if (IS_ERR(page))
|
||||
goto out;
|
||||
wait_on_page_locked(page);
|
||||
out:
|
||||
return page;
|
||||
}
|
||||
|
||||
static
|
||||
int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros);
|
||||
|
||||
/**
|
||||
* ecryptfs_fill_zeros
|
||||
* @file: The ecryptfs file
|
||||
* @new_length: The new length of the data in the underlying file;
|
||||
* everything between the prior end of the file and the
|
||||
* new end of the file will be filled with zero's.
|
||||
* new_length must be greater than current length
|
||||
*
|
||||
* Function for handling lseek-ing past the end of the file.
|
||||
*
|
||||
* This function does not support shrinking, only growing a file.
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise.
|
||||
*/
|
||||
int ecryptfs_fill_zeros(struct file *file, loff_t new_length)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
pgoff_t old_end_page_index = 0;
|
||||
pgoff_t index = old_end_page_index;
|
||||
int old_end_pos_in_page = -1;
|
||||
pgoff_t new_end_page_index;
|
||||
int new_end_pos_in_page;
|
||||
loff_t cur_length = i_size_read(inode);
|
||||
|
||||
if (cur_length != 0) {
|
||||
index = old_end_page_index =
|
||||
((cur_length - 1) >> PAGE_CACHE_SHIFT);
|
||||
old_end_pos_in_page = ((cur_length - 1) & ~PAGE_CACHE_MASK);
|
||||
}
|
||||
new_end_page_index = ((new_length - 1) >> PAGE_CACHE_SHIFT);
|
||||
new_end_pos_in_page = ((new_length - 1) & ~PAGE_CACHE_MASK);
|
||||
ecryptfs_printk(KERN_DEBUG, "old_end_page_index = [0x%.16x]; "
|
||||
"old_end_pos_in_page = [%d]; "
|
||||
"new_end_page_index = [0x%.16x]; "
|
||||
"new_end_pos_in_page = [%d]\n",
|
||||
old_end_page_index, old_end_pos_in_page,
|
||||
new_end_page_index, new_end_pos_in_page);
|
||||
if (old_end_page_index == new_end_page_index) {
|
||||
/* Start and end are in the same page; we just need to
|
||||
* set a portion of the existing page to zero's */
|
||||
rc = write_zeros(file, index, (old_end_pos_in_page + 1),
|
||||
(new_end_pos_in_page - old_end_pos_in_page));
|
||||
if (rc)
|
||||
ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], "
|
||||
"index=[0x%.16x], "
|
||||
"old_end_pos_in_page=[d], "
|
||||
"(PAGE_CACHE_SIZE - new_end_pos_in_page"
|
||||
"=[%d]"
|
||||
")=[d]) returned [%d]\n", file, index,
|
||||
old_end_pos_in_page,
|
||||
new_end_pos_in_page,
|
||||
(PAGE_CACHE_SIZE - new_end_pos_in_page),
|
||||
rc);
|
||||
goto out;
|
||||
}
|
||||
/* Fill the remainder of the previous last page with zeros */
|
||||
rc = write_zeros(file, index, (old_end_pos_in_page + 1),
|
||||
((PAGE_CACHE_SIZE - 1) - old_end_pos_in_page));
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], "
|
||||
"index=[0x%.16x], old_end_pos_in_page=[d], "
|
||||
"(PAGE_CACHE_SIZE - old_end_pos_in_page)=[d]) "
|
||||
"returned [%d]\n", file, index,
|
||||
old_end_pos_in_page,
|
||||
(PAGE_CACHE_SIZE - old_end_pos_in_page), rc);
|
||||
goto out;
|
||||
}
|
||||
index++;
|
||||
while (index < new_end_page_index) {
|
||||
/* Fill all intermediate pages with zeros */
|
||||
rc = write_zeros(file, index, 0, PAGE_CACHE_SIZE);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], "
|
||||
"index=[0x%.16x], "
|
||||
"old_end_pos_in_page=[d], "
|
||||
"(PAGE_CACHE_SIZE - new_end_pos_in_page"
|
||||
"=[%d]"
|
||||
")=[d]) returned [%d]\n", file, index,
|
||||
old_end_pos_in_page,
|
||||
new_end_pos_in_page,
|
||||
(PAGE_CACHE_SIZE - new_end_pos_in_page),
|
||||
rc);
|
||||
goto out;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
/* Fill the portion at the beginning of the last new page with
|
||||
* zero's */
|
||||
rc = write_zeros(file, index, 0, (new_end_pos_in_page + 1));
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "write_zeros(file="
|
||||
"[%p], index=[0x%.16x], 0, "
|
||||
"new_end_pos_in_page=[%d]"
|
||||
"returned [%d]\n", file, index,
|
||||
new_end_pos_in_page, rc);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_writepage
|
||||
* @page: Page that is locked before this call is made
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
struct ecryptfs_page_crypt_context ctx;
|
||||
int rc;
|
||||
|
||||
ctx.page = page;
|
||||
ctx.mode = ECRYPTFS_WRITEPAGE_MODE;
|
||||
ctx.param.wbc = wbc;
|
||||
rc = ecryptfs_encrypt_page(&ctx);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "Error encrypting "
|
||||
"page (upper index [0x%.16x])\n", page->index);
|
||||
ClearPageUptodate(page);
|
||||
goto out;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the data from the lower file file at index lower_page_index
|
||||
* and copies that data into page.
|
||||
*
|
||||
* @param page Page to fill
|
||||
* @param lower_page_index Index of the page in the lower file to get
|
||||
*/
|
||||
int ecryptfs_do_readpage(struct file *file, struct page *page,
|
||||
pgoff_t lower_page_index)
|
||||
{
|
||||
int rc;
|
||||
struct dentry *dentry;
|
||||
struct file *lower_file;
|
||||
struct dentry *lower_dentry;
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
char *page_data;
|
||||
struct page *lower_page = NULL;
|
||||
char *lower_page_data;
|
||||
const struct address_space_operations *lower_a_ops;
|
||||
|
||||
dentry = file->f_path.dentry;
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
inode = dentry->d_inode;
|
||||
lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
lower_a_ops = lower_inode->i_mapping->a_ops;
|
||||
lower_page = read_cache_page(lower_inode->i_mapping, lower_page_index,
|
||||
(filler_t *)lower_a_ops->readpage,
|
||||
(void *)lower_file);
|
||||
if (IS_ERR(lower_page)) {
|
||||
rc = PTR_ERR(lower_page);
|
||||
lower_page = NULL;
|
||||
ecryptfs_printk(KERN_ERR, "Error reading from page cache\n");
|
||||
goto out;
|
||||
}
|
||||
wait_on_page_locked(lower_page);
|
||||
page_data = kmap_atomic(page, KM_USER0);
|
||||
lower_page_data = kmap_atomic(lower_page, KM_USER1);
|
||||
memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE);
|
||||
kunmap_atomic(lower_page_data, KM_USER1);
|
||||
kunmap_atomic(page_data, KM_USER0);
|
||||
flush_dcache_page(page);
|
||||
rc = 0;
|
||||
out:
|
||||
if (likely(lower_page))
|
||||
page_cache_release(lower_page);
|
||||
if (rc == 0)
|
||||
SetPageUptodate(page);
|
||||
else
|
||||
ClearPageUptodate(page);
|
||||
return rc;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
static void set_header_info(char *page_virt,
|
||||
struct ecryptfs_crypt_stat *crypt_stat)
|
||||
{
|
||||
size_t written;
|
||||
int save_num_header_extents_at_front =
|
||||
crypt_stat->num_header_extents_at_front;
|
||||
|
||||
crypt_stat->num_header_extents_at_front = 1;
|
||||
ecryptfs_write_header_metadata(page_virt + 20, crypt_stat, &written);
|
||||
crypt_stat->num_header_extents_at_front =
|
||||
save_num_header_extents_at_front;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_readpage
|
||||
* @file: This is an ecryptfs file
|
||||
* @page: ecryptfs associated page to stick the read data into
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
int rc = 0;
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
|
||||
BUG_ON(!(file && file->f_path.dentry && file->f_path.dentry->d_inode));
|
||||
crypt_stat = &ecryptfs_inode_to_private(file->f_path.dentry->d_inode)
|
||||
->crypt_stat;
|
||||
if (!crypt_stat
|
||||
|| !(crypt_stat->flags & ECRYPTFS_ENCRYPTED)
|
||||
|| (crypt_stat->flags & ECRYPTFS_NEW_FILE)) {
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"Passing through unencrypted page\n");
|
||||
rc = ecryptfs_do_readpage(file, page, page->index);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error reading page; rc = "
|
||||
"[%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
} else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
|
||||
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
|
||||
int num_pages_in_header_region =
|
||||
(crypt_stat->header_extent_size
|
||||
/ PAGE_CACHE_SIZE);
|
||||
|
||||
if (page->index < num_pages_in_header_region) {
|
||||
char *page_virt;
|
||||
|
||||
page_virt = kmap_atomic(page, KM_USER0);
|
||||
memset(page_virt, 0, PAGE_CACHE_SIZE);
|
||||
if (page->index == 0) {
|
||||
rc = ecryptfs_read_xattr_region(
|
||||
page_virt, file->f_path.dentry);
|
||||
set_header_info(page_virt, crypt_stat);
|
||||
}
|
||||
kunmap_atomic(page_virt, KM_USER0);
|
||||
flush_dcache_page(page);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error reading xattr "
|
||||
"region\n");
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
rc = ecryptfs_do_readpage(
|
||||
file, page,
|
||||
(page->index
|
||||
- num_pages_in_header_region));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error reading page; "
|
||||
"rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rc = ecryptfs_do_readpage(file, page, page->index);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error reading page; rc = "
|
||||
"[%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rc = ecryptfs_decrypt_page(file, page);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error decrypting page; "
|
||||
"rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
out:
|
||||
if (rc)
|
||||
ClearPageUptodate(page);
|
||||
ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\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;
|
||||
char *page_virt;
|
||||
|
||||
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;
|
||||
page_virt = kmap_atomic(page, KM_USER0);
|
||||
memset((page_virt + end_byte_in_page), 0,
|
||||
(PAGE_CACHE_SIZE - end_byte_in_page));
|
||||
kunmap_atomic(page_virt, KM_USER0);
|
||||
flush_dcache_page(page);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ecryptfs_prepare_write(struct file *file, struct page *page,
|
||||
unsigned from, unsigned to)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (from == 0 && to == PAGE_CACHE_SIZE)
|
||||
goto out; /* If we are writing a full page, it will be
|
||||
up to date. */
|
||||
if (!PageUptodate(page))
|
||||
rc = ecryptfs_do_readpage(file, page, page->index);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
|
||||
struct inode *lower_inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error calling lower writepage(); "
|
||||
"rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
|
||||
page_cache_release(lower_page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static
|
||||
void ecryptfs_release_lower_page(struct page *lower_page, int page_locked)
|
||||
{
|
||||
if (page_locked)
|
||||
unlock_page(lower_page);
|
||||
page_cache_release(lower_page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 file *lower_file,
|
||||
struct inode *lower_inode,
|
||||
struct inode *inode)
|
||||
{
|
||||
int rc = 0;
|
||||
struct page *header_page;
|
||||
char *header_virt;
|
||||
const struct address_space_operations *lower_a_ops;
|
||||
u64 file_size;
|
||||
|
||||
retry:
|
||||
header_page = grab_cache_page(lower_inode->i_mapping, 0);
|
||||
if (!header_page) {
|
||||
ecryptfs_printk(KERN_ERR, "grab_cache_page for "
|
||||
"lower_page_index 0 failed\n");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
lower_a_ops = lower_inode->i_mapping->a_ops;
|
||||
rc = lower_a_ops->prepare_write(lower_file, header_page, 0, 8);
|
||||
if (rc) {
|
||||
if (rc == AOP_TRUNCATED_PAGE) {
|
||||
ecryptfs_release_lower_page(header_page, 0);
|
||||
goto retry;
|
||||
} else
|
||||
ecryptfs_release_lower_page(header_page, 1);
|
||||
goto out;
|
||||
}
|
||||
file_size = (u64)i_size_read(inode);
|
||||
ecryptfs_printk(KERN_DEBUG, "Writing size: [0x%.16x]\n", file_size);
|
||||
file_size = cpu_to_be64(file_size);
|
||||
header_virt = kmap_atomic(header_page, KM_USER0);
|
||||
memcpy(header_virt, &file_size, sizeof(u64));
|
||||
kunmap_atomic(header_virt, KM_USER0);
|
||||
flush_dcache_page(header_page);
|
||||
rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8);
|
||||
if (rc < 0)
|
||||
ecryptfs_printk(KERN_ERR, "Error commiting header page "
|
||||
"write\n");
|
||||
if (rc == AOP_TRUNCATED_PAGE) {
|
||||
ecryptfs_release_lower_page(header_page, 0);
|
||||
goto retry;
|
||||
} else
|
||||
ecryptfs_release_lower_page(header_page, 1);
|
||||
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty_sync(inode);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_write_inode_size_to_xattr(struct inode *lower_inode,
|
||||
struct inode *inode,
|
||||
struct dentry *ecryptfs_dentry,
|
||||
int lower_i_mutex_held)
|
||||
{
|
||||
ssize_t size;
|
||||
void *xattr_virt;
|
||||
struct dentry *lower_dentry;
|
||||
u64 file_size;
|
||||
int rc;
|
||||
|
||||
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;
|
||||
}
|
||||
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
|
||||
if (!lower_dentry->d_inode->i_op->getxattr ||
|
||||
!lower_dentry->d_inode->i_op->setxattr) {
|
||||
printk(KERN_WARNING
|
||||
"No support for setting xattr in lower filesystem\n");
|
||||
rc = -ENOSYS;
|
||||
kmem_cache_free(ecryptfs_xattr_cache, xattr_virt);
|
||||
goto out;
|
||||
}
|
||||
if (!lower_i_mutex_held)
|
||||
mutex_lock(&lower_dentry->d_inode->i_mutex);
|
||||
size = lower_dentry->d_inode->i_op->getxattr(lower_dentry,
|
||||
ECRYPTFS_XATTR_NAME,
|
||||
xattr_virt,
|
||||
PAGE_CACHE_SIZE);
|
||||
if (!lower_i_mutex_held)
|
||||
mutex_unlock(&lower_dentry->d_inode->i_mutex);
|
||||
if (size < 0)
|
||||
size = 8;
|
||||
file_size = (u64)i_size_read(inode);
|
||||
file_size = cpu_to_be64(file_size);
|
||||
memcpy(xattr_virt, &file_size, sizeof(u64));
|
||||
if (!lower_i_mutex_held)
|
||||
mutex_lock(&lower_dentry->d_inode->i_mutex);
|
||||
rc = lower_dentry->d_inode->i_op->setxattr(lower_dentry,
|
||||
ECRYPTFS_XATTR_NAME,
|
||||
xattr_virt, size, 0);
|
||||
if (!lower_i_mutex_held)
|
||||
mutex_unlock(&lower_dentry->d_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 file *lower_file,
|
||||
struct inode *lower_inode,
|
||||
struct inode *inode,
|
||||
struct dentry *ecryptfs_dentry,
|
||||
int lower_i_mutex_held)
|
||||
{
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
|
||||
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
|
||||
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
|
||||
return ecryptfs_write_inode_size_to_xattr(lower_inode, inode,
|
||||
ecryptfs_dentry,
|
||||
lower_i_mutex_held);
|
||||
else
|
||||
return ecryptfs_write_inode_size_to_header(lower_file,
|
||||
lower_inode,
|
||||
inode);
|
||||
}
|
||||
|
||||
int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
|
||||
struct file *lower_file,
|
||||
unsigned long lower_page_index, int byte_offset,
|
||||
int region_bytes)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
retry:
|
||||
*lower_page = grab_cache_page(lower_inode->i_mapping, lower_page_index);
|
||||
if (!(*lower_page)) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_ERR, "Error attempting to grab "
|
||||
"lower page with index [0x%.16x]\n",
|
||||
lower_page_index);
|
||||
goto out;
|
||||
}
|
||||
rc = lower_inode->i_mapping->a_ops->prepare_write(lower_file,
|
||||
(*lower_page),
|
||||
byte_offset,
|
||||
region_bytes);
|
||||
if (rc) {
|
||||
if (rc == AOP_TRUNCATED_PAGE) {
|
||||
ecryptfs_release_lower_page(*lower_page, 0);
|
||||
goto retry;
|
||||
} else {
|
||||
ecryptfs_printk(KERN_ERR, "prepare_write for "
|
||||
"lower_page_index = [0x%.16x] failed; rc = "
|
||||
"[%d]\n", lower_page_index, rc);
|
||||
ecryptfs_release_lower_page(*lower_page, 1);
|
||||
(*lower_page) = NULL;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_commit_lower_page
|
||||
*
|
||||
* Returns zero on success; non-zero on error
|
||||
*/
|
||||
int
|
||||
ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode,
|
||||
struct file *lower_file, int byte_offset,
|
||||
int region_size)
|
||||
{
|
||||
int page_locked = 1;
|
||||
int rc = 0;
|
||||
|
||||
rc = lower_inode->i_mapping->a_ops->commit_write(
|
||||
lower_file, lower_page, byte_offset, region_size);
|
||||
if (rc == AOP_TRUNCATED_PAGE)
|
||||
page_locked = 0;
|
||||
if (rc < 0) {
|
||||
ecryptfs_printk(KERN_ERR,
|
||||
"Error committing write; rc = [%d]\n", rc);
|
||||
} else
|
||||
rc = 0;
|
||||
ecryptfs_release_lower_page(lower_page, page_locked);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_copy_page_to_lower
|
||||
*
|
||||
* Used for plaintext pass-through; no page index interpolation
|
||||
* required.
|
||||
*/
|
||||
int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode,
|
||||
struct file *lower_file)
|
||||
{
|
||||
int rc = 0;
|
||||
struct page *lower_page;
|
||||
|
||||
rc = ecryptfs_get_lower_page(&lower_page, lower_inode, lower_file,
|
||||
page->index, 0, PAGE_CACHE_SIZE);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error attempting to get page "
|
||||
"at index [0x%.16x]\n", page->index);
|
||||
goto out;
|
||||
}
|
||||
/* TODO: aops */
|
||||
memcpy((char *)page_address(lower_page), page_address(page),
|
||||
PAGE_CACHE_SIZE);
|
||||
rc = ecryptfs_commit_lower_page(lower_page, lower_inode, lower_file,
|
||||
0, PAGE_CACHE_SIZE);
|
||||
if (rc)
|
||||
ecryptfs_printk(KERN_ERR, "Error attempting to commit page "
|
||||
"at index [0x%.16x]\n", page->index);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct kmem_cache *ecryptfs_xattr_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_commit_write
|
||||
* @file: The eCryptfs file object
|
||||
* @page: The eCryptfs page
|
||||
* @from: Ignored (we rotate the page IV on each write)
|
||||
* @to: Ignored
|
||||
*
|
||||
* 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_commit_write(struct file *file, struct page *page,
|
||||
unsigned from, unsigned to)
|
||||
{
|
||||
struct ecryptfs_page_crypt_context ctx;
|
||||
loff_t pos;
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
struct file *lower_file;
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
int rc;
|
||||
|
||||
inode = page->mapping->host;
|
||||
lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
mutex_lock(&lower_inode->i_mutex);
|
||||
crypt_stat = &ecryptfs_inode_to_private(file->f_path.dentry->d_inode)
|
||||
->crypt_stat;
|
||||
if (crypt_stat->flags & ECRYPTFS_NEW_FILE) {
|
||||
ecryptfs_printk(KERN_DEBUG, "ECRYPTFS_NEW_FILE flag set in "
|
||||
"crypt_stat at memory location [%p]\n", crypt_stat);
|
||||
crypt_stat->flags &= ~(ECRYPTFS_NEW_FILE);
|
||||
} else
|
||||
ecryptfs_printk(KERN_DEBUG, "Not a new file\n");
|
||||
ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page"
|
||||
"(page w/ index = [0x%.16x], to = [%d])\n", page->index,
|
||||
to);
|
||||
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%.16x]\n",
|
||||
page->index);
|
||||
goto out;
|
||||
}
|
||||
ctx.page = page;
|
||||
ctx.mode = ECRYPTFS_PREPARE_COMMIT_MODE;
|
||||
ctx.param.lower_file = lower_file;
|
||||
rc = ecryptfs_encrypt_page(&ctx);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper "
|
||||
"index [0x%.16x])\n", page->index);
|
||||
goto out;
|
||||
}
|
||||
inode->i_blocks = lower_inode->i_blocks;
|
||||
pos = (page->index << PAGE_CACHE_SHIFT) + to;
|
||||
if (pos > i_size_read(inode)) {
|
||||
i_size_write(inode, pos);
|
||||
ecryptfs_printk(KERN_DEBUG, "Expanded file size to "
|
||||
"[0x%.16x]\n", i_size_read(inode));
|
||||
}
|
||||
rc = ecryptfs_write_inode_size_to_metadata(lower_file, lower_inode,
|
||||
inode, file->f_dentry,
|
||||
ECRYPTFS_LOWER_I_MUTEX_HELD);
|
||||
if (rc)
|
||||
printk(KERN_ERR "Error writing inode size to metadata; "
|
||||
"rc = [%d]\n", rc);
|
||||
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty_sync(inode);
|
||||
out:
|
||||
if (rc < 0)
|
||||
ClearPageUptodate(page);
|
||||
else
|
||||
SetPageUptodate(page);
|
||||
mutex_unlock(&lower_inode->i_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_zeros
|
||||
* @file: The ecryptfs file
|
||||
* @index: The index in which we are writing
|
||||
* @start: The position after the last block of data
|
||||
* @num_zeros: The number of zeros to write
|
||||
*
|
||||
* Write a specified number of zero's to a page.
|
||||
*
|
||||
* (start + num_zeros) must be less than or equal to PAGE_CACHE_SIZE
|
||||
*/
|
||||
static
|
||||
int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros)
|
||||
{
|
||||
int rc = 0;
|
||||
struct page *tmp_page;
|
||||
char *tmp_page_virt;
|
||||
|
||||
tmp_page = ecryptfs_get1page(file, index);
|
||||
if (IS_ERR(tmp_page)) {
|
||||
ecryptfs_printk(KERN_ERR, "Error getting page at index "
|
||||
"[0x%.16x]\n", index);
|
||||
rc = PTR_ERR(tmp_page);
|
||||
goto out;
|
||||
}
|
||||
rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error preparing to write zero's "
|
||||
"to remainder of page at index [0x%.16x]\n",
|
||||
index);
|
||||
page_cache_release(tmp_page);
|
||||
goto out;
|
||||
}
|
||||
tmp_page_virt = kmap_atomic(tmp_page, KM_USER0);
|
||||
memset(((char *)tmp_page_virt + start), 0, num_zeros);
|
||||
kunmap_atomic(tmp_page_virt, KM_USER0);
|
||||
flush_dcache_page(tmp_page);
|
||||
rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros);
|
||||
if (rc < 0) {
|
||||
ecryptfs_printk(KERN_ERR, "Error attempting to write zero's "
|
||||
"to remainder of page at index [0x%.16x]\n",
|
||||
index);
|
||||
page_cache_release(tmp_page);
|
||||
goto out;
|
||||
}
|
||||
rc = 0;
|
||||
page_cache_release(tmp_page);
|
||||
out:
|
||||
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;
|
||||
}
|
||||
|
||||
static void ecryptfs_sync_page(struct page *page)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
struct page *lower_page;
|
||||
|
||||
inode = page->mapping->host;
|
||||
lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
/* NOTE: Recently swapped with grab_cache_page(), since
|
||||
* sync_page() just makes sure that pending I/O gets done. */
|
||||
lower_page = find_lock_page(lower_inode->i_mapping, page->index);
|
||||
if (!lower_page) {
|
||||
ecryptfs_printk(KERN_DEBUG, "find_lock_page failed\n");
|
||||
return;
|
||||
}
|
||||
lower_page->mapping->a_ops->sync_page(lower_page);
|
||||
ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n",
|
||||
lower_page->index);
|
||||
unlock_page(lower_page);
|
||||
page_cache_release(lower_page);
|
||||
}
|
||||
|
||||
struct address_space_operations ecryptfs_aops = {
|
||||
.writepage = ecryptfs_writepage,
|
||||
.readpage = ecryptfs_readpage,
|
||||
.prepare_write = ecryptfs_prepare_write,
|
||||
.commit_write = ecryptfs_commit_write,
|
||||
.bmap = ecryptfs_bmap,
|
||||
.sync_page = ecryptfs_sync_page,
|
||||
};
|
||||
255
fs/ecryptfs/netlink.c
Normal file
255
fs/ecryptfs/netlink.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
*
|
||||
* Copyright (C) 2004-2006 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 <net/sock.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/random.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
static struct sock *ecryptfs_nl_sock;
|
||||
|
||||
/**
|
||||
* ecryptfs_send_netlink
|
||||
* @data: The data to include as the payload
|
||||
* @data_len: The byte count of the data
|
||||
* @msg_ctx: The netlink context that will be used to handle the
|
||||
* response message
|
||||
* @msg_type: The type of netlink message to send
|
||||
* @msg_flags: The flags to include in the netlink header
|
||||
* @daemon_pid: The process id of the daemon to send the message to
|
||||
*
|
||||
* Sends the data to the specified daemon pid and uses the netlink
|
||||
* context element to store the data needed for validation upon
|
||||
* receiving the response. The data and the netlink context can be
|
||||
* null if just sending a netlink header is sufficient. Returns zero
|
||||
* upon sending the message; non-zero upon error.
|
||||
*/
|
||||
int ecryptfs_send_netlink(char *data, int data_len,
|
||||
struct ecryptfs_msg_ctx *msg_ctx, u16 msg_type,
|
||||
u16 msg_flags, pid_t daemon_pid)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct nlmsghdr *nlh;
|
||||
struct ecryptfs_message *msg;
|
||||
size_t payload_len;
|
||||
int rc;
|
||||
|
||||
payload_len = ((data && data_len) ? (sizeof(*msg) + data_len) : 0);
|
||||
skb = alloc_skb(NLMSG_SPACE(payload_len), GFP_KERNEL);
|
||||
if (!skb) {
|
||||
rc = -ENOMEM;
|
||||
ecryptfs_printk(KERN_ERR, "Failed to allocate socket buffer\n");
|
||||
goto out;
|
||||
}
|
||||
nlh = NLMSG_PUT(skb, daemon_pid, msg_ctx ? msg_ctx->counter : 0,
|
||||
msg_type, payload_len);
|
||||
nlh->nlmsg_flags = msg_flags;
|
||||
if (msg_ctx && payload_len) {
|
||||
msg = (struct ecryptfs_message *)NLMSG_DATA(nlh);
|
||||
msg->index = msg_ctx->index;
|
||||
msg->data_len = data_len;
|
||||
memcpy(msg->data, data, data_len);
|
||||
}
|
||||
rc = netlink_unicast(ecryptfs_nl_sock, skb, daemon_pid, 0);
|
||||
if (rc < 0) {
|
||||
ecryptfs_printk(KERN_ERR, "Failed to send eCryptfs netlink "
|
||||
"message; rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
rc = 0;
|
||||
goto out;
|
||||
nlmsg_failure:
|
||||
rc = -EMSGSIZE;
|
||||
kfree_skb(skb);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_process_nl_reponse
|
||||
* @skb: The socket buffer containing the netlink message of state
|
||||
* RESPONSE
|
||||
*
|
||||
* Processes a response message after sending a operation request to
|
||||
* userspace. Attempts to assign the msg to a netlink context element
|
||||
* at the index specified in the msg. The sk_buff and nlmsghdr must
|
||||
* be validated before this function. Returns zero upon delivery to
|
||||
* desired context element; non-zero upon delivery failure or error.
|
||||
*/
|
||||
static int ecryptfs_process_nl_response(struct sk_buff *skb)
|
||||
{
|
||||
struct nlmsghdr *nlh = (struct nlmsghdr*)skb->data;
|
||||
struct ecryptfs_message *msg = NLMSG_DATA(nlh);
|
||||
int rc;
|
||||
|
||||
if (skb->len - NLMSG_HDRLEN - sizeof(*msg) != msg->data_len) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_ERR, "Received netlink message with "
|
||||
"incorrectly specified data length\n");
|
||||
goto out;
|
||||
}
|
||||
rc = ecryptfs_process_response(msg, NETLINK_CREDS(skb)->uid,
|
||||
NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq);
|
||||
if (rc)
|
||||
printk(KERN_ERR
|
||||
"Error processing response message; rc = [%d]\n", rc);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_process_nl_helo
|
||||
* @skb: The socket buffer containing the nlmsghdr in HELO state
|
||||
*
|
||||
* Gets uid and pid of the skb and adds the values to the daemon id
|
||||
* hash. Returns zero after adding a new daemon id to the hash list;
|
||||
* non-zero otherwise.
|
||||
*/
|
||||
static int ecryptfs_process_nl_helo(struct sk_buff *skb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ecryptfs_process_helo(ECRYPTFS_TRANSPORT_NETLINK,
|
||||
NETLINK_CREDS(skb)->uid,
|
||||
NETLINK_CREDS(skb)->pid);
|
||||
if (rc)
|
||||
printk(KERN_WARNING "Error processing HELO; rc = [%d]\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_process_nl_quit
|
||||
* @skb: The socket buffer containing the nlmsghdr in QUIT state
|
||||
*
|
||||
* Gets uid and pid of the skb and deletes the corresponding daemon
|
||||
* id, if it is the registered that is requesting the
|
||||
* deletion. Returns zero after deleting the desired daemon id;
|
||||
* non-zero otherwise.
|
||||
*/
|
||||
static int ecryptfs_process_nl_quit(struct sk_buff *skb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ecryptfs_process_quit(NETLINK_CREDS(skb)->uid,
|
||||
NETLINK_CREDS(skb)->pid);
|
||||
if (rc)
|
||||
printk(KERN_WARNING
|
||||
"Error processing QUIT message; rc = [%d]\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_receive_nl_message
|
||||
*
|
||||
* Callback function called by netlink system when a message arrives.
|
||||
* If the message looks to be valid, then an attempt is made to assign
|
||||
* it to its desired netlink context element and wake up the process
|
||||
* that is waiting for a response.
|
||||
*/
|
||||
static void ecryptfs_receive_nl_message(struct sock *sk, int len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct nlmsghdr *nlh;
|
||||
int rc = 0; /* skb_recv_datagram requires this */
|
||||
|
||||
receive:
|
||||
skb = skb_recv_datagram(sk, 0, 0, &rc);
|
||||
if (rc == -EINTR)
|
||||
goto receive;
|
||||
else if (rc < 0) {
|
||||
ecryptfs_printk(KERN_ERR, "Error occurred while "
|
||||
"receiving eCryptfs netlink message; "
|
||||
"rc = [%d]\n", rc);
|
||||
return;
|
||||
}
|
||||
nlh = (struct nlmsghdr *)skb->data;
|
||||
if (!NLMSG_OK(nlh, skb->len)) {
|
||||
ecryptfs_printk(KERN_ERR, "Received corrupt netlink "
|
||||
"message\n");
|
||||
goto free;
|
||||
}
|
||||
switch (nlh->nlmsg_type) {
|
||||
case ECRYPTFS_NLMSG_RESPONSE:
|
||||
if (ecryptfs_process_nl_response(skb)) {
|
||||
ecryptfs_printk(KERN_WARNING, "Failed to "
|
||||
"deliver netlink response to "
|
||||
"requesting operation\n");
|
||||
}
|
||||
break;
|
||||
case ECRYPTFS_NLMSG_HELO:
|
||||
if (ecryptfs_process_nl_helo(skb)) {
|
||||
ecryptfs_printk(KERN_WARNING, "Failed to "
|
||||
"fulfill HELO request\n");
|
||||
}
|
||||
break;
|
||||
case ECRYPTFS_NLMSG_QUIT:
|
||||
if (ecryptfs_process_nl_quit(skb)) {
|
||||
ecryptfs_printk(KERN_WARNING, "Failed to "
|
||||
"fulfill QUIT request\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ecryptfs_printk(KERN_WARNING, "Dropping netlink "
|
||||
"message of unrecognized type [%d]\n",
|
||||
nlh->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
free:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_init_netlink
|
||||
*
|
||||
* Initializes the daemon id hash list, netlink context array, and
|
||||
* necessary locks. Returns zero upon success; non-zero upon error.
|
||||
*/
|
||||
int ecryptfs_init_netlink(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ecryptfs_nl_sock = netlink_kernel_create(NETLINK_ECRYPTFS, 0,
|
||||
ecryptfs_receive_nl_message,
|
||||
THIS_MODULE);
|
||||
if (!ecryptfs_nl_sock) {
|
||||
rc = -EIO;
|
||||
ecryptfs_printk(KERN_ERR, "Failed to create netlink socket\n");
|
||||
goto out;
|
||||
}
|
||||
ecryptfs_nl_sock->sk_sndtimeo = ECRYPTFS_DEFAULT_SEND_TIMEOUT;
|
||||
rc = 0;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_release_netlink
|
||||
*
|
||||
* Frees all memory used by the netlink context array and releases the
|
||||
* netlink socket.
|
||||
*/
|
||||
void ecryptfs_release_netlink(void)
|
||||
{
|
||||
if (ecryptfs_nl_sock && ecryptfs_nl_sock->sk_socket)
|
||||
sock_release(ecryptfs_nl_sock->sk_socket);
|
||||
ecryptfs_nl_sock = NULL;
|
||||
}
|
||||
180
fs/ecryptfs/super.c
Normal file
180
fs/ecryptfs/super.c
Normal file
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* 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/seq_file.h>
|
||||
#include <linux/crypto.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
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 *ecryptfs_inode;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache,
|
||||
GFP_KERNEL);
|
||||
if (unlikely(!ecryptfs_inode))
|
||||
goto out;
|
||||
ecryptfs_init_crypt_stat(&ecryptfs_inode->crypt_stat);
|
||||
inode = &ecryptfs_inode->vfs_inode;
|
||||
out:
|
||||
return inode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
ecryptfs_destruct_crypt_stat(&inode_info->crypt_stat);
|
||||
kmem_cache_free(ecryptfs_inode_info_cache, inode_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_init_inode
|
||||
* @inode: The ecryptfs inode
|
||||
*
|
||||
* Set up the ecryptfs inode.
|
||||
*/
|
||||
void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode)
|
||||
{
|
||||
ecryptfs_set_inode_lower(inode, lower_inode);
|
||||
inode->i_ino = lower_inode->i_ino;
|
||||
inode->i_version++;
|
||||
inode->i_op = &ecryptfs_main_iops;
|
||||
inode->i_fop = &ecryptfs_main_fops;
|
||||
inode->i_mapping->a_ops = &ecryptfs_aops;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_put_super
|
||||
* @sb: Pointer to the ecryptfs super block
|
||||
*
|
||||
* Final actions when unmounting a file system.
|
||||
* This will handle deallocation and release of our private data.
|
||||
*/
|
||||
static void ecryptfs_put_super(struct super_block *sb)
|
||||
{
|
||||
struct ecryptfs_sb_info *sb_info = ecryptfs_superblock_to_private(sb);
|
||||
|
||||
ecryptfs_destruct_mount_crypt_stat(&sb_info->mount_crypt_stat);
|
||||
kmem_cache_free(ecryptfs_sb_info_cache, sb_info);
|
||||
ecryptfs_set_superblock_private(sb, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return vfs_statfs(ecryptfs_dentry_to_lower(dentry), buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_clear_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_clear_inode(struct inode *inode)
|
||||
{
|
||||
iput(ecryptfs_inode_to_lower(inode));
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_show_options
|
||||
*
|
||||
* Prints the directory we are currently mounted over.
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_show_options(struct seq_file *m, struct vfsmount *mnt)
|
||||
{
|
||||
struct super_block *sb = mnt->mnt_sb;
|
||||
struct dentry *lower_root_dentry = ecryptfs_dentry_to_lower(sb->s_root);
|
||||
struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(sb->s_root);
|
||||
char *tmp_page;
|
||||
char *path;
|
||||
int rc = 0;
|
||||
|
||||
tmp_page = (char *)__get_free_page(GFP_KERNEL);
|
||||
if (!tmp_page) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
path = d_path(lower_root_dentry, lower_mnt, tmp_page, PAGE_SIZE);
|
||||
if (IS_ERR(path)) {
|
||||
rc = PTR_ERR(path);
|
||||
goto out;
|
||||
}
|
||||
seq_printf(m, ",dir=%s", path);
|
||||
free_page((unsigned long)tmp_page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
const struct super_operations ecryptfs_sops = {
|
||||
.alloc_inode = ecryptfs_alloc_inode,
|
||||
.destroy_inode = ecryptfs_destroy_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.put_super = ecryptfs_put_super,
|
||||
.statfs = ecryptfs_statfs,
|
||||
.remount_fs = NULL,
|
||||
.clear_inode = ecryptfs_clear_inode,
|
||||
.show_options = ecryptfs_show_options
|
||||
};
|
||||
Reference in New Issue
Block a user