[PATCH] eCryptfs: open-code flag checking and manipulation
[deliverable/linux.git] / fs / ecryptfs / crypto.c
index ed35a9712fa15ae8a2c87e5a0c5405b3a162a835..6ac630625b70821274c4b8c753fbc90bc1daaec1 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 1997-2004 Erez Zadok
  * Copyright (C) 2001-2004 Stony Brook University
- * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Copyright (C) 2004-2007 International Business Machines Corp.
  *   Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
  *             Michael C. Thompson <mcthomps@us.ibm.com>
  *
@@ -94,25 +94,53 @@ static int ecryptfs_calculate_md5(char *dst,
                                  struct ecryptfs_crypt_stat *crypt_stat,
                                  char *src, int len)
 {
-       int rc = 0;
        struct scatterlist sg;
+       struct hash_desc desc = {
+               .tfm = crypt_stat->hash_tfm,
+               .flags = CRYPTO_TFM_REQ_MAY_SLEEP
+       };
+       int rc = 0;
 
-       mutex_lock(&crypt_stat->cs_md5_tfm_mutex);
+       mutex_lock(&crypt_stat->cs_hash_tfm_mutex);
        sg_init_one(&sg, (u8 *)src, len);
-       if (!crypt_stat->md5_tfm) {
-               crypt_stat->md5_tfm =
-                       crypto_alloc_tfm("md5", CRYPTO_TFM_REQ_MAY_SLEEP);
-               if (!crypt_stat->md5_tfm) {
-                       rc = -ENOMEM;
+       if (!desc.tfm) {
+               desc.tfm = crypto_alloc_hash(ECRYPTFS_DEFAULT_HASH, 0,
+                                            CRYPTO_ALG_ASYNC);
+               if (IS_ERR(desc.tfm)) {
+                       rc = PTR_ERR(desc.tfm);
                        ecryptfs_printk(KERN_ERR, "Error attempting to "
-                                       "allocate crypto context\n");
+                                       "allocate crypto context; rc = [%d]\n",
+                                       rc);
                        goto out;
                }
+               crypt_stat->hash_tfm = desc.tfm;
+       }
+       crypto_hash_init(&desc);
+       crypto_hash_update(&desc, &sg, len);
+       crypto_hash_final(&desc, dst);
+       mutex_unlock(&crypt_stat->cs_hash_tfm_mutex);
+out:
+       return rc;
+}
+
+int ecryptfs_crypto_api_algify_cipher_name(char **algified_name,
+                                          char *cipher_name,
+                                          char *chaining_modifier)
+{
+       int cipher_name_len = strlen(cipher_name);
+       int chaining_modifier_len = strlen(chaining_modifier);
+       int algified_name_len;
+       int rc;
+
+       algified_name_len = (chaining_modifier_len + cipher_name_len + 3);
+       (*algified_name) = kmalloc(algified_name_len, GFP_KERNEL);
+       if (!(*algified_name)) {
+               rc = -ENOMEM;
+               goto out;
        }
-       crypto_digest_init(crypt_stat->md5_tfm);
-       crypto_digest_update(crypt_stat->md5_tfm, &sg, 1);
-       crypto_digest_final(crypt_stat->md5_tfm, dst);
-       mutex_unlock(&crypt_stat->cs_md5_tfm_mutex);
+       snprintf((*algified_name), algified_name_len, "%s(%s)",
+                chaining_modifier, cipher_name);
+       rc = 0;
 out:
        return rc;
 }
@@ -178,8 +206,8 @@ ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
        memset((void *)crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat));
        mutex_init(&crypt_stat->cs_mutex);
        mutex_init(&crypt_stat->cs_tfm_mutex);
-       mutex_init(&crypt_stat->cs_md5_tfm_mutex);
-       ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_STRUCT_INITIALIZED);
+       mutex_init(&crypt_stat->cs_hash_tfm_mutex);
+       crypt_stat->flags |= ECRYPTFS_STRUCT_INITIALIZED;
 }
 
 /**
@@ -191,9 +219,9 @@ ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
 void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
 {
        if (crypt_stat->tfm)
-               crypto_free_tfm(crypt_stat->tfm);
-       if (crypt_stat->md5_tfm)
-               crypto_free_tfm(crypt_stat->md5_tfm);
+               crypto_free_blkcipher(crypt_stat->tfm);
+       if (crypt_stat->hash_tfm)
+               crypto_free_hash(crypt_stat->hash_tfm);
        memset(crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat));
 }
 
@@ -203,7 +231,7 @@ void ecryptfs_destruct_mount_crypt_stat(
        if (mount_crypt_stat->global_auth_tok_key)
                key_put(mount_crypt_stat->global_auth_tok_key);
        if (mount_crypt_stat->global_key_tfm)
-               crypto_free_tfm(mount_crypt_stat->global_key_tfm);
+               crypto_free_blkcipher(mount_crypt_stat->global_key_tfm);
        memset(mount_crypt_stat, 0, sizeof(struct ecryptfs_mount_crypt_stat));
 }
 
@@ -269,11 +297,15 @@ static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
                               struct scatterlist *src_sg, int size,
                               unsigned char *iv)
 {
+       struct blkcipher_desc desc = {
+               .tfm = crypt_stat->tfm,
+               .info = iv,
+               .flags = CRYPTO_TFM_REQ_MAY_SLEEP
+       };
        int rc = 0;
 
        BUG_ON(!crypt_stat || !crypt_stat->tfm
-              || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
-                                      ECRYPTFS_STRUCT_INITIALIZED));
+              || !(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED));
        if (unlikely(ecryptfs_verbosity > 0)) {
                ecryptfs_printk(KERN_DEBUG, "Key size [%d]; key:\n",
                                crypt_stat->key_size);
@@ -282,8 +314,8 @@ static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
        }
        /* Consider doing this once, when the file is opened */
        mutex_lock(&crypt_stat->cs_tfm_mutex);
