2005-11-19 04:11:36

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 0/12: eCryptfs] eCryptfs version 0.1

This is a follow-up set of patches in response to comments made after
our first submission to the LKML at the beginning of this month. We
were able to track down and resolve several bugs, and we feel that
eCryptfs is now ready to be merged into the -mm tree as an
experimental filesystem.

We have successfully run a series of tests, including FSX,
Connectathon, and Bonnie. In addition, we are able to successfully
compile the Linux kernel under eCryptfs, both with and without
multiple threads on a multi-processor PPC64 machine.

We are able to invoke a bug that terminates a process when we stress
the filesystem with multiple concurrent operations (FSX, multiple
Connectathon jobs, and a kernel compile running simultaneously), but
we cannot oops the kernel with any of our test cases.

eCryptfs utilizes David Howells' keyring; at mount, eCryptfs version
0.1 expects an existing authentication token in the user's session
keyring. The tarball containing the code to do this is available from
the eCryptfs SourceForge site:

http://sourceforge.net/projects/ecryptfs/

Several features demonstrated in prototypes of eCryptfs at OLS in the
past have been left out of this release until they can be thoroughly
tested; the reduced complexity of this patch set should make it easier
to evaluate for initial inclusion into the Linux kernel. Future
updates will provide policy support, which will entail per-file
passphrase and per-file public key support.

Thanks,
Phillip


2005-11-19 04:14:49

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 1/12: eCryptfs] Makefile and Kconfig

These patches modify fs/Makefile and fs/Kconfig to provide build
support for eCryptfs.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

Kconfig | 10 ++++++++++
Makefile | 1 +
2 files changed, 11 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/Makefile 2005-11-18 11:38:19.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/Makefile 2005-11-18 11:40:30.000000000 -0600
@@ -67,6 +67,7 @@
obj-$(CONFIG_ISO9660_FS) += isofs/
obj-$(CONFIG_DEVFS_FS) += devfs/
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
+obj-$(CONFIG_ECRYPTFS) += ecryptfs/
obj-$(CONFIG_HFS_FS) += hfs/
obj-$(CONFIG_VXFS_FS) += freevxfs/
obj-$(CONFIG_NFS_FS) += nfs/
--- linux-2.6.15-rc1-mm1/fs/Kconfig 2005-11-18 11:38:19.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/Kconfig 2005-11-18 11:40:30.000000000 -0600
@@ -989,6 +989,16 @@

If unsure, say N.

+config ECRYPTFS
+ tristate "eCrypt filesystem layer support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && KEYS && CRYPTO
+ help
+ Encrypted filesystem that operates on the VFS layer. See
+ Documentation/ecryptfs.txt to learn more about eCryptfs.
+
+ To compile this file system support as a module, choose M here: the
+ module will be called ecryptfs.
+
config HFS_FS
tristate "Apple Macintosh file system support (EXPERIMENTAL)"
depends on EXPERIMENTAL

2005-11-19 04:16:16

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 2/12: eCryptfs] Documentation

This patch provides documentation for using eCryptfs.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

ecryptfs.txt | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 81 insertions(+)
--- linux-2.6.15-rc1-mm1/Documentation/ecryptfs.txt 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/Documentation/ecryptfs.txt 2005-11-18 11:46:12.000000000 -0600
@@ -0,0 +1,81 @@
+eCryptfs: A stacked cryptographic filesystem for Linux
+Maintainer: Phillip Hellewell
+Lead developer: Michael A. Halcrow <[email protected]>
+Developers: Michael C. Thompson
+ Kent Yoder
+Current Release Version: 0.1
+
+This software is currently undergoing development. Make sure to
+maintain a backup copy of any data you write into eCryptfs.
+
+eCryptfs requires the userspace tools downloadable from the
+SourceForge site:
+
+http://sourceforge.net/projects/ecryptfs/
+
+Requirements include:
+ - Kernel version 2.6.15-rc1-mm1 or higher
+ - eCryptfs will work with 2.6.14, but you will need the
+ export_user_type.patch applied
+ - David Howells' userspace keyring headers and libraries, obtainable
+ from http://people.redhat.com/~dhowells/keyutils/
+ - (You will need to apply the keyutil_h_fix.diff patch to version 0.3)
+ - GnuPG Made Easy (GPGME)
+ - Building the kernel with:
+ - Cryptographic API
+ - Blowfish cipher
+ - Key retention support
+ - Filesystems->Miscellaneous filesystems->eCryptfs
+
+
+BUILD AND INSTALL INSTRUCTIONS
+
+If you are installing from the patch set:
+1) Apply export_user_type.patch to the kernel if you are running
+ kernel version 2.6.14
+2) Apply all patches in the patches/ directory to the kernel
+
+Once eCryptfs is already in the kernel:
+3) Select build options (see Requirements) and build kernel
+4) Apply keyutil_h_fix.diff to the keyutils if you are running version
+ 0.3
+5) Run make and make install from the request-key/ directory.
+
+
+MOUNT-WIDE PASSPHRASE
+
+Create a new directory into which eCryptfs will write its encrypted
+files (i.e., /root/crypt). Then, create the mount point directory
+(i.e., /mnt/crypt). Now it's time to mount eCryptfs:
+
+mount -t ecryptfs /root/crypt /mnt/crypt
+
+You should be prompted for a passphrase and a salt (the salt may be
+blank).
+
+Try writing a new file:
+
+echo "Hello, World" > /mnt/crypt/hello.txt
+
+The operation will complete. Notice that there is a new file in
+/root/crypt that is 3 pages (12288 bytes) in size. This is the
+encrypted underlying file for what you just wrote. To test reading,
+from start to finish, you need to clear the user session keyring:
+
+keyctl clear @u
+
+Then umount /mnt/crypt and mount again per the instructions given
+above.
+
+cat /mnt/crypt/hello.txt
+
+
+NOTES
+
+eCryptfs should only be mounted on (1) empty directories or (2)
+directories containing files only created by eCryptfs. If you mount a
+directory that has pre-existing files not created by eCryptfs, then
+behavior is undefined.
+
+Mike Halcrow
[email protected]

2005-11-19 04:17:00

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 3/12: eCryptfs] Makefile

Makefile for eCryptfs.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

Makefile | 7 +++++++
1 files changed, 7 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/Makefile 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/Makefile 2005-11-08 10:43:42.000000000 -0600
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux 2.6 eCryptfs
+#
+
+obj-$(CONFIG_ECRYPTFS) += ecryptfs.o
+
+ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o

2005-11-19 04:17:44

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 4/12: eCryptfs] Main module functions

Provides functions to initialize the eCryptfs module and eCryptfs
mounts. Allocates and deallocates kmem_cache regions.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

main.c | 734 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 734 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/main.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/main.c 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,734 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * 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/netlink.h>
+#include <linux/mount.h>
+#include <linux/dcache.h>
+#include <linux/pagemap.h>
+#include <linux/key.h>
+#include <linux/parser.h>
+#include <keys/user-type.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * Module parameter that defines the ecryptfs_verbosity level.
+ */
+int ecryptfs_verbosity = 1;
+
+module_param(ecryptfs_verbosity, int, 0);
+MODULE_PARM_DESC(ecryptfs_verbosity,
+ "Initial verbosity level (0 or 1; defaults to "
+ "0, which is Quiet)");
+
+void __ecryptfs_printk(int verb, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if ((ecryptfs_verbosity >= verb) && printk_ratelimit())
+ vprintk(fmt, args);
+ va_end(args);
+}
+
+/**
+ * Interposes upper and lower dentries.
+ * This function will call an ecryptfs_inode into existance through the call to
+ * iget(sb, lower_inode->i_ino).
+ *
+ * @param lower_dentry existing dentry in the lower filesystem
+ * @param dentry ecryptfs' dentry
+ * @param sb eCryptfs's super_block
+ * @param flag If set to true, then d_add is called, else d_instantiate
+ * is called.
+ * @return 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;
+ int err = 0;
+ struct inode *inode;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], "
+ "lower_dentry->d_name.name = [%s], dentry = "
+ "[%p], dentry->d_name.name = [%s], sb = [%p]; "
+ "flag = [%.4x]; lower_dentry->d_count "
+ "= [%d]; dentry->d_count = [%d]\n", lower_dentry,
+ lower_dentry->d_name.name, dentry, dentry->d_name.name,
+ sb, flag, atomic_read(&lower_dentry->d_count),
+ atomic_read(&dentry->d_count));
+ lower_inode = lower_dentry->d_inode;
+ if (lower_inode->i_sb != ECRYPTFS_SUPERBLOCK_TO_LOWER(sb)) {
+ err = -EXDEV;
+ goto out;
+ }
+ inode = iget(sb, lower_inode->i_ino);
+ if (!inode) {
+ err = -EACCES;
+ goto out;
+ }
+ /* This check is required here because if we failed to allocated the
+ * required space for an inode_info_cache struct, then the only way
+ * we know we failed, is by the pointer being NULL */
+ if (!ECRYPTFS_INODE_TO_PRIVATE(inode)) {
+ ecryptfs_printk(1, KERN_ERR, "Out of memory. Failure to "
+ "allocate memory in ecryptfs_read_inode.\n");
+ err = -ENOMEM;
+ BUG();
+ goto out;
+ }
+
+ if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
+ ECRYPTFS_INODE_TO_LOWER(inode) = igrab(lower_inode);
+ /* If we are still NULL at this point, igrab failed.
+ * We are _NOT_ supposed to be failing here */
+ if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
+ BUG();
+ err = -EINVAL;
+ goto out;
+ }
+ }
+ 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;
+ /* TODO: Is there a better way to identify if the inode is
+ * special? */
+ if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
+ S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(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);
+ ecryptfs_copy_attr_all(inode, lower_inode);
+ /* This size will be overwritten for real files w/ headers and
+ * other metadata */
+ ecryptfs_copy_inode_size(inode, lower_inode);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = %d\n", err);
+ return err;
+}
+
+/**
+ * For rvalue references; better to just use the macro.
+ */
+inline struct dentry *ecryptfs_lower_dentry(struct dentry *dentry)
+{
+ ASSERT(dentry);
+ return ECRYPTFS_DENTRY_TO_LOWER(dentry);
+}
+
+enum { ecryptfs_opt_sig, ecryptfs_opt_debug, ecryptfs_opt_cipher,
+ ecryptfs_opt_err
+};
+
+static match_table_t tokens = {
+ {ecryptfs_opt_sig, "sig=%s"},
+ {ecryptfs_opt_debug, "debug=%u"},
+ {ecryptfs_opt_cipher, "cipher=%s"},
+ {ecryptfs_opt_err, NULL}
+};
+
+/**
+ * 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.
+ *
+ * N.B. 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.
+ *
+ * @param sb
+ * @param options
+ * @return 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;
+ struct key *auth_tok_key = NULL;
+ struct ecryptfs_auth_tok *auth_tok = NULL;
+ struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->mount_crypt_stats);
+ 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;
+ int cipher_name_len;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; options = [%s]\n", options);
+ 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:
+ sig_src = args[0].from;
+ sig_dst =
+ mount_crypt_stats->global_auth_tok_sig;
+ memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX);
+ sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0';
+ ecryptfs_printk(1, KERN_NOTICE,
+ "The mount_crypt_stats "
+ "global_auth_tok_sig set to: "
+ "[%s]\n", sig_dst);
+ sig_set = 1;
+ break;
+ case ecryptfs_opt_debug:
+ debug_src = args[0].from;
+ ecryptfs_verbosity =
+ (int)simple_strtol(debug_src, &debug_src,
+ 0);
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Verbosity set to [%d]" "\n",
+ ecryptfs_verbosity);
+ break;
+ case ecryptfs_opt_cipher:
+ cipher_name_src = args[0].from;
+ cipher_name_dst =
+ mount_crypt_stats->
+ global_default_cipher_name;
+ strncpy(cipher_name_dst, cipher_name_src,
+ ECRYPTFS_MAX_CIPHER_NAME_SIZE);
+ ecryptfs_printk(1, KERN_NOTICE,
+ "The mount_crypt_stats "
+ "global_default_cipher_name set to: "
+ "[%s]\n", cipher_name_dst);
+ cipher_name_set = 1;
+ break;
+ case ecryptfs_opt_err:
+ default:
+ ecryptfs_printk(1, KERN_WARNING,
+ "eCryptfs: unrecognized option '%s'\n",
+ options);
+ }
+ }
+ /* Do not support lack of mount-wide signature in 0.1
+ * release */
+ if (!sig_set) {
+ rc = -EINVAL;
+ ecryptfs_printk(0, 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_stats->global_default_cipher_name,
+ ECRYPTFS_DEFAULT_CIPHER, cipher_name_len);
+ mount_crypt_stats->global_default_cipher_name[cipher_name_len]
+ = '\0';
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Requesting the key with description: "
+ "[%s]\n", mount_crypt_stats->global_auth_tok_sig);
+ /* N.B. 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_stats->global_auth_tok_sig,
+ NULL);
+ if (!auth_tok_key || IS_ERR(auth_tok_key)) {
+ ecryptfs_printk(0, KERN_ERR, "Could not find key with "
+ "description: [%s]\n",
+ mount_crypt_stats->global_auth_tok_sig);
+ process_request_key_err(PTR_ERR(auth_tok_key));
+ rc = -EINVAL;
+ goto out;
+ }
+ auth_tok = (struct ecryptfs_auth_tok *)KEY_PAYLOAD_DATA(auth_tok_key);
+ if (auth_tok->instanceof != ECRYPTFS_PASSWORD) {
+ ecryptfs_printk(0, KERN_ERR, "Invalid auth_tok structure "
+ "returned from key");
+ rc = -EINVAL;
+ goto out;
+ }
+ mount_crypt_stats->global_auth_tok_key = auth_tok_key;
+ mount_crypt_stats->global_auth_tok = auth_tok;
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+kmem_cache_t *ecryptfs_sb_info_cache;
+
+/**
+ * Preform the cleanup for ecryptfs_read_super()
+ */
+static inline void ecryptfs_cleanup_read_super(struct super_block *sb)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], sb->s_root = [%p] "
+ "ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb) = [%p] "
+ "sb->s_root.d_name->name = [%s]\n", sb,
+ sb->s_root, ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb),
+ sb->s_root->d_name.name);
+ up_write(&sb->s_umount);
+ deactivate_super(sb);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Sets up what we can of the sb, rest is done in ecryptfs_read_super
+ *
+ * @param sb The ecryptfs super block
+ * @param raw_data The options passed to mount
+ * @param silent Not used but required by function prototype
+ * @return Zero on success; non-zero otherwise
+ */
+static int
+ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent)
+{
+ int err = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p] raw_data = [%s] "
+ "silent = [%d]\n", sb, (char *)raw_data, silent);
+ /* Released in ecryptfs_put_super() */
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) =
+ kmem_cache_alloc(ecryptfs_sb_info_cache, SLAB_KERNEL);
+ if (!ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb)) {
+ ecryptfs_printk(0, KERN_WARNING, "Out of memory\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ memset(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb), 0,
+ sizeof(struct ecryptfs_sb_info));
+ 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(0, KERN_ERR, "d_alloc failed\n");
+ err = -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_DENTRY_TO_PRIVATE_SM(sb->s_root) =
+ (struct ecryptfs_dentry_info *)
+ kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL);
+ if (!ECRYPTFS_DENTRY_TO_PRIVATE_SM(sb->s_root)) {
+ ecryptfs_printk(0, KERN_ERR,
+ "dentry_info_cache alloc failed\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ memset(ECRYPTFS_DENTRY_TO_PRIVATE(sb->s_root), 0,
+ sizeof(struct ecryptfs_dentry_info));
+ err = 0;
+out:
+ /* Should be able to rely on deactive_super called from get_sb_nodev */
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return err;
+}
+
+/**
+ * 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 err;
+ struct nameidata nd;
+ struct dentry *lower_root;
+
+ memset(&nd, 0, sizeof(struct nameidata));
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], dev_name = [%s]\n",
+ sb, dev_name);
+ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
+ if (err) {
+ ecryptfs_printk(0, KERN_WARNING, "path_lookup() failed\n");
+ goto out_free;
+ }
+ lower_root = nd.dentry;
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt = nd.mnt;
+ if (!lower_root->d_inode) {
+ ecryptfs_printk(0, KERN_WARNING,
+ "No directory to interpose on\n");
+ err = -ENOENT;
+ goto out_free;
+ }
+ ECRYPTFS_SUPERBLOCK_TO_LOWER(sb) = lower_root->d_sb;
+ sb->s_maxbytes = lower_root->d_sb->s_maxbytes;
+ ECRYPTFS_DENTRY_TO_LOWER(sb->s_root) = lower_root;
+ if ((err = ecryptfs_interpose(lower_root, sb->s_root, sb, 0)))
+ goto out_free;
+ err = 0;
+ goto out;
+out_free:
+ path_release(&nd);
+ ecryptfs_cleanup_read_super(sb);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return err;
+}
+
+/**
+ * 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 struct super_block *ecryptfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *raw_data)
+{
+ int err;
+ struct super_block *sb = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; fs_type = [%p], flags = [%d],"
+ " dev_name = [%s], raw_data = [%s]\n",
+ fs_type, flags, dev_name, (char *)raw_data);
+ sb = get_sb_nodev(fs_type, flags, raw_data, ecryptfs_fill_super);
+ if (IS_ERR(sb)) {
+ ecryptfs_printk(0, KERN_ERR, "Getting sb failed. "
+ "sb = [%p]\n", sb);
+ goto out;
+ }
+ err = ecryptfs_parse_options(sb, raw_data);
+ if (err) {
+ sb = ERR_PTR(err);
+ goto out;
+ }
+ err = ecryptfs_read_super(sb, dev_name);
+ if (err) {
+ sb = ERR_PTR(err);
+ ecryptfs_printk(0, KERN_ERR, "Reading sb failed. "
+ "sb = [%p]\n", sb);
+ }
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%p]\n", sb);
+ return sb;
+}
+
+/**
+ * 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)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], sb->s_root = [%p] "
+ "ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb) = [%p]\n", sb,
+ sb->s_root, ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb));
+ generic_shutdown_super(sb);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+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
+};
+
+/**
+ * Initializes the ecryptfs_inode_info_cache when it is created
+ */
+static void
+inode_info_init_once(void *vptr, kmem_cache_t * 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);
+}
+
+/* This provides a means of backing out cache creations out of the kernel
+ * so that we can elegantly fail should we run out of memory.
+ */
+#define ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE 0x0001
+#define ECRYPTFS_AUTH_TOK_PKT_SET_CACHE 0x0002
+#define ECRYPTFS_AUTH_TOK_REQUEST_CACHE 0x0004
+#define ECRYPTFS_AUTH_TOK_REQUEST_BLOB_CACHE 0x0008
+#define ECRYPTFS_FILE_INFO_CACHE 0x0010
+#define ECRYPTFS_DENTRY_INFO_CACHE 0x0020
+#define ECRYPTFS_INODE_INFO_CACHE 0x0040
+#define ECRYPTFS_SB_INFO_CACHE 0x0080
+#define ECRYPTFS_HEADER_CACHE_0 0x0100
+#define ECRYPTFS_HEADER_CACHE_1 0x0200
+#define ECRYPTFS_HEADER_CACHE_2 0x0400
+#define ECRYPTFS_LOWER_PAGE_CACHE 0x0800
+#define ECRYPTFS_CACHE_CREATION_SUCCESS 0x0FF1
+
+static short ecryptfs_allocated_caches;
+
+/**
+ * @return Zero on success; non-zero otherwise
+ *
+ * Sets ecryptfs_allocated_caches with flags so that we can
+ * free created caches should we run out of memory during
+ * creation period.
+ *
+ * The overhead for doing this is offset by the fact that we
+ * only do this once, and that should there be insufficient
+ * memory, then we can elegantly fail, and not leave extra
+ * caches around, or worse, panic the kernel trying to free
+ * something that's not there.
+ */
+static int ecryptfs_init_kmem_caches(void)
+{
+ int rc = 0;
+
+ ecryptfs_auth_tok_list_item_cache =
+ kmem_cache_create("ecryptfs_auth_tok_list_item",
+ sizeof(struct ecryptfs_auth_tok_list_item),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (ecryptfs_auth_tok_list_item_cache)
+ rc |= ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_auth_tok_list_item "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_file_info_cache =
+ kmem_cache_create("ecryptfs_file_cache",
+ sizeof(struct ecryptfs_file_info),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (ecryptfs_file_info_cache)
+ rc |= ECRYPTFS_FILE_INFO_CACHE;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_file_cache "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_dentry_info_cache =
+ kmem_cache_create("ecryptfs_dentry_cache",
+ sizeof(struct ecryptfs_dentry_info),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (ecryptfs_dentry_info_cache)
+ rc |= ECRYPTFS_DENTRY_INFO_CACHE;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_dentry_cache "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_inode_info_cache =
+ kmem_cache_create("ecryptfs_inode_cache",
+ sizeof(struct ecryptfs_inode_info), 0,
+ SLAB_HWCACHE_ALIGN, inode_info_init_once, NULL);
+ if (ecryptfs_inode_info_cache)
+ rc |= ECRYPTFS_INODE_INFO_CACHE;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_inode_cache "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_sb_info_cache =
+ kmem_cache_create("ecryptfs_sb_cache",
+ sizeof(struct ecryptfs_sb_info),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (ecryptfs_sb_info_cache)
+ rc |= ECRYPTFS_SB_INFO_CACHE;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_sb_cache "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_header_cache_0 =
+ kmem_cache_create("ecryptfs_headers_0", PAGE_CACHE_SIZE,
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (ecryptfs_header_cache_0)
+ rc |= ECRYPTFS_HEADER_CACHE_0;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_0 "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_header_cache_1 =
+ kmem_cache_create("ecryptfs_headers_1", PAGE_CACHE_SIZE,
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (ecryptfs_header_cache_1)
+ rc |= ECRYPTFS_HEADER_CACHE_1;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_1 "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_header_cache_2 =
+ kmem_cache_create("ecryptfs_headers_2", PAGE_CACHE_SIZE,
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (ecryptfs_header_cache_2)
+ rc |= ECRYPTFS_HEADER_CACHE_2;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_2 "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_lower_page_cache =
+ kmem_cache_create("ecryptfs_lower_page_cache", PAGE_CACHE_SIZE,
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (ecryptfs_lower_page_cache)
+ rc |= ECRYPTFS_LOWER_PAGE_CACHE;
+ else
+ ecryptfs_printk(0, KERN_WARNING, "ecryptfs_lower_page_cache "
+ "kmem_cache_create failed\n");
+
+ ecryptfs_allocated_caches = rc;
+ rc = ECRYPTFS_CACHE_CREATION_SUCCESS ^ rc;
+ return rc;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+static int ecryptfs_free_kmem_caches(void)
+{
+ int rc = 0;
+ int err;
+
+ if (ecryptfs_allocated_caches & ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE) {
+ rc = kmem_cache_destroy(ecryptfs_auth_tok_list_item_cache);
+ if (rc)
+ ecryptfs_printk(0, KERN_WARNING,
+ "Not all ecryptfs_auth_tok_"
+ "list_item_cache structures were "
+ "freed\n");
+ }
+ if (ecryptfs_allocated_caches & ECRYPTFS_FILE_INFO_CACHE) {
+ err = kmem_cache_destroy(ecryptfs_file_info_cache);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING,
+ "Not all ecryptfs_file_info_"
+ "cache regions were freed\n");
+ rc |= err;
+ }
+ if (ecryptfs_allocated_caches & ECRYPTFS_DENTRY_INFO_CACHE) {
+ err = kmem_cache_destroy(ecryptfs_dentry_info_cache);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING,
+ "Not all ecryptfs_dentry_info_"
+ "cache regions were freed\n");
+ rc |= err;
+ }
+ if (ecryptfs_allocated_caches & ECRYPTFS_INODE_INFO_CACHE) {
+ err = kmem_cache_destroy(ecryptfs_inode_info_cache);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING,
+ "Not all ecryptfs_inode_info_"
+ "cache regions were freed\n");
+ rc |= err;
+ }
+ if (ecryptfs_allocated_caches & ECRYPTFS_SB_INFO_CACHE) {
+ err = kmem_cache_destroy(ecryptfs_sb_info_cache);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING,
+ "Not all ecryptfs_sb_info_"
+ "cache regions were freed\n");
+ rc |= err;
+ }
+ if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_0) {
+ err = kmem_cache_destroy(ecryptfs_header_cache_0);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+ "header_cache_0 regions were freed\n");
+ rc |= err;
+ }
+ if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_1) {
+ err = kmem_cache_destroy(ecryptfs_header_cache_1);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+ "header_cache_1 regions were freed\n");
+ rc |= err;
+ }
+ if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_2) {
+ err = kmem_cache_destroy(ecryptfs_header_cache_2);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+ "header_cache_2 regions were freed\n");
+ rc |= err;
+ }
+ if (ecryptfs_allocated_caches & ECRYPTFS_LOWER_PAGE_CACHE) {
+ err = kmem_cache_destroy(ecryptfs_lower_page_cache);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+ "lower_page_cache regions were "
+ "freed\n");
+ rc |= err;
+ }
+ return rc;
+}
+
+static int __init init_ecryptfs_fs(void)
+{
+ int rc;
+
+ rc = ecryptfs_init_kmem_caches();
+ if (rc) {
+ ecryptfs_printk(0, KERN_EMERG, "Failure occured while "
+ "attempting to create caches [CREATED: %x]."
+ "Now freeing caches.\n",
+ ecryptfs_allocated_caches);
+ ecryptfs_free_kmem_caches();
+ return -ENOMEM;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Registering eCryptfs\n");
+ return register_filesystem(&ecryptfs_fs_type);
+}
+
+static void __exit exit_ecryptfs_fs(void)
+{
+ int rc;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Unregistering eCryptfs\n");
+ unregister_filesystem(&ecryptfs_fs_type);
+ rc = ecryptfs_free_kmem_caches();
+ if (rc)
+ ecryptfs_printk(0, KERN_EMERG, "Failure occured while "
+ "attempting to free caches: [%d]\n", rc);
+}
+
+MODULE_AUTHOR("Michael A. Halcrow <[email protected]>");
+MODULE_DESCRIPTION("eCryptfs");
+
+MODULE_LICENSE("GPL");
+
+module_init(init_ecryptfs_fs)
+module_exit(exit_ecryptfs_fs)

2005-11-19 04:19:12

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 6/12: eCryptfs] Superblock operations

eCryptfs superblock operations and inode
allocation/deallocation/initialization functions.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

super.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 250 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/super.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/super.c 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,250 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * 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 "ecryptfs_kernel.h"
+
+kmem_cache_t *ecryptfs_inode_info_cache;
+
+/**
+ * Called to bring an inode into existence.
+ *
+ * Note that setting the self referencing pointer doesn't work here:
+ * i.e. ECRYPTFS_INODE_TO_PRIVATE_SM(inode)=ei;
+ *
+ * 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.
+ *
+ * @param sb Pointer to the super block of the filesystem
+ * @return Pointer to a newly allocated inode, NULL otherwise
+ */
+static struct inode *ecryptfs_alloc_inode(struct super_block *sb) {
+ struct ecryptfs_inode_info *ecryptfs_inode = NULL;
+ struct inode *inode = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p]\n", sb);
+ ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache,
+ SLAB_KERNEL);
+ if (unlikely(!ecryptfs_inode)) {
+ ecryptfs_printk(1, KERN_WARNING,
+ "Failed to allocate new inode\n");
+ goto out;
+ }
+ ecryptfs_init_crypt_stats(&(ecryptfs_inode->crypt_stats));
+ inode = &(ecryptfs_inode->vfs_inode);
+ out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; inode = [%p]\n", inode);
+ return 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_stats 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_crypt_stats *crypt_stats;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]\n", inode);
+ crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode))->crypt_stats;
+ ecryptfs_destruct_crypt_stats(crypt_stats);
+ kmem_cache_free(ecryptfs_inode_info_cache,
+ ECRYPTFS_INODE_TO_PRIVATE(inode));
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Return a pointer to our ecryptfs_inode_info struct.
+ * This is specified by the kernel documentation for the alloc_inode &
+ * destroy_inode change. We use this function to get a handle to our
+ * ecryptfs specific data.
+ *
+ * @param inode The inode component of the ecryptfs_inode_info we want to find
+ * @return Handle to our ecryptfs_inode_info
+ */
+static inline struct ecryptfs_inode_info *ECRYPTFS_I(struct inode *inode)
+{
+ return list_entry(inode, struct ecryptfs_inode_info, vfs_inode);
+}
+
+/**
+ * Set up the ecryptfs inode.
+ */
+static void ecryptfs_read_inode(struct inode *inode)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]\n", inode);
+ /* This is where we setup the self-reference in the vfs_inode's
+ * u.generic_ip. That way we don't have to walk the list again. */
+ ECRYPTFS_INODE_TO_PRIVATE_SM(inode) = ECRYPTFS_I(inode);
+ ECRYPTFS_INODE_TO_LOWER(inode) = NULL;
+ inode->i_version++;
+ inode->i_op = &ecryptfs_main_iops;
+ inode->i_fop = &ecryptfs_main_fops;
+ inode->i_mapping->a_ops = &ecryptfs_aops;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+
+/**
+ * This is called through iput_final().
+ * This is function will replace generic_drop_inode. The end result of which
+ * is we are skipping the check in inode->i_nlink, which we do not use.
+ */
+static void ecryptfs_drop_inode(struct inode *inode) {
+ generic_delete_inode(inode);
+}
+
+/**
+ * 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_printk(1, KERN_NOTICE, "Enter\n");
+ mntput(sb_info->lower_mnt);
+ key_put(sb_info->mount_crypt_stats.global_auth_tok_key);
+ kmem_cache_free(ecryptfs_sb_info_cache, sb_info);
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) = NULL;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Get the filesystem statistics. Currently, we let this pass right through
+ * to the lower filesystem and take no action ourselves
+ *
+ * TODO: Any stats need to be transposed?
+ */
+static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+ int err = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ err = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n",err);
+ return err;
+}
+
+/**
+ * TODO: Not implemented.
+ * Called to ask filesystem to change mount options.
+ */
+static int ecryptfs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+ return -ENOSYS;
+}
+
+/**
+ * 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.
+ *
+ * TODO: Why do we just not drop the reference to the lower inode in
+ * ecryptfs_destroy_inode?
+ */
+static void ecryptfs_clear_inode(struct inode *inode)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]; i_ino = [%lu]\n",
+ inode, inode->i_ino);
+ iput(ECRYPTFS_INODE_TO_LOWER(inode));
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Called in do_umount() if the MNT_FORCE flag was used and this
+ * function is defined. See comment in linux/fs/super.c:do_umount().
+ * Used only in nfs, to kill any pending RPC tasks, so that subsequent
+ * code can actually succeed and won't leave tasks that need handling.
+ *
+ * PS. I wonder if this is somehow useful to undo damage that was
+ * left in the kernel after a user level file server (such as amd)
+ * dies.
+ */
+static void ecryptfs_umount_begin(struct super_block *sb)
+{
+ struct super_block *lower_sb;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ /* TODO: Explain why we are umounting the lower superblock */
+ lower_sb = ECRYPTFS_SUPERBLOCK_TO_LOWER(sb);
+ if (lower_sb->s_op->umount_begin)
+ lower_sb->s_op->umount_begin(lower_sb);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/* TODO: Where is this normally declared? */
+int seq_printf(struct seq_file *m, const char *f, ...);
+
+/**
+ * Prints the directory we are currently mounted over
+ *
+ * @return 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;
+ int ret = 0;
+ char *tmp = NULL;
+ char *path;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ tmp = (char *)__get_free_page(GFP_KERNEL);
+ if (!tmp) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* TODO: wrap? */
+ path = d_path(ECRYPTFS_DENTRY_TO_LOWER(sb->s_root),
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt, tmp,
+ PAGE_SIZE);
+ seq_printf(m, ",dir=%s", path);
+ free_page((unsigned long)tmp);
+ out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; ret = [%d]\n",ret);
+ return ret;
+}
+
+/* TODO: Provide operations? (quota stuff, dirty, sync) */
+struct super_operations ecryptfs_sops = {
+ .alloc_inode = ecryptfs_alloc_inode,
+ .destroy_inode = ecryptfs_destroy_inode,
+ .read_inode = ecryptfs_read_inode,
+ .drop_inode = ecryptfs_drop_inode,
+ .put_super = ecryptfs_put_super,
+ .statfs = ecryptfs_statfs,
+ .remount_fs = ecryptfs_remount_fs,
+ .clear_inode = ecryptfs_clear_inode,
+ .umount_begin = ecryptfs_umount_begin,
+ .show_options = ecryptfs_show_options
+};