-       rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key,
-                                 crypt_stat->key_size);
+       rc = crypto_blkcipher_setkey(crypt_stat->tfm, crypt_stat->key,
+                                    crypt_stat->key_size);
        if (rc) {
                ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n",
                                rc);
@@ -292,7 +324,7 @@ static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
                goto out;
        }
        ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size);
-       crypto_cipher_encrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size, iv);
+       crypto_blkcipher_encrypt_iv(&desc, dest_sg, src_sg, size);
        mutex_unlock(&crypt_stat->cs_tfm_mutex);
 out:
        return rc;
@@ -396,10 +428,10 @@ static int ecryptfs_read_in_page(struct ecryptfs_page_crypt_context *ctx,
                        goto out;
                }
        } else {
-               rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL,
-                                                     lower_inode,
-                                                     lower_page_idx);
-               if (rc) {
+               *lower_page = grab_cache_page(lower_inode->i_mapping,
+                                             lower_page_idx);
+               if (!(*lower_page)) {
+                       rc = -EINVAL;
                        ecryptfs_printk(
                                KERN_ERR, "Error attempting to grab and map "
                                "lower page with index [0x%.16x]; rc = [%d]\n",
@@ -452,7 +484,7 @@ int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx)
        lower_inode = ecryptfs_inode_to_lower(ctx->page->mapping->host);
        inode_info = ecryptfs_inode_to_private(ctx->page->mapping->host);
        crypt_stat = &inode_info->crypt_stat;
-       if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) {
+       if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
                rc = ecryptfs_copy_page_to_lower(ctx->page, lower_inode,
                                                 ctx->param.lower_file);
                if (rc)
@@ -584,7 +616,7 @@ int ecryptfs_decrypt_page(struct file *file, struct page *page)
        crypt_stat = &(ecryptfs_inode_to_private(
                               page->mapping->host)->crypt_stat);
        lower_inode = ecryptfs_inode_to_lower(page->mapping->host);
-       if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) {
+       if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
                rc = ecryptfs_do_readpage(file, page, page->index);
                if (rc)
                        ecryptfs_printk(KERN_ERR, "Error attempting to copy "
@@ -595,7 +627,7 @@ int ecryptfs_decrypt_page(struct file *file, struct page *page)
        num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size;
        base_extent = (page->index * num_extents_per_page);
        lower_page_virt = kmem_cache_alloc(ecryptfs_lower_page_cache,
-                                          SLAB_KERNEL);
+                                          GFP_KERNEL);
        if (!lower_page_virt) {
                rc = -ENOMEM;
                ecryptfs_printk(KERN_ERR, "Error getting page for encrypted "
@@ -675,12 +707,17 @@ static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
                               struct scatterlist *src_sg, int size,
                               unsigned char *iv)
 {
+       struct blkcipher_desc desc = {
+               .tfm = crypt_stat->tfm,
+               .info = iv,
+               .flags = CRYPTO_TFM_REQ_MAY_SLEEP
+       };
        int rc = 0;
 
        /* Consider doing this once, when the file is opened */
        mutex_lock(&crypt_stat->cs_tfm_mutex);
-       rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key,
-                                 crypt_stat->key_size);
+       rc = crypto_blkcipher_setkey(crypt_stat->tfm, crypt_stat->key,
+                                    crypt_stat->key_size);
        if (rc) {
                ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n",
                                rc);
@@ -689,8 +726,7 @@ static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
                goto out;
        }
        ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size);
-       rc = crypto_cipher_decrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size,
-                                     iv);
+       rc = crypto_blkcipher_decrypt_iv(&desc, dest_sg, src_sg, size);
        mutex_unlock(&crypt_stat->cs_tfm_mutex);
        if (rc) {
                ecryptfs_printk(KERN_ERR, "Error decrypting; rc = [%d]\n",
@@ -759,6 +795,7 @@ ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
  */
 int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat)
 {
+       char *full_alg_name;
        int rc = -EINVAL;
 
        if (!crypt_stat->cipher) {
@@ -775,16 +812,23 @@ int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat)
                goto out;
        }
        mutex_lock(&crypt_stat->cs_tfm_mutex);
-       crypt_stat->tfm = crypto_alloc_tfm(crypt_stat->cipher,
-                                          ECRYPTFS_DEFAULT_CHAINING_MODE
-                                          | CRYPTO_TFM_REQ_WEAK_KEY);
-       mutex_unlock(&crypt_stat->cs_tfm_mutex);
-       if (!crypt_stat->tfm) {
+       rc = ecryptfs_crypto_api_algify_cipher_name(&full_alg_name,
+                                                   crypt_stat->cipher, "cbc");
+       if (rc)
+               goto out;
+       crypt_stat->tfm = crypto_alloc_blkcipher(full_alg_name, 0,
+                                                CRYPTO_ALG_ASYNC);
+       kfree(full_alg_name);
+       if (IS_ERR(crypt_stat->tfm)) {
+               rc = PTR_ERR(crypt_stat->tfm);
                ecryptfs_printk(KERN_ERR, "cryptfs: init_crypt_ctx(): "
                                "Error initializing cipher [%s]\n",
                                crypt_stat->cipher);
+               mutex_unlock(&crypt_stat->cs_tfm_mutex);
                goto out;
        }
+       crypto_blkcipher_set_flags(crypt_stat->tfm, CRYPTO_TFM_REQ_WEAK_KEY);
+       mutex_unlock(&crypt_stat->cs_tfm_mutex);
        rc = 0;
 out:
        return rc;
@@ -818,7 +862,10 @@ void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat)
                        ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE;
        } else
                crypt_stat->header_extent_size = PAGE_CACHE_SIZE;
-       crypt_stat->num_header_extents_at_front = 1;
+       if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
+               crypt_stat->num_header_extents_at_front = 0;
+       else
+               crypt_stat->num_header_extents_at_front = 1;
 }
 
 /**
@@ -834,7 +881,7 @@ int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat)
 
        BUG_ON(crypt_stat->iv_bytes > MD5_DIGEST_SIZE);
        BUG_ON(crypt_stat->iv_bytes <= 0);
-       if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID)) {
+       if (!(crypt_stat->flags & ECRYPTFS_KEY_VALID)) {
                rc = -EINVAL;
                ecryptfs_printk(KERN_WARNING, "Session key not valid; "
                                "cannot generate root IV\n");
@@ -851,8 +898,7 @@ int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat)
 out:
        if (rc) {
                memset(crypt_stat->root_iv, 0, crypt_stat->iv_bytes);
-               ECRYPTFS_SET_FLAG(crypt_stat->flags,
-                                 ECRYPTFS_SECURITY_WARNING);
+               crypt_stat->flags |= ECRYPTFS_SECURITY_WARNING;
        }
        return rc;
 }
@@ -860,7 +906,7 @@ out:
 static void ecryptfs_generate_new_key(struct ecryptfs_crypt_stat *crypt_stat)
 {
        get_random_bytes(crypt_stat->key, crypt_stat->key_size);
-       ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+       crypt_stat->flags |= ECRYPTFS_KEY_VALID;
        ecryptfs_compute_root_iv(crypt_stat);
        if (unlikely(ecryptfs_verbosity > 0)) {
                ecryptfs_printk(KERN_DEBUG, "Generated new session key:\n");
@@ -869,6 +915,22 @@ static void ecryptfs_generate_new_key(struct ecryptfs_crypt_stat *crypt_stat)
        }
 }
 
+/**
+ * ecryptfs_copy_mount_wide_flags_to_inode_flags
+ *
+ * This function propagates the mount-wide flags to individual inode
+ * flags.
+ */
+static void ecryptfs_copy_mount_wide_flags_to_inode_flags(
+       struct ecryptfs_crypt_stat *crypt_stat,
+       struct ecryptfs_mount_crypt_stat *mount_crypt_stat)
+{
+       if (mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED)
+               crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
+       if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)
+               crypt_stat->flags |= ECRYPTFS_VIEW_AS_ENCRYPTED;
+}
+
 /**
  * ecryptfs_set_default_crypt_stat_vals
  * @crypt_stat
@@ -879,10 +941,12 @@ static void ecryptfs_set_default_crypt_stat_vals(
        struct ecryptfs_crypt_stat *crypt_stat,
        struct ecryptfs_mount_crypt_stat *mount_crypt_stat)
 {
+       ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat,
+                                                     mount_crypt_stat);
        ecryptfs_set_default_sizes(crypt_stat);
        strcpy(crypt_stat->cipher, ECRYPTFS_DEFAULT_CIPHER);
        crypt_stat->key_size = ECRYPTFS_DEFAULT_KEY_BYTES;
-       ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+       crypt_stat->flags &= ~(ECRYPTFS_KEY_VALID);
        crypt_stat->file_version = ECRYPTFS_FILE_VERSION;
        crypt_stat->mount_crypt_stat = mount_crypt_stat;
 }
@@ -922,8 +986,10 @@ int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry)
        if (mount_crypt_stat->global_auth_tok) {
                ecryptfs_printk(KERN_DEBUG, "Initializing context for new "
                                "file using mount_crypt_stat\n");
-               ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
-               ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+               crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
+               crypt_stat->flags |= ECRYPTFS_KEY_VALID;
+               ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat,
+                                                             mount_crypt_stat);
                memcpy(crypt_stat->keysigs[crypt_stat->num_keysigs++],
                       mount_crypt_stat->global_auth_tok_sig,
                       ECRYPTFS_SIG_SIZE_HEX);
@@ -956,7 +1022,7 @@ int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry)
  *
  * Returns one if marker found; zero if not found
  */