2005-11-19 04:18:38

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 5/12: eCryptfs] Header declarations

This header contains declarations for various structs used in
eCryptfs. Some of these are accessed both in userspace and in the
kernel, so it is important to make sure that the struct declarations
match in both places.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

ecryptfs_kernel.h | 430 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 430 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/ecryptfs_kernel.h 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/ecryptfs_kernel.h 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,430 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * Kernel declarations.
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * 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 <linux/fs.h>
+#include <asm/semaphore.h>
+#include <asm/scatterlist.h>
+
+#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 16
+#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
+#define ECRYPTFS_DEFAULT_IV_BYTES 8
+#define ECRYPTFS_FILE_VERSION 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 0x01
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x02
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x04
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x08
+ int32_t flags;
+ int32_t encrypted_key_size;
+ int32_t decrypted_key_size;
+ uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+ uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+};
+
+/**
+ * States for this struct are:
+ * - Uninstantiated: no password, but the salt, encrypted session
+ * key, etc. are filled in
+ * - Uninstantiated: no password, salt, etc. are filled in; at this
+ * time, there is no reason for the auth_tok to exist and be in
+ * this state
+ * - Instantiated: password, but no salt, and hence no encrypted
+ * session key, etc. eCryptfs will generate those items when the
+ * time come to write out the headers to disk
+ * - Instantiated: password, salt, encrypted session key, etc. This
+ * is typically used to actually obtain the session key for the
+ * file
+ */
+struct ecryptfs_password {
+ int32_t saltless; /* If set, this is the ``seed'' token
+ * from which other salted tokens are
+ * derived. Note that this is _not_
+ * the same as a token that just has
+ * not received its salt yet. */
+ int32_t password_size;
+ int32_t hash_algo;
+ int32_t hash_iterations;
+ int32_t session_key_encryption_key_size; /* In bytes */
+ int32_t session_key_encryption_key_set;
+ uint8_t salt[ECRYPTFS_SALT_SIZE];
+ /* Always in expanded hex */
+ uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+ uint8_t password[ECRYPTFS_MAX_PASSWORD_LENGTH];
+ /* Iterated-hash concatenation of salt and passphrase */
+ uint8_t session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+/* May be a password or a private key */
+struct ecryptfs_auth_tok {
+ int32_t instantiated; /* When not instantiated, this struct only
+ * contains enough information to construct
+ * the file headers, which contain token
+ * descriptors */
+#define ECRYPTFS_PASSWORD 0
+#define ECRYPTFS_PRIVATE_KEY 1
+ int32_t instanceof;
+ int32_t expired;
+ uid_t uid;
+ int64_t creation_time;
+ int64_t expiration_time;
+ /* This is in case we want userspace to extract the session
+ * key */
+ struct ecryptfs_session_key session_key;
+ union {
+ struct ecryptfs_password password;
+ /* Private key is in future eCryptfs releases */
+ } token;
+};
+
+void dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
+extern void ecryptfs_to_hex(char *dst, char *src, int src_size);
+extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
+
+/* For ecryptfs_auth_tok_packet_set.packet_set_type */
+#define ECRYPTFS_PACKET_SET_TYPE_PASSWORD 0
+
+#ifndef ECRYPTFS_DEBUG
+#define ECRYPTFS_DEBUG 1
+#endif
+
+/* See RFC 2440 */
+struct ecryptfs_key_record {
+ unsigned char type;
+ unsigned char sig[ECRYPTFS_SIG_SIZE];
+ u16 enc_key_size_bits;
+ unsigned char enc_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+};
+#define KEY_REC_SIZE(key_rec) \
+ ( sizeof(struct ecryptfs_key_record) - ECRYPTFS_MAX_KEY_BYTES \
+ + key_rec.enc_key_size_bits/8 )
+
+/* TODO: kref */
+struct ecryptfs_auth_tok_list {
+ struct ecryptfs_auth_tok *auth_tok;
+ struct list_head list;
+};
+
+/* Structure prototypes */
+struct ecryptfs_crypt_stats;
+struct ecryptfs_mount_crypt_stats;
+
+#define KEY_PAYLOAD_DATA(key) \
+ ( ((struct user_key_payload*)key->payload.data)->data)
+#define KEY_PAYLOAD_LEN(key) \
+ ( ((struct user_key_payload*)key->payload.data)->datalen)
+
+#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 64 /* TODO: make it a list */
+#define ECRYPTFS_MAX_IV_BYTES 8 /* 64 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 "blowfish"
+#define ECRYPTFS_DEFAULT_KEY_BYTES 16
+
+/**
+ * IV_SIZE (i.e., 8 bytes)
+ * HMAC_SIZE (i.e., 20 bytes)
+ * IVS_PER_PAGE (i.e., 512)
+ * IVS_PER_PAGE * HMAC_SIZE = TOTAL_SIZE_PER_EXTENT; 512 * 20 = 10240
+ * TOTAL_SIZE_PER_EXTENT / PAGE_SIZE = NUM_PAGES_FOR_HMACS = 2.5
+ * PIIHHHHHDx1024IIHHHHHDx1024...
+ * PIDx512IDx512...
+ * Or: [Ix146+Hx146] (wastes 8 bytes per page)
+ */
+
+/**
+ * This is the primary struct associated with each encrypted file.
+ *
+ * TODO: cache align/pack?
+ */
+struct ecryptfs_crypt_stats {
+ int struct_initialized;
+ int policy_applied;
+ int new_file;
+ int encrypted;
+ int security_warning; /* This flag is set if something happens
+ * that could weaken the security of the
+ * file */
+ int iv_bytes; /* Set to 0 if encryption not enabled */
+ int records_per_page; /* extent_size / (iv_bytes + hmac_bytes) */
+#define ECRYPTFS_IV_ROTATE_NO 0
+#define ECRYPTFS_IV_ROTATE_INCREMENT 1
+#define ECRYPTFS_IV_ROTATE_PERMUTATE 2
+#define ECRYPTFS_IV_ROTATE_RANDOM 3
+ int rotate_iv; /* Whether or not to rotate the IV on each
+ * write (performance vs. security tradeoff) */
+ int encrypt_iv_pages; /* To attempt to hide sparse regions? */
+ int num_keysigs;
+ int num_header_pages; /* Number of pages that comprise the
+ * header */
+ int extent_size;
+ int key_size_bits;
+ int key_valid;
+ struct crypto_tfm *tfm;
+ unsigned char iv[ECRYPTFS_MAX_IV_BYTES];
+ unsigned char key[ECRYPTFS_MAX_KEY_BYTES];
+ char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
+ struct semaphore iv_sem;
+ char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX];
+};
+
+/* inode private data. */
+struct ecryptfs_inode_info {
+ struct inode *wii_inode;
+ struct ecryptfs_crypt_stats crypt_stats;
+ struct inode vfs_inode;
+};
+
+/* dentry private data. */
+struct ecryptfs_dentry_info {
+ struct dentry *wdi_dentry;
+ struct ecryptfs_crypt_stats *crypt_stats;
+};
+
+/**
+ * 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_stats {
+ /* N.B. Pointers to memory we do not own, do not free these */
+ struct ecryptfs_auth_tok *global_auth_tok;
+ struct key *global_auth_tok_key;
+ char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
+ char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
+};
+
+/* super block private data. */
+struct ecryptfs_sb_info {
+ struct super_block *wsi_sb;
+ struct vfsmount *lower_mnt;
+ struct ecryptfs_mount_crypt_stats mount_crypt_stats;
+};
+
+/* file private data. */
+struct ecryptfs_file_info {
+ struct file *wfi_file;
+ struct ecryptfs_crypt_stats *crypt_stats;
+};
+
+/* auth_tok <=> encrypted_session_key mappings */
+struct ecryptfs_auth_tok_list_item {
+ struct list_head list;
+ struct ecryptfs_auth_tok auth_tok;
+ char encrypted_session_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+extern inline pgoff_t
+ecryptfs_pg_idx_to_lwr_pg_idx(struct ecryptfs_crypt_stats *crypt_stats,
+ pgoff_t idx)
+{
+ return ((idx / crypt_stats->records_per_page) + idx
+ + crypt_stats->num_header_pages + 1);
+}
+
+#ifndef DEFAULT_POLLMASK
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+#endif /* ndef DEFAULT_POLLMASK */
+
+#define OBSERVE_ASSERTS 1
+#ifdef OBSERVE_ASSERTS
+#define ASSERT(EX) \
+do { \
+ if (unlikely(!(EX))) { \
+ printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \
+ __FILE__, __LINE__, __FUNCTION__); \
+ BUG(); \
+ } \
+} while (0)
+#else
+#define ASSERT(EX) ;
+#endif /* OBSERVE_ASSERTS */
+
+/**
+ * Halcrow: What does the kernel VFS do to ensure that there is no
+ * contention for file->private_data?
+ */
+#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \
+ ((file)->private_data))
+#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data)
+#define ECRYPTFS_FILE_TO_LOWER(file) \
+ ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file)
+#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \
+ (ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \
+ (super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \
+ (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
+#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \
+ (dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \
+ (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry)
+
+/*
+ * Flags for ecryptfs_{en,de}code_filename
+ * DO_DOTS means the special entries . and .. should be encoded (for symlink)
+ * SKIP_DOTS means they should be preserved intact
+ */
+#define ECRYPTFS_DO_DOTS 0
+#define ECRYPTFS_SKIP_DOTS 1
+
+/**
+ * EXTERNALS:
+ */
+extern struct file_operations ecryptfs_main_fops;
+extern struct file_operations ecryptfs_dir_fops;
+extern struct inode_operations ecryptfs_main_iops;
+extern struct inode_operations ecryptfs_dir_iops;
+extern struct inode_operations ecryptfs_symlink_iops;
+extern struct super_operations ecryptfs_sops;
+extern struct dentry_operations ecryptfs_dops;
+extern struct address_space_operations ecryptfs_aops;
+struct ecryptfs_key_record;
+struct ecryptfs_auth_tok;
+
+/**
+ * TODO: Make eCryptfs's memory usage as lean as possible
+ */
+extern kmem_cache_t *ecryptfs_auth_tok_list_item_cache;
+extern kmem_cache_t *ecryptfs_file_info_cache;
+extern kmem_cache_t *ecryptfs_dentry_info_cache;
+extern kmem_cache_t *ecryptfs_inode_info_cache;
+extern kmem_cache_t *ecryptfs_sb_info_cache;
+extern kmem_cache_t *ecryptfs_header_cache_0;
+extern kmem_cache_t *ecryptfs_header_cache_1;
+extern kmem_cache_t *ecryptfs_header_cache_2;
+extern kmem_cache_t *ecryptfs_lower_page_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(const char *name, int length,
+ char **decrypted_name, int skip_dots,
+ struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_encode_filename(const char *name, int length,
+ char **encoded_name, int skip_dots,
+ struct ecryptfs_crypt_stats *crypt_stats);
+struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry);
+
+void ecryptfs_copy_attr_times(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src);
+
+#define ecryptfs_printk(verb, type, fmt, arg...) \
+ __ecryptfs_printk((verb), type "%s: " fmt, __FUNCTION__, ## arg);
+void __ecryptfs_printk(int verb, const char *fmt, ...);
+
+extern int ecryptfs_verbosity;
+
+/* crypto */
+void ecryptfs_dump_hex(char *data, int bytes);
+int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
+ int sg_size);
+void ecryptfs_rotate_iv(unsigned char *iv);
+void ecryptfs_init_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats);
+void ecryptfs_destruct_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_write_inode_size_to_header(struct file *lower_file,
+ struct inode *lower_inode,
+ struct inode *inode);
+int do_encrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *dest_page, struct page *src_page,
+ unsigned char *iv);
+int do_encrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *dest_page, int dest_offset,
+ struct page *src_page, int src_offset, int size,
+ unsigned char *iv);
+int do_decrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *dest_page, struct page *src_page,
+ unsigned char *iv);
+int do_decrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *dest_page, int dest_offset,
+ struct page *src_page, int src_offset, int size,
+ unsigned char *iv);
+int do_encrypt_virt(struct ecryptfs_crypt_stats *crypt_stats,
+ char *dest_virt_addr, const char *src_virt_addr,
+ int size, unsigned char *iv);
+int do_decrypt_virt(struct ecryptfs_crypt_stats *crypt_stats,
+ char *dest_virt_addr, const char *src_virt_addr,
+ int size, unsigned char *iv);
+int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
+ struct file *lower_file);
+int ecryptfs_write_headers_virt(char *page_virt,
+ struct ecryptfs_crypt_stats *crypt_stats,
+ struct dentry *ecryptfs_dentry, int version);
+int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
+ struct file *lower_file);
+int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry);
+int contains_ecryptfs_marker(char *data);
+int ecryptfs_read_header_region(char *data, struct dentry *dentry,
+ struct nameidata *nd);
+u16 ecryptfs_code_for_cipher_string(char *str);
+int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code);
+void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_generate_key_packet_set(char *dest_base,
+ struct ecryptfs_crypt_stats *crypt_stats,
+ struct dentry *ecryptfs_dentry, int *len);
+int process_request_key_err(long err_code);
+int ecryptfs_parse_packet_set(unsigned char *dest,
+ struct ecryptfs_crypt_stats *crypt_stats,
+ struct dentry *ecryptfs_dentry, int version);
+
+/* inode.c */
+int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
+
+#endif /* ndef ECRYPTFS_KERNEL_H */

2005-11-19 04:20:34

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 7/12: eCryptfs] File operations

eCryptfs file operations. Includes code to read header information
from the underyling file when needed.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

file.c | 710 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 710 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/file.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/file.c 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,710 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2004 Erez Zadok
+ * Copyright (c) 2001-2004 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * 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 "ecryptfs_kernel.h"
+
+/**
+ * @param file File we are seeking in
+ * @param offset The offset to seek to
+ * @param origin Where to start seek from
+ * (0=beginning,1=cur pos,2=end of file)
+ * @return 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 ret;
+ int rc;
+ struct file *lower_file = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; offset = [%lld] origin = [%d]",
+ "\n", offset, origin);
+ if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file))
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ /* Intent: 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 */
+ if (offset > i_size_read(file->f_dentry->d_inode)) {
+ rc = ecryptfs_truncate(file->f_dentry, offset);
+ if (rc) {
+ ret = rc;
+ ecryptfs_printk(0, KERN_ERR, "Error on attempt to "
+ "truncate to (higher) offset; rc = "
+ "[%d]\n", rc);
+ goto out;
+ }
+ }
+ ret = generic_file_llseek(file, offset, origin);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; ret = [%lld]\n", ret);
+ return ret;
+}
+
+/**
+ * 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 file *file, char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ int err = 0;
+ struct dentry *lower_dentry;
+ struct vfsmount *lower_vfsmount;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ err = generic_file_read(file, buf, count, ppos);
+ if (err >= 0) {
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(file->f_dentry);
+ lower_vfsmount = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ file->f_dentry->d_inode->i_sb)->lower_mnt;
+ touch_atime(lower_vfsmount, lower_dentry);
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+ return err;
+}
+
+static ssize_t
+ecryptfs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ int rc = -EINVAL;
+ struct file *lower_file = NULL;
+ loff_t pos = *ppos;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+ if (!lower_file->f_op || !lower_file->f_op->read)
+ goto out;
+ rc = lower_file->f_op->read(lower_file, buf, count, &pos);
+ if (rc >= 0)
+ /* atime should also be updated for reads of size zero
+ * or more */
+ ecryptfs_copy_attr_atime(file->f_dentry->d_inode,
+ lower_file->f_dentry->d_inode);
+ lower_file->f_pos = *ppos = pos;
+ memcpy(&(file->f_ra), &(lower_file->f_ra),
+ sizeof(struct file_ra_state));
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Directory write operation.
+ *
+ * TODO: Encrypt the directory pages also if policy calls for it
+ */
+static ssize_t ecryptfs_dir_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int rc = -EINVAL;
+ struct file *lower_file = NULL;
+ struct inode *inode;
+ struct inode *lower_inode;
+ loff_t pos = *ppos;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+ inode = file->f_dentry->d_inode;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ if (!lower_file->f_op || !lower_file->f_op->write)
+ goto out;
+ /* adjust for append -- seek to the end of the file */
+ if ((file->f_flags & O_APPEND) && (count != 0))
+ pos = i_size_read(inode);
+ if (count != 0)
+ rc = lower_file->f_op->write(lower_file, buf, count, &pos);
+ else
+ rc = 0;
+ /* copy ctime and mtime from lower layer attributes
+ * atime is unchanged for both layers */
+ if (rc >= 0)
+ ecryptfs_copy_attr_times(inode, lower_inode);
+ /* because pwrite() does not have any way to tell us that it
+ * is our caller, then we don't know for sure if we have to
+ * update the file positions. This hack relies on write()
+ * having passed us the "real" pointer of its struct file's
+ * f_pos field. */
+ lower_file->f_pos = *ppos = pos;
+ /* update this inode's size */
+ if (pos > i_size_read(inode))
+ i_size_write(inode, pos);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+struct ecryptfs_getdents_callback {
+ void *dirent;
+ struct dentry *dentry;
+ filldir_t filldir;
+ int err;
+ int filldir_called;
+ int entries_written;
+};
+
+/* copied from generic filldir in fs/readir.c */
+static int
+ecryptfs_filldir(void *dirent, const char *name, int namelen, loff_t offset,
+ ino_t ino, unsigned int d_type)
+{
+ struct ecryptfs_crypt_stats *crypt_stats;
+ struct ecryptfs_getdents_callback *buf =
+ (struct ecryptfs_getdents_callback *)dirent;
+ int rc;
+ char *decoded_name;
+ int decoded_length;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter w/ name = [%.*s]\n", namelen,
+ name);
+ /* Get the crypto stats for this file */
+ /* TODO: If filldir oopses, look here ... */
+ crypt_stats = ECRYPTFS_DENTRY_TO_PRIVATE(buf->dentry)->crypt_stats;
+ buf->filldir_called++;
+ /* TODO: Halcrow: Check the headers of the file to determine
+ * if the filename needs decryption (or hiding, or
+ * obfuscation, etc.) */
+ decoded_length = ecryptfs_decode_filename(name, namelen, &decoded_name,
+ ECRYPTFS_SKIP_DOTS,
+ crypt_stats);
+ if (decoded_length < 0)
+ return 0;
+ rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset,
+ ino, d_type);
+ kfree(decoded_name);
+ if (rc >= 0)
+ buf->entries_written++;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * @param file The ecryptfs file struct
+ * @param filldir The filldir callback function
+ */
+static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+ int rc = -ENOTDIR;
+ struct file *lower_file = NULL;
+ struct inode *inode;
+ struct ecryptfs_getdents_callback buf;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ inode = file->f_dentry->d_inode;
+ buf.dirent = dirent;
+ buf.dentry = file->f_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)
+ ecryptfs_copy_attr_atime(inode, lower_file->f_dentry->d_inode);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static unsigned int ecryptfs_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = DEFAULT_POLLMASK;
+ struct file *lower_file = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+ if (!lower_file->f_op || !lower_file->f_op->poll)
+ goto out;
+ mask = lower_file->f_op->poll(lower_file, wait);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; mask = [%x]\n", mask);
+ return mask;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+static int
+read_inode_size_from_header(struct file *lower_file,
+ struct inode *lower_inode, struct inode *inode)
+{
+ int rc = 0;
+ struct page *header_page;
+ unsigned char *header_virt;
+ u64 data_size;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter w/ lower_inode = [%p]; inode = "
+ "[%p]\n", lower_inode, inode);
+ header_page = grab_cache_page(lower_inode->i_mapping, 0);
+ if (!header_page) {
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "grab_cache_page for header page "
+ "failed\n");
+ goto out;
+ }
+ header_virt = kmap(header_page);
+ rc = lower_inode->i_mapping->a_ops->readpage(lower_file, header_page);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error reading header page\n");
+ goto out_unmap;
+ }
+ memcpy(&data_size, header_virt, sizeof(data_size));
+ i_size_write(inode, (loff_t) data_size);
+ ecryptfs_printk(1, KERN_NOTICE, "Read inode size from header: [%llu]\n",
+ i_size_read(inode));
+out_unmap:
+ kunmap(header_page);
+ page_cache_release(header_page);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+kmem_cache_t *ecryptfs_file_info_cache;
+
+/**
+ * Opens the file specified by inode.
+ *
+ * @param inode inode speciying file to open
+ * @param file Structure to return filled in
+ * @return Zero on success; non-zero otherwise
+ */
+static int ecryptfs_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct ecryptfs_crypt_stats *crypt_stats = NULL;
+ struct dentry *ecryptfs_dentry = file->f_dentry;
+ struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
+ struct inode *lower_inode = NULL;
+ struct file *lower_file = NULL;
+ struct vfsmount *lower_mnt;
+ int lower_flags;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; i_ino = [%lu] inode = [%p] "
+ "inode->i_size = [%lld] inode->i_count = [%d] file->"
+ "f_dentry = [%p] file->f_dentry->d_name.name = [%s] "
+ "file->f_dentry->d_name.len = [%d]\n", inode->i_ino,
+ inode, atomic_read(&inode->i_count), i_size_read(inode),
+ ecryptfs_dentry, ecryptfs_dentry->d_name.name,
+ ecryptfs_dentry->d_name.len);
+
+ /* ECRYPTFS_DENTRY_TO_PRIVATE(ecryptfs_dentry) Allocated in
+ * ecryptfs_lookup() */
+ /* Released in ecryptfs_release or end of function if failure */
+ ECRYPTFS_FILE_TO_PRIVATE_SM(file) =
+ kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+ if (!ECRYPTFS_FILE_TO_PRIVATE_SM(file)) {
+ ecryptfs_printk(0, KERN_ERR,
+ "Error attempting to allocate memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
+ crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats);
+ if (!crypt_stats->policy_applied) {
+ ecryptfs_printk(1, KERN_NOTICE, "Setting flags for stats...\n");
+ /* Policy code enabled in future release */
+ crypt_stats->policy_applied = 1;
+ crypt_stats->encrypted = 1;
+ }
+ /* This mntget & dget is undone via fput when the file is released */
+ dget(lower_dentry);
+ 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_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+ mntget(lower_mnt);
+ /* Corresponding fput() in ecryptfs_release() */
+ lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags);
+ if (IS_ERR(lower_file)) {
+ rc = PTR_ERR(lower_file);
+ ecryptfs_printk(0, KERN_ERR, "Error opening lower file\n");
+ goto out_puts;
+ }
+ ECRYPTFS_FILE_TO_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(1, KERN_NOTICE, "This is a directory\n");
+ crypt_stats->encrypted = 0;
+ rc = 0;
+ goto out;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "(file->f_flags & O_CREAT) = [%d] "
+ "lower_inode->i_size = [%llu] crypt_stats->struct_"
+ "initialized = [%d] crypt_stats->key_valid = [%d]\n",
+ (file->f_flags & O_CREAT),
+ (unsigned long long)lower_inode->i_size,
+ crypt_stats->struct_initialized,
+ crypt_stats->key_valid);
+ /* TODO: This was originally used to determine if it was a new file,
+ * but if we are allowing pass-through mode, then we couldbe opening
+ * a 0-length normal file.. in which case, this can't cause an error
+ * So.... what do we want to do? */
+ if (lower_inode->i_size == 0) {
+ ecryptfs_printk(0, KERN_EMERG, "Zero-length lower file; "
+ "ecryptfs_create() had a problem?\n");
+ rc = -ENOENT;
+ goto out_puts;
+ } else if (!crypt_stats->policy_applied || !crypt_stats->key_valid) {
+ /* crypto.c */
+ rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file);
+ if (rc) {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Valid headers not found\n");
+ crypt_stats->encrypted = 0;
+ } else
+ read_inode_size_from_header(lower_file, lower_inode,
+ inode);
+ } else
+ ecryptfs_printk(1, KERN_NOTICE, "crypt_stats->struct_"
+ "initialized = [%d]; crypt_stats->key_valid = "
+ "[%d]\n", crypt_stats->struct_initialized,
+ crypt_stats->key_valid);
+ ECRYPTFS_FILE_TO_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:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int ecryptfs_flush(struct file *file)
+{
+ int rc = 0;
+ struct file *lower_file = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+ 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);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int ecryptfs_release(struct inode *ecryptfs_inode, struct file *file)
+{
+ int rc = 0;
+ struct file *lower_file = NULL;
+ struct inode *lower_inode;
+
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Enter; ecryptfs_inode->i_count = [%d]\n",
+ ecryptfs_inode->i_count);
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ kmem_cache_free(ecryptfs_file_info_cache,
+ ECRYPTFS_FILE_TO_PRIVATE(file));
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(ecryptfs_inode);
+ fput(lower_file);
+ ecryptfs_inode->i_blocks = lower_inode->i_blocks;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int
+ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ int rc = -EINVAL;
+ struct file *lower_file = NULL;
+ struct dentry *lower_dentry;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (NULL == file) {
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ if (lower_dentry->d_inode->i_fop
+ && lower_dentry->d_inode->i_fop->fsync) {
+ down(&lower_dentry->d_inode->i_sem);
+ rc = lower_dentry->d_inode->i_fop->fsync(lower_file,
+ lower_dentry,
+ datasync);
+ up(&lower_dentry->d_inode->i_sem);
+ }
+ } else {
+ if (NULL == ECRYPTFS_FILE_TO_PRIVATE(file)) {
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "ECRYPTFS_FILE_TO_PRIVATE"
+ "(file=[%p]) == NULL\n", file);
+ goto out;
+ }
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ if (lower_file->f_op && lower_file->f_op->fsync) {
+ down(&lower_dentry->d_inode->i_sem);
+ rc = lower_file->f_op->fsync(lower_file, lower_dentry,
+ datasync);
+ up(&lower_dentry->d_inode->i_sem);
+ }
+ }
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static void locks_delete_block(struct file_lock *waiter)
+{
+ lock_kernel();
+ list_del_init(&waiter->fl_block);
+ list_del_init(&waiter->fl_link);
+ waiter->fl_next = NULL;
+ unlock_kernel();
+}
+
+static int ecryptfs_posix_lock(struct file *file, struct file_lock *fl, int cmd)
+{
+ int rc;
+
+lock_file:
+ rc = posix_lock_file(file, fl);
+ if ((rc != -EAGAIN) || (cmd == F_SETLK))
+ goto out;
+ rc = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
+ if (!rc)
+ goto lock_file;
+ locks_delete_block(fl);
+out:
+ return rc;
+}
+
+static int ecryptfs_setlk(struct file *file, int cmd, struct file_lock *fl)
+{
+ int rc = -EINVAL;
+ struct inode *inode, *lower_inode;
+ struct file *lower_file = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ inode = file->f_dentry->d_inode;
+ lower_inode = lower_file->f_dentry->d_inode;
+ /* Don't allow mandatory locks on files that may be memory mapped
+ * and shared. */
+ if (IS_MANDLOCK(lower_inode) &&
+ (lower_inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
+ mapping_writably_mapped(lower_file->f_mapping)) {
+ rc = -EAGAIN;
+ goto out;
+ }
+ if (cmd == F_SETLKW)
+ fl->fl_flags |= FL_SLEEP;
+ rc = -EBADF;
+ switch (fl->fl_type) {
+ case F_RDLCK:
+ if (!(lower_file->f_mode & FMODE_READ))
+ goto out;
+ break;
+ case F_WRLCK:
+ if (!(lower_file->f_mode & FMODE_WRITE))
+ goto out;
+ break;
+ case F_UNLCK:
+ break;
+ default:
+ rc = -EINVAL;
+ goto out;
+ }
+ fl->fl_file = lower_file;
+ rc = security_file_lock(lower_file, fl->fl_type);
+ if (rc)
+ goto out;
+ if (lower_file->f_op && lower_file->f_op->lock != NULL) {
+ rc = lower_file->f_op->lock(lower_file, cmd, fl);
+ if (rc)
+ goto out;
+ goto upper_lock;
+ }
+ rc = ecryptfs_posix_lock(lower_file, fl, cmd);
+ if (rc)
+ goto out;
+upper_lock:
+ fl->fl_file = file;
+ rc = ecryptfs_posix_lock(file, fl, cmd);
+ if (rc) {
+ fl->fl_type = F_UNLCK;
+ fl->fl_file = lower_file;
+ ecryptfs_posix_lock(lower_file, fl, cmd);
+ }
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int ecryptfs_getlk(struct file *file, struct file_lock *fl)
+{
+ int rc = 0;
+ struct file_lock *tempfl = NULL;
+
+ if (file->f_op && file->f_op->lock) {
+ rc = file->f_op->lock(file, F_GETLK, fl);
+ if (rc < 0)
+ goto out;
+ } else
+ tempfl = posix_test_lock(file, fl);
+ if (!tempfl)
+ fl->fl_type = F_UNLCK;
+ else
+ memcpy(fl, tempfl, sizeof(struct file_lock));
+out:
+ return rc;
+}
+
+static int ecryptfs_fasync(int fd, struct file *file, int flag)
+{
+ int rc = 0;
+ struct file *lower_file = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file))
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ else {
+ rc = -EINVAL;
+ goto out;
+ }
+ if (lower_file->f_op && lower_file->f_op->fasync)
+ rc = lower_file->f_op->fasync(fd, lower_file, flag);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+ return rc;
+}
+
+static int ecryptfs_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+ int rc = 0;
+ struct file *lower_file = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL)
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ ASSERT(lower_file != NULL);
+ rc = -EINVAL;
+ if (!fl)
+ goto out;
+ fl->fl_file = lower_file;
+ switch (cmd) {
+ case F_GETLK:
+ case F_GETLK64:
+ rc = ecryptfs_getlk(lower_file, fl);
+ break;
+ case F_SETLK:
+ case F_SETLKW:
+ case F_SETLK64:
+ case F_SETLKW64:
+ fl->fl_file = file;
+ rc = ecryptfs_setlk(file, cmd, fl);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ fl->fl_file = file;
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+ 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;
+
+ if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL)
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ ASSERT(lower_file != NULL);
+ /* TODO: Is this a superfluous check for f_op? */
+ 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);
+
+struct file_operations ecryptfs_dir_fops = {
+ .read = ecryptfs_read,
+ .write = ecryptfs_dir_write,
+ .readdir = ecryptfs_readdir,
+ .poll = ecryptfs_poll,
+ .ioctl = ecryptfs_ioctl,
+ .mmap = generic_file_mmap,
+ .open = ecryptfs_open,
+ .flush = ecryptfs_flush,
+ .release = ecryptfs_release,
+ .fsync = ecryptfs_fsync,
+ .fasync = ecryptfs_fasync,
+ .lock = ecryptfs_lock,
+ .sendfile = ecryptfs_sendfile,
+};
+
+struct file_operations ecryptfs_main_fops = {
+ .llseek = ecryptfs_llseek,
+ .read = ecryptfs_read_update_atime,
+ .write = generic_file_write,
+ .readdir = ecryptfs_readdir,
+ .poll = ecryptfs_poll,
+ .ioctl = ecryptfs_ioctl,
+ .mmap = generic_file_mmap,
+ .open = ecryptfs_open,
+ .flush = ecryptfs_flush,
+ .release = ecryptfs_release,
+ .fsync = ecryptfs_fsync,
+ .fasync = ecryptfs_fasync,
+ .lock = ecryptfs_lock,
+ .sendfile = ecryptfs_sendfile,
+};
+
+/**
+ * TODO: sysfs
+ */
+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) != NULL)
+ 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;
+}

2005-11-19 04:20:59

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 8/12: eCryptfs] Dentry operations

eCryptfs dentry operations.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

dentry.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 105 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/dentry.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/dentry.c 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,105 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * 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 "ecryptfs_kernel.h"
+
+/**
+ * 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.
+ *
+ *
+ * @param dentry ecryptfs dentry
+ * @param nd
+ * @return 1 if valid, 0 otherwise
+ */
+static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+ int err = 1;
+ struct dentry *lower_dentry;
+ struct dentry *saved_dentry;
+ struct vfsmount *saved_vfsmount;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n",
+ dentry->d_name.name);
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ if (!lower_dentry) {
+ err = 0;
+ goto out;
+ }
+ if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
+ goto out;
+ /* Call the lower dentry's d_revalidate (assuming it has one) */
+ saved_dentry = nd->dentry;
+ saved_vfsmount = nd->mnt;
+ nd->dentry = lower_dentry;
+ nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(dentry->d_sb)->lower_mnt;
+ err = lower_dentry->d_op->d_revalidate(lower_dentry, nd);
+ nd->dentry = saved_dentry;
+ nd->mnt = saved_vfsmount;
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return err;
+}
+
+kmem_cache_t *ecryptfs_dentry_info_cache;
+
+/* Notes:
+ * Called when a dentry is really deallocated
+ * Sanity check? wrapper around ecryptfs_dput()
+ */
+static void ecryptfs_d_release(struct dentry *dentry)
+{
+ struct dentry *lower_dentry;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name->name = [%s]\n",
+ dentry->d_name.name);
+ if (!dentry) {
+ ecryptfs_printk(0, KERN_ERR, "NULL dentry\n");
+ goto out;
+ }
+ if (!ECRYPTFS_DENTRY_TO_PRIVATE(dentry)) {
+ ecryptfs_printk(1, KERN_ERR, "dentry without private data: "
+ "[%*s]\n", dentry->d_name.len,
+ dentry->d_name.name);
+ goto out;
+ }
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (ECRYPTFS_DENTRY_TO_PRIVATE(dentry))
+ kmem_cache_free(ecryptfs_dentry_info_cache,
+ ECRYPTFS_DENTRY_TO_PRIVATE(dentry));
+ if (lower_dentry)
+ dput(lower_dentry);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+ return;
+}
+
+struct dentry_operations ecryptfs_dops = {
+ .d_revalidate = ecryptfs_d_revalidate,
+ .d_release = ecryptfs_d_release,
+};

2005-11-19 04:21:38

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 9/12: eCryptfs] Inode operations

eCryptfs inode operations. Includes functions to support inode
interpolation between upper and lower inodes.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

inode.c | 1084 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1084 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/inode.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/inode.c 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,1084 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2004 Erez Zadok
+ * Copyright (c) 2001-2004 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompsion <[email protected]>
+ *
+ * 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/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/crypto.h>
+#include "ecryptfs_kernel.h"
+
+static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+ struct dentry *dir;
+
+ dir = dget(dentry->d_parent);
+ down(&(dir->d_inode->i_sem));
+ return dir;
+}
+
+static inline void unlock_parent(struct dentry *dentry)
+{
+ up(&(dentry->d_parent->d_inode->i_sem));
+ dput(dentry->d_parent);
+}
+
+static inline void unlock_dir(struct dentry *dir)
+{
+ up(&dir->d_inode->i_sem);
+ dput(dir);
+}
+
+void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ ecryptfs_printk(1, KERN_NOTICE, "src->i_size = [%lld]\n", src->i_size);
+ ecryptfs_printk(1, KERN_NOTICE, "src->i_blocks = [%lu]\n",
+ src->i_blocks);
+ i_size_write(dst, i_size_read((struct inode *)src));
+ dst->i_blocks = src->i_blocks;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src)
+{
+ ASSERT(dest != NULL);
+ ASSERT(src != NULL);
+ dest->i_atime = src->i_atime;
+}
+
+void ecryptfs_copy_attr_times(struct inode *dest, const struct inode *src)
+{
+ ASSERT(dest != NULL);
+ ASSERT(src != NULL);
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+}
+
+static void ecryptfs_copy_attr_timesizes(struct inode *dest,
+ const struct inode *src)
+{
+ ASSERT(dest != NULL);
+ ASSERT(src != NULL);
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ ecryptfs_copy_inode_size(dest, src);
+}
+
+void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ ASSERT(dest != NULL);
+ ASSERT(src != NULL);
+ dest->i_mode = src->i_mode;
+ dest->i_nlink = src->i_nlink;
+ dest->i_uid = src->i_uid;
+ dest->i_gid = src->i_gid;
+ dest->i_rdev = src->i_rdev;
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ dest->i_blksize = src->i_blksize;
+ ecryptfs_printk(1, KERN_NOTICE, "src->i_blksize = [%lu]\n",
+ src->i_blksize);
+ dest->i_blkbits = src->i_blkbits;
+ dest->i_flags = src->i_flags;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Creates our file in the lower file system
+ *
+ * @param lower_dir_inode inode of the parent in the lower fs of the new file
+ * @param lower_dentry New file's dentry in the lower fs
+ * @param ecryptfs_dentry New file's dentry in ecryptfs
+ * @param mode The mode of the new file
+ * @param nd nameidata of ecryptfs' parent's dentry & vfsmnt
+ * @return 0 on success; non-zero on error condition
+ */
+static int
+ecryptfs_create_underlying_file(struct inode *lower_dir_inode,
+ struct dentry *lower_dentry,
+ struct dentry *ecryptfs_dentry, int mode,
+ struct nameidata *nd)
+{
+ int rc;
+ struct dentry *saved_dentry = NULL;
+ struct vfsmount *saved_vfsmount = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ saved_dentry = nd->dentry;
+ saved_vfsmount = nd->mnt;
+ nd->dentry = lower_dentry;
+ nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ ecryptfs_dentry->d_sb)->lower_mnt;
+ rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd);
+ nd->dentry = saved_dentry;
+ nd->mnt = saved_vfsmount;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Creates the underlying file and the eCryptfs inode which will link to
+ * it. It will also update the eCryptfs directory inode to mimic the
+ * stats of the lower directory inode
+ *
+ * @param directory_inode inode of the new file's dentry's parent in ecryptfs
+ * @param ecryptfs_dentry New file's dentry in ecryptfs
+ * @param mode The mode of the new file
+ * @param nd nameidata of ecryptfs' parent's dentry & vfsmnt
+ * @return 0 on success; non-zero on error condition
+ */
+static int
+ecryptfs_do_create(struct inode *directory_inode,
+ struct dentry *ecryptfs_dentry, int mode,
+ struct nameidata *nd)
+{
+ int rc;
+ struct dentry *lower_dentry;
+ struct dentry *lower_dir_dentry;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = ecryptfs_lower_dentry(ecryptfs_dentry);
+ if (IS_ERR(lower_dentry)) {
+ ecryptfs_printk(0, KERN_ERR, "ecryptfs dentry doesn't know"
+ "about its lower counterpart\n");
+ rc = PTR_ERR(lower_dentry);
+ goto out;
+ }
+ lower_dir_dentry = lock_parent(lower_dentry);
+ if (unlikely(IS_ERR(lower_dir_dentry))) {
+ ecryptfs_printk(0, KERN_ERR, "Error locking directory of "
+ "dentry\n");
+ rc = PTR_ERR(lower_dir_dentry);
+ goto out;
+ }
+ rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode,
+ lower_dentry, ecryptfs_dentry,
+ mode, nd);
+ if (unlikely(rc)) {
+ ecryptfs_printk(0, KERN_ERR,
+ "Failure to create underlying file\n");
+ goto out_lock;
+ }
+ rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry,
+ directory_inode->i_sb, 0);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Failure in ecryptfs_interpose\n");
+ goto out_lock;
+ }
+ ecryptfs_copy_attr_timesizes(directory_inode,
+ lower_dir_dentry->d_inode);
+out_lock:
+ unlock_dir(lower_dir_dentry);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * This is the code which will grow the file to be 8192 or
+ * 12288, depending on whether the file derived-IV or
+ * written-IV formatted.
+ */
+static int grow_file(struct dentry *ecryptfs_dentry, struct file *lower_file,
+ struct inode *inode, struct inode *lower_inode)
+{
+ int rc = 0;
+ struct file fake_file;
+
+ memset(&fake_file, 0, sizeof(fake_file));
+ fake_file.f_dentry = ecryptfs_dentry;
+ ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_file) =
+ kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+ if (!(ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_file))) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ ECRYPTFS_FILE_TO_LOWER(&fake_file) = lower_file;
+ ecryptfs_fill_zeros(&fake_file, 1);
+ kmem_cache_free(ecryptfs_file_info_cache,
+ ECRYPTFS_FILE_TO_PRIVATE(&fake_file));
+ i_size_write(inode, 0);
+ ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
+ ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats.new_file = 1;
+out:
+ return rc;
+}
+
+/**
+ * Force the file to be changed from a basic empty file to an ecryptfs file
+ * with a header, 1st IV page & 1st data page
+ *
+ * @return Zero on success
+ */
+static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry)
+{
+ int rc = 0;
+ int lower_flags;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ struct dentry *lower_dentry;
+ struct dentry *tlower_dentry = NULL;
+ struct file *lower_file;
+ struct inode *inode, *lower_inode;
+ struct vfsmount *lower_mnt;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n",
+ ecryptfs_dentry->d_name.name);
+
+ lower_dentry = ecryptfs_lower_dentry(ecryptfs_dentry);
+ if (IS_ERR(lower_dentry)) {
+ ecryptfs_printk(0, KERN_ERR, "ecryptfs dentry doesn't know"
+ "about its lower counterpart\n");
+ rc = PTR_ERR(lower_dentry);
+ goto out;
+ }
+
+ ecryptfs_printk(1, KERN_NOTICE, "lower_dentry->d_name.name = [%s]\n",
+ lower_dentry->d_name.name);
+
+ /* This code is migrated from ecryptfs_open...
+ * there is no reason that this shouldn't just "work"
+ * The flags and mode for the file passed into ecryptfs_open is:
+ * f->f_flags = flags;
+ * f->f_mode = ((flags+1) & O_ACCMODE)
+ * | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
+ *
+ * Where flags = O_CREAT | O_WRONLY | O_TRUNC &
+ * ( #if BITS_PER_LONG != 32 ; flags |= O_LARGEFILE; #endif )
+ *
+ * Code to initialize the file to be a valid ecryptfs file
+ */
+ inode = ecryptfs_dentry->d_inode;
+ crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats);
+ /* TODO: Initialization is done in alloc_inode, BUG if its not done */
+ if (!crypt_stats->struct_initialized)
+ BUG();
+ tlower_dentry = dget(lower_dentry);
+ if (!tlower_dentry) {
+ rc = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR, "Error dget'ing lower_dentry\n");
+ goto out;
+ }
+ lower_flags = ((O_CREAT | O_WRONLY | O_TRUNC) & O_ACCMODE) | O_RDWR;
+#if BITS_PER_LONG != 32
+ lower_flags |= O_LARGEFILE;
+#endif
+ lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+ mntget(lower_mnt);
+ /* Corresponding fput() at end of func */
+ lower_file = dentry_open(tlower_dentry, lower_mnt, lower_flags);
+ if (IS_ERR(lower_file)) {
+ rc = PTR_ERR(lower_file);
+ ecryptfs_printk(0, KERN_ERR,
+ "Error opening dentry; rc = [%i]\n", rc);
+ goto out;
+ }
+ /* fput(lower_file) should handle the puts if we do this */
+ lower_file->f_dentry = tlower_dentry;
+ lower_file->f_vfsmnt = lower_mnt;
+ lower_inode = tlower_dentry->d_inode;
+ if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
+ ecryptfs_printk(1, KERN_NOTICE, "This is a directory\n");
+ crypt_stats->encrypted = 0;
+ goto out_fput;
+ }
+ crypt_stats->new_file = 1;
+ ecryptfs_printk(1, KERN_NOTICE, "Initializing crypto context\n");
+ rc = ecryptfs_new_file_context(ecryptfs_dentry); /* crypto.c */
+ if (rc) {
+ ecryptfs_printk(1, KERN_NOTICE, "Error creating new file "
+ "context\n");
+ goto out_fput;
+ }
+ rc = ecryptfs_write_headers(ecryptfs_dentry, lower_file);
+ if (rc) {
+ ecryptfs_printk(1, KERN_NOTICE, "Error writing headers\n");
+ goto out_fput;
+ }
+ rc = grow_file(ecryptfs_dentry, lower_file, inode, lower_inode);
+out_fput:
+ fput(lower_file);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Creates a new file.
+ *
+ * @param dir The inode of the directory in which to create the file.
+ * @param dentry The eCryptfs dentry
+ * @param mode The mode of the new file.
+ * @param nd nameidata
+ * @return 0 on success; non-zero on error condition
+ */
+static int
+ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry,
+ int mode, struct nameidata *nd)
+{
+ int rc;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; ecryptfs_dentry->d_name.name = "
+ "[%s], directory_inode=[%p], ecryptfs_dentry->d_parent"
+ "->d_inode=[%p], nd->dentry=[%p], nd->last.name=[%s], "
+ "ecryptfs_dentry=[%p]\n", ecryptfs_dentry->d_name.name,
+ directory_inode, ecryptfs_dentry->d_parent->d_inode,
+ nd->dentry, nd->last.name, ecryptfs_dentry);
+
+ rc = ecryptfs_do_create(directory_inode, ecryptfs_dentry, mode, nd);
+ if (unlikely(rc)) {
+ ecryptfs_printk(0, KERN_WARNING, "Failed to create file in"
+ "lower filesystem\n");
+ goto out;
+ }
+ /* At this point, a file exists on "disk", we need to make sure
+ * that this on disk file is prepared to be an ecryptfs file */
+ rc = ecryptfs_initialize_file(ecryptfs_dentry);
+ out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Find a file on disk. If the file does not exist, then we'll add it to the
+ * dentry cache and continue on to read it from the disk.
+ *
+ * @param dir inode
+ * @param dentry dentry
+ * @param nd nameidata; may be NULL
+ */
+static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ int err = 0;
+ struct dentry *lower_dir_dentry;
+ struct dentry *lower_dentry;
+ struct dentry *tlower_dentry = NULL;
+ char *encoded_name;
+ unsigned int encoded_namelen;
+ struct ecryptfs_crypt_stats *crypt_stats = NULL;
+ char *page_virt = NULL;
+ struct inode *lower_inode;
+ unsigned long long file_size;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dir = [%p], dentry->d_name.nam"
+ "e = [%s], nd = [%p]\n", dir, dentry->d_name.name, nd);
+
+ lower_dir_dentry = ecryptfs_lower_dentry(dentry->d_parent);
+ dentry->d_op = &ecryptfs_dops;
+
+ /* Sanity Check: Make sure we can operate on the file */
+ if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, "."))
+ || (dentry->d_name.len == 2 && !strcmp(dentry->d_name.name, "..")))
+ goto out_drop;
+
+ encoded_namelen = ecryptfs_encode_filename(dentry->d_name.name,
+ dentry->d_name.len,
+ &encoded_name,
+ ECRYPTFS_SKIP_DOTS,
+ crypt_stats);
+ if (encoded_namelen < 0) {
+ err = encoded_namelen;
+ goto out_drop;
+ }
+ /* TODO: pretty sure we need to do a dput(lower_dentry) to
+ * counter */
+ ecryptfs_printk(1, KERN_NOTICE, "encoded_name = [%s]; encoded_namelen "
+ "= [%d]\n", encoded_name, encoded_namelen);
+ /* TODO: BUG: What to do on symlink to directory? */
+ lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry,
+ encoded_namelen - 1);
+ kfree(encoded_name);
+ if (IS_ERR(lower_dentry)) {
+ ecryptfs_printk(0, KERN_ERR, "ERR from lower_dentry\n");
+ err = PTR_ERR(lower_dentry);
+ goto out_drop;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "lower_dentry = [%p]; lower_dentry->"
+ "d_name.name = [%s]\n", lower_dentry,
+ lower_dentry->d_name.name);
+ lower_inode = lower_dentry->d_inode;
+ ecryptfs_copy_attr_atime(dir, lower_dir_dentry->d_inode);
+
+ /* Sanity check */
+ ASSERT(atomic_read(&lower_dentry->d_count));
+
+ /* Private allocation */
+ ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) =
+ kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL);
+ if (!ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry)) {
+ err = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR, "Out of memory whilst attempting "
+ "to allocate ecryptfs_dentry_info struct\n");
+ goto out_dput;
+ }
+
+ ECRYPTFS_DENTRY_TO_LOWER(dentry) = lower_dentry;
+ if (!lower_dentry->d_inode) {
+ /* We want to add because we couldn't find in lower */
+ d_add(dentry, NULL);
+ goto out;
+ }
+ err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 1);
+ if (err) {
+ ecryptfs_printk(0, KERN_ERR, "Error interposing\n");
+ goto out_dput;
+ }
+ /* Do we want to just get a handle to the directory and its lower
+ * and not abort with puts?
+ */
+ if (S_ISDIR(lower_inode->i_mode)) {
+ ecryptfs_printk(1, KERN_NOTICE, "Is a directory; returning\n");
+ goto out;
+ }
+ if (S_ISLNK(lower_inode->i_mode)) {
+ ecryptfs_printk(1, KERN_NOTICE, "Is a symlink; returning\n");
+ goto out;
+ }
+ /* We have a NULL dentry, can we just skip over the read here? */
+ if (!nd) {
+ ecryptfs_printk(1, KERN_NOTICE, "We have a NULL nd, just leave"
+ "as we *think* we are about to unlink\n");
+ goto out;
+ }
+ tlower_dentry = dget(lower_dentry);
+ if (!tlower_dentry || IS_ERR(tlower_dentry)) {
+ err = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR, "Cannot dget lower_dentry\n");
+ goto out_dput;
+ }
+ /* Released in this function */
+ page_virt =
+ (char *)kmem_cache_alloc(ecryptfs_header_cache_2,
+ SLAB_USER);
+ if (!page_virt) {
+ err = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR,
+ "Cannot ecryptfs_kmalloc a page\n");
+ goto out_dput;
+ }
+
+ /* Use the file's "header" to determine if its an ecryptfs file */
+ err = ecryptfs_read_header_region(page_virt, tlower_dentry, nd);
+ /* Force default values if we haven't parsed the header */
+ crypt_stats =
+ &(ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats);
+ if (!crypt_stats->policy_applied)
+ ecryptfs_set_default_sizes(crypt_stats);
+
+ if (err) {
+ err = 0;
+ ecryptfs_printk(1, KERN_WARNING, "Error reading header region;"
+ " assuming unencrypted\n");
+ } else {
+ if (!contains_ecryptfs_marker(page_virt
+ + ECRYPTFS_FILE_SIZE_BYTES)) {
+ ecryptfs_printk(0, KERN_WARNING, "Underlying file "
+ "lacks recognizable eCryptfs marker\n");
+ }
+ memcpy(&file_size, page_virt, sizeof(file_size));
+ dentry->d_inode->i_size = file_size;
+ }
+ kmem_cache_free(ecryptfs_header_cache_2, page_virt);
+ goto out;
+
+out_dput:
+ dput(lower_dentry);
+ if (tlower_dentry)
+ dput(tlower_dentry);
+out_drop:
+ d_drop(dentry);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return ERR_PTR(err);
+}
+
+static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ int err;
+ struct dentry *lower_old_dentry;
+ struct dentry *lower_new_dentry;
+ struct dentry *lower_dir_dentry;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_old_dentry = ecryptfs_lower_dentry(old_dentry);
+ lower_new_dentry = ecryptfs_lower_dentry(new_dentry);
+ dget(lower_old_dentry);
+ dget(lower_new_dentry);
+ lower_dir_dentry = lock_parent(lower_new_dentry);
+ err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
+ lower_new_dentry);
+ if (err || !lower_new_dentry->d_inode)
+ goto out_lock;
+ err = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
+ if (err)
+ goto out_lock;
+ ecryptfs_copy_attr_timesizes(dir, lower_new_dentry->d_inode);
+ old_dentry->d_inode->i_nlink =
+ ECRYPTFS_INODE_TO_LOWER(old_dentry->d_inode)->i_nlink;
+out_lock:
+ unlock_dir(lower_dir_dentry);
+ dput(lower_new_dentry);
+ dput(lower_old_dentry);
+ if (!new_dentry->d_inode)
+ d_drop(new_dentry);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return err;
+}
+
+static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int rc = 0;
+ struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ struct inode *lower_dir_inode = ECRYPTFS_INODE_TO_LOWER(dir);
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name = [%s]\n",
+ dentry->d_name.name);
+ lock_parent(lower_dentry);
+ rc = vfs_unlink(lower_dir_inode, lower_dentry);
+ if (rc) {
+ ecryptfs_printk(1, KERN_ERR, "Error in vfs_unlink\n");
+ goto out_unlock;
+ }
+ ecryptfs_copy_attr_times(dir, lower_dir_inode);
+ dentry->d_inode->i_nlink =
+ ECRYPTFS_INODE_TO_LOWER(dentry->d_inode)->i_nlink;
+ dentry->d_inode->i_ctime = dir->i_ctime;
+out_unlock:
+ unlock_parent(lower_dentry);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]",rc);
+ return rc;
+}
+
+static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct dentry *lower_dir_dentry;
+ umode_t mode;
+ char *encoded_symname;
+ unsigned int encoded_symlen;
+ struct ecryptfs_crypt_stats *crypt_stats = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ dget(lower_dentry);
+ lower_dir_dentry = lock_parent(lower_dentry);
+ mode = S_IALLUGO;
+ encoded_symlen = ecryptfs_encode_filename(symname, strlen(symname),
+ &encoded_symname,
+ ECRYPTFS_DO_DOTS,
+ crypt_stats);
+ if (encoded_symlen < 0) {
+ err = encoded_symlen;
+ goto out_lock;
+ }
+ err = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
+ encoded_symname, mode);
+ kfree(encoded_symname);
+ if (err || !lower_dentry->d_inode)
+ goto out_lock;
+ err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+ if (err)
+ goto out_lock;
+ ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+out_lock:
+ unlock_dir(lower_dir_dentry);
+ dput(lower_dentry);
+ if (!dentry->d_inode)
+ d_drop(dentry);
+ return err;
+}
+
+static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct dentry *lower_dir_dentry;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ lower_dir_dentry = lock_parent(lower_dentry);
+ err = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode);
+ if (err || !lower_dentry->d_inode)
+ goto out;
+ err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+ if (err)
+ goto out;
+ ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+ dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
+out:
+ unlock_dir(lower_dir_dentry);
+ if (!dentry->d_inode)
+ d_drop(dentry);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return err;
+}
+
+static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int err = 0;
+ struct dentry *tdentry = NULL;
+ struct dentry *lower_dentry;
+ struct dentry *tlower_dentry = NULL;
+ struct dentry *lower_dir_dentry;
+ struct dentry *tlower_dir_dentry = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ if (!(tdentry = dget(dentry))) {
+ err = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "Error dget'ing dentry [%p]\n",
+ dentry);
+ goto out;
+ }
+ lower_dir_dentry = lock_parent(lower_dentry);
+ if (!(tlower_dentry = dget(lower_dentry))) {
+ err = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "Error dget'ing lower_dentry "
+ "[%p]\n", lower_dentry);
+ goto out;
+ }
+ err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+ if (!err) {
+ d_delete(tlower_dentry);
+ tlower_dentry = NULL;
+ }
+ ecryptfs_copy_attr_times(dir, lower_dir_dentry->d_inode);
+ dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
+ unlock_dir(lower_dir_dentry);
+ if (!err)
+ d_drop(dentry);
+out:
+ if (tdentry)
+ dput(tdentry);
+ if (tlower_dentry)
+ dput(tlower_dentry);
+ if (tlower_dir_dentry)
+ dput(tlower_dir_dentry);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return err;
+}
+
+static int
+ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct dentry *lower_dir_dentry;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ lower_dir_dentry = lock_parent(lower_dentry);
+ err = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev);
+ if (err || !lower_dentry->d_inode)
+ goto out;
+ err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+ if (err)
+ goto out;
+ ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+out:
+ unlock_dir(lower_dir_dentry);
+ if (!dentry->d_inode)
+ d_drop(dentry);
+ return err;
+}
+
+static int
+ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int err;
+ struct dentry *lower_old_dentry;
+ struct dentry *lower_new_dentry;
+ struct dentry *lower_old_dir_dentry;
+ struct dentry *lower_new_dir_dentry;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_old_dentry = ecryptfs_lower_dentry(old_dentry);
+ lower_new_dentry = ecryptfs_lower_dentry(new_dentry);
+ dget(lower_old_dentry);
+ dget(lower_new_dentry);
+ lower_old_dir_dentry = dget_parent(lower_old_dentry);
+ lower_new_dir_dentry = dget_parent(lower_new_dentry);
+ lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
+ err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
+ lower_new_dir_dentry->d_inode, lower_new_dentry);
+ if (err)
+ goto out_lock;
+ ecryptfs_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
+ if (new_dir != old_dir)
+ ecryptfs_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
+out_lock:
+ unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
+ dput(lower_new_dentry);
+ dput(lower_old_dentry);
+ return err;
+}
+
+static int
+ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
+{
+ int err;
+ struct dentry *lower_dentry;
+ char *decoded_name;
+ char *lower_buf;
+ mm_segment_t old_fs;
+ struct ecryptfs_crypt_stats *crypt_stats;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ if (!lower_dentry->d_inode->i_op ||
+ !lower_dentry->d_inode->i_op->readlink) {
+ err = -EINVAL;
+ goto out;
+ }
+ /* Released in this function */
+ lower_buf = kmalloc(bufsiz, GFP_KERNEL);
+ if (lower_buf == NULL) {
+ ecryptfs_printk(0, KERN_ERR, "Out of memory\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ecryptfs_printk(1, KERN_NOTICE, "Calling readlink w/ "
+ "lower_dentry->d_name.name = [%s]\n",
+ lower_dentry->d_name.name);
+ err = lower_dentry->d_inode->i_op->readlink(lower_dentry,
+ (char __user *)lower_buf,
+ bufsiz);
+ set_fs(old_fs);
+ if (err >= 0) {
+ crypt_stats = NULL;
+ err = ecryptfs_decode_filename(lower_buf, err,
+ &decoded_name,
+ ECRYPTFS_DO_DOTS, crypt_stats);
+ if (err == -ENOMEM)
+ goto out_free_lower_buf;
+ if (err > 0) {
+ ecryptfs_printk(1, KERN_NOTICE, "Copying [%d] bytes "
+ "to userspace: [%*s]\n", err, err,
+ decoded_name);
+ if (copy_to_user(buf, decoded_name, err))
+ err = -EFAULT;
+ }
+ kfree(decoded_name);
+ ecryptfs_copy_attr_atime(dentry->d_inode,
+ lower_dentry->d_inode);
+ }
+out_free_lower_buf:
+ kfree(lower_buf);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return err;
+}
+
+static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *buf;
+ int len = PAGE_SIZE, rc;
+ mm_segment_t old_fs;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n",
+ dentry->d_name.name);
+ /* Released in ecryptfs_put_link(); only release here on error */
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ecryptfs_printk(1, KERN_NOTICE, "Calling readlink w/ "
+ "dentry->d_name.name = [%s]\n", dentry->d_name.name);
+ rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len);
+ buf[rc] = '\0';
+ set_fs(old_fs);
+ if (rc < 0)
+ goto out_free;
+ rc = 0;
+ nd_set_link(nd, buf);
+ goto out;
+out_free:
+ kfree(buf);
+out:
+ return ERR_PTR(rc);
+}
+
+static inline void
+ecryptfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+{
+ /* Free the char* */
+ kfree(nd_get_link(nd));
+}
+
+/**
+ * Calculate the requried size of the lower file based on the specified size
+ * of the upper file. This calculation is based on the number of headers in
+ * the underlying file, the number of records (IV/HMAC/IV+HMAC) per page
+ * that can be stored, and the extent size of the underlying file (page size).
+ *
+ * @param crypt_stats crypt_stats associated with file
+ * @param upper_size size of the upper file
+ * @return calculated size of the lower file.
+ */
+static loff_t
+upper_size_to_lower_size(struct ecryptfs_crypt_stats *crypt_stats,
+ loff_t upper_size)
+{
+ int upper_idx = 0;
+ int lower_idx;
+ loff_t lower_size = upper_size;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; upper_size = [%lld]\n",
+ upper_size);
+ /* TODO: Does this need to be based on the extent size? */
+ if (likely(upper_size > 0))
+ upper_idx = (upper_size - 1) >> PAGE_CACHE_SHIFT;
+ else if (!upper_size)
+ upper_idx = 0;
+ else /* Sanity check, size shouldn't be negative */
+ BUG();
+ lower_idx = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, upper_idx);
+ lower_size = ((lower_idx + 1) << PAGE_CACHE_SHIFT);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; lower_size = [%lld]\n",
+ lower_size);
+ return lower_size;
+}
+
+/**
+ * Function to handle truncations modifying the size of the file. Note
+ * that the file sizes are interpolated. When expanding, we are simply
+ * writing strings of 0's out. When truncating, we need to modify the
+ * underlying file size accordingly to the page index interpolations.
+ *
+ * N.B. No claims are made about integrety of the encrypted data when
+ * shrinking a file. (But its probably going to be lost)
+ *
+ * TODO: Support holes. Should we also be supporting preservation of
+ * the encrypted data?
+ *
+ * @param dentry The ecryptfs layer dentry
+ * @param new_length The length to expand the file to
+ * @return Zero on success; non-zero otherwise
+ */
+int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
+{
+ int rc = 0;
+ struct inode *inode = dentry->d_inode;
+ struct dentry *lower_dentry;
+ struct file fake_ecryptfs_file, *lower_file = NULL;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ loff_t i_size = i_size_read(inode);
+ loff_t lower_size_before_truncate;
+ loff_t lower_size_after_truncate;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry = [%p], new_length = "
+ "[%lld], i_size_read(inode) = [%lld]\n",
+ dentry, new_length, i_size);
+ /* Sanity checks */
+ if (unlikely((new_length == i_size)))
+ goto out;
+ crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats;
+ if (unlikely(!crypt_stats)) {
+ ecryptfs_printk(0, KERN_ERR, "NULL crypt_stats on dentry with "
+ "d_name.name = [%s]\n", dentry->d_name.name);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Set up a fake ecryptfs file, this is used to interface with the file
+ * in the underlying filesystem so that the truncation has an effect
+ * there as well. */
+ memset(&fake_ecryptfs_file, 0, sizeof(struct file));
+ fake_ecryptfs_file.f_dentry = dentry;
+ /* Released at out_free: */
+ ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_ecryptfs_file) =
+ kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+ if (unlikely(!ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file))) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ /* This dget & mntget is released through fput at out_fput: */
+ dget(lower_dentry);
+ mntget(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt);
+ lower_file = dentry_open(
+ lower_dentry,
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt, O_RDWR);
+ if (unlikely(IS_ERR(lower_file))) {
+ rc = PTR_ERR(lower_file);
+ goto out_free;
+ }
+ ECRYPTFS_FILE_TO_LOWER(&fake_ecryptfs_file) = lower_file;
+
+ /* Switch on growing or shrinking file */
+ if (new_length > i_size) {
+ rc = ecryptfs_fill_zeros(&fake_ecryptfs_file, new_length);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR,
+ "Problem with fill_zeros\n");
+ goto out_fput;
+ }
+ i_size_write(inode, new_length);
+ rc = ecryptfs_write_inode_size_to_header(lower_file,
+ lower_dentry->d_inode,
+ inode);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR,
+ "Problem with ecryptfs_write"
+ "_inode_size\n");
+ goto out_fput;
+ }
+ } else { /* new_length < i_size_read(inode) */
+ vmtruncate(inode, new_length);
+ ecryptfs_write_inode_size_to_header(lower_file,
+ lower_dentry->d_inode,
+ inode);
+ /* We are reducing the size of the ecryptfs file, and need to
+ * know if we need to reduce the size of the lower file. */
+ lower_size_before_truncate =
+ upper_size_to_lower_size(crypt_stats, i_size);
+ lower_size_after_truncate =
+ upper_size_to_lower_size(crypt_stats, new_length);
+ if (lower_size_after_truncate < lower_size_before_truncate)
+ vmtruncate(lower_dentry->d_inode,
+ lower_size_after_truncate);
+ }
+ /* Update the access times */
+ lower_dentry->d_inode->i_mtime = CURRENT_TIME;
+ lower_dentry->d_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty_sync(inode);
+out_fput:
+ /* TODO: Determine which of these need to really be called.
+ * filp_close() will call fput(), but do we need to do the extra work
+ * that filp_close() provides?
+ if (lower_file)
+ filp_close(lower_file, NULL);
+ */
+ fput(lower_file);
+out_free:
+ if (ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file))
+ kmem_cache_free(ecryptfs_file_info_cache,
+ ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file));
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int
+ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+ struct inode *lower_inode;
+ int rc = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p], mask=[%d], nd ="
+ "[%p]\n", inode, mask, nd);
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ if (nd)
+ ecryptfs_printk(1, KERN_NOTICE, "nd->dentry = [%p]\n",
+ nd->dentry);
+ rc = permission(lower_inode, mask, nd);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Updates the metadata of an inode. If the update is to the size
+ * i.e. truncation, then ecryptfs_truncate will handle the size modification
+ * of both the ecryptfs inode and the lower inode.
+ *
+ * All other metadata changes will be passed right to the lower filesystem,
+ * and we will just update our inode to look like the lower.
+ *
+ * @param dentry dentry handle to the inode to modify
+ * @param ia structure with flags of what to change and values
+ */
+static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+ int err = 0;
+ struct dentry *lower_dentry;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n",
+ dentry->d_name.name);
+ crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats;
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ inode = dentry->d_inode;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ if (ia->ia_valid & ATTR_SIZE) {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "ia->ia_valid = [0x%x] ATTR_SIZE" " = [0x%x]\n",
+ ia->ia_valid, ATTR_SIZE);
+ err = ecryptfs_truncate(dentry, ia->ia_size);
+ /* ecryptfs_truncate handles resizing of the lower file */
+ ia->ia_valid &= ~ATTR_SIZE;
+ ecryptfs_printk(1, KERN_NOTICE, "ia->ia_valid = [%x]\n",
+ ia->ia_valid);
+ if (err < 0)
+ goto out;
+ }
+ err = notify_change(lower_dentry, ia);
+out:
+ ecryptfs_copy_attr_all(inode, lower_inode);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n");
+ return err;
+}
+
+struct inode_operations ecryptfs_symlink_iops = {
+ .readlink = ecryptfs_readlink,
+ .follow_link = ecryptfs_follow_link,
+ .put_link = ecryptfs_put_link,
+ .permission = ecryptfs_permission,
+ .setattr = ecryptfs_setattr,
+};
+
+struct inode_operations ecryptfs_dir_iops = {
+ .create = ecryptfs_create,
+ .lookup = ecryptfs_lookup,
+ .link = ecryptfs_link,
+ .unlink = ecryptfs_unlink,
+ .symlink = ecryptfs_symlink,
+ .mkdir = ecryptfs_mkdir,
+ .rmdir = ecryptfs_rmdir,
+ .mknod = ecryptfs_mknod,
+ .rename = ecryptfs_rename,
+ .permission = ecryptfs_permission,
+ .setattr = ecryptfs_setattr,
+};
+
+struct inode_operations ecryptfs_main_iops = {
+ .permission = ecryptfs_permission,
+ .setattr = ecryptfs_setattr,
+};