-int contains_ecryptfs_marker(char *data)
+static int contains_ecryptfs_marker(char *data)
 {
        u32 m_1, m_2;
 
@@ -982,7 +1048,8 @@ struct ecryptfs_flag_map_elem {
 /* Add support for additional flags by adding elements here. */
 static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = {
        {0x00000001, ECRYPTFS_ENABLE_HMAC},
-       {0x00000002, ECRYPTFS_ENCRYPTED}
+       {0x00000002, ECRYPTFS_ENCRYPTED},
+       {0x00000004, ECRYPTFS_METADATA_IN_XATTR}
 };
 
 /**
@@ -1005,11 +1072,9 @@ static int ecryptfs_process_flags(struct ecryptfs_crypt_stat *crypt_stat,
        for (i = 0; i < ((sizeof(ecryptfs_flag_map)
                          / sizeof(struct ecryptfs_flag_map_elem))); i++)
                if (flags & ecryptfs_flag_map[i].file_flag) {
-                       ECRYPTFS_SET_FLAG(crypt_stat->flags,
-                                         ecryptfs_flag_map[i].local_flag);
+                       crypt_stat->flags |= ecryptfs_flag_map[i].local_flag;
                } else
-                       ECRYPTFS_CLEAR_FLAG(crypt_stat->flags,
-                                           ecryptfs_flag_map[i].local_flag);
+                       crypt_stat->flags &= ~(ecryptfs_flag_map[i].local_flag);
        /* Version is in top 8 bits of the 32-bit flag vector */
        crypt_stat->file_version = ((flags >> 24) & 0xFF);
        (*bytes_read) = 4;
@@ -1046,8 +1111,7 @@ write_ecryptfs_flags(char *page_virt, struct ecryptfs_crypt_stat *crypt_stat,
 
        for (i = 0; i < ((sizeof(ecryptfs_flag_map)
                          / sizeof(struct ecryptfs_flag_map_elem))); i++)
-               if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
-                                       ecryptfs_flag_map[i].local_flag))
+               if (crypt_stat->flags & ecryptfs_flag_map[i].local_flag)
                        flags |= ecryptfs_flag_map[i].file_flag;
        /* Version is in top 8 bits of the 32-bit flag vector */
        flags |= ((((u8)crypt_stat->file_version) << 24) & 0xFF000000);
@@ -1142,39 +1206,55 @@ int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code)
  *
  * Returns zero on success; non-zero otherwise
  */