2005-11-19 04:23:20

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 11/12: eCryptfs] Keystore

eCryptfs keystore. Packet generation and parsing code. Authentication
token management code. This file has been trimmed down considerably to
support only mount-wide passphrases in the 0.1 release.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

keystore.c | 820 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 820 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/keystore.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/keystore.c 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,820 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * In-kernel key management code. Includes functions to parse and
+ * write authentication token-related packets with the underlying
+ * file.
+ *
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * 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/string.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/pagemap.h>
+#include <linux/key.h>
+#include <linux/random.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * request_key returned an error instead of a valid key address;
+ * determine the type of error, make appropriate log entries, and
+ * return an error code.
+ */
+int process_request_key_err(long err_code)
+{
+ int rc = 0;
+
+ switch (err_code) {
+ case ENOKEY:
+ ecryptfs_printk(0, KERN_WARNING, "No key\n");
+ rc = -ENOENT;
+ break;
+ case EKEYEXPIRED:
+ ecryptfs_printk(0, KERN_WARNING, "Key expired\n");
+ rc = -ETIME;
+ break;
+ case EKEYREVOKED:
+ ecryptfs_printk(0, KERN_WARNING, "Key revoked\n");
+ rc = -EINVAL;
+ break;
+ default:
+ ecryptfs_printk(0, KERN_WARNING, "Unknown error code: "
+ "[%lu]\n", err_code);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static 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(1, KERN_NOTICE, "Auth tok at mem loc [%p]:\n",
+ auth_tok);
+ ecryptfs_printk(1, KERN_NOTICE, " * instanceof = [%d]\n",
+ auth_tok->instanceof);
+ ecryptfs_printk(1, KERN_NOTICE, " * instantiated = [%d]\n",
+ auth_tok->instantiated);
+ switch (auth_tok->instanceof) {
+ case ECRYPTFS_PASSWORD:
+ ecryptfs_printk(1, KERN_NOTICE, " * password = [%s]\n",
+ auth_tok->token.password.password);
+ ecryptfs_printk(1, KERN_NOTICE, " * password_size = [%d]\n",
+ auth_tok->token.password.password_size);
+ ecryptfs_to_hex(salt, auth_tok->token.password.salt,
+ ECRYPTFS_SALT_SIZE);
+ salt[ECRYPTFS_SALT_SIZE * 2] = '\0';
+ ecryptfs_printk(1, KERN_NOTICE, " * salt = [%s]\n", salt);
+ ecryptfs_printk(1, KERN_NOTICE, " * saltless = [%d]\n",
+ auth_tok->token.password.saltless);
+ memcpy(sig, auth_tok->token.password.signature,
+ ECRYPTFS_SIG_SIZE_HEX);
+ sig[ECRYPTFS_SIG_SIZE_HEX] = '\0';
+ ecryptfs_printk(1, KERN_NOTICE, " * signature = [%s]\n", sig);
+ break;
+ case ECRYPTFS_PRIVATE_KEY:
+ ecryptfs_printk(1, KERN_NOTICE, " * (NO PRIVATE KEY SUPPORT "
+ "IN ECRYPTFS VERSION 0.1)\n");
+ break;
+ default:
+ ecryptfs_printk(1, KERN_NOTICE, " * Unrecognized instanceof\n");
+ }
+ ecryptfs_printk(1, KERN_NOTICE, " * 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(1, KERN_NOTICE,
+ " * Userspace decrypt request set\n");
+ if (auth_tok->session_key.flags
+ & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT)
+ ecryptfs_printk(1, KERN_NOTICE,
+ " * Userspace encrypt request set\n");
+ if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_DECRYPTED_KEY) {
+ ecryptfs_printk(1, KERN_NOTICE, " * Contains decrypted key\n");
+ ecryptfs_printk(1, KERN_NOTICE,
+ " * session_key.decrypted_key_size = [0x%x]\n",
+ auth_tok->session_key.decrypted_key_size);
+ ecryptfs_printk(1, KERN_NOTICE, " * 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(1, KERN_NOTICE, " * Contains encrypted key\n");
+ ecryptfs_printk(1, KERN_NOTICE,
+ " * session_key.encrypted_key_size = [0x%x]\n",
+ auth_tok->session_key.encrypted_key_size);
+ ecryptfs_printk(1, KERN_NOTICE, " * 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);
+ }
+}
+
+static void wipe_auth_tok_list(struct list_head *auth_tok_list_head)
+{
+ struct list_head *walker;
+ struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ walker = auth_tok_list_head->next;
+ while (walker != auth_tok_list_head) {
+ auth_tok_list_item =
+ list_entry(walker, struct ecryptfs_auth_tok_list_item,
+ list);
+ walker = auth_tok_list_item->list.next;
+ kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
+ auth_tok_list_item);
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+kmem_cache_t *ecryptfs_auth_tok_list_item_cache;
+
+/**
+ * @return Zero on success
+ */
+static int parse_packet_length(unsigned char *data, int *offset, int *size)
+{
+ int rc = 0;
+
+ if (data[(*offset)] < 192) {
+ /* One-byte length */
+ (*size) = data[(*offset)++];
+ } else if (data[(*offset)] < 224) {
+ /* Two-byte length */
+ (*size) = ((data[(*offset)++] - 192) * 256);
+ (*size) += (data[(*offset)++] + 192);
+ } else if (data[(*offset)] == 255) {
+ /* Three-byte length; we're not supposed to see this */
+ ecryptfs_printk(0, KERN_ERR, "Three-byte packet length not "
+ "supported\n");
+ rc = -EINVAL;
+ goto out;
+ } else {
+ ecryptfs_printk(0, KERN_ERR, "Error parsing packet length\n");
+ rc = -EINVAL;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+static int write_packet_length(char *dest, int *dest_offset, int size)
+{
+ int rc = 0;
+
+ if (size < 192) {
+ dest[(*dest_offset)] = size;
+ (*dest_offset)++;
+ } else if (size < 65536) {
+ dest[(*dest_offset)] = (((size - 192) / 256) + 192);
+ (*dest_offset)++;
+ dest[(*dest_offset)] = ((size - 192) % 256);
+ (*dest_offset)++;
+ } else {
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_WARNING,
+ "Unsupported packet size: [%d]\n", size);
+ }
+ return rc;
+}
+
+/**
+ * Passphrase packet.
+ *
+ * @return New offset in the packet set page; error value on error.
+ */
+static int
+parse_tag_3_packet(struct ecryptfs_crypt_stats *crypt_stats,
+ unsigned char *data, struct list_head *auth_tok_list)
+{
+ int i = 0;
+ int rc = 0;
+ int body_size = 0;
+ struct ecryptfs_auth_tok *auth_tok;
+ struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (data[i++] != 0x8c) {
+ ecryptfs_printk(0, KERN_ERR, "Enter w/ first byte != 0x8c\n");
+ rc = -EINVAL;
+ goto out_no_mem;
+ }
+ /* Released: wipe_auth_tok_list called in ecryptfs_parse_packet_set or
+ * at end of function upon failure */
+ auth_tok_list_item =
+ kmem_cache_alloc(ecryptfs_auth_tok_list_item_cache,
+ SLAB_KERNEL);
+ if (!auth_tok_list_item) {
+ ecryptfs_printk(0, KERN_ERR, "Unable to allocate memory\n");
+ rc = -ENOMEM;
+ goto out_no_mem;
+ }
+ auth_tok = &auth_tok_list_item->auth_tok;
+ memset(auth_tok, 0, sizeof(struct ecryptfs_auth_tok));
+ /* TODO: Make *sure* it's encrypted; do this w/ policy tokens.
+ * Actually, this is probably not the right place to set this
+ * flag. If the eCryptfs marker matches, then we probably have
+ * a real tag 3 packet, and hence the file is most likely
+ * encrypted. */
+ rc = parse_packet_length(data, &i, &body_size);
+ if (rc)
+ goto out;
+ auth_tok->session_key.encrypted_key_size =
+ body_size - (0x05 + ECRYPTFS_SALT_SIZE);
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypted key size = [%d]\n",
+ auth_tok->session_key.encrypted_key_size);
+ if (data[i++] != 0x04) {
+ ecryptfs_printk(1, KERN_NOTICE, "Unknown version number "
+ "[%d]\n", data[i - 1]);
+ rc = -EINVAL;
+ goto out;
+ }
+ ecryptfs_cipher_code_to_string(crypt_stats->cipher, (u16)data[i]);
+ switch(data[i++]) {
+ case 0x07:
+ crypt_stats->key_size_bits = 128;
+ break;
+ case 0x08:
+ crypt_stats->key_size_bits = 192;
+ break;
+ case 0x09:
+ crypt_stats->key_size_bits = 256;
+ break;
+ }
+ ecryptfs_init_crypt_ctx(crypt_stats);
+ if (data[i++] != 0x03) {
+ ecryptfs_printk(0, KERN_ERR, "Only S2K ID 3 is currently "
+ "supported\n");
+ rc = -ENOSYS;
+ goto out;
+ }
+ /* TODO: finish the hash mapping */
+ switch (data[i++]) {
+ case 0x01:
+ /* Choose MD5 */
+ memcpy(auth_tok->token.password.salt, &data[i],
+ ECRYPTFS_SALT_SIZE);
+ i += ECRYPTFS_SALT_SIZE;
+ auth_tok->token.password.hash_iterations =
+ ((u32) 16 + (data[i] & 15)) << ((data[i] >> 4) + 6);
+ i++;
+ memcpy(auth_tok->session_key.encrypted_key, &data[i],
+ auth_tok->session_key.encrypted_key_size);
+ i += auth_tok->session_key.encrypted_key_size;
+ auth_tok->session_key.flags &= ~ECRYPTFS_CONTAINS_DECRYPTED_KEY;
+ auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_ENCRYPTED_KEY;
+ auth_tok->token.password.hash_algo = 0x01;
+ break;
+ default:
+ ecryptfs_printk(0, KERN_ERR, "Unsupported hash algorithm: "
+ "[%d]\n", data[i - 1]);
+ rc = -ENOSYS;
+ goto out;
+ }
+ /* TODO: Use the keyring */
+ auth_tok->uid = current->uid;
+ auth_tok->instanceof = ECRYPTFS_PASSWORD;
+ if (data[i++] != 0xed) {
+ ecryptfs_printk(0, KERN_ERR, "No (ecryptfs-specific) literal "
+ "packet containing authentication token "
+ "signature found after tag 3 packet\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (data[i++] != (ECRYPTFS_SIG_SIZE + 13)) {
+ ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (data[i++] != 0x62) {
+ ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (data[i++] != 0x08) {
+ ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ i += 12; /* We don't care about the filename or the timestamp */
+ /* TODO: Parametarize; we might actually want userspace to
+ * decrypt the session key. */
+ auth_tok->session_key.flags &=
+ ~ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT;
+ auth_tok->session_key.flags &=
+ ~ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT;
+ ecryptfs_to_hex(auth_tok->token.password.signature, &data[i],
+ ECRYPTFS_SIG_SIZE);
+ auth_tok->token.password.signature[ECRYPTFS_PASSWORD_SIG_SIZE] = '\0';
+ rc = i;
+ list_add(&auth_tok_list_item->list, auth_tok_list);
+ crypt_stats->encrypted = 1;
+ goto out_success;
+out:
+ kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
+ auth_tok_list_item);
+out_no_mem:
+out_success:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Decrypt the session key with the given auth_tok.
+ *
+ * TODO: Performance: This is a good candidate for optimization.
+ *
+ * @param auth_tok
+ * @return 0 on success; non-zero error otherwise
+ */
+static int decrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
+ struct ecryptfs_crypt_stats *crypt_stats)
+{
+ int rc = 0;
+ struct ecryptfs_password *password_s_ptr;
+ struct crypto_tfm *tfm = NULL;
+ struct scatterlist src_sg[2], dst_sg[2];
+ /* TODO: Use virt_to_scatterlist for these */
+ char *encrypted_session_key;
+ char *session_key;
+
+ password_s_ptr = &auth_tok->token.password;
+ if (password_s_ptr->session_key_encryption_key_set)
+ ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key "
+ "set; skipping key generation\n");
+ ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key (size [%d])"
+ ":\n", password_s_ptr->session_key_encryption_key_size);
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(password_s_ptr->session_key_encryption_key,
+ password_s_ptr->
+ session_key_encryption_key_size);
+ tfm = crypto_alloc_tfm(crypt_stats->cipher, 0);
+ if (!tfm) {
+ ecryptfs_printk(0, KERN_ERR, "Error allocating crypto "
+ "context\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ crypto_cipher_setkey(tfm, password_s_ptr->session_key_encryption_key,
+ password_s_ptr->session_key_encryption_key_size);
+ /* TODO: virt_to_scatterlist */
+ encrypted_session_key = (char *)__get_free_page(GFP_KERNEL);
+ if (!encrypted_session_key) {
+ ecryptfs_printk(0, KERN_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ session_key = (char *)__get_free_page(GFP_KERNEL);
+ if (!session_key) {
+ kfree(encrypted_session_key);
+ ecryptfs_printk(0, KERN_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ memcpy(encrypted_session_key, auth_tok->session_key.encrypted_key,
+ auth_tok->session_key.encrypted_key_size);
+ src_sg[0].page = virt_to_page(encrypted_session_key);
+ src_sg[0].offset = 0;
+ /* TODO: key_size < PAGE_CACHE_SIZE */
+ src_sg[0].length = auth_tok->session_key.encrypted_key_size;
+ dst_sg[0].page = virt_to_page(session_key);
+ dst_sg[0].offset = 0;
+ auth_tok->session_key.decrypted_key_size =
+ auth_tok->session_key.encrypted_key_size;
+ dst_sg[0].length = auth_tok->session_key.encrypted_key_size;
+ /* TODO: Handle error condition */
+ crypto_cipher_decrypt(tfm, dst_sg, src_sg,
+ auth_tok->session_key.encrypted_key_size);
+ auth_tok->session_key.decrypted_key_size =
+ auth_tok->session_key.encrypted_key_size;
+ memcpy(auth_tok->session_key.decrypted_key, session_key,
+ auth_tok->session_key.decrypted_key_size);
+ auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY;
+ memcpy(crypt_stats->key, auth_tok->session_key.decrypted_key,
+ auth_tok->session_key.decrypted_key_size);
+ crypt_stats->key_valid = 1;
+ crypt_stats->key_size_bits =
+ auth_tok->session_key.decrypted_key_size * 8;
+ ecryptfs_printk(1, KERN_NOTICE, "Decrypted session key:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ free_page((unsigned long)encrypted_session_key);
+ free_page((unsigned long)session_key);
+out:
+ if (tfm)
+ crypto_free_tfm(tfm);
+ return rc;
+}
+
+/**
+ * N.B. This comment is applicable to 0.2 release (and later) only.
+ * Extract the authentication token signatures. eCryptfs expects this
+ * function to recover the symmetric key of the crypt_stats structure
+ * if at all possible, given the current packet set. Authentication
+ * tokens are composed of the tokens themselves and their descriptors.
+ * It is possible to have an authentication token object in the
+ * keyring that only has a descriptor and not a token component. Each
+ * eCryptfs file header contains a concatenation of descriptors. For
+ * each descriptor, this function assures that an authentication token
+ * object is instantiated in the keyring. This instantiation takes
+ * place in the request_key callout application. Thus, the callout
+ * application requires the complete descriptor. The crypt_stats
+ * object contains a set of descriptors that apply to this file. When
+ * the headers are written out, they are re-constructed from the set
+ * of authentication token descriptors.
+ *
+ * If at any point we have a problem parsing the packets, we will -EIO and
+ * just bail out.
+ *
+ * GOAL: Get crypt_stats to have the file's session key.
+ *
+ * @param dest The header page in memory
+ * @param version Version of file format, to guide parsing behavior
+ * @return 0 if a valid authentication token was retrieved and processed;
+ * negative value for file not encrypted or for error conditions
+ */
+int ecryptfs_parse_packet_set(unsigned char *dest,
+ struct ecryptfs_crypt_stats *crypt_stats,
+ struct dentry *ecryptfs_dentry, int version)
+{
+ int i = 0;
+ int rc = 0;
+ int found_auth_tok = 0;
+ int next_packet_is_auth_tok_packet;
+ char sig[ECRYPTFS_SIG_SIZE_HEX];
+ struct list_head auth_tok_list;
+ struct list_head *walker;
+ struct ecryptfs_auth_tok *chosen_auth_tok = NULL;
+ struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ ecryptfs_dentry->d_sb)->mount_crypt_stats);
+ struct ecryptfs_auth_tok *candidate_auth_tok = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ ecryptfs_set_default_sizes(crypt_stats);
+ INIT_LIST_HEAD(&auth_tok_list);
+
+ /* Parse the header to find as many packets as we can, these will be
+ * added the our &auth_tok_list */
+ next_packet_is_auth_tok_packet = 1;
+ while (next_packet_is_auth_tok_packet)
+ switch (dest[i]) {
+ case 0x8c: /* tag 3 packet; s2k */
+ rc = parse_tag_3_packet(crypt_stats,
+ (unsigned char *)&dest[i],
+ &auth_tok_list);
+ if (rc != -EINVAL && rc != -ENOMEM) {
+ i += rc;
+ rc = 0;
+ } else {
+ ecryptfs_printk(0, KERN_ERR, "Error parsing "
+ "tag 3 packet\n");
+ rc = -EIO;
+ goto out_wipe_list;
+ }
+ break;
+ default:
+ ecryptfs_printk(1, KERN_NOTICE, "No packet at offset "
+ "[%d] of the file header; hex value of "
+ "character is [0x%.2x]\n", i, dest[i]);
+ next_packet_is_auth_tok_packet = 0;
+ }
+ if (list_empty(&auth_tok_list)) {
+ rc = -EINVAL; /* Do not support non-encrypted files */
+ goto out;
+ }
+
+ /* If we have a global auth tok, then use it should be tried */
+ if (mount_crypt_stats->global_auth_tok) {
+ memcpy(sig, mount_crypt_stats->global_auth_tok_sig,
+ ECRYPTFS_SIG_SIZE_HEX);
+ chosen_auth_tok = mount_crypt_stats->global_auth_tok;
+ } else
+ BUG(); /* This should not be the case in 0.1 release */
+ /* Scan list to see if our chosen_auth_tok works */
+ list_for_each(walker, &auth_tok_list) {
+ struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+ auth_tok_list_item =
+ list_entry(walker, struct ecryptfs_auth_tok_list_item,
+ list);
+ candidate_auth_tok = &auth_tok_list_item->auth_tok;
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Considering cadidate auth tok:\n");
+ ecryptfs_dump_auth_tok(candidate_auth_tok);
+ }
+ /* TODO: Replace ECRYPTFS_SIG_SIZE_HEX w/ dynamic value */
+ if ((candidate_auth_tok->instanceof == ECRYPTFS_PASSWORD) &&
+ !strncmp(candidate_auth_tok->token.password.signature,
+ sig, ECRYPTFS_SIG_SIZE_HEX)) {
+ found_auth_tok = 1;
+ goto leave_list;
+ /* TODO: Transfer the common salt into the
+ * crypt_stats salt */
+ }
+ }
+leave_list:
+ if (!found_auth_tok) {
+ ecryptfs_printk(0, KERN_ERR, "Could not find authentication "
+ "token on temporary list for sig [%.*s]\n",
+ ECRYPTFS_SIG_SIZE_HEX, sig);
+ goto out_wipe_list;
+ } else {
+ memcpy(&(candidate_auth_tok->token.password),
+ &(chosen_auth_tok->token.password),
+ sizeof(struct ecryptfs_password));
+ decrypt_session_key(candidate_auth_tok, crypt_stats);
+ }
+
+out_wipe_list:
+ wipe_auth_tok_list(&auth_tok_list);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Write passphrase packet
+ *
+ * @param dest Buffer into which to write the packet
+ * @param max Maximum number of bytes that can be writtn
+ * @return Number of bytes written; 0 on error
+ */
+static int
+write_tag_3_packet(char *dest, int dest_offset, int max,
+ struct ecryptfs_auth_tok *auth_tok,
+ struct ecryptfs_crypt_stats *crypt_stats)
+{
+ int rc = 0;
+ struct ecryptfs_key_record key_rec;
+ int i;
+ int signature_is_valid = 0;
+ int encrypted_session_key_valid = 0;
+ char session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+ struct scatterlist dest_sg[2];
+ struct scatterlist src_sg[2];
+ struct crypto_tfm *tfm = NULL;
+ int key_rec_size;
+ int offset_save = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; dest_offset = [%d]\n",
+ dest_offset);
+ /* Check for a valid signature on the auth_tok */
+ for (i = 0; i < ECRYPTFS_SIG_SIZE_HEX; i++)
+ signature_is_valid |= auth_tok->token.password.signature[i];
+ if (!signature_is_valid)
+ BUG();
+ ecryptfs_from_hex(key_rec.sig, auth_tok->token.password.signature,
+ ECRYPTFS_SIG_SIZE);
+ key_rec.enc_key_size_bits = crypt_stats->key_size_bits;
+ key_rec.type = ECRYPTFS_PACKET_SET_TYPE_PASSWORD;
+
+ encrypted_session_key_valid = 0;
+ if (auth_tok->session_key.encrypted_key_size == 0)
+ auth_tok->session_key.encrypted_key_size =
+ ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES;
+ for (i = 0; i < auth_tok->session_key.encrypted_key_size; i++)
+ encrypted_session_key_valid |=
+ auth_tok->session_key.encrypted_key[i];
+ if (auth_tok->session_key.encrypted_key_size == 0) {
+ ecryptfs_printk(0, KERN_WARNING, "auth_tok->session_key."
+ "encrypted_key_size == 0");
+ auth_tok->session_key.encrypted_key_size =
+ ECRYPTFS_DEFAULT_KEY_BYTES;
+ }
+ if (encrypted_session_key_valid) {
+ memcpy(key_rec.enc_key,
+ auth_tok->session_key.encrypted_key,
+ auth_tok->session_key.encrypted_key_size);
+ goto encrypted_session_key_set;
+ }
+ if (auth_tok->token.password.session_key_encryption_key_set) {
+ ecryptfs_printk(1, KERN_NOTICE, "Using previously generated "
+ "session key encryption key of size [%d]\n",
+ auth_tok->token.password.
+ session_key_encryption_key_size);
+ memcpy(session_key_encryption_key,
+ auth_tok->token.password.session_key_encryption_key,
+ auth_tok->token.password.
+ session_key_encryption_key_size);
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Cached session key " "encryption key: \n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(session_key_encryption_key, 16);
+ goto session_key_encryption_key_set;
+ }
+ session_key_encryption_key_set:
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key:"
+ "\n");
+ ecryptfs_dump_hex(session_key_encryption_key, 16);
+ }
+ /* Encrypt the key with the key encryption key */
+ /* Set up the scatterlists */
+ rc = virt_to_scatterlist(crypt_stats->key,
+ crypt_stats->key_size_bits / 8, src_sg, 2);
+ if (!rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error generating scatterlist "
+ "for crypt_stats session key\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = virt_to_scatterlist(key_rec.enc_key,
+ key_rec.enc_key_size_bits / 8, dest_sg, 2);
+ if (!rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error generating scatterlist "
+ "for crypt_stats encrypted session key\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ /* Initialize the key encryption context */
+ ASSERT(crypt_stats->cipher);
+ if ((tfm = crypto_alloc_tfm(crypt_stats->cipher, 0)) == NULL) {
+ ecryptfs_printk(0, KERN_ERR, "Could not initialize crypto "
+ "context for cipher [%s]\n",
+ crypt_stats->cipher);
+ rc = 0;
+ goto out;
+ }
+ /* Set the key encryption key */
+ rc = crypto_cipher_setkey(tfm, session_key_encryption_key,
+ ECRYPTFS_DEFAULT_KEY_BYTES);
+ if (rc < 0) {
+ ecryptfs_printk(0, KERN_ERR, "Error setting key for crypto "
+ "context\n");
+ rc = 0;
+ goto out;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypting [%d] bytes of the key\n",
+ crypt_stats->key_size_bits / 8);
+ crypto_cipher_encrypt(tfm, dest_sg, src_sg,
+ crypt_stats->key_size_bits / 8);
+
+ ecryptfs_printk(1, KERN_NOTICE, "This should be the encrypted key:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(key_rec.enc_key,
+ key_rec.enc_key_size_bits / 8);
+encrypted_session_key_set:
+ /* Now we have a valid key_rec. Append it to the
+ * key_rec set. */
+ key_rec_size = KEY_REC_SIZE(key_rec);
+ if ((dest_offset + key_rec_size) >= ECRYPTFS_MAX_KEYSET_SIZE) {
+ ecryptfs_printk(0, KERN_ERR, "Keyset too large\n");
+ rc = 0;
+ goto out;
+ }
+ offset_save = dest_offset;
+ if ((dest_offset + 0x05 + ECRYPTFS_SALT_SIZE
+ + (key_rec.enc_key_size_bits / 8)) >= PAGE_CACHE_SIZE) {
+ ecryptfs_printk(0, KERN_ERR, "Too many authentication tokens; "
+ "cryptfs does not yet support this many\n");
+ rc = 0;
+ goto out;
+ }
+ /* This format is inspired by OpenPGP; see RFC 2440
+ * packet tag 3 */
+ *(dest + (dest_offset++)) = 0x8c; /* tag 3 */
+ /* ver+cipher+s2k+hash+salt+iter+enc_key */
+ rc = write_packet_length(dest, &dest_offset,
+ (0x05 + ECRYPTFS_SALT_SIZE
+ + (key_rec.enc_key_size_bits / 8)));
+ *(dest + (dest_offset++)) = 0x04; /* version 4 */
+
+ rc = ecryptfs_code_for_cipher_string(crypt_stats->cipher);
+ if (rc == 0) {
+ ecryptfs_printk(0, KERN_WARNING, "Unable to generate code for "
+ "cipher [%s]\n", crypt_stats->cipher);
+ goto out;
+ }
+ if (rc == 0x07) {
+ switch (crypt_stats->key_size_bits) {
+ case 128:
+ break;
+ case 192:
+ rc = 0x08; /* AES-192 */
+ break;
+ case 256:
+ rc = 0x09; /* AES-256 */
+ break;
+ default:
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_WARNING, "Unsupported AES key "
+ "size: [%d]\n",
+ crypt_stats->key_size_bits);
+ goto out;
+ }
+ }
+ *(dest + (dest_offset++)) = rc;
+ rc = 0;
+ *(dest + (dest_offset++)) = 0x03; /* S2K */
+ *(dest + (dest_offset++)) = 0x01; /* MD5 (TODO) */
+ memcpy((dest + dest_offset), auth_tok->token.password.salt,
+ ECRYPTFS_SALT_SIZE);
+ dest_offset += ECRYPTFS_SALT_SIZE; /* salt */
+ *(dest + (dest_offset++)) = 0x60; /* hash iterations */
+ memcpy((dest + dest_offset), key_rec.enc_key,
+ key_rec.enc_key_size_bits / 8);
+ dest_offset += (key_rec.enc_key_size_bits / 8);
+ /* Write auth tok signature packet */
+ *(dest + (dest_offset++)) = 0xed; /* tag 11 */
+ *(dest + (dest_offset++)) = ECRYPTFS_SIG_SIZE + 13; /* packet
+ * length */
+ *(dest + (dest_offset++)) = 0x62; /* binary type */
+ *(dest + (dest_offset++)) = 0x08; /* filename length */
+ strncpy((dest + dest_offset), "_CONSOLE", 0x08);
+ dest_offset += 0x08;
+ memset((dest + dest_offset), 0, 4);
+ dest_offset += 4;
+ memcpy((dest + dest_offset), key_rec.sig, ECRYPTFS_SIG_SIZE);
+ dest_offset += ECRYPTFS_SIG_SIZE;
+ *(dest + (dest_offset)) = 0x00; /* NULL terminator */
+ rc = dest_offset;
+out:
+ if (tfm)
+ crypto_free_tfm(tfm);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Generates a key packet set and writes it to the virtual address
+ * passed in.
+ *
+ * @param dest Page to which to write the key record set
+ * @param crypt_stats The cryptographic context from which the authentication
+ * tokens will be retrieved
+ * @param len The amount written
+ * @return Zero on success
+ */
+int
+ecryptfs_generate_key_packet_set(char *dest_base,
+ struct ecryptfs_crypt_stats *crypt_stats,
+ struct dentry *ecryptfs_dentry, int *len)
+{
+ int rc = 0;
+ struct ecryptfs_auth_tok *auth_tok;
+ int dest_offset = 0;
+ struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ ecryptfs_dentry->d_sb)->mount_crypt_stats);
+ int written;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ (*len) = 0;
+ if (mount_crypt_stats->global_auth_tok) {
+ auth_tok = mount_crypt_stats->global_auth_tok;
+ switch (auth_tok->instanceof) {
+ case ECRYPTFS_PASSWORD:
+ written = write_tag_3_packet(dest_base, dest_offset,
+ PAGE_CACHE_SIZE, auth_tok,
+ crypt_stats);
+ break;
+ default:
+ ecryptfs_printk(0, KERN_WARNING, "Unknown "
+ "authentication token type [%d]\n",
+ auth_tok->instanceof);
+ rc = -EINVAL;
+ goto out;
+ }
+ if (written == 0) {
+ ecryptfs_printk(1, KERN_WARNING, "Error writing "
+ "authentication token packet with sig "
+ "= [%s]\n",
+ mount_crypt_stats->global_auth_tok_sig);
+ rc = -EIO;
+ goto out;
+ } else {
+ dest_offset += written;
+ (*len) += written;
+ }
+ } else
+ BUG();
+ out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}

2005-11-19 04:22:36

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 10/12: eCryptfs] Mmap operations

eCryptfs mmap operations. The bulk encryption and decryption takes
place here.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

mmap.c | 1153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1153 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/mmap.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/mmap.c 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,1153 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * This is where eCryptfs handles the bulk encryption and decryption,
+ * with upper-lower file index interpolations.
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * 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 <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+static int ecryptfs_commit_write(struct file *file, struct page *page,
+ unsigned from, unsigned to);
+static int ecryptfs_prepare_write(struct file *file, struct page *page,
+ unsigned from, unsigned to);
+
+kmem_cache_t *ecryptfs_lower_page_cache;
+
+static inline pgoff_t
+record_idx(struct ecryptfs_crypt_stats *crypt_stats, pgoff_t idx)
+{
+ return (idx % crypt_stats->records_per_page);
+}
+
+static inline pgoff_t
+last_records_page_idx(struct ecryptfs_crypt_stats *crypt_stats, pgoff_t idx)
+{
+ pgoff_t lower_data_idx;
+ pgoff_t lower_record_idx;
+
+ lower_data_idx = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, idx);
+ lower_record_idx = record_idx(crypt_stats, idx);
+ return (lower_data_idx - lower_record_idx - 1);
+}
+
+/**
+ * Get one page from cache or lower f/s, return error otherwise.
+ *
+ * @return 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;
+ int rc;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+ mapping = inode->i_mapping;
+ if (index < 0) {
+ ecryptfs_printk(1, KERN_ERR, "BUG: index=%d\n", index);
+ page = ERR_PTR(-EIO);
+ goto out;
+ }
+ 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);
+ if (!PageUptodate(page)) {
+ lock_page(page);
+ rc = mapping->a_ops->readpage(file, page);
+ if (rc) {
+ page = ERR_PTR(rc);
+ goto out;
+ }
+ wait_on_page_locked(page);
+ if (!PageUptodate(page)) {
+ page = ERR_PTR(-EIO);
+ goto out;
+ }
+ }
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+ return page;
+}
+
+/**
+ * Write a specified number of zero's to a page.
+ *
+ * N.B. (start + num_zeros) _must_ be less than or equal to PAGE_CACHE_SIZE
+ *
+ * @param file The ecryptfs file
+ * @param index The index in which we are writing
+ * @param start The position after the last block of data
+ * @param num_zeros The number of zeros to write
+ */
+static
+int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros)
+{
+ int rc = 0;
+ struct page *tmp_page;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; index = [%lu], start offset = "
+ "[%d] num_zeros = [%d]\n", index, start, num_zeros);
+ tmp_page = ecryptfs_get1page(file, index);
+ if (IS_ERR(tmp_page)) {
+ ecryptfs_printk(0, KERN_ERR, "Error getting page at index "
+ "[%lu]\n", index);
+ rc = PTR_ERR(tmp_page);
+ goto out;
+ }
+ kmap(tmp_page);
+ memset(((char *)page_address(tmp_page) + start), 0, num_zeros);
+ rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error preparing to write zero's "
+ "to remainder of page at index [%lu]\n", index);
+ kunmap(tmp_page);
+ page_cache_release(tmp_page);
+ goto out;
+ }
+ rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros);
+ if (rc < 0) {
+ ecryptfs_printk(0, KERN_ERR, "Error attempting to write zero's "
+ "to remainder of page at index [%lu]\n", index);
+ kunmap(tmp_page);
+ page_cache_release(tmp_page);
+ goto out;
+ }
+ rc = 0;
+ kunmap(tmp_page);
+ page_cache_release(tmp_page);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+ return rc;
+}
+
+/**
+ * Function for handling creation of holes when lseek-ing past the end
+ * of the file and then writing some data.
+ *
+ * N.B.
+ * This function does NOT support shrinking, only growing a file.
+ * The code _will_ BUG() if this requirement is not met.
+ * The new_length _myust_ be greater than the current length.
+ *
+ * @param file The ecryptfs file
+ * @param 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.
+ * N.B. new_length must be greater than current length
+ * @return 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_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);
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; inode->i_size = [%llu]; "
+ "new_length = [%llu]\n", cur_length, new_length);
+ /* Sanity check */
+ if (cur_length >= new_length) {
+ ecryptfs_printk(0, KERN_ERR, "Called with new_length less than "
+ "or equal to the current length, this is "
+ "against function restrictions!\n");
+ BUG();
+ }
+ /* N.B. Notes on index calculations
+ * It is important to note that the size of the file will always be 1
+ * greater than the last block of data's position added to the data's
+ * index shifted by the page cache size.
+ * size = index * PAGE_CACHE_SIZE + pos + 1
+ *
+ * Example, a file with 1 block of data in the 0th index at the 0th
+ * position will be a file of size 1. Likewise, a file of 100 blocks
+ * of data will have it's last block in the 99th position.
+ *
+ * Therefore, a file of size page with PAGE_CACHE_SIZE blocks of data
+ * will have its last block of data in the (PAGE_CACHE_SIZE-1)th
+ * position, in the 0th index. A file with (PAGE_CACHE_SIZE + 1) blocks
+ * of data will have its last block of data in the 0th position of the
+ * 1st index. This calculation can be shown as:
+ * position = (size - 1) & ~PAGE_CACHE_MASK
+ * index = (size - 1) >> PAGE_CACHE_SHIFT
+ *
+ * Example:
+ * PAGE_CACHE_SHIFT = 12
+ * PAGE_CACHE_SIZE = (1UL << PAGE_CACHE_SHIFT) [or 4096]
+ * PAGE_CACHE_MASK = (~(PAGE_CACHE_SIZE-1)) [or ~(4096-1)]
+ *
+ * A size of 4095 yields:
+ * position = (4095 - 1) & ~PAGE_CACHE_MASK => 4094
+ * index = (4095 - 1) >> 12 => 0
+ *
+ * A size of 4096 yields:
+ * position = (4096 - 1) & ~PAGE_CACHE_MASK => 4095
+ * index = (4096 - 1) >> 12 => 0
+ *
+ * A size of 4097 yields:
+ * position = (4097 - 1) & ~PAGE_CACHE_MASK => 0
+ * index = (4097 - 1) >> 12 => 1
+ *
+ * From these examples, you can see that the logic that a
+ * file's last block of data is at the (size - 1)th position
+ * follows.
+ *
+ * A 0-length file is by this logic, undefined if positions
+ * and indicies are required to be positive integers. However,
+ * this is intuative, as a 0-length file has no data which to
+ * give a position and index to. However, to make our
+ * calculations work, we will use the default value of -1 if
+ * the size is 0. The following will apply:
+ * old size = 0
+ * old position = -1
+ * old index = 0
+ * new size = 1
+ * new position = 0
+ * new index = 0
+ *
+ * write_zeros(file,0,(-1+1) => 0, ((size-1) - (-1)) => size)
+ *
+ * Therefore, if the current size of the file is 0, then
+ * setting our index and position to -1 will result in valid
+ * calculations for the new index and position for the new
+ * size.
+ */
+ 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(1, KERN_NOTICE, "old_end_page_index = [%lu]; "
+ "old_end_pos_in_page = [%d]; "
+ "new_end_page_index = [%lu]; "
+ "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(0, KERN_ERR, "write_zeros(file=[%p], "
+ "index=[%lu], 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;
+ }
+ /* Else, new position is outside of the current index.
+ * Fill remainder of current page with zeros.
+ * For each page after that page, will entire page with zeros.
+ * Upon reaching new last page, write as many zeros as required
+ * to fullfil the new size. */
+ /* 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(0, KERN_ERR, "write_zeros(file=[%p], "
+ "index=[%lu], 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(0, KERN_ERR, "write_zeros(file=[%p], "
+ "index=[%lu], 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(0, KERN_ERR, "write_zeros(file="
+ "[%p], index=[%lu], 0, "
+ "new_end_pos_in_page=[%d]"
+ "returned [%d]\n", file, index,
+ new_end_pos_in_page, rc);
+ goto out;
+ }
+out:
+ ecryptfs_printk((rc == 0 ? 1 : 0), KERN_NOTICE, "Exit; rc = [%d]\n",
+ rc);
+ return rc;
+}
+
+/**
+ * @param lower_file Can be NULL
+ * @return Zero on success
+ */
+static int
+ecryptfs_read_rotate_write_iv(char *iv, struct inode *inode,
+ int lower_iv_idx, struct file *lower_file,
+ struct page *page)
+{
+ int rc = 0;
+ pgoff_t records_page_index;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ struct page *records_page;
+ char *records_virt;
+ int lower_file_needs_fput = 0;
+ struct address_space_operations *lower_a_ops;
+ struct inode *lower_inode;
+
+ crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats);
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ lower_a_ops = lower_inode->i_mapping->a_ops;
+ records_page_index = last_records_page_idx(crypt_stats, page->index);
+ ecryptfs_printk(1, KERN_NOTICE, "records_page_index = [%lu]\n",
+ records_page_index);
+ records_page = grab_cache_page(lower_inode->i_mapping,
+ records_page_index);
+ if (!records_page) {
+ ecryptfs_printk(0, KERN_ERR, "records_page == NULL "
+ "after grab_cache_page at index [%lu]"
+ "\n", records_page_index);
+ rc = -EIO;
+ goto out;
+ }
+ /* TODO: Assume encrypted only for version 0.1 */
+ ecryptfs_printk(1, KERN_NOTICE, "lower_iv_idx = [%d]\n", lower_iv_idx);
+ records_virt = kmap(records_page);
+ if (!records_virt) {
+ rc = -ENOMEM;
+ ecryptfs_printk(1, KERN_NOTICE, "Error in kmap\n");
+ goto out_unlock_and_release;
+ }
+ if (!lower_file) {
+ struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
+
+ if (!lower_inode->i_dentry.next) {
+ rc = -EINVAL;
+ ecryptfs_printk(1, KERN_NOTICE, "No dentry for "
+ "lower_inode\n");
+ goto out_unmap;
+ }
+ lower_dentry = list_entry(lower_inode->i_dentry.next,
+ struct dentry, d_alias);
+ mntget(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt);
+ lower_mnt =
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+ lower_file = dentry_open(lower_dentry, lower_mnt, FMODE_READ);
+ if (IS_ERR(lower_file)) {
+ rc = PTR_ERR(lower_file);
+ ecryptfs_printk(0, KERN_ERR,
+ "Error opening dentry; rc = [%i]\n",
+ rc);
+ mntput(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt);
+ goto out_unmap;
+ }
+ lower_file_needs_fput = 1;
+ }
+ rc = lower_a_ops->prepare_write(lower_file, records_page, lower_iv_idx,
+ (lower_iv_idx + crypt_stats->iv_bytes));
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error in lower prepare_write() "
+ "call: rc = [%d]\n", rc);
+ goto out_unmap;
+ }
+ down(&crypt_stats->iv_sem);
+ ecryptfs_rotate_iv(crypt_stats->iv);
+ memcpy(iv, crypt_stats->iv, crypt_stats->iv_bytes);
+ up(&crypt_stats->iv_sem);
+ memcpy(records_virt + lower_iv_idx, crypt_stats->iv,
+ crypt_stats->iv_bytes);
+ rc = lower_a_ops->commit_write(lower_file, records_page, lower_iv_idx,
+ (lower_iv_idx + crypt_stats->iv_bytes));
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error in lower commit_write() "
+ "call: rc = [%d]\n", rc);
+ goto out_unmap;
+ }
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = "
+ "[%lu]\n", records_page->index);
+out_unmap:
+ kunmap(records_page);
+out_unlock_and_release:
+ unlock_page(records_page);
+ page_cache_release(records_page);
+ ecryptfs_printk(1, KERN_NOTICE, "After committing IV write, "
+ "lower_inode->i_blocks = [%lu]\n",
+ lower_inode->i_blocks);
+out:
+ if (lower_file_needs_fput)
+ fput(lower_file);
+ return rc;
+}
+
+/**
+ * @return Zero on success; negative on error
+ */
+static int encrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *page, struct page *lower_page, char *iv)
+{
+ int rc = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Calling do_encrypt_page()\n");
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n");
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+ ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes before "
+ "encryption:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex((char *)page_address(page), 8);
+ rc = do_encrypt_page_offset(crypt_stats, lower_page, 0,
+ page, 0, crypt_stats->extent_size, iv);
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypted [%d] bytes\n", rc);
+ ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes after " "encryption:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex((char *)page_address(lower_page), 8);
+ if (rc > 0)
+ rc = 0;
+ return rc;
+}
+
+/**
+ * @param page Page that is locked before this call is made
+ */
+static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+ int rc = -EIO;
+ int err = 0;
+ unsigned long lower_index;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct page *lower_page;
+ char *kaddr, *lower_kaddr;
+ struct ecryptfs_crypt_stats *crypt_stats;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%ld]; "
+ "page->mapping->host = [%p]\n", page->index,
+ page->mapping->host);
+ inode = page->mapping->host;
+ crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats);
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ lower_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, page->index);
+ ecryptfs_printk(1, KERN_NOTICE, "Grab lower_idx = [%ld]\n",
+ lower_index);
+ lower_page = grab_cache_page(lower_inode->i_mapping, lower_index);
+ if (!lower_page) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ kaddr = (char *)kmap(page);
+ if (!kaddr) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ lower_kaddr = (char *)kmap(lower_page);
+ if (!lower_kaddr) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ if (crypt_stats->encrypted) {
+ char iv[ECRYPTFS_MAX_IV_BYTES];
+ int record_byte_offset;
+ /* TODO: HMAC: Include HMAC bytes in the record size */
+ record_byte_offset = (record_idx(crypt_stats, page->index)
+ * crypt_stats->iv_bytes);
+ rc = ecryptfs_read_rotate_write_iv(iv, inode,
+ record_byte_offset, NULL,
+ page);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error rotating "
+ "IV; write failure. Assuming "
+ "IV page corruption; writing "
+ "0's to associated data extent"
+ ".\n");
+ memset(lower_kaddr, 0, crypt_stats->extent_size);
+ err = -EIO;
+ goto do_lower_write;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n");
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+ err = encrypt_page(crypt_stats, page, lower_page, iv);
+ if (err)
+ ecryptfs_printk(0, KERN_WARNING, "Error encrypting "
+ "page (upper index [%llu])\n",
+ page->index);
+ } else
+ memcpy(lower_kaddr, kaddr, crypt_stats->extent_size);
+do_lower_write:
+ kunmap(page);
+ kunmap(lower_page);
+ rc = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
+ if (rc)
+ ecryptfs_printk(0, KERN_ERR, "Error calling lower writepage(); "
+ "rc = [%d]\n", rc);
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ page_cache_release(lower_page);
+ if (rc)
+ ClearPageUptodate(page);
+ else
+ SetPageUptodate(page);
+ unlock_page(page);
+out:
+ if (err)
+ rc = err;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ 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
+ */
+static int ecryptfs_do_readpage(struct file *file, struct page *page,
+ pgoff_t lower_page_index)
+{
+ int rc = -EIO;
+ 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;
+ struct address_space_operations *lower_a_ops;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_page_index = [%lu]\n",
+ lower_page_index);
+ dentry = file->f_dentry;
+ if (NULL == ECRYPTFS_FILE_TO_PRIVATE_SM(file)) {
+ rc = -ENOENT;
+ ecryptfs_printk(0, KERN_ERR, "No lower file info\n");
+ goto out;
+ }
+ 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(0, KERN_ERR, "Error reading from page cache\n");
+ goto out;
+ }
+ wait_on_page_locked(lower_page);
+ if (!PageUptodate(lower_page)) {
+ lock_page(lower_page);
+ rc = lower_a_ops->readpage(lower_file, lower_page);
+ if (rc) {
+ lower_page = NULL;
+ rc = -EIO;
+ ecryptfs_printk(0, KERN_ERR, "Error reading lower "
+ "page at index=[%lu]\n",
+ lower_page_index);
+ goto out;
+ }
+ wait_on_page_locked(lower_page);
+ if (!PageUptodate(lower_page)) {
+ rc = -EIO;
+ ecryptfs_printk(0, KERN_ERR, "Error reading lower "
+ "page at index=[%lu]\n",
+ lower_page_index);
+ goto out;
+ }
+ }
+ page_data = (char *)kmap(page);
+ if (!page_data) {
+ rc = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR, "Error mapping page\n");
+ goto out;
+ }
+ lower_page_data = (char *)kmap(lower_page);
+ if (!lower_page_data) {
+ rc = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR, "Error mapping page\n");
+ kunmap(page);
+ goto out;
+ }
+ /* TODO: Copy sub-page amount of data? */
+ memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE);
+ kunmap(lower_page);
+ kunmap(page);
+ rc = 0;
+ out:
+ if (likely(lower_page))
+ page_cache_release(lower_page);
+ if (rc == 0)
+ SetPageUptodate(page);
+ else
+ ClearPageUptodate(page);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/* Action descriptor for decrypt_page() */
+#define ECRYPTFS_ACTION_COPY 0
+#define ECRYPTFS_ACTION_DECRYPT 1
+
+/**
+ * Decrypt a page of data
+ *
+ * @return Zero on success
+ */
+static int decrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+ struct file *file, char *iv, struct page *page,
+ pgoff_t lower_page_index, int decrypt)
+{
+ int rc = 0;
+ char *lower_page_encrypted_virt;
+ struct page *lower_page_encrypted;
+ int decrypt_to;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_page_index = [%d]\n",
+ lower_page_index);
+ lower_page_encrypted_virt =
+ kmem_cache_alloc(ecryptfs_lower_page_cache, SLAB_KERNEL);
+ if (!lower_page_encrypted_virt) {
+ rc = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR, "Error getting page for "
+ "encrypted lower page\n");
+ ClearPageUptodate(page);
+ goto out;
+ }
+ lower_page_encrypted = virt_to_page(lower_page_encrypted_virt);
+ rc = ecryptfs_do_readpage(file, lower_page_encrypted, lower_page_index);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error reading lower encrypted "
+ "page\n");
+ ClearPageUptodate(page);
+ goto out;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Decrypting page with iv:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+ ecryptfs_printk(1, KERN_NOTICE, "Using session key encryption key:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ decrypt_to = crypt_stats->extent_size;
+ ecryptfs_printk(1, KERN_NOTICE, "Decrypting to: [%d]\n", decrypt_to);
+ ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes before decryption:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex((char *)page_address(lower_page_encrypted),
+ 8);
+ if (decrypt)
+ do_decrypt_page_offset(crypt_stats, page,
+ 0, lower_page_encrypted,
+ 0, decrypt_to, iv);
+ else
+ memcpy(page, lower_page_encrypted_virt, decrypt_to);
+ ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes after decryption:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex((char *)page_address(page), 8);
+ kmem_cache_free(ecryptfs_lower_page_cache, lower_page_encrypted_virt);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Read in a page
+ *
+ * @param file This is an ecryptfs file
+ * @param page ecryptfs associated page to stick the read data into
+ * @return Zero on success; non-zero on error
+ */
+static int ecryptfs_readpage(struct file *file, struct page *page)
+{
+ int rc = 0;
+ struct ecryptfs_crypt_stats *crypt_stats = NULL;
+ char iv[ECRYPTFS_MAX_IV_BYTES];
+ int iv_is_nonzero = 0;
+ char *records_virt;
+ struct page *records_page;
+ int record_byte_offset;
+ int iv_byte_offset;
+ int i;
+ pgoff_t records_page_index;
+ pgoff_t lower_page_index;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%ld]\n",
+ page->index);
+ ASSERT(file && file->f_dentry && file->f_dentry->d_inode);
+ crypt_stats =
+ &ECRYPTFS_INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stats;
+ /* If the file is neither encrypted nor HMAC-verified, then we
+ * have passthrough mode. */
+ if (!crypt_stats || !crypt_stats->encrypted) {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Passing through unencrypted page\n");
+ rc = ecryptfs_do_readpage(file, page, page->index);
+ goto out;
+ }
+ /* The file is encrypted, hmac verified, or both. */
+ ecryptfs_printk(1, KERN_NOTICE,
+ "crypt_stats->iv_bytes = [%d]\n",
+ crypt_stats->iv_bytes);
+ ecryptfs_printk(1, KERN_NOTICE,
+ "crypt_stats->records_per_page = [%d]\n",
+ crypt_stats->records_per_page);
+
+ /* Get the relevant IV/HMAC page */
+ records_page_index = last_records_page_idx(crypt_stats, page->index);
+ ecryptfs_printk(1, KERN_NOTICE, "records_page_index = [%lu]\n",
+ records_page_index);
+ records_virt = (char *)__get_free_page(GFP_KERNEL);
+ if (!records_virt) {
+ ecryptfs_printk(0, KERN_ERR, "Error getting free page");
+ rc = -ENOMEM;
+ ClearPageUptodate(page);
+ goto out;
+ }
+ records_page = virt_to_page(records_virt);
+ rc = ecryptfs_do_readpage(file, records_page, records_page_index);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error reading IV/HMAC page");
+ ClearPageUptodate(page);
+ goto out;
+ }
+ /* TODO: HMAC: Include HMAC bytes in the record size */
+ record_byte_offset = (record_idx(crypt_stats, page->index)
+ * crypt_stats->iv_bytes);
+ iv_byte_offset = -1;
+ if (crypt_stats->encrypted) {
+ iv_byte_offset = record_byte_offset;
+ iv_is_nonzero = 0;
+ for (i = 0; i < crypt_stats->iv_bytes; i++)
+ iv_is_nonzero |= (records_virt + iv_byte_offset)[i];
+ memcpy(iv, (records_virt + iv_byte_offset),
+ crypt_stats->iv_bytes);
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "record_byte_offset = [%d]\n",
+ record_byte_offset);
+ ecryptfs_printk(1, KERN_NOTICE, "iv_byte_offset = [%d]\n",
+ iv_byte_offset);
+ free_page((unsigned long)records_virt);
+ lower_page_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats,
+ page->index);
+ ecryptfs_printk(1, KERN_NOTICE, "lower_page_index = [%lu]\n",
+ lower_page_index);
+ ecryptfs_printk(1, KERN_NOTICE, "iv_is_nonzero = [%d]\n",
+ iv_is_nonzero);
+ if (crypt_stats->encrypted && iv_is_nonzero) {
+ rc = decrypt_page(crypt_stats, file, iv, page,
+ lower_page_index, ECRYPTFS_ACTION_DECRYPT);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error decrypting "
+ "page; rc = [%d]\n", rc);
+ goto out;
+ }
+ } else {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Passing through unencrypted page\n");
+ rc = ecryptfs_do_readpage(file, page, lower_page_index);
+ }
+ SetPageUptodate(page);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n",
+ page->index);
+ unlock_page(page);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static void fill_zeros_to_end_of_page(struct page *page, unsigned 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) {
+ 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(page);
+ if (!page_virt) {
+ goto out;
+ }
+ memset((page_virt + end_byte_in_page), 0,
+ (PAGE_CACHE_SIZE - end_byte_in_page));
+ kunmap(page);
+ }
+out:
+ return;
+}
+
+static int ecryptfs_prepare_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{
+ int err = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%lu]; from = "
+ "[%d]; to = [%d]\n", page->index, from, to);
+ kmap(page);
+ 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))
+ err = ecryptfs_do_readpage(file, page, page->index);
+out:
+ return err;
+}
+
+/**
+ * @return Zero on success
+ */
+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;
+ struct address_space_operations *lower_a_ops;
+ unsigned long long file_size;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ header_page = grab_cache_page(lower_inode->i_mapping, 0);
+ if (!header_page) {
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "grab_cache_page for header page "
+ "failed\n");
+ goto out;
+ }
+ header_virt = kmap(header_page);
+ lower_a_ops = lower_inode->i_mapping->a_ops;
+ rc = lower_a_ops->prepare_write(lower_file, header_page, 0, 8);
+ file_size = (unsigned long long)i_size_read(inode);
+ memcpy(header_virt, &file_size, 8);
+ rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8);
+ if (rc < 0)
+ ecryptfs_printk(0, KERN_ERR, "Error commiting header page "
+ "write\n");
+ kunmap(header_page);
+ ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n",
+ header_page->index);
+ unlock_page(header_page);
+ page_cache_release(header_page);
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty_sync(inode);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * 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.
+ *
+ * @param file The eCryptfs file object
+ * @param page The eCryptfs page
+ * @param from Ignored (we rotate the page IV on each write)
+ * @param to Ignored
+ * @return
+ */
+static int ecryptfs_commit_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{
+ int rc = -ENOMEM;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct page *lower_page = NULL;
+ struct file *lower_file = NULL;
+ loff_t pos;
+ unsigned bytes = to - from;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ pgoff_t lower_page_index;
+ struct address_space_operations *lower_a_ops;
+ struct dentry *ecryptfs_dentry;
+
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Enter; page->index = [%lu]; from = [%d]; to = [%d]\n",
+ page->index, from, to);
+ ecryptfs_dentry = file->f_dentry;
+ inode = page->mapping->host;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ down(&lower_inode->i_sem);
+ if (!(lower_inode->i_mapping
+ && lower_inode->i_mapping->a_ops
+ && lower_inode->i_mapping->a_ops->prepare_write
+ && lower_inode->i_mapping->a_ops->commit_write)) {
+ ecryptfs_printk(0, KERN_ERR,
+ "a_ops of lower inode not valid\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ lower_a_ops = lower_inode->i_mapping->a_ops;
+ ASSERT(file && file->f_dentry && file->f_dentry->d_inode);
+ crypt_stats =
+ &ECRYPTFS_INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stats;
+ if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file))
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ if (!crypt_stats) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = ecryptfs_init_crypt_ctx(crypt_stats);
+ if (rc)
+ ecryptfs_printk(1, KERN_NOTICE, "Problem with "
+ "initializing crypto context\n");
+ if (crypt_stats->new_file) {
+ struct page *header_page;
+ char *header_virt;
+
+ crypt_stats->new_file = 0;
+ header_page = grab_cache_page(lower_inode->i_mapping, 0);
+ if (!header_page) {
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "grab_cache_page for "
+ "header page failed\n");
+ goto out;
+ }
+ header_virt = kmap(header_page);
+ rc = lower_a_ops->prepare_write(lower_file, header_page,
+ 0, crypt_stats->extent_size);
+ rc = ecryptfs_write_headers_virt(header_virt, crypt_stats,
+ ecryptfs_dentry,
+ ECRYPTFS_FILE_VERSION);
+ if (rc) {
+ ecryptfs_printk(0, KERN_WARNING, "Error generating "
+ "header; rc = [%d]\n", rc);
+ rc = -EIO;
+ goto out;
+ }
+ memset(header_virt, 0, 8);
+ rc = lower_a_ops->commit_write(lower_file, header_page,
+ 0, crypt_stats->extent_size);
+ if (rc < 0)
+ ecryptfs_printk(0, KERN_ERR, "Error commiting header "
+ "page write\n");
+ kunmap(header_page);
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Unlocking header page with index = " "[%lu]\n",
+ header_page->index);
+ unlock_page(header_page);
+ page_cache_release(header_page);
+ if (rc < 0)
+ goto out;
+ rc = 0;
+ ecryptfs_printk(1, KERN_NOTICE, "lower_inode->i_blocks = "
+ "[%lu]\n", lower_inode->i_blocks);
+ i_size_write(inode, 0);
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty_sync(inode);
+ } else
+ ecryptfs_printk(1, KERN_NOTICE, "Not a new file\n");
+ /* Translate the page index */
+ lower_page_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats,
+ page->index);
+ ecryptfs_printk(1, KERN_NOTICE, "lower_page_index = [%lu]\n",
+ lower_page_index);
+ lower_page = grab_cache_page(lower_inode->i_mapping, lower_page_index);
+ if (!lower_page) {
+ ecryptfs_printk(0, KERN_ERR, "grab_cache_page for "
+ "lower_page_index=[%lu] failed\n",
+ lower_page_index);
+ goto out;
+ }
+ kmap(lower_page);
+ rc = lower_a_ops->prepare_write(lower_file, lower_page,
+ 0, crypt_stats->extent_size);
+ if (rc)
+ goto out_unlock_lower;
+ fill_zeros_to_end_of_page(page, to);
+ if (!crypt_stats->encrypted) {
+ /* TODO: aops */
+ memcpy((char *)page_address(lower_page),
+ (char *)page_address(page), crypt_stats->extent_size);
+ } else {
+ /* The file is either encrypted or HMAC'd */
+ char iv[ECRYPTFS_MAX_IV_BYTES];
+ int record_byte_offset;
+ ecryptfs_printk(1, KERN_NOTICE,
+ "crypt_stats->iv_bytes = [%d]\n",
+ crypt_stats->iv_bytes);
+ /* TODO: HMAC: Include HMAC bytes in the record size */
+ record_byte_offset = (record_idx(crypt_stats, page->index)
+ * crypt_stats->iv_bytes);
+ rc = ecryptfs_read_rotate_write_iv(iv, inode,
+ record_byte_offset,
+ lower_file, page);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error rotating IV\n");
+ goto out_unlock_lower;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n");
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+ rc = encrypt_page(crypt_stats, page, lower_page, iv);
+ if (rc) {
+ ecryptfs_printk(0, KERN_WARNING, "Error encrypting "
+ "page (upper index [%llu])\n",
+ page->index);
+ goto out;
+ }
+ }
+ rc = lower_a_ops->commit_write(lower_file, lower_page, 0,
+ crypt_stats->extent_size);
+ if (rc < 0) {
+ ecryptfs_printk(0, KERN_ERR,
+ "Error committing write; rc = [%d]\n", rc);
+ goto out_unlock_lower;
+ }
+ rc = bytes;
+ 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(1, KERN_NOTICE, "Expanded file size to "
+ "[%lld]\n", i_size_read(inode));
+ }
+ ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty_sync(inode);
+
+out_unlock_lower:
+ kunmap(lower_page);
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Unlocking lower page with index = [%lu]\n",
+ lower_page->index);
+ unlock_page(lower_page);
+ page_cache_release(lower_page);
+ kunmap(page); /* mapped in prepare_write */
+out:
+ if (rc < 0)
+ ClearPageUptodate(page);
+ else
+ SetPageUptodate(page);
+ up(&lower_inode->i_sem);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block)
+{
+ int rc = 0;
+ struct inode *inode;
+ struct inode *lower_inode;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ 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;
+}
+
+/**
+ * This function is copied verbatim from mm/filemap.c. It should be
+ * simply moved to some header file instead.
+ */
+static int sync_page(struct page *page)
+{
+ struct address_space *mapping = page->mapping;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (mapping && mapping->a_ops && mapping->a_ops->sync_page)
+ return mapping->a_ops->sync_page(page);
+ return 0;
+}
+
+static int ecryptfs_sync_page(struct page *page)
+{
+ int rc = 0;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct page *lower_page;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ inode = page->mapping->host;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ lower_page = grab_cache_page(lower_inode->i_mapping, page->index);
+ if (!lower_page) {
+ rc = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR, "Error from grab_cache_page "
+ "(no mem?)\n");
+ goto out;
+ }
+ rc = sync_page(lower_page);
+ ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n",
+ lower_page->index);
+ unlock_page(lower_page);
+ page_cache_release(lower_page);
+out:
+ return rc;
+}
+
+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,
+};

2005-11-19 04:24:09

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 12/12: eCryptfs] Crypto functions

eCryptfs crypto functions. Scatterlist abstraction functions. Page
encryption/decryption functions. Inode cryptographic context
initialization functions. Header region manipulation
functions. Functions in which filename and xattr encoding/decoding can
be easily implemented.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>
Signed-off-by: Michael Thompson <[email protected]>

---

crypto.c | 955 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 955 insertions(+)
--- linux-2.6.15-rc1-mm1/fs/ecryptfs/crypto.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.15-rc1-mm1-ecryptfs/fs/ecryptfs/crypto.c 2005-11-18 11:20:09.000000000 -0600
@@ -0,0 +1,955 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2004 Erez Zadok
+ * Copyright (c) 2001-2004 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * 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/pagemap.h>
+#include <linux/random.h>
+#include <linux/compiler.h>
+#include <linux/key.h>
+#include <linux/namei.h>
+#include <linux/crypto.h>
+#include <linux/file.h>
+#include <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * Requirement:
+ * Size of dst buffer needs to be atleast src_size * 2
+ */
+inline void ecryptfs_to_hex(char *dst, char *src, int src_size)
+{
+ int x;
+
+ for (x = 0; x < src_size; x++)
+ sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
+}
+
+/**
+ * Requirement:
+ * Size of src buffer needs to be atleast twice that of dst_size
+ */
+inline void ecryptfs_from_hex(char *dst, char *src, int dst_size)
+{
+ int x;
+ char tmp[3] = { 0, };
+
+ for (x = 0; x < dst_size; x++) {
+ tmp[0] = src[x * 2];
+ tmp[1] = src[x * 2 + 1];
+ dst[x] = (unsigned char)simple_strtol(tmp, NULL, 16);
+ }
+}
+
+static int iv_mixer;
+
+/**
+ * Rotate the initialization vector for an extent. This stirs things
+ * up to help protect against linear cryptanalysis when an attacker
+ * may have access to several encryptions based on the same IV.
+ */
+void ecryptfs_rotate_iv(unsigned char *iv)
+{
+ int i = (ECRYPTFS_MAX_IV_BYTES - sizeof(iv_mixer));
+ int zero_test = 0;
+
+ while ((i -= sizeof(iv_mixer)) >= 0)
+ zero_test |= ((*((int *)(iv + i))) ^=
+ (iv_mixer *= (*(int *)(iv + i))));
+ while (unlikely(!zero_test)) {
+ get_random_bytes(iv, ECRYPTFS_MAX_IV_BYTES);
+ zero_test = 0;
+ i = ECRYPTFS_MAX_IV_BYTES / sizeof(int);
+ while (i--)
+ zero_test |= *((int *)(iv + i));
+ }
+}
+
+/**
+ * Initialize the crypt_stats structure. This involves setting an
+ * initial IV, indicating how many header pages we have on the file by
+ * default, initializing the list of raw authentication token packets
+ * (TODO: deprecated/replaced w/ auth_tok sigs pointing to keyring
+ * structures), setting the extent size (TODO: this is the page size;
+ * as it now stands, everything falls apart if the page size is
+ * anything but 4096), and finally setting a flag to indicate that the
+ * structure is initialized.
+ *
+ * @param crypt_stats Pointer to the crypt_stats struct to
+ * initialize.
+ */
+void ecryptfs_init_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ memset((void *)crypt_stats, 0, sizeof(struct ecryptfs_crypt_stats));
+ init_MUTEX(&crypt_stats->iv_sem);
+ down(&crypt_stats->iv_sem);
+ get_random_bytes(&crypt_stats->iv, ECRYPTFS_MAX_IV_BYTES);
+ up(&crypt_stats->iv_sem);
+ get_random_bytes(&iv_mixer, sizeof(iv_mixer));
+ crypt_stats->num_header_pages = 1; /* TODO: Remove with policy */
+ crypt_stats->struct_initialized = 1;
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Releases all memory associated with a crypt_stats struct.
+ */
+void ecryptfs_destruct_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (crypt_stats->tfm) {
+ crypto_free_tfm(crypt_stats->tfm);
+ crypt_stats->tfm = NULL;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Dump hexadecimal representation of char array
+ *
+ * @param data
+ * @param bytes
+ */
+void ecryptfs_dump_hex(char *data, int bytes)
+{
+ int i = 0;
+ int pretty_print = 1;
+
+ if (bytes != 0) {
+ printk(KERN_NOTICE "0x%.2x.", (unsigned char)data[i]);
+ i++;
+ }
+ while (i < bytes) {
+ printk("0x%.2x.", (unsigned char)data[i]);
+ i++;
+ if (i % 16 == 0) {
+ printk("\n");
+ pretty_print = 0;
+ } else
+ pretty_print = 1;
+ }
+ if (pretty_print)
+ printk("\n");
+}
+
+/**
+ * Fills in a scatterlist array with page references for a passed
+ * virtual address: James Morris
+ *
+ * @param addr Virtual address
+ * @param size Size of data; should be an even multiple of the block
+ * size
+ * @param sg Pointer to scatterlist array
+ * @param sg_size Max array size
+ * @return Number of scatterlist structs in array used
+ */
+int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
+ int sg_size)
+{
+ int i = 0;
+ struct page *pg;
+ int offset;
+ int remainder_of_page;
+
+ while (size > 0 && i < sg_size) {
+ pg = virt_to_page(addr);
+ offset = offset_in_page(addr);
+ sg[i].page = pg;
+ sg[i].offset = offset;
+ remainder_of_page = PAGE_CACHE_SIZE - offset;
+ if (size >= remainder_of_page) {
+ sg[i].length = remainder_of_page;
+ addr += remainder_of_page;
+ size -= remainder_of_page;
+ } else {
+ sg[i].length = size;
+ addr += size;
+ size = 0;
+ }
+ i++;
+ }
+ if (size > 0)
+ return -ENOMEM;
+ return i;
+}
+
+/**
+ * @return Number of bytes encrypted; negative value on error
+ */
+static int do_encrypt_scatterlist(struct ecryptfs_crypt_stats *crypt_stats,
+ struct scatterlist *dest_sg,
+ struct scatterlist *src_sg, int size,
+ unsigned char *iv)
+{
+ int rc = 0;
+
+ if (!crypt_stats || !crypt_stats->tfm
+ || !crypt_stats->struct_initialized) {
+ ecryptfs_printk(0, KERN_ERR,
+ "Called w/ invalid crypt_stats state\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Key size [%d]; key:\n",
+ crypt_stats->key_size_bits / 8);
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ rc = crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error setting key; rc = [%d]\n",
+ rc);
+ rc = -EINVAL;
+ goto out;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypting [%d] bytes.\n", size);
+ if (crypt_stats->tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB) {
+ crypt_stats->security_warning = 1;
+ crypto_cipher_encrypt(crypt_stats->tfm, dest_sg, src_sg, size);
+ } else if (crypt_stats->tfm->crt_cipher.cit_mode
+ == CRYPTO_TFM_MODE_CFB
+ || crypt_stats->tfm->crt_cipher.cit_mode
+ == CRYPTO_TFM_MODE_CBC)
+ crypto_cipher_encrypt_iv(crypt_stats->tfm, dest_sg, src_sg,
+ size, iv);
+ else {
+ ecryptfs_printk(0, KERN_ERR,
+ "Unsupported block cipher mode: [%d]\n",
+ crypt_stats->tfm->crt_cipher.cit_mode);
+ rc = -ENOSYS;
+ goto out;
+ }
+ rc = size;
+ /* TODO: crypt_stats->iv size must be equal to the block size;
+ * verify this*/
+out:
+ return rc;
+}
+
+int do_encrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *dest_page, int dest_offset,
+ struct page *src_page, int src_offset, int size,
+ unsigned char *iv)
+{
+ struct scatterlist src_sg[2], dest_sg[2];
+
+ ecryptfs_printk(1, KERN_NOTICE, "Called with dest_page->index = [%lu], "
+ "src_page->index = [%lu], dest_offset = [%d], "
+ "src_offset = [%d]\n", dest_page->index,
+ src_page->index, dest_offset, src_offset);
+ src_sg[0].page = src_page;
+ src_sg[0].offset = src_offset;
+ src_sg[0].length = size;
+ dest_sg[0].page = dest_page;
+ dest_sg[0].offset = dest_offset;
+ dest_sg[0].length = size;
+ return do_encrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);;
+}
+
+int
+do_encrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *dest_page, struct page *src_page,
+ unsigned char *iv)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Called with dest_page->index = [%lu] "
+ "and src_page->index = [%lu]\n", dest_page->index,
+ src_page->index);
+ return do_encrypt_page_offset(crypt_stats, dest_page, 0, src_page, 0,
+ PAGE_CACHE_SIZE, iv);
+}
+
+/**
+ * Encrypt from a virtual address to a virtual address.
+ *
+ * @return
+ */
+int do_encrypt_virt(struct ecryptfs_crypt_stats *crypt_stats,
+ char *dest_virt_addr, const char *src_virt_addr,
+ int size, unsigned char *iv)
+{
+ /* TODO: 32 is a magic number */
+ struct scatterlist src_sg[32];
+ struct scatterlist dest_sg[32];
+ int rc;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Source:\n");
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex((char *)src_virt_addr, size);
+ rc = virt_to_scatterlist(src_virt_addr, size, src_sg, 32);
+ if (rc == -ENOMEM) {
+ ecryptfs_printk(0, KERN_ERR, "do_encrypt_virt: No memory for "
+ "this operation\n");
+ goto out;
+
+ }
+ rc = virt_to_scatterlist(dest_virt_addr, size, dest_sg, 32);
+ if (rc == -ENOMEM) {
+ ecryptfs_printk(0, KERN_ERR, "do_encrypt_virt: No memory for "
+ "this operation\n");
+ goto out;
+ }
+ rc = do_encrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);
+ ecryptfs_printk(1, KERN_NOTICE, "Destination:\n");
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex((char *)dest_virt_addr, size);
+out:
+ return rc;
+}
+
+/**
+ * @return Number of bytes decrypted; negative value on error
+ */
+static int do_decrypt_scatterlist(struct ecryptfs_crypt_stats *crypt_stats,
+ struct scatterlist *dest_sg,
+ struct scatterlist *src_sg, int size,
+ unsigned char *iv)
+{
+ int rc = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ /* TODO: This should be done when the file is opened */
+ ecryptfs_printk(1, KERN_NOTICE, "Key size [%d]; key:\n",
+ crypt_stats->key_size_bits / 8);
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex(crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ rc = crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error setting key; rc = [%d]\n",
+ rc);
+ rc = -EINVAL;
+ goto out;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Decrypting [%d] bytes.\n", size);
+ if (crypt_stats->tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB) {
+ rc = crypto_cipher_decrypt(crypt_stats->tfm, dest_sg, src_sg,
+ size);
+
+ } else if (crypt_stats->tfm->crt_cipher.cit_mode
+ == CRYPTO_TFM_MODE_CFB
+ || crypt_stats->tfm->crt_cipher.cit_mode
+ == CRYPTO_TFM_MODE_CBC) {
+ rc = crypto_cipher_decrypt_iv(crypt_stats->tfm, dest_sg, src_sg,
+ size, iv);
+ if (rc) {
+ ecryptfs_printk(0, KERN_ERR, "Error decrypting; rc = "
+ "[%d]\n", rc);
+ goto out;
+ }
+ } else {
+ ecryptfs_printk(0, KERN_ERR,
+ "Unsupported block cipher mode: [%d]\n",
+ crypt_stats->tfm->crt_cipher.cit_mode);
+ rc = -ENOSYS;
+ goto out;
+ }
+ rc = size;
+ out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * @return Number of bytes decrypted
+ */
+int do_decrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *dest_page, int dest_offset,
+ struct page *src_page, int src_offset, int size,
+ unsigned char *iv)
+{
+ struct scatterlist src_sg[2], dest_sg[2];
+
+ src_sg[0].page = src_page;
+ src_sg[0].offset = src_offset;
+ src_sg[0].length = size;
+ dest_sg[0].page = dest_page;
+ dest_sg[0].offset = dest_offset;
+ dest_sg[0].length = size;
+ return do_decrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);;
+}
+
+int
+do_decrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+ struct page *dest_page, struct page *src_page,
+ unsigned char *iv)
+{
+ return do_decrypt_page_offset(crypt_stats, dest_page, 0, src_page, 0,
+ PAGE_CACHE_SIZE, iv);
+}
+
+#define ECRYPTFS_MAX_SCATTERLIST_LEN 4
+
+int do_decrypt_virt(struct ecryptfs_crypt_stats *crypt_stats,
+ char *dest_virt_addr, const char *src_virt_addr,
+ int size, unsigned char *iv)
+{
+ struct scatterlist src_sg[ECRYPTFS_MAX_SCATTERLIST_LEN];
+ struct scatterlist dest_sg[ECRYPTFS_MAX_SCATTERLIST_LEN];
+ int rc;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Source:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex((char *)src_virt_addr, size);
+ rc = virt_to_scatterlist(src_virt_addr, size, src_sg,
+ ECRYPTFS_MAX_SCATTERLIST_LEN);
+ if (rc == -ENOMEM) {
+ ecryptfs_printk(0, KERN_ERR, "do_decrypt_virt: No memory for "
+ "this operation\n");
+ goto out;
+
+ }
+ rc = virt_to_scatterlist(dest_virt_addr, size, dest_sg,
+ ECRYPTFS_MAX_SCATTERLIST_LEN);
+ if (rc == -ENOMEM) {
+ ecryptfs_printk(0, KERN_ERR, "do_decrypt_virt: No memory for "
+ "this operation\n");
+ goto out;
+ }
+ rc = do_decrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);
+ ecryptfs_printk(1, KERN_NOTICE, "Destination:\n");
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex((char *)dest_virt_addr, size);
+out:
+ return rc;
+}
+
+/**
+ * Initialize the crypto context
+ *
+ * TODO: Performance: Keep a cache of initialized cipher contexts;
+ * only init if needed
+ */
+int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stats *crypt_stats)
+{
+ int rc = -EINVAL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (crypt_stats->cipher == NULL) {
+ ecryptfs_printk(1, KERN_NOTICE, "No cipher specified\n");
+ goto out;
+ }
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Initializing cipher [%s]; strlen = [%d]\n",
+ crypt_stats->cipher, (int)strlen(crypt_stats->cipher));
+ if (crypt_stats->tfm != NULL) {
+ ecryptfs_printk(1, KERN_WARNING, "Crypto context already "
+ "initialized\n");
+ goto out;
+ }
+ crypt_stats->tfm = crypto_alloc_tfm(crypt_stats->cipher,
+ CRYPTO_TFM_MODE_CBC);
+ if (crypt_stats->tfm == NULL) {
+ ecryptfs_printk(0, KERN_ERR, "cryptfs: init_crypt_ctx(): Error "
+ "initializing cipher [%s]\n",
+ crypt_stats->cipher);
+ goto out;
+ }
+ rc = 0;
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static inline pgoff_t records_per_page(struct ecryptfs_crypt_stats *crypt_stats)
+{
+ return (crypt_stats->extent_size / crypt_stats->iv_bytes);
+}
+
+void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stats *crypt_stats)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ /* Default values; may be overwritten as we are parsing the
+ * packets. */
+ crypt_stats->extent_size = PAGE_SIZE;
+ crypt_stats->iv_bytes = ECRYPTFS_DEFAULT_IV_BYTES;
+ crypt_stats->records_per_page = records_per_page(crypt_stats);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Default values in the event that policy does not override them.
+ */
+static void
+ecryptfs_set_default_crypt_stats_vals(struct ecryptfs_crypt_stats *crypt_stats)
+{
+ int key_size_bits = ECRYPTFS_DEFAULT_KEY_BYTES * 8;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ strcpy(crypt_stats->cipher, ECRYPTFS_DEFAULT_CIPHER);
+ get_random_bytes(crypt_stats->key, key_size_bits / 8);
+ crypt_stats->key_size_bits = key_size_bits;
+ crypt_stats->key_valid = 1;
+ ecryptfs_printk(1, KERN_NOTICE, "Generated new session key:\n");
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex(crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ ecryptfs_set_default_sizes(crypt_stats);
+ ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * If the crypto context for the file has not yet been established,
+ * this is where we do that. Establishing a new crypto context
+ * involves the following decisions:
+ * - What cipher to use?
+ * - What set of authentication tokens to use?
+ * Here we just worry about getting enough information into the
+ * authentication tokens so that we know that they are available.
+ * We associate the available authentication tokens with the new file
+ * via the set of signatures in the crypt_stats struct. Later, when
+ * the headers are actually written out, we may again defer to
+ * userspace to perform the encryption of the session key; for the
+ * foreseeable future, this will be the case with public key packets.
+ *
+ * @param ecryptfs_dentry
+ * @return Zero on success; non-zero otherwise
+ */
+/* Associate an authentication token(s) with the file */
+int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry)
+{
+ int rc = 0;
+ struct ecryptfs_crypt_stats *crypt_stats =
+ &ECRYPTFS_INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stats;
+ struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ ecryptfs_dentry->d_sb)->mount_crypt_stats);
+ int cipher_name_len;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ ecryptfs_set_default_crypt_stats_vals(crypt_stats);
+ /* See if there are mount crypt options */
+ if (mount_crypt_stats->global_auth_tok) {
+ ecryptfs_printk(1, KERN_NOTICE, "Initializing context for new "
+ "file using mount_crypt_stats\n");
+ crypt_stats->encrypted = 1;
+ crypt_stats->key_valid = 1;
+ memcpy(crypt_stats->keysigs[crypt_stats->num_keysigs++],
+ mount_crypt_stats->global_auth_tok_sig,
+ ECRYPTFS_SIG_SIZE_HEX);
+ cipher_name_len =
+ strlen(mount_crypt_stats->global_default_cipher_name);
+ memcpy(crypt_stats->cipher,
+ mount_crypt_stats->global_default_cipher_name,
+ cipher_name_len);
+ crypt_stats->cipher[cipher_name_len] = '\0';
+ } else
+ /* We should not encounter this scenario since we
+ * should detect lack of global_auth_tok at mount time
+ * TODO: Applies to 0.1 release only; remember to
+ * remove in future release */
+ BUG();
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * @return One if marker found; zero if not found
+ */
+int contains_ecryptfs_marker(char *data)
+{
+ u32 m_1, m_2, ver;
+
+ memcpy(&m_1, data, 4);
+ memcpy(&m_2, (data + 4), 4);
+ ver = (m_2 ^ (m_1 ^ MAGIC_ECRYPTFS_MARKER));
+ /* There is a 2^(32-4) chance for each file that a
+ * non-eCryptfs file will be start to be mistaken for an
+ * eCryptfs file */
+ if (ver > 15)
+ ver = 0;
+ return (int)ver;
+}
+
+/**
+ * Marker = 0x3c81b7f5
+ */
+static int write_ecryptfs_marker(char *page_virt)
+{
+ u32 m_1, m_2, ver;
+ int version = 1;
+
+ ver = (u32)version;
+ get_random_bytes(&m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ memcpy(page_virt, &m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ m_2 = ((m_1 ^ ver) ^ MAGIC_ECRYPTFS_MARKER);
+ memcpy(page_virt + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2), &m_2,
+ (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ return MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
+}
+
+/**
+ * @return Zero on no match
+ */
+u16 ecryptfs_code_for_cipher_string(char *str)
+{
+ u16 rc = 0;
+ if (strcmp(str, "des3_ede") == 0) {
+ rc = 0x02;
+ } else if (strcmp(str, "cast5") == 0) {
+ rc = 0x03;
+ } else if (strcmp(str, "blowfish") == 0) {
+ rc = 0x04;
+ } else if (strcmp(str, "aes") == 0) {
+ rc = 0x07;
+ }
+ return rc;
+}
+
+/**
+ * @return Zero on success
+ */
+int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code)
+{
+ int rc = 0;
+ switch(cipher_code) {
+ case 0x01:
+ /* IDEA not supported */
+ str[0] = '\0';
+ ecryptfs_printk(0, KERN_WARNING, "Cipher code not supported: "
+ "[%d]\n", cipher_code);
+ rc = -ENOSYS;
+ break;
+ case 0x02:
+ /* Choose Triple-DES */
+ strcpy(str, "des3_ede");
+ break;
+ case 0x03:
+ /* Choose CAST5 */
+ strcpy(str, "cast5");
+ break;
+ case 0x04:
+ /* Choose blowfish */
+ strcpy(str, "blowfish");
+ break;
+ case 0x07:
+ /* Choose AES-128 */
+ strcpy(str, "aes");
+ break;
+ case 0x08:
+ /* Choose AES-192 */
+ strcpy(str, "aes");
+ break;
+ case 0x09:
+ /* Choose AES-256 */
+ strcpy(str, "aes");
+ break;
+ default:
+ str[0] = '\0';
+ ecryptfs_printk(0, KERN_WARNING, "Cipher code not recognized: "
+ "[%d]\n", cipher_code);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+int ecryptfs_read_header_region(char *data, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ int rc = 0;
+ struct vfsmount *mnt = NULL;
+ struct file *file = NULL;
+ mm_segment_t oldfs;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ mnt = mntget(nd->mnt);
+ file = dentry_open(dentry, mnt, O_RDONLY);
+ if (IS_ERR(file)) {
+ ecryptfs_printk(1, KERN_NOTICE, "Error opening file to "
+ "determine interpolated filesize\n");
+ mntput(mnt);
+ rc = PTR_ERR(file);
+ goto out;
+ }
+ if (!file || !file->f_op || !file->f_op->read) {
+ ecryptfs_printk(1, KERN_NOTICE, "File has no read op\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ file->f_pos = 0;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ rc = file->f_op->read(file, (char __user *)data,
+ PAGE_CACHE_SIZE, &file->f_pos);
+ set_fs(oldfs);
+ fput(file);
+ rc = 0;
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n",rc);
+ return rc;
+}
+
+kmem_cache_t *ecryptfs_header_cache_0;
+kmem_cache_t *ecryptfs_header_cache_1;
+kmem_cache_t *ecryptfs_header_cache_2;
+
+/**
+ * The file size is written along a different execution path than the
+ * rest of the headers; we treat it as a special case in eCryptfs.
+ *
+ * Proposed header format is in flux; this is the current proposal for
+ * later versions of eCryptfs, for compatibility:
+ *
+ * BYTE RANGE: DESCRIPTION
+ * -----------------------
+ * 00 - 07: File size
+ * 08 - 11: Random value (M_1)
+ * 12 - 15: M_1 ^ VER ^ MARKER
+ * 16 - 16: Hash identifier
+ * 17 - 18: Cipher identifier
+ * 19 - 19: Number of header pages
+ * 20 - 63: Reserved
+ * 64 - ..: RFC2440 packet set
+ *
+ * @return Zero on success
+ */
+int ecryptfs_write_headers_virt(char *page_virt,
+ struct ecryptfs_crypt_stats *crypt_stats,
+ struct dentry *ecryptfs_dentry, int version)
+{
+ int ecryptfs_marker_len;
+ int rc = 0;
+ int written;
+ int offset;
+
+ offset = ECRYPTFS_FILE_SIZE_BYTES;
+ ecryptfs_marker_len = write_ecryptfs_marker(page_virt + offset);
+ offset += ecryptfs_marker_len;
+ rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stats,
+ ecryptfs_dentry, &written);
+ if (rc)
+ ecryptfs_printk(0, KERN_WARNING, "Error generating key packet "
+ "set; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Write the file headers out. This will likely involve a userspace
+ * callout, in which the session key is encrypted with one or more
+ * public keys and/or the passphrase necessary to do the encryption is
+ * retrieved via a prompt. Exactly what happens at this point should
+ * be policy-dependent.
+ *
+ * @param lower_file The lower file struct, which was returned from
+ * dentry_open
+ * @return Zero on success; non-zero on error
+ */
+int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
+ struct file *lower_file)
+{
+ int rc = 0;
+ char *page_virt;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ mm_segment_t oldfs;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE(
+ ecryptfs_dentry->d_inode)->crypt_stats;
+ if (likely(1 == crypt_stats->encrypted)) {
+ if (!crypt_stats->key_valid) {
+ ecryptfs_printk(1, KERN_NOTICE, "Key is "
+ "invalid; bailing out\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ } else {
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_WARNING,
+ "Called with crypt_stats->encrypted == 0\n");
+ goto out;
+ }
+ /* Released in this function */
+ page_virt = kmem_cache_alloc(ecryptfs_header_cache_0, SLAB_USER);
+ if (!page_virt) {
+ ecryptfs_printk(0, KERN_ERR, "Out of memory\n");
+ return -ENOMEM;
+ }
+ rc = ecryptfs_write_headers_virt(page_virt, crypt_stats,
+ ecryptfs_dentry,
+ ECRYPTFS_FILE_VERSION);
+ if (unlikely(rc != 0)) {
+ ecryptfs_printk(0, KERN_ERR, "Error whilst writing headers\n");
+ goto out_free;
+ }
+ rc = 0;
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Writing key packet set to underlying file\n");
+ lower_file->f_pos = 0;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ lower_file->f_op->write(lower_file, (char __user *)page_virt,
+ PAGE_CACHE_SIZE, &lower_file->f_pos);
+ set_fs(oldfs);
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Done writing key packet set to underlying file.\n");
+out_free:
+ kmem_cache_free(ecryptfs_header_cache_0, page_virt);
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * @return Zero on success
+ */
+int ecryptfs_read_headers_virt(char *page_virt,
+ struct ecryptfs_crypt_stats* crypt_stats,
+ struct dentry *ecryptfs_dentry)
+{
+ int rc = 0;
+ int offset;
+ int version;
+
+ offset = ECRYPTFS_FILE_SIZE_BYTES;
+ version = contains_ecryptfs_marker(page_virt + offset);
+ if (version == 0)
+ ecryptfs_printk(0, KERN_WARNING, "Valid eCryptfs marker not "
+ "found\n");
+ offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
+ rc = ecryptfs_parse_packet_set((page_virt + offset), crypt_stats,
+ ecryptfs_dentry, version);
+ return rc;
+}
+
+/**
+ * @return Zero if valid headers found and parsed; non-zero otherwise
+ */
+int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
+ struct file *lower_file)
+{
+ int rc = 0;
+ char *page_virt;
+ mm_segment_t oldfs;
+ ssize_t bytes_read;
+ struct ecryptfs_crypt_stats *crypt_stats =
+ &ECRYPTFS_INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stats;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ /* Read the first page from the underlying file */
+ page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, SLAB_USER);
+ if (!page_virt) {
+ rc = -ENOMEM;
+ ecryptfs_printk(0, KERN_ERR, "Unable to allocate page_virt\n");
+ goto out;
+ }
+ lower_file->f_pos = 0;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ bytes_read =
+ lower_file->f_op->read(lower_file, (char __user *)page_virt,
+ PAGE_CACHE_SIZE, &lower_file->f_pos);
+ set_fs(oldfs);
+ if (bytes_read != PAGE_CACHE_SIZE) {
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "Expected size of header not read."
+ "Instead [%d] bytes were read\n", bytes_read);
+ goto out;
+ }
+ rc = ecryptfs_read_headers_virt(page_virt, crypt_stats,
+ ecryptfs_dentry);
+ kmem_cache_free(ecryptfs_header_cache_1, page_virt);
+ if (rc) {
+ ecryptfs_printk(1, KERN_NOTICE, "File not encrypted\n");
+ rc = -EINVAL;
+ goto out;
+ }
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * N.B. The concept of encoded filenames does not apply for 0.1 release
+ *
+ * Encrypts and encodes a filename into something that constitutes a
+ * valid filename for a filesystem, with printable characters.
+ *
+ * We assume that we have a properly initialized crypto context,
+ * pointed to by crypt_stats->tfm.
+ *
+ * TODO: Implement filename encryption and encoding here, in place of
+ * memcpy.
+ *
+ * @return Length of encoded filename; negative if error
+ */
+int
+ecryptfs_encode_filename(const char *name, int length, char **encoded_name,
+ int skip_dots,
+ struct ecryptfs_crypt_stats *crypt_stats)
+{
+ int error = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; length = [%d]\n", length);
+ (*encoded_name) = kmalloc(length + 2, GFP_KERNEL);
+ if (!(*encoded_name)) {
+ error = -ENOMEM;
+ goto out;
+ }
+ memcpy((void *)(*encoded_name), (void *)name, length);
+ (*encoded_name)[length] = '\0';
+ error = length + 1;
+ out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; error = [%d]\n", error);
+ return error;
+}
+
+/**
+ * N.B. The concept of encoded filenames does not apply for 0.1 release
+ *
+ * Decrypts and decodes the filename
+ *
+ * TODO: Implement filename decoding and decryption here, in place of
+ * memcpy.
+ *
+ * @return Length of decoded filename; negative if error
+ */
+int
+ecryptfs_decode_filename(const char *name, int length, char **decrypted_name,
+ int skip_dots,
+ struct ecryptfs_crypt_stats *crypt_stats)
+{
+ int error = 0;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; length = [%d]\n", length);
+ (*decrypted_name) = kmalloc(length + 2, GFP_KERNEL);
+ if (!(*decrypted_name)) {
+ error = -ENOMEM;
+ goto out;
+ }
+ memcpy((void *)(*decrypted_name), (void *)name, length);
+ (*decrypted_name)[length + 1] = '\0'; /* Only for convenience
+ * in printing out the
+ * string in debug
+ * messages */
+ error = length;
+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; error = [%d]\n", error);
+ return error;
+}

2005-11-19 06:17:21

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 0/12: eCryptfs] eCryptfs version 0.1

Phillip Hellewell <[email protected]> wrote:
>
> eCryptfs
>

If Linux is going to offer a feature like this then people have to be able
to trust it to be quite secure. What we don't want to happen is to
distribute it for six months and then be buried in reports of
vulnerabilites from cryptography specialists. Even worse if those reports
lead to exploits.

So I guess what I'm asking is: has this code been reviewed by crypto
experts? Bearing in mind that it'll be world-class crypto people who will
try to poke holes in it.

2005-11-19 10:37:37

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 5/12: eCryptfs] Header declarations

Hi Phillip,

On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> +struct ecryptfs_session_key {
> +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x01
> +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x02
> +#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x04
> +#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x08
> + int32_t flags;
> + int32_t encrypted_key_size;
> + int32_t decrypted_key_size;
> + uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
> + uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];

s32 and u8 are preferred in the kernel.

> +#define OBSERVE_ASSERTS 1
> +#ifdef OBSERVE_ASSERTS
> +#define ASSERT(EX) \
> +do { \
> + if (unlikely(!(EX))) { \
> + printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \
> + __FILE__, __LINE__, __FUNCTION__); \
> + BUG(); \
> + } \
> +} while (0)
> +#else
> +#define ASSERT(EX) ;
> +#endif /* OBSERVE_ASSERTS */

Any reason why you can't just use BUG and BUG_ON()?

> +
> +/**
> + * Halcrow: What does the kernel VFS do to ensure that there is no
> + * contention for file->private_data?
> + */

Please elaborate?

> +#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \
> + ((file)->private_data))
> +#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data)
> +#define ECRYPTFS_FILE_TO_LOWER(file) \
> + ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file)
> +#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \
> + (ino)->u.generic_ip)
> +#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
> +#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode)
> +#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \
> + (super)->s_fs_info)
> +#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
> +#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \
> + (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
> +#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
> +#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \
> + (dentry)->d_fsdata)
> +#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \
> + (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry)