-int ecryptfs_read_header_region(char *data, struct dentry *dentry,
-                               struct vfsmount *mnt)
+static int ecryptfs_read_header_region(char *data, struct dentry *dentry,
+                                      struct vfsmount *mnt)
 {
-       struct file *file;
+       struct file *lower_file;
        mm_segment_t oldfs;
        int rc;
 
-       mnt = mntget(mnt);
-       file = dentry_open(dentry, mnt, O_RDONLY);
-       if (IS_ERR(file)) {
-               ecryptfs_printk(KERN_DEBUG, "Error opening file to "
-                               "read header region\n");
-               mntput(mnt);
-               rc = PTR_ERR(file);
+       if ((rc = ecryptfs_open_lower_file(&lower_file, dentry, mnt,
+                                          O_RDONLY))) {
+               printk(KERN_ERR
+                      "Error opening lower_file to read header region\n");
                goto out;
        }
-       file->f_pos = 0;
+       lower_file->f_pos = 0;
        oldfs = get_fs();
        set_fs(get_ds());
        /* For releases 0.1 and 0.2, all of the header information
         * fits in the first data extent-sized region. */
-       rc = file->f_op->read(file, (char __user *)data,
-                             ECRYPTFS_DEFAULT_EXTENT_SIZE, &file->f_pos);
+       rc = lower_file->f_op->read(lower_file, (char __user *)data,
+                             ECRYPTFS_DEFAULT_EXTENT_SIZE, &lower_file->f_pos);
        set_fs(oldfs);
-       fput(file);
+       if ((rc = ecryptfs_close_lower_file(lower_file))) {
+               printk(KERN_ERR "Error closing lower_file\n");
+               goto out;
+       }
        rc = 0;
 out:
        return rc;
 }
 
-static void
-write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat,
-                     size_t *written)
+int ecryptfs_read_and_validate_header_region(char *data, struct dentry *dentry,
+                                            struct vfsmount *mnt)
+{
+       int rc;
+
+       rc = ecryptfs_read_header_region(data, dentry, mnt);
+       if (rc)
+               goto out;
+       if (!contains_ecryptfs_marker(data + ECRYPTFS_FILE_SIZE_BYTES))
+               rc = -EINVAL;
+out:
+       return rc;
+}
+
+
+void
+ecryptfs_write_header_metadata(char *virt,
+                              struct ecryptfs_crypt_stat *crypt_stat,
+                              size_t *written)
 {
        u32 header_extent_size;
        u16 num_header_extents_at_front;
@@ -1223,9 +1303,9 @@ struct kmem_cache *ecryptfs_header_cache_2;
  *
  * Returns zero on success
  */
-int ecryptfs_write_headers_virt(char *page_virt,
-                               struct ecryptfs_crypt_stat *crypt_stat,
-                               struct dentry *ecryptfs_dentry)
+static int ecryptfs_write_headers_virt(char *page_virt, size_t *size,
+                                      struct ecryptfs_crypt_stat *crypt_stat,
+                                      struct dentry *ecryptfs_dentry)
 {
        int rc;
        size_t written;
@@ -1236,7 +1316,8 @@ int ecryptfs_write_headers_virt(char *page_virt,
        offset += written;
        write_ecryptfs_flags((page_virt + offset), crypt_stat, &written);
        offset += written;
-       write_header_metadata((page_virt + offset), crypt_stat, &written);
+       ecryptfs_write_header_metadata((page_virt + offset), crypt_stat,
+                                      &written);
        offset += written;
        rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stat,
                                              ecryptfs_dentry, &written,
@@ -1244,11 +1325,70 @@ int ecryptfs_write_headers_virt(char *page_virt,
        if (rc)
                ecryptfs_printk(KERN_WARNING, "Error generating key packet "
                                "set; rc = [%d]\n", rc);
+       if (size) {
+               offset += written;
+               *size = offset;
+       }
+       return rc;
+}
+
+static int ecryptfs_write_metadata_to_contents(struct ecryptfs_crypt_stat *crypt_stat,
+                                              struct file *lower_file,
+                                              char *page_virt)
+{
+       mm_segment_t oldfs;
+       int current_header_page;
+       int header_pages;
+       ssize_t size;
+       int rc = 0;
+
+       lower_file->f_pos = 0;
+       oldfs = get_fs();
+       set_fs(get_ds());
+       size = vfs_write(lower_file, (char __user *)page_virt, PAGE_CACHE_SIZE,
+                        &lower_file->f_pos);
+       if (size < 0) {
+               rc = (int)size;
+               printk(KERN_ERR "Error attempting to write lower page; "
+                      "rc = [%d]\n", rc);
+               set_fs(oldfs);
+               goto out;
+       }
+       header_pages = ((crypt_stat->header_extent_size
+                        * crypt_stat->num_header_extents_at_front)
+                       / PAGE_CACHE_SIZE);
+       memset(page_virt, 0, PAGE_CACHE_SIZE);
+       current_header_page = 1;
+       while (current_header_page < header_pages) {
+               size = vfs_write(lower_file, (char __user *)page_virt,
+                                PAGE_CACHE_SIZE, &lower_file->f_pos);
+               if (size < 0) {
+                       rc = (int)size;
+                       printk(KERN_ERR "Error attempting to write lower page; "
+                              "rc = [%d]\n", rc);
+                       set_fs(oldfs);
+                       goto out;
+               }
+               current_header_page++;
+       }
+       set_fs(oldfs);
+out:
+       return rc;
+}
+
+static int ecryptfs_write_metadata_to_xattr(struct dentry *ecryptfs_dentry,
+                                           struct ecryptfs_crypt_stat *crypt_stat,
+                                           char *page_virt, size_t size)
+{
+       int rc;
+
+       rc = ecryptfs_setxattr(ecryptfs_dentry, ECRYPTFS_XATTR_NAME, page_virt,
+                              size, 0);
        return rc;
 }
 
 /**
- * ecryptfs_write_headers
+ * ecryptfs_write_metadata
  * @lower_file: The lower file struct, which was returned from dentry_open
  *
  * Write the file headers out.  This will likely involve a userspace
@@ -1259,22 +1399,18 @@ int ecryptfs_write_headers_virt(char *page_virt,
  *
  * Returns zero on success; non-zero on error
  */
-int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
-                          struct file *lower_file)
+int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry,
+                           struct file *lower_file)
 {
-       mm_segment_t oldfs;
        struct ecryptfs_crypt_stat *crypt_stat;
        char *page_virt;
-       int current_header_page;
-       int header_pages;
+       size_t size;
        int rc = 0;
 
        crypt_stat = &ecryptfs_inode_to_private(
                ecryptfs_dentry->d_inode)->crypt_stat;
-       if (likely(ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
-                                      ECRYPTFS_ENCRYPTED))) {
-               if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
-                                        ECRYPTFS_KEY_VALID)) {
+       if (likely(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
+               if (!(crypt_stat->flags & ECRYPTFS_KEY_VALID)) {
                        ecryptfs_printk(KERN_DEBUG, "Key is "
                                        "invalid; bailing out\n");
                        rc = -EINVAL;
@@ -1287,54 +1423,42 @@ int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
                goto out;
        }
        /* Released in this function */
-       page_virt = kmem_cache_alloc(ecryptfs_header_cache_0, SLAB_USER);
+       page_virt = kmem_cache_zalloc(ecryptfs_header_cache_0, GFP_USER);
        if (!page_virt) {
                ecryptfs_printk(KERN_ERR, "Out of memory\n");
                rc = -ENOMEM;
                goto out;
        }
-       memset(page_virt, 0, PAGE_CACHE_SIZE);
-       rc = ecryptfs_write_headers_virt(page_virt, crypt_stat,
-                                        ecryptfs_dentry);
+       rc = ecryptfs_write_headers_virt(page_virt, &size, crypt_stat,
+                                        ecryptfs_dentry);
        if (unlikely(rc)) {
                ecryptfs_printk(KERN_ERR, "Error whilst writing headers\n");
                memset(page_virt, 0, PAGE_CACHE_SIZE);
                goto out_free;
        }
-       ecryptfs_printk(KERN_DEBUG,
-                       "Writing key packet set to underlying file\n");
-       lower_file->f_pos = 0;
-       oldfs = get_fs();
-       set_fs(get_ds());
-       ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->"
-                       "write() w/ header page; lower_file->f_pos = "
-                       "[0x%.16x]\n", lower_file->f_pos);
-       lower_file->f_op->write(lower_file, (char __user *)page_virt,
-                               PAGE_CACHE_SIZE, &lower_file->f_pos);
-       header_pages = ((crypt_stat->header_extent_size
-                        * crypt_stat->num_header_extents_at_front)
-                       / PAGE_CACHE_SIZE);
-       memset(page_virt, 0, PAGE_CACHE_SIZE);
-       current_header_page = 1;
-       while (current_header_page < header_pages) {
-               ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->"
-                               "write() w/ zero'd page; lower_file->f_pos = "
-                               "[0x%.16x]\n", lower_file->f_pos);
-               lower_file->f_op->write(lower_file, (char __user *)page_virt,
-                                       PAGE_CACHE_SIZE, &lower_file->f_pos);
-               current_header_page++;
+       if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
+               rc = ecryptfs_write_metadata_to_xattr(ecryptfs_dentry,
+                                                     crypt_stat, page_virt,
+                                                     size);
+       else
+               rc = ecryptfs_write_metadata_to_contents(crypt_stat, lower_file,
+                                                        page_virt);
+       if (rc) {
+               printk(KERN_ERR "Error writing metadata out to lower file; "
+                      "rc = [%d]\n", rc);
+               goto out_free;
        }
-       set_fs(oldfs);
-       ecryptfs_printk(KERN_DEBUG,
-                       "Done writing key packet set to underlying file.\n");
 out_free:
        kmem_cache_free(ecryptfs_header_cache_0, page_virt);
 out:
        return rc;
 }
 
+#define ECRYPTFS_DONT_VALIDATE_HEADER_SIZE 0
+#define ECRYPTFS_VALIDATE_HEADER_SIZE 1
 static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat,
-                                char *virt, int *bytes_read)
+                                char *virt, int *bytes_read,
+                                int validate_header_size)
 {
        int rc = 0;
        u32 header_extent_size;
@@ -1349,9 +1473,10 @@ static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat,
        crypt_stat->num_header_extents_at_front =
                (int)num_header_extents_at_front;
        (*bytes_read) = 6;
-       if ((crypt_stat->header_extent_size
-            * crypt_stat->num_header_extents_at_front)
-           < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) {
+       if ((validate_header_size == ECRYPTFS_VALIDATE_HEADER_SIZE)
+           && ((crypt_stat->header_extent_size
+                * crypt_stat->num_header_extents_at_front)
+               < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE)) {
                rc = -EINVAL;
                ecryptfs_printk(KERN_WARNING, "Invalid header extent size: "
                                "[%d]\n", crypt_stat->header_extent_size);
@@ -1382,7 +1507,8 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
  */
 static int ecryptfs_read_headers_virt(char *page_virt,
                                      struct ecryptfs_crypt_stat *crypt_stat,
-                                     struct dentry *ecryptfs_dentry)
+                                     struct dentry *ecryptfs_dentry,
+                                     int validate_header_size)
 {
        int rc = 0;
        int offset;
@@ -1416,7 +1542,7 @@ static int ecryptfs_read_headers_virt(char *page_virt,
        offset += bytes_read;
        if (crypt_stat->file_version >= 1) {
                rc = parse_header_metadata(crypt_stat, (page_virt + offset),
-                                          &bytes_read);
+                                          &bytes_read, validate_header_size);
                if (rc) {
                        ecryptfs_printk(KERN_WARNING, "Error reading header "
                                        "metadata; rc = [%d]\n", rc);
@@ -1431,12 +1557,60 @@ out:
 }
 
 /**
- * ecryptfs_read_headers
+ * ecryptfs_read_xattr_region
+ *
+ * Attempts to read the crypto metadata from the extended attribute
+ * region of the lower file.
+ */
+int ecryptfs_read_xattr_region(char *page_virt, struct dentry *ecryptfs_dentry)
+{
+       ssize_t size;
+       int rc = 0;
+
+       size = ecryptfs_getxattr(ecryptfs_dentry, ECRYPTFS_XATTR_NAME,
+                                page_virt, ECRYPTFS_DEFAULT_EXTENT_SIZE);
+       if (size < 0) {
+               printk(KERN_DEBUG "Error attempting to read the [%s] "
+                      "xattr from the lower file; return value = [%zd]\n",
+                      ECRYPTFS_XATTR_NAME, size);
+               rc = -EINVAL;
+               goto out;
+       }
+out:
+       return rc;
+}
+
+int ecryptfs_read_and_validate_xattr_region(char *page_virt,
+                                           struct dentry *ecryptfs_dentry)
+{
+       int rc;
+
+       rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_dentry);
+       if (rc)
+               goto out;
+       if (!contains_ecryptfs_marker(page_virt + ECRYPTFS_FILE_SIZE_BYTES)) {
+               printk(KERN_WARNING "Valid data found in [%s] xattr, but "
+                       "the marker is invalid\n", ECRYPTFS_XATTR_NAME);
+               rc = -EINVAL;
+       }
+out:
+       return rc;
+}
+
+/**
+ * ecryptfs_read_metadata
+ *
+ * Common entry point for reading file metadata. From here, we could
+ * retrieve the header information from the header region of the file,
+ * the xattr region of the file, or some other repostory that is
+ * stored separately from the file itself. The current implementation
+ * supports retrieving the metadata information from the file contents
+ * and from the xattr region.
  *
  * Returns zero if valid headers found and parsed; non-zero otherwise
  */
-int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
-                         struct file *lower_file)
+int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry,
+                          struct file *lower_file)
 {
        int rc = 0;
        char *page_virt = NULL;
@@ -1444,9 +1618,14 @@ int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
        ssize_t bytes_read;
        struct ecryptfs_crypt_stat *crypt_stat =
            &ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat;
+       struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+               &ecryptfs_superblock_to_private(
+                       ecryptfs_dentry->d_sb)->mount_crypt_stat;
 
+       ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat,
+                                                     mount_crypt_stat);
        /* Read the first page from the underlying file */
-       page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, SLAB_USER);
+       page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, GFP_USER);
        if (!page_virt) {
                rc = -ENOMEM;
                ecryptfs_printk(KERN_ERR, "Unable to allocate page_virt\n");
@@ -1465,11 +1644,36 @@ int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
                goto out;
        }
        rc = ecryptfs_read_headers_virt(page_virt, crypt_stat,
-                                       ecryptfs_dentry);
+                                       ecryptfs_dentry,
+                                       ECRYPTFS_VALIDATE_HEADER_SIZE);
        if (rc) {
-               ecryptfs_printk(KERN_DEBUG, "Valid eCryptfs headers not "
-                               "found\n");
-               rc = -EINVAL;
+               rc = ecryptfs_read_xattr_region(page_virt,
+                                               ecryptfs_dentry);
+               if (rc) {
+                       printk(KERN_DEBUG "Valid eCryptfs headers not found in "
+                              "file header region or xattr region\n");
+                       rc = -EINVAL;
+                       goto out;
+               }
+               rc = ecryptfs_read_headers_virt(page_virt, crypt_stat,
+                                               ecryptfs_dentry,
+                                               ECRYPTFS_DONT_VALIDATE_HEADER_SIZE);
+               if (rc) {
+                       printk(KERN_DEBUG "Valid eCryptfs headers not found in "
+                              "file xattr region either\n");
+                       rc = -EINVAL;
+               }
+               if (crypt_stat->mount_crypt_stat->flags
+                   & ECRYPTFS_XATTR_METADATA_ENABLED) {
+                       crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
+               } else {
+                       printk(KERN_WARNING "Attempt to access file with "
+                              "crypto metadata only in the extended attribute "
+                              "region, but eCryptfs was mounted without "
+                              "xattr support enabled. eCryptfs will not treat "
+                              "this like an encrypted file.\n");
+                       rc = -EINVAL;
+               }
        }
 out:
        if (page_virt) {
@@ -1573,84 +1777,52 @@ out:
 
 /**
  * ecryptfs_process_cipher - Perform cipher initialization.
- * @tfm: Crypto context set by this function
  * @key_tfm: Crypto context for key material, set by this function
- * @cipher_name: Name of the cipher.
- * @key_size: Size of the key in bytes.
+ * @cipher_name: Name of the cipher
+ * @key_size: Size of the key in bytes
  *
  * Returns zero on success. Any crypto_tfm structs allocated here
  * should be released by other functions, such as on a superblock put
  * event, regardless of whether this function succeeds for fails.
  */
 int
-ecryptfs_process_cipher(struct crypto_tfm **tfm, struct crypto_tfm **key_tfm,
-                       char *cipher_name, size_t key_size)
+ecryptfs_process_cipher(struct crypto_blkcipher **key_tfm, char *cipher_name,
+                       size_t *key_size)
 {
        char dummy_key[ECRYPTFS_MAX_KEY_BYTES];
+       char *full_alg_name;
        int rc;
 
-       *tfm = *key_tfm = NULL;
-       if (key_size > ECRYPTFS_MAX_KEY_BYTES) {
+       *key_tfm = NULL;
+       if (*key_size > ECRYPTFS_MAX_KEY_BYTES) {
                rc = -EINVAL;
                printk(KERN_ERR "Requested key size is [%Zd] bytes; maximum "
-                      "allowable is [%d]\n", key_size, ECRYPTFS_MAX_KEY_BYTES);
+                     "allowable is [%d]\n", *key_size, ECRYPTFS_MAX_KEY_BYTES);
                goto out;
        }
-       *tfm = crypto_alloc_tfm(cipher_name, (ECRYPTFS_DEFAULT_CHAINING_MODE
-                                             | CRYPTO_TFM_REQ_WEAK_KEY));
-       if (!(*tfm)) {
-               rc = -EINVAL;
-               printk(KERN_ERR "Unable to allocate crypto cipher with name "
-                      "[%s]\n", cipher_name);
+       rc = ecryptfs_crypto_api_algify_cipher_name(&full_alg_name, cipher_name,
+                                                   "ecb");
+       if (rc)
                goto out;
-       }
-       *key_tfm = crypto_alloc_tfm(cipher_name, CRYPTO_TFM_REQ_WEAK_KEY);
-       if (!(*key_tfm)) {
-               rc = -EINVAL;
+       *key_tfm = crypto_alloc_blkcipher(full_alg_name, 0, CRYPTO_ALG_ASYNC);
+       kfree(full_alg_name);
+       if (IS_ERR(*key_tfm)) {
+               rc = PTR_ERR(*key_tfm);
                printk(KERN_ERR "Unable to allocate crypto cipher with name "
-                      "[%s]\n", cipher_name);
-               goto out;
-       }
-       if (key_size < crypto_tfm_alg_min_keysize(*tfm)) {
-               rc = -EINVAL;
-               printk(KERN_ERR "Request key size is [%Zd]; minimum key size "
-                      "supported by cipher [%s] is [%d]\n", key_size,
-                      cipher_name, crypto_tfm_alg_min_keysize(*tfm));
-               goto out;
-       }
-       if (key_size < crypto_tfm_alg_min_keysize(*key_tfm)) {
-               rc = -EINVAL;
-               printk(KERN_ERR "Request key size is [%Zd]; minimum key size "
-                      "supported by cipher [%s] is [%d]\n", key_size,
-                      cipher_name, crypto_tfm_alg_min_keysize(*key_tfm));
-               goto out;
-       }
-       if (key_size > crypto_tfm_alg_max_keysize(*tfm)) {
-               rc = -EINVAL;
-               printk(KERN_ERR "Request key size is [%Zd]; maximum key size "
-                      "supported by cipher [%s] is [%d]\n", key_size,
-                      cipher_name, crypto_tfm_alg_min_keysize(*tfm));
-               goto out;
-       }
-       if (key_size > crypto_tfm_alg_max_keysize(*key_tfm)) {
-               rc = -EINVAL;
-               printk(KERN_ERR "Request key size is [%Zd]; maximum key size "
-                      "supported by cipher [%s] is [%d]\n", key_size,
-                      cipher_name, crypto_tfm_alg_min_keysize(*key_tfm));
+                      "[%s]; rc = [%d]\n", cipher_name, rc);
                goto out;
        }
-       get_random_bytes(dummy_key, key_size);
-       rc = crypto_cipher_setkey(*tfm, dummy_key, key_size);
-       if (rc) {
-               printk(KERN_ERR "Error attempting to set key of size [%Zd] for "
-                      "cipher [%s]; rc = [%d]\n", key_size, cipher_name, rc);
-               rc = -EINVAL;
-               goto out;
+       crypto_blkcipher_set_flags(*key_tfm, CRYPTO_TFM_REQ_WEAK_KEY);
+       if (*key_size == 0) {
+               struct blkcipher_alg *alg = crypto_blkcipher_alg(*key_tfm);
+
+               *key_size = alg->max_keysize;
        }
-       rc = crypto_cipher_setkey(*key_tfm, dummy_key, key_size);
+       get_random_bytes(dummy_key, *key_size);
+       rc = crypto_blkcipher_setkey(*key_tfm, dummy_key, *key_size);
        if (rc) {
                printk(KERN_ERR "Error attempting to set key of size [%Zd] for "
-                      "cipher [%s]; rc = [%d]\n", key_size, cipher_name, rc);
+                      "cipher [%s]; rc = [%d]\n", *key_size, cipher_name, rc);
                rc = -EINVAL;
                goto out;
        }
This page took 0.1132 seconds and 5 git commands to generate.