These wrappers seem rather pointless and obfuscating...

> +int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
> + int sg_size);

Doesn't seem ecryptfs specific, why is it here?

Pekka

2005-11-19 10:47:24

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 4/12: eCryptfs] Main module functions

On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> +int ecryptfs_verbosity = 1;
> +
> +module_param(ecryptfs_verbosity, int, 0);
> +MODULE_PARM_DESC(ecryptfs_verbosity,
> + "Initial verbosity level (0 or 1; defaults to "
> + "0, which is Quiet)");
> +
> +void __ecryptfs_printk(int verb, const char *fmt, ...)
> +{
> + va_list args;
> + va_start(args, fmt);
> + if ((ecryptfs_verbosity >= verb) && printk_ratelimit())
> + vprintk(fmt, args);
> + va_end(args);
> +}
> +

[snip]

> +int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
> + struct super_block *sb, int flag)
> +{
> + struct inode *lower_inode;
> + int err = 0;
> + struct inode *inode;
> +
> + ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], "
> + "lower_dentry->d_name.name = [%s], dentry = "
> + "[%p], dentry->d_name.name = [%s], sb = [%p]; "
> + "flag = [%.4x]; lower_dentry->d_count "
> + "= [%d]; dentry->d_count = [%d]\n", lower_dentry,
> + lower_dentry->d_name.name, dentry, dentry->d_name.name,
> + sb, flag, atomic_read(&lower_dentry->d_count),
> + atomic_read(&dentry->d_count));

Could you use KERN_DEBUG instead and drop ecryptfs_printk()?

> + if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
> + ECRYPTFS_INODE_TO_LOWER(inode) = igrab(lower_inode);
> + /* If we are still NULL at this point, igrab failed.
> + * We are _NOT_ supposed to be failing here */
> + if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {

If you're worried about assignment, why not use the normal idiom:

if (!p) {
...
}

> + BUG();
> + err = -EINVAL;
> + goto out;

Why do you want to BUG() and then handle the situation?

> +kmem_cache_t *ecryptfs_sb_info_cache;

We have struct kmem_cache now so please consider using it instead.

> +/* This provides a means of backing out cache creations out of the kernel
> + * so that we can elegantly fail should we run out of memory.
> + */
> +#define ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE 0x0001
> +#define ECRYPTFS_AUTH_TOK_PKT_SET_CACHE 0x0002
> +#define ECRYPTFS_AUTH_TOK_REQUEST_CACHE 0x0004
> +#define ECRYPTFS_AUTH_TOK_REQUEST_BLOB_CACHE 0x0008
> +#define ECRYPTFS_FILE_INFO_CACHE 0x0010
> +#define ECRYPTFS_DENTRY_INFO_CACHE 0x0020
> +#define ECRYPTFS_INODE_INFO_CACHE 0x0040
> +#define ECRYPTFS_SB_INFO_CACHE 0x0080
> +#define ECRYPTFS_HEADER_CACHE_0 0x0100
> +#define ECRYPTFS_HEADER_CACHE_1 0x0200
> +#define ECRYPTFS_HEADER_CACHE_2 0x0400
> +#define ECRYPTFS_LOWER_PAGE_CACHE 0x0800
> +#define ECRYPTFS_CACHE_CREATION_SUCCESS 0x0FF1

A better way would be to either (1) make ecryptfs_init_kmem_caches()
clean up after itself on error using gotos or (2) keep caches NULL
before initialization and check for that in
ecryptfs_free_kmem_caches().

> +
> +static short ecryptfs_allocated_caches;
> +
> +/**
> + * @return Zero on success; non-zero otherwise
> + *
> + * Sets ecryptfs_allocated_caches with flags so that we can
> + * free created caches should we run out of memory during
> + * creation period.
> + *
> + * The overhead for doing this is offset by the fact that we
> + * only do this once, and that should there be insufficient
> + * memory, then we can elegantly fail, and not leave extra
> + * caches around, or worse, panic the kernel trying to free
> + * something that's not there.
> + */
> +static int ecryptfs_init_kmem_caches(void)
> +{
> + int rc = 0;
> +
> + ecryptfs_auth_tok_list_item_cache =
> + kmem_cache_create("ecryptfs_auth_tok_list_item",
> + sizeof(struct ecryptfs_auth_tok_list_item),
> + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> + if (ecryptfs_auth_tok_list_item_cache)
> + rc |= ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_auth_tok_list_item "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_file_info_cache =
> + kmem_cache_create("ecryptfs_file_cache",
> + sizeof(struct ecryptfs_file_info),
> + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> + if (ecryptfs_file_info_cache)
> + rc |= ECRYPTFS_FILE_INFO_CACHE;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_file_cache "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_dentry_info_cache =
> + kmem_cache_create("ecryptfs_dentry_cache",
> + sizeof(struct ecryptfs_dentry_info),
> + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> + if (ecryptfs_dentry_info_cache)
> + rc |= ECRYPTFS_DENTRY_INFO_CACHE;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_dentry_cache "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_inode_info_cache =
> + kmem_cache_create("ecryptfs_inode_cache",
> + sizeof(struct ecryptfs_inode_info), 0,
> + SLAB_HWCACHE_ALIGN, inode_info_init_once, NULL);
> + if (ecryptfs_inode_info_cache)
> + rc |= ECRYPTFS_INODE_INFO_CACHE;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_inode_cache "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_sb_info_cache =
> + kmem_cache_create("ecryptfs_sb_cache",
> + sizeof(struct ecryptfs_sb_info),
> + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> + if (ecryptfs_sb_info_cache)
> + rc |= ECRYPTFS_SB_INFO_CACHE;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_sb_cache "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_header_cache_0 =
> + kmem_cache_create("ecryptfs_headers_0", PAGE_CACHE_SIZE,
> + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> + if (ecryptfs_header_cache_0)
> + rc |= ECRYPTFS_HEADER_CACHE_0;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_0 "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_header_cache_1 =
> + kmem_cache_create("ecryptfs_headers_1", PAGE_CACHE_SIZE,
> + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> + if (ecryptfs_header_cache_1)
> + rc |= ECRYPTFS_HEADER_CACHE_1;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_1 "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_header_cache_2 =
> + kmem_cache_create("ecryptfs_headers_2", PAGE_CACHE_SIZE,
> + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> + if (ecryptfs_header_cache_2)
> + rc |= ECRYPTFS_HEADER_CACHE_2;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_2 "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_lower_page_cache =
> + kmem_cache_create("ecryptfs_lower_page_cache", PAGE_CACHE_SIZE,
> + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> + if (ecryptfs_lower_page_cache)
> + rc |= ECRYPTFS_LOWER_PAGE_CACHE;
> + else
> + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_lower_page_cache "
> + "kmem_cache_create failed\n");
> +
> + ecryptfs_allocated_caches = rc;
> + rc = ECRYPTFS_CACHE_CREATION_SUCCESS ^ rc;
> + return rc;
> +}
> +
> +/**
> + * @return Zero on success; non-zero otherwise
> + */
> +static int ecryptfs_free_kmem_caches(void)
> +{
> + int rc = 0;
> + int err;
> +
> + if (ecryptfs_allocated_caches & ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE) {
> + rc = kmem_cache_destroy(ecryptfs_auth_tok_list_item_cache);
> + if (rc)
> + ecryptfs_printk(0, KERN_WARNING,
> + "Not all ecryptfs_auth_tok_"
> + "list_item_cache structures were "
> + "freed\n");
> + }
> + if (ecryptfs_allocated_caches & ECRYPTFS_FILE_INFO_CACHE) {
> + err = kmem_cache_destroy(ecryptfs_file_info_cache);
> + if (err)
> + ecryptfs_printk(0, KERN_WARNING,
> + "Not all ecryptfs_file_info_"
> + "cache regions were freed\n");
> + rc |= err;
> + }
> + if (ecryptfs_allocated_caches & ECRYPTFS_DENTRY_INFO_CACHE) {
> + err = kmem_cache_destroy(ecryptfs_dentry_info_cache);
> + if (err)
> + ecryptfs_printk(0, KERN_WARNING,
> + "Not all ecryptfs_dentry_info_"
> + "cache regions were freed\n");
> + rc |= err;
> + }
> + if (ecryptfs_allocated_caches & ECRYPTFS_INODE_INFO_CACHE) {
> + err = kmem_cache_destroy(ecryptfs_inode_info_cache);
> + if (err)
> + ecryptfs_printk(0, KERN_WARNING,
> + "Not all ecryptfs_inode_info_"
> + "cache regions were freed\n");
> + rc |= err;
> + }
> + if (ecryptfs_allocated_caches & ECRYPTFS_SB_INFO_CACHE) {
> + err = kmem_cache_destroy(ecryptfs_sb_info_cache);
> + if (err)
> + ecryptfs_printk(0, KERN_WARNING,
> + "Not all ecryptfs_sb_info_"
> + "cache regions were freed\n");
> + rc |= err;
> + }
> + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_0) {
> + err = kmem_cache_destroy(ecryptfs_header_cache_0);
> + if (err)
> + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
> + "header_cache_0 regions were freed\n");
> + rc |= err;
> + }
> + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_1) {
> + err = kmem_cache_destroy(ecryptfs_header_cache_1);
> + if (err)
> + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
> + "header_cache_1 regions were freed\n");
> + rc |= err;
> + }
> + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_2) {
> + err = kmem_cache_destroy(ecryptfs_header_cache_2);
> + if (err)
> + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
> + "header_cache_2 regions were freed\n");
> + rc |= err;
> + }
> + if (ecryptfs_allocated_caches & ECRYPTFS_LOWER_PAGE_CACHE) {
> + err = kmem_cache_destroy(ecryptfs_lower_page_cache);
> + if (err)
> + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
> + "lower_page_cache regions were "
> + "freed\n");
> + rc |= err;
> + }
> + return rc;
> +}
> +
> +static int __init init_ecryptfs_fs(void)
> +{
> + int rc;
> +
> + rc = ecryptfs_init_kmem_caches();
> + if (rc) {
> + ecryptfs_printk(0, KERN_EMERG, "Failure occured while "
> + "attempting to create caches [CREATED: %x]."
> + "Now freeing caches.\n",
> + ecryptfs_allocated_caches);
> + ecryptfs_free_kmem_caches();
> + return -ENOMEM;
> + }
> + ecryptfs_printk(1, KERN_NOTICE, "Registering eCryptfs\n");
> + return register_filesystem(&ecryptfs_fs_type);

register_filesystem() can fail in which case youre leaking all the
slab caches here.

Pekka

2005-11-19 10:50:17

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 6/12: eCryptfs] Superblock operations

On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> +/**
> + * This is called through iput_final().
> + * This is function will replace generic_drop_inode. The end result of which
> + * is we are skipping the check in inode->i_nlink, which we do not use.
> + */
> +static void ecryptfs_drop_inode(struct inode *inode) {
> + generic_delete_inode(inode);
> +}

Please drop this useless wrapper and introduce it when it actually
does something.

Pekka

2005-11-19 10:53:06

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 7/12: eCryptfs] File operations

On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> +/**
> + * Opens the file specified by inode.
> + *
> + * @param inode inode speciying file to open
> + * @param file Structure to return filled in
> + * @return Zero on success; non-zero otherwise
> + */

General comment: please use the kerneldoc format properly or don't use
kerneldoc at all.

Pekka

2005-11-20 15:34:44

by Anton Altaparmakov

[permalink] [raw]
Subject: Re: [PATCH 4/12: eCryptfs] Main module functions

On Sat, 19 Nov 2005, Pekka Enberg wrote:
> On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> > + BUG();
> > + err = -EINVAL;
> > + goto out;
>
> Why do you want to BUG() and then handle the situation?

Because you can define BUG() to nothing (on embedded builds for example)
and then you would be screwed if you don't handle the error gracefully.
You should never assume something does not return, except perhaps a
panic() although someone might even get rid of that one day...

Best regards,

Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/

2005-11-20 19:12:28

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 4/12: eCryptfs] Main module functions

Hi Anton,

On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> > > + BUG();
> > > + err = -EINVAL;
> > > + goto out;

On Sat, 19 Nov 2005, Pekka Enberg wrote:
> > Why do you want to BUG() and then handle the situation?

On Sun, 2005-11-20 at 15:34 +0000, Anton Altaparmakov wrote:
> Because you can define BUG() to nothing (on embedded builds for example)
> and then you would be screwed if you don't handle the error gracefully.
> You should never assume something does not return, except perhaps a
> panic() although someone might even get rid of that one day...

You have a point but in this case, I don't understand why they don't
just handle it gracefully since they clearly can do so. Also, I was
under the impression that people who disable BUG() are knowingly taking
the risk...

Pekka

2005-11-21 15:50:44

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 5/12: eCryptfs] Header declarations

On 11/19/05, Pekka Enberg <[email protected]> wrote:
> Hi Phillip,
>
> On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> > +struct ecryptfs_session_key {
> > +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x01
> > +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x02
> > +#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x04
> > +#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x08
> > + int32_t flags;
> > + int32_t encrypted_key_size;
> > + int32_t decrypted_key_size;
> > + uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
> > + uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
>
> s32 and u8 are preferred in the kernel.

Thanks for that, lots of little things like this were undoubtably
missed, we use u/s# in other areas. Changed and will be resent after
all comments are in.

>
> > +#define OBSERVE_ASSERTS 1
> > +#ifdef OBSERVE_ASSERTS
> > +#define ASSERT(EX) \
> > +do { \
> > + if (unlikely(!(EX))) { \
> > + printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \
> > + __FILE__, __LINE__, __FUNCTION__); \
> > + BUG(); \
> > + } \
> > +} while (0)
> > +#else
> > +#define ASSERT(EX) ;
> > +#endif /* OBSERVE_ASSERTS */
>
> Any reason why you can't just use BUG and BUG_ON()?

No reason? I think we've had this comment before, not sure if we have
had a decent reason though... let me see if I can find out why we do
it this way.

> > +
> > +/**
> > + * Halcrow: What does the kernel VFS do to ensure that there is no
> > + * contention for file->private_data?
> > + */
>
> Please elaborate?

I believe this is an old and lingering comment. While I don't know who
originated, it sounds like a question to Michael Halcrow regarding
locking of a struct file's private data... It will be removed for next
submission.

>
> > +#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \
> > + ((file)->private_data))
> > +#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data)
> > +#define ECRYPTFS_FILE_TO_LOWER(file) \
> > + ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file)
> > +#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \
> > + (ino)->u.generic_ip)
> > +#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
> > +#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode)
> > +#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \
> > + (super)->s_fs_info)
> > +#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
> > +#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \
> > + (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
> > +#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
> > +#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \
> > + (dentry)->d_fsdata)
> > +#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \
> > + (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry)
>
> These wrappers seem rather pointless and obfuscating...

I find them make the code a bit more clear. Then again, I understand
what they do and use them a lot, so I am clearly jaded. You are the
first person to comment on these wrappers (all previous comments where
regarding some debug wrappers we had in when we first submitted, I
like those ones too, but I guess my opinion doesn't carry enough
weight here ;P) There is no functional reason why these can't be
removed, but like I said, I think they make the code a lot easier to
read once you know all they do is sugar-coat a potentially long, and
potentially confusing, chain of refences, and turn that into some
phrase-like macro.

>
> > +int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
> > + int sg_size);
>
> Doesn't seem ecryptfs specific, why is it here?

Ah ha, that should read ecryptfs_virt_to_scatterlist. Again, will be
changed for next submission. While reviewing the code, there seem to
be a few more like this. I'll make sure they get updated.

>
> Pekka
> -
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

2005-11-21 15:57:08

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 6/12: eCryptfs] Superblock operations

On 11/19/05, Pekka Enberg <[email protected]> wrote:
> On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> > +/**
> > + * This is called through iput_final().
> > + * This is function will replace generic_drop_inode. The end result of which
> > + * is we are skipping the check in inode->i_nlink, which we do not use.
> > + */
> > +static void ecryptfs_drop_inode(struct inode *inode) {
> > + generic_delete_inode(inode);
> > +}
>
> Please drop this useless wrapper and introduce it when it actually
> does something.

It does do something. By providing this function, we over-ride the
default flow of execution.

If we did not provide this function, the flow would be the following:

iput_final -> generic_drop_inode -> generic_delete_inode (or
generic_forget_inode).

However, since we do not care about the i_nlink value, which
generic_drop_inode checks in order to call generic_delete_inode, we
simply circumvent the check by redirecting the flow thusly:

iput_final -> ecryptfs_drop_inode -> generic_delete_inode

I don't see a problem with doing that, but perhaps there is? Please
elaborate if so.

>
> Pekka
> -
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

2005-11-21 15:58:34

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 7/12: eCryptfs] File operations

On 11/19/05, Pekka Enberg <[email protected]> wrote:
> On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> > +/**
> > + * Opens the file specified by inode.
> > + *
> > + * @param inode inode speciying file to open
> > + * @param file Structure to return filled in
> > + * @return Zero on success; non-zero otherwise
> > + */
>
> General comment: please use the kerneldoc format properly or don't use
> kerneldoc at all.

OK, this will be fixed for the next submission.

>
> Pekka
> -
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

2005-11-21 16:02:00

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 6/12: eCryptfs] Superblock operations

Hi,

On 11/19/05, Pekka Enberg <[email protected]> wrote:
> > > +/**
> > > + * This is called through iput_final().
> > > + * This is function will replace generic_drop_inode. The end result of which
> > > + * is we are skipping the check in inode->i_nlink, which we do not use.
> > > + */
> > > +static void ecryptfs_drop_inode(struct inode *inode) {
> > > + generic_delete_inode(inode);
> > > +}
> >
> > Please drop this useless wrapper and introduce it when it actually
> > does something.

On Mon, 2005-11-21 at 09:57 -0600, Michael Thompson wrote:
> I don't see a problem with doing that, but perhaps there is? Please
> elaborate if so.

You can set ecryptfs_sops->drop_inode to generic_delete_inode directly,
no?

Pekka

2005-11-21 16:10:45

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 4/12: eCryptfs] Main module functions

On 11/19/05, Pekka Enberg <[email protected]> wrote:
> On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> > +int ecryptfs_verbosity = 1;
> > +
> > +module_param(ecryptfs_verbosity, int, 0);
> > +MODULE_PARM_DESC(ecryptfs_verbosity,
> > + "Initial verbosity level (0 or 1; defaults to "
> > + "0, which is Quiet)");
> > +
> > +void __ecryptfs_printk(int verb, const char *fmt, ...)
> > +{
> > + va_list args;
> > + va_start(args, fmt);
> > + if ((ecryptfs_verbosity >= verb) && printk_ratelimit())
> > + vprintk(fmt, args);
> > + va_end(args);
> > +}
> > +
>
> [snip]
>
> > +int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
> > + struct super_block *sb, int flag)
> > +{
> > + struct inode *lower_inode;
> > + int err = 0;
> > + struct inode *inode;
> > +
> > + ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], "
> > + "lower_dentry->d_name.name = [%s], dentry = "
> > + "[%p], dentry->d_name.name = [%s], sb = [%p]; "
> > + "flag = [%.4x]; lower_dentry->d_count "
> > + "= [%d]; dentry->d_count = [%d]\n", lower_dentry,
> > + lower_dentry->d_name.name, dentry, dentry->d_name.name,
> > + sb, flag, atomic_read(&lower_dentry->d_count),
> > + atomic_read(&dentry->d_count));
>
> Could you use KERN_DEBUG instead and drop ecryptfs_printk()?

I don't follow with what you mean. We use ecryptfs_printk for 2 reasons:
1) Debugging. There is (or atleast at one time was) a large amount of
ecryptfs_printk's scattered around during the development process.
Obviously, much has been removed because we don't need them much
anymore. However, the normal printk functionality doesn't, afaik,
provide function & line number by default. ecryptfs_printk inserts it
automatically for easy of use.

2) Verbosity switch. The intent, eventually atleast, is to be able to
toggle the value of the verbosity level of ecryptfs at run time (this
isn't implemented at the moment)... or atleast that's my plan :P Its
not my project so I have to get "approval" etc. The 0th argument is
the verbosity in which this message applies (1 being just informative,
0 being critical).


>
> > + if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
> > + ECRYPTFS_INODE_TO_LOWER(inode) = igrab(lower_inode);
> > + /* If we are still NULL at this point, igrab failed.
> > + * We are _NOT_ supposed to be failing here */
> > + if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
>
> If you're worried about assignment, why not use the normal idiom:
>
> if (!p) {
> ...
> }

No good reason. Is there a reason to change this? (other than
consistancy, which I agree with, so I'll change it)

>
> > + BUG();
> > + err = -EINVAL;
> > + goto out;
>
> Why do you want to BUG() and then handle the situation?

Simply, we don't. Atleast, not in production code. This was in there
to be a clear catch if we were ever to fall into this case. A remnant
of development I am afraid.

>
> > +kmem_cache_t *ecryptfs_sb_info_cache;
>
> We have struct kmem_cache now so please consider using it instead.

Noted. Changed.

>
> > +/* This provides a means of backing out cache creations out of the kernel
> > + * so that we can elegantly fail should we run out of memory.
> > + */
> > +#define ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE 0x0001
> > +#define ECRYPTFS_AUTH_TOK_PKT_SET_CACHE 0x0002
> > +#define ECRYPTFS_AUTH_TOK_REQUEST_CACHE 0x0004
> > +#define ECRYPTFS_AUTH_TOK_REQUEST_BLOB_CACHE 0x0008
> > +#define ECRYPTFS_FILE_INFO_CACHE 0x0010
> > +#define ECRYPTFS_DENTRY_INFO_CACHE 0x0020
> > +#define ECRYPTFS_INODE_INFO_CACHE 0x0040
> > +#define ECRYPTFS_SB_INFO_CACHE 0x0080
> > +#define ECRYPTFS_HEADER_CACHE_0 0x0100
> > +#define ECRYPTFS_HEADER_CACHE_1 0x0200
> > +#define ECRYPTFS_HEADER_CACHE_2 0x0400
> > +#define ECRYPTFS_LOWER_PAGE_CACHE 0x0800
> > +#define ECRYPTFS_CACHE_CREATION_SUCCESS 0x0FF1
>
> A better way would be to either (1) make ecryptfs_init_kmem_caches()
> clean up after itself on error using gotos or (2) keep caches NULL
> before initialization and check for that in
> ecryptfs_free_kmem_caches().

Only problem with cleanp gotos is it would make the function
needlessly larger than it already is. I do like the idea of the NULL
thing better, will have a go at it.

>
> > +
> > +static short ecryptfs_allocated_caches;
> > +
> > +/**
> > + * @return Zero on success; non-zero otherwise
> > + *
> > + * Sets ecryptfs_allocated_caches with flags so that we can
> > + * free created caches should we run out of memory during
> > + * creation period.
> > + *
> > + * The overhead for doing this is offset by the fact that we
> > + * only do this once, and that should there be insufficient
> > + * memory, then we can elegantly fail, and not leave extra
> > + * caches around, or worse, panic the kernel trying to free
> > + * something that's not there.
> > + */
> > +static int ecryptfs_init_kmem_caches(void)
> > +{
> > + int rc = 0;
> > +
> > + ecryptfs_auth_tok_list_item_cache =
> > + kmem_cache_create("ecryptfs_auth_tok_list_item",
> > + sizeof(struct ecryptfs_auth_tok_list_item),
> > + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> > + if (ecryptfs_auth_tok_list_item_cache)
> > + rc |= ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_auth_tok_list_item "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_file_info_cache =
> > + kmem_cache_create("ecryptfs_file_cache",
> > + sizeof(struct ecryptfs_file_info),
> > + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> > + if (ecryptfs_file_info_cache)
> > + rc |= ECRYPTFS_FILE_INFO_CACHE;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_file_cache "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_dentry_info_cache =
> > + kmem_cache_create("ecryptfs_dentry_cache",
> > + sizeof(struct ecryptfs_dentry_info),
> > + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> > + if (ecryptfs_dentry_info_cache)
> > + rc |= ECRYPTFS_DENTRY_INFO_CACHE;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_dentry_cache "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_inode_info_cache =
> > + kmem_cache_create("ecryptfs_inode_cache",
> > + sizeof(struct ecryptfs_inode_info), 0,
> > + SLAB_HWCACHE_ALIGN, inode_info_init_once, NULL);
> > + if (ecryptfs_inode_info_cache)
> > + rc |= ECRYPTFS_INODE_INFO_CACHE;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_inode_cache "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_sb_info_cache =
> > + kmem_cache_create("ecryptfs_sb_cache",
> > + sizeof(struct ecryptfs_sb_info),
> > + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> > + if (ecryptfs_sb_info_cache)
> > + rc |= ECRYPTFS_SB_INFO_CACHE;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_sb_cache "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_header_cache_0 =
> > + kmem_cache_create("ecryptfs_headers_0", PAGE_CACHE_SIZE,
> > + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> > + if (ecryptfs_header_cache_0)
> > + rc |= ECRYPTFS_HEADER_CACHE_0;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_0 "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_header_cache_1 =
> > + kmem_cache_create("ecryptfs_headers_1", PAGE_CACHE_SIZE,
> > + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> > + if (ecryptfs_header_cache_1)
> > + rc |= ECRYPTFS_HEADER_CACHE_1;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_1 "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_header_cache_2 =
> > + kmem_cache_create("ecryptfs_headers_2", PAGE_CACHE_SIZE,
> > + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> > + if (ecryptfs_header_cache_2)
> > + rc |= ECRYPTFS_HEADER_CACHE_2;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_2 "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_lower_page_cache =
> > + kmem_cache_create("ecryptfs_lower_page_cache", PAGE_CACHE_SIZE,
> > + 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
> > + if (ecryptfs_lower_page_cache)
> > + rc |= ECRYPTFS_LOWER_PAGE_CACHE;
> > + else
> > + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_lower_page_cache "
> > + "kmem_cache_create failed\n");
> > +
> > + ecryptfs_allocated_caches = rc;
> > + rc = ECRYPTFS_CACHE_CREATION_SUCCESS ^ rc;
> > + return rc;
> > +}
> > +
> > +/**
> > + * @return Zero on success; non-zero otherwise
> > + */
> > +static int ecryptfs_free_kmem_caches(void)
> > +{
> > + int rc = 0;
> > + int err;
> > +
> > + if (ecryptfs_allocated_caches & ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE) {
> > + rc = kmem_cache_destroy(ecryptfs_auth_tok_list_item_cache);
> > + if (rc)
> > + ecryptfs_printk(0, KERN_WARNING,
> > + "Not all ecryptfs_auth_tok_"
> > + "list_item_cache structures were "
> > + "freed\n");
> > + }
> > + if (ecryptfs_allocated_caches & ECRYPTFS_FILE_INFO_CACHE) {
> > + err = kmem_cache_destroy(ecryptfs_file_info_cache);
> > + if (err)
> > + ecryptfs_printk(0, KERN_WARNING,
> > + "Not all ecryptfs_file_info_"
> > + "cache regions were freed\n");
> > + rc |= err;
> > + }
> > + if (ecryptfs_allocated_caches & ECRYPTFS_DENTRY_INFO_CACHE) {
> > + err = kmem_cache_destroy(ecryptfs_dentry_info_cache);
> > + if (err)
> > + ecryptfs_printk(0, KERN_WARNING,
> > + "Not all ecryptfs_dentry_info_"
> > + "cache regions were freed\n");
> > + rc |= err;
> > + }
> > + if (ecryptfs_allocated_caches & ECRYPTFS_INODE_INFO_CACHE) {
> > + err = kmem_cache_destroy(ecryptfs_inode_info_cache);
> > + if (err)
> > + ecryptfs_printk(0, KERN_WARNING,
> > + "Not all ecryptfs_inode_info_"
> > + "cache regions were freed\n");
> > + rc |= err;
> > + }
> > + if (ecryptfs_allocated_caches & ECRYPTFS_SB_INFO_CACHE) {
> > + err = kmem_cache_destroy(ecryptfs_sb_info_cache);
> > + if (err)
> > + ecryptfs_printk(0, KERN_WARNING,
> > + "Not all ecryptfs_sb_info_"
> > + "cache regions were freed\n");
> > + rc |= err;
> > + }
> > + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_0) {
> > + err = kmem_cache_destroy(ecryptfs_header_cache_0);
> > + if (err)
> > + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
> > + "header_cache_0 regions were freed\n");
> > + rc |= err;
> > + }
> > + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_1) {
> > + err = kmem_cache_destroy(ecryptfs_header_cache_1);
> > + if (err)
> > + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
> > + "header_cache_1 regions were freed\n");
> > + rc |= err;
> > + }
> > + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_2) {
> > + err = kmem_cache_destroy(ecryptfs_header_cache_2);
> > + if (err)
> > + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
> > + "header_cache_2 regions were freed\n");
> > + rc |= err;
> > + }
> > + if (ecryptfs_allocated_caches & ECRYPTFS_LOWER_PAGE_CACHE) {
> > + err = kmem_cache_destroy(ecryptfs_lower_page_cache);
> > + if (err)
> > + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
> > + "lower_page_cache regions were "
> > + "freed\n");
> > + rc |= err;
> > + }
> > + return rc;
> > +}
> > +
> > +static int __init init_ecryptfs_fs(void)
> > +{
> > + int rc;
> > +
> > + rc = ecryptfs_init_kmem_caches();
> > + if (rc) {
> > + ecryptfs_printk(0, KERN_EMERG, "Failure occured while "
> > + "attempting to create caches [CREATED: %x]."
> > + "Now freeing caches.\n",
> > + ecryptfs_allocated_caches);
> > + ecryptfs_free_kmem_caches();
> > + return -ENOMEM;
> > + }
> > + ecryptfs_printk(1, KERN_NOTICE, "Registering eCryptfs\n");
> > + return register_filesystem(&ecryptfs_fs_type);
>
> register_filesystem() can fail in which case youre leaking all the
> slab caches here.

I wasn't aware it _could_ fail, thanks for that.

>
> Pekka
> -
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

2005-11-21 16:12:54

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 4/12: eCryptfs] Main module functions

On 11/21/05, Michael Thompson <[email protected]> wrote:
> On 11/19/05, Pekka Enberg <[email protected]> wrote:
> > On 11/19/05, Phillip Hellewell <[email protected]> wrote:
> > > + return register_filesystem(&ecryptfs_fs_type);
> >
> > register_filesystem() can fail in which case youre leaking all the
> > slab caches here.
>
> I wasn't aware it _could_ fail, thanks for that.

Well, let me rephrase: I never bothered to check. Its obvious it can
fail, I'm just embarresed that I never bothered to find out... again,
thanks for catching that.

>
> >
> > Pekka
> > -
> > To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> Michael C. Thompson <[email protected]>
> Software-Engineer, IBM LTC Security
>


--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

2005-11-21 16:13:59

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 6/12: eCryptfs] Superblock operations

On 11/21/05, Pekka Enberg <[email protected]> wrote:
> Hi,
>
> On 11/19/05, Pekka Enberg <[email protected]> wrote:
> > > > +/**
> > > > + * This is called through iput_final().
> > > > + * This is function will replace generic_drop_inode. The end result of which
> > > > + * is we are skipping the check in inode->i_nlink, which we do not use.
> > > > + */
> > > > +static void ecryptfs_drop_inode(struct inode *inode) {
> > > > + generic_delete_inode(inode);
> > > > +}
> > >
> > > Please drop this useless wrapper and introduce it when it actually
> > > does something.
>
> On Mon, 2005-11-21 at 09:57 -0600, Michael Thompson wrote:
> > I don't see a problem with doing that, but perhaps there is? Please
> > elaborate if so.
>
> You can set ecryptfs_sops->drop_inode to generic_delete_inode directly,
> no?

Yes, I do believe I could do that and save a function call. My mind is
wobbely today.

>
> Pekka
>
>


--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

2005-11-21 16:15:55

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 6/12: eCryptfs] Superblock operations

On 11/21/05, Michael Thompson <[email protected]> wrote:
> On 11/21/05, Pekka Enberg <[email protected]> wrote:
> > Hi,
> >
> > On 11/19/05, Pekka Enberg <[email protected]> wrote:
> > > > > +/**
> > > > > + * This is called through iput_final().
> > > > > + * This is function will replace generic_drop_inode. The end result of which
> > > > > + * is we are skipping the check in inode->i_nlink, which we do not use.
> > > > > + */
> > > > > +static void ecryptfs_drop_inode(struct inode *inode) {
> > > > > + generic_delete_inode(inode);
> > > > > +}
> > > >
> > > > Please drop this useless wrapper and introduce it when it actually
> > > > does something.
> >
> > On Mon, 2005-11-21 at 09:57 -0600, Michael Thompson wrote:
> > > I don't see a problem with doing that, but perhaps there is? Please
> > > elaborate if so.
> >
> > You can set ecryptfs_sops->drop_inode to generic_delete_inode directly,
> > no?
>
> Yes, I do believe I could do that and save a function call. My mind is
> wobbely today.

Very wobbley, can't even spell right. Is this an acceptable solution?
I didn't even bother to ask that ;)

>
> >
> > Pekka
> >
> >
>
>
> --
> Michael C. Thompson <[email protected]>
> Software-Engineer, IBM LTC Security
>


--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

2005-11-21 16:20:36

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 6/12: eCryptfs] Superblock operations

Hi Michael,

On 11/21/05, Pekka Enberg <[email protected]> wrote:
> > > You can set ecryptfs_sops->drop_inode to generic_delete_inode directly,
> > > no?

On Mon, 2005-11-21 at 09:57 -0600, Michael Thompson wrote:
> > Yes, I do believe I could do that and save a function call. My mind is
> > wobbely today.
>
> Very wobbley, can't even spell right. Is this an acceptable solution?
> I didn't even bother to ask that ;)

Yes, that's what I was suggesting.

Pekka

2005-11-21 16:21:33

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 4/12: eCryptfs] Main module functions

Hi,

On 11/19/05, Pekka Enberg <[email protected]> wrote:
> > > + ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], "
> > > + "lower_dentry->d_name.name = [%s], dentry = "
> > > + "[%p], dentry->d_name.name = [%s], sb = [%p]; "
> > > + "flag = [%.4x]; lower_dentry->d_count "
> > > + "= [%d]; dentry->d_count = [%d]\n", lower_dentry,
> > > + lower_dentry->d_name.name, dentry, dentry->d_name.name,
> > > + sb, flag, atomic_read(&lower_dentry->d_count),
> > > + atomic_read(&dentry->d_count));
> >
> > Could you use KERN_DEBUG instead and drop ecryptfs_printk()?

On Mon, 2005-11-21 at 10:10 -0600, Michael Thompson wrote:
> I don't follow with what you mean. We use ecryptfs_printk for 2 reasons:

Okay, let me spell it out for you: I think it is damn ugly :-)

> 1) Debugging. There is (or atleast at one time was) a large amount of
> ecryptfs_printk's scattered around during the development process.
> Obviously, much has been removed because we don't need them much
> anymore. However, the normal printk functionality doesn't, afaik,
> provide function & line number by default. ecryptfs_printk inserts it
> automatically for easy of use.

Like you said, not all debugging aids should be merged. I don't think
function and line number is enough an argument to justify putting your
own printk() in. That's my thinking anyway.

> 2) Verbosity switch. The intent, eventually atleast, is to be able to
> toggle the value of the verbosity level of ecryptfs at run time (this
> isn't implemented at the moment)... or atleast that's my plan :P Its
> not my project so I have to get "approval" etc. The 0th argument is
> the verbosity in which this message applies (1 being just informative,
> 0 being critical).

Do you really need it? Wouldn't it be better if you figured out which
debug printk() statements make sense and leave those in with KERN_DEBUG?

Pekka

2005-11-21 20:32:29

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 0/12: eCryptfs] eCryptfs version 0.1

On Fri, Nov 18, 2005 at 10:16:59PM -0800, Andrew Morton wrote:
> If Linux is going to offer a feature like this then people have to
> be able to trust it to be quite secure. What we don't want to
> happen is to distribute it for six months and then be buried in
> reports of vulnerabilites from cryptography specialists. Even worse
> if those reports lead to exploits.

I think you brought up two categories of potential security
vulnerabilities. The first has to do with the theoretical security of
the algorithms -- do the encrypted files really have the attribute
such that decrypting the files without the proper key is
computationally infeasible? This is the job for the cryptographers to
confront.

The other category has to do with ``exploits''; I assume you are
talking about -- for instance -- malicious files that are able to
circumvent the intended behavior of the code. Such vulnerabilities may
coerce the filesystem to dump the secret key out to an insecure
location. This is an extension of the general ``correctness'' problem
that can be an issue with any code. I would say that this is the job
of the engineers to help prevent. It basically involves verification
that eCryptfs is handling all of its memory correctly (i.e., via data
and control flow analysis).

> So I guess what I'm asking is: has this code been reviewed by crypto
> experts? Bearing in mind that it'll be world-class crypto people
> who will try to poke holes in it.

Let's start with the first issue: the theoretical algorithmic security
of eCryptfs. The good news is that there is no new crypto in the
filesystem; we use only existing algorithms. Of course, these
algorithms need to be used correctly in order for them to be of any
use at all (key management typically being the weak point, in my
opinion, which is why the roadmap for eCryptfs incorporates robust
policy-based key management). I give a list of assumptions with regard
to these algorithms further down.

I have thought long and hard about how I am putting it all together
and have presented the design (and my justifications) to several
audiences over the last two years, including crypto researchers, and
nobody has complained (thus far). For those just tuning in, my paper
starts on page 209 of the first half of the OLS 2005 proceedings:

http://www.linuxsymposium.org/2005/linuxsymposium_procv1.pdf

I am actually trying to publicize the code as much as possible at this
point for that exact reason -- so more security and cryptography
experts can analyze our current eCryptfs implementation to criticize
it. I am interested in knowing (a) how well my design holds up and (b)
how well my implementation reflects my design.

The code correctness problem necessitates a significant engineering
effort, given that eCryptfs occupies several thousand lines of new
code. We have been practicing due diligence in having frequent code
reviews over the last year, and we have used various static source
code analysis tools to help out in pinpointing potential problems. We
have a structured internal process of peer review before committing
any changes to the code base. We are making every effort to use our
development resources to ferret out correctness flaws, but community
involvement would help quicken the pace. By submitting our code for
inclusion in the kernel, we are now asking for many eyes to help make
any remaining bugs in eCryptfs shallow.

Keep in mind that eCryptfs is one piece that takes part in confronting
a very large and complex general security problem. Up to this point, I
have been focusing my efforts on the scenario wherein the physical
media is separated from its trusted host environment; my primary goal
is that the sensitive data on the storage media, when separated from
the control of the trusted host, will be unreadable without the proper
key to decrypt it.

There are many other things that should be done to ensure access
control-type security on the trusted host system itself; that sort of
security must be coordinated with the distro's to provide all the
desirable attributes of a comprehensively secure deployment.
Basically, I am stating that, assuming perfect correctness, the
problem of an access control violation on the host causing weakly- or
non-encrypted data to be written to disk, or causing the key to be
compromised, is outside the scope of eCryptfs's specific function,
which focuses on data-at-rest outside the control of the host. It is
the ``assuming perfect correctness'' part that needs verification at
this point. :-)

The express job of eCryptfs is to make sure that if the media is
separated from the trusted host environment, then the confidentiality
of the data on the media is enforced cryptographically. In later
updates to eCryptfs, we will also include integrity verification to
make sure that unauthorized modifications to the underlying data were
not made outside the control of the trusted host. This is easily
accomplished with another standard cryptographic construct; HMAC
values can simply be written side-by-side with IV's on a per-extent
basis.

That said, I am certainly not opposed to exploring how eCryptfs can
coordinate with other access control mechanisms (i.e., SE Linux) to
help prevent access control violations on the host. Another example
that springs to mind, in terms of cooperation with MAC, is an SE
Linux-eCryptfs policy that states, ``Any file object written to
location ABC must be encrypted with crypto context XYZ.''

Now, by way of plugging together existing crypto constructs, I suppose
it is time to state my assumptions:

1) The kernel get_random_bytes() call always provides a random number
suitable for use as a symmetric Session Key.

2) The symmetric ciphers in the kernel are cryptographically secure
according to current industry norms and expectations in relation to
key length (if not, then eCryptfs will need to include a
``known good'' cipher list to compare the requested cipher name
against).

3) It is still the case that the Initialization Vector does not need
to be secret and that Cipher Block Chaining (CBC) mode is secure,
when used over 4096-byte chunks of data. The one thing I would like
feedback on is my IV permutation code; I am wondering if it will be
necessary to enforce a Feistel network-like property in each
permutation of the IV as we walk down the extents, or if a simple
increment (or other naive transformation) of the IV is sufficient
for each successive extent. Comments?

4) The header file format in RFC2440 (OpenPGP) is secure for the
purpose of encrypting and storing the encrypted Session Key.

5) Knowledge of the number of bytes in the decrypted version of the
file is not significantly helpful in cryptanalysis.

6) Users will select cryptographically strong passphrases (note that
later versions of eCryptfs will use public key and TPM technology).

This list of assumptions will likely grow as eCryptfs undergoes more
scrutiny. Of these assumptions, I would say that assumption 6 is the
weakest; in version 0.1 of eCryptfs, we do allow for a salt to be
specified by the user, but that reduces to basically adding more
characters to the passphrase (we could have hacked it and included a
randomly-generated salt in a mount-associated structure, but that
would violate the per-file crypto context strategy of the filesystem
in general). Future versions of eCryptfs, which will have per-file
passphrase application rather than mount-wide, will use more
intelligent salting to help mitigate the weak passphrase
problem. Ultimately, though, the ``right'' solution is to depend on
more than just a passphrase for protection, and that is part of the
roadmap for eCryptfs.

There are, of course, some things that users could do that might make
their files easier to attack by way of cryptanalysis. Layering
encryption is one such thing (i.e., copying an already encrypted file
with cipher XYZ into an eCryptfs mount point that is using that same
cipher (and key)). This is a general problem w/ all crypto
applications though.

Basically, I am asserting that, as long as my assumptions are valid,
eCryptfs accomplishes its goal of cryptographically enforcing
confidentiality of data at rest on secondary storage. There are plenty
of things that users can (and, unfortunately, will) do to compromise
the cryptographic security afforded by any application, and so that
problem deserves at least as much attention as the cryptographic
properties of the filesystem.

That is one of the reasons why I have been emphasizing making the key
management process as transparent to the user as possible, so that
good practices can be enacted while being as unobtrusive to the user
as possible, so that the crypto is not circumvented or disabled by the
user in frustration (this is likely the biggest security risk in any
crypto app deployment). Once policy support is in place, nothing is
stopping the user from doing something silly like selecting DES with a
key size of 56 bits, and eCryptfs will need to have a way of warning
the user when the security of the filesystem may be in question
(sanity-checking policies against ``best practices'' standards).

That said, there are things that the eCryptfs userspace mount wrapper
could do to help the user out in the version 0.1 release, like do a
standard weak-passphrase check against the passphrase given to the
mount wrapper (actually, now that I think about it, I see no reason
why the eCryptfs mount wrapper could not use PAM). My recommendation
right now is that the user generate a cryptographically random string
of printable bytes for the passphrase, having a sufficient length so
as to be, given the base of valid passphrase characters, sufficient in
size given the keyspace for the cipher being used. In other words, the
problem of brute-forcing the Session Key and brute-forcing the
passphrase should be computationally equivalent.

In later versions of eCryptfs, there will be public key and TPM
support. Then, once policy support is in place and eCryptfs is being
shipped as part of the distro (making proper use of a TPM), the whole
package will be even more secure and transparent to the user
experience. One step at a time, though. I would like to get the kinks
worked out of the basic passphrase-based modes of operation
(especially correct and stable VFS-related operation) before adding
more complexity to eCryptfs.

Thanks,
Mike

2005-11-21 21:41:48

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 0/12: eCryptfs] eCryptfs version 0.1

On Mon, 21 Nov 2005, Michael Halcrow wrote:

> I think you brought up two categories of potential security
> vulnerabilities.

> The first has to do with the theoretical security of
> the algorithms -- do the encrypted files really have the attribute
> such that decrypting the files without the proper key is
> computationally infeasible? This is the job for the cryptographers to
> confront.
>
> The other category has to do with ``exploits''; I assume you are
> talking about -- for instance -- malicious files that are able to
> circumvent the intended behavior of the code. Such vulnerabilities may
> coerce the filesystem to dump the secret key out to an insecure
> location. This is an extension of the general ``correctness'' problem
> that can be an issue with any code. I would say that this is the job
> of the engineers to help prevent. It basically involves verification
> that eCryptfs is handling all of its memory correctly (i.e., via data
> and control flow analysis).

There's a third important category: the design of the _system_.

(Which you end up discussing somewhat further in the email).

It would be great to have a document which describes the design of the
system and includes a comprehensive security analysis.


- James
--
James Morris
<[email protected]>

2005-11-21 22:11:32

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 0/12: eCryptfs] eCryptfs version 0.1

On 11/21/05, James Morris <[email protected]> wrote:
> On Mon, 21 Nov 2005, Michael Halcrow wrote:
>
> > I think you brought up two categories of potential security
> > vulnerabilities.
>
> > The first has to do with the theoretical security of
> > the algorithms -- do the encrypted files really have the attribute
> > such that decrypting the files without the proper key is
> > computationally infeasible? This is the job for the cryptographers to
> > confront.
> >
> > The other category has to do with ``exploits''; I assume you are
> > talking about -- for instance -- malicious files that are able to
> > circumvent the intended behavior of the code. Such vulnerabilities may
> > coerce the filesystem to dump the secret key out to an insecure
> > location. This is an extension of the general ``correctness'' problem
> > that can be an issue with any code. I would say that this is the job
> > of the engineers to help prevent. It basically involves verification
> > that eCryptfs is handling all of its memory correctly (i.e., via data
> > and control flow analysis).
>
> There's a third important category: the design of the _system_.
>
> (Which you end up discussing somewhat further in the email).
>
> It would be great to have a document which describes the design of the
> system and includes a comprehensive security analysis.

Kernel programmers making documentation? You must be joking! (Side
joke... someone somewhere, which I have now forgetten, made a similar
comment).

For documentation, nothing formal exists, and while we were planning
on having some, it sounds like it might be a good thing to start
sooner than later.

I (or someone else) will get back to you when we figure out how we
want to approach this.

>
>
> - James
> --
> James Morris
> <[email protected]>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